OpenTransportUDP.lisp - clic - Clic is an command line interactive client for gopher written in Common LISP
 (HTM) git clone git://bitreich.org/clic/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/clic/
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Tags
 (DIR) README
 (DIR) LICENSE
       ---
       OpenTransportUDP.lisp (6398B)
       ---
            1 ;;;-*-Mode: LISP; Package: CCL -*-
            2 ;;
            3 ;;; OpenTransportUDP.lisp
            4 ;;; Copyright 2012 Chun Tian (binghe) <binghe.lisp@gmail.com>
            5 
            6 ;;; UDP extension to OpenTransport.lisp (with some TCP patches)
            7 
            8 (in-package "CCL")
            9 
           10 (eval-when (:compile-toplevel :load-toplevel :execute)
           11   (require :opentransport))
           12 
           13 ;; MCL Issue 28: Passive TCP streams should be able to listen to the loopback interface
           14 ;; see http://code.google.com/p/mcl/issues/detail?id=28 for details
           15 
           16 (defparameter *passive-interface-address* NIL
           17   "Address to use for passive connections - optionally bind to loopback address while opening a tcp stream")
           18 
           19 (advise local-interface-ip-address
           20   (or *passive-interface-address* (:do-it))
           21   :when :around :name 'override-local-interface-ip-address)
           22 
           23 ;; MCL Issue 29: Passive TCP connections on OS assigned ports
           24 ;; see http://code.google.com/p/mcl/issues/detail?id=29 for details
           25 (advise ot-conn-tcp-passive-connect
           26   (destructuring-bind (conn port &optional (allow-reuse t)) arglist
           27     (declare (ignore allow-reuse))
           28     (if (eql port #$kOTAnyInetAddress)
           29         ;; Avoids registering a proxy for port 0 but instead registers one for the true port:
           30         (multiple-value-bind (proxy result)
           31             (let* ((*opentransport-class-proxies* NIL) ; makes ot-find-proxy return NIL
           32                    (result (:do-it)) ;; pushes onto *opentransport-class-proxies*
           33                    (proxy (prog1
           34                               (pop *opentransport-class-proxies*)
           35                             (assert (not *opentransport-class-proxies*))))
           36                    (context (cdr proxy))
           37                    (tmpconn (make-ot-conn :context context 
           38                                           :endpoint (pref context :ot-context.ref)))
           39                    (localaddress (ot-conn-tcp-get-addresses tmpconn)))
           40               (declare (dynamic-extent tmpconn))
           41               ;; replace original set in body of function
           42               (setf (ot-conn-local-address conn) localaddress)
           43               (values
           44                (cons localaddress context)
           45                result))
           46           ;; need to be outside local binding of *opentransport-class-proxies* 
           47           (without-interrupts
           48               (push proxy *opentransport-class-proxies*))
           49           result)
           50         (:do-it)))
           51   :when :around :name 'ot-conn-tcp-passive-connect-any-address)
           52 
           53 (defun open-udp-socket (&key local-address local-port)
           54   (init-opentransport)
           55   (let (endpoint ; TODO: opentransport-alloc-endpoint-from-freelist
           56         (err #$kOTNoError)
           57         (configptr (ot-cloned-configuration traps::$kUDPName)))
           58     (rlet ((errP :osstatus))
           59       (setq endpoint #+carbon-compat (#_OTOpenEndpointInContext configptr 0 (%null-ptr) errP *null-ptr*)
           60                      #-carbon-compat (#_OTOpenEndpoint configptr 0 (%null-ptr) errP)
           61             err (pref errP :osstatus))
           62       (if (eql err #$kOTNoError)
           63           (let* ((context (ot-make-endpoint-context endpoint nil nil)) ; no notifier, not minimal
           64                  (conn (make-ot-conn :context context :endpoint endpoint)))
           65             (macrolet ((check-ot-error-return (error-context)
           66                          `(unless (eql (setq err (pref errP :osstatus)) #$kOTNoError)
           67                             (values (ot-error err ,error-context)))))
           68               (setf (ot-conn-bindreq conn) 
           69                     #-carbon-compat (#_OTAlloc endpoint #$T_BIND #$T_ADDR errP)
           70                     #+carbon-compat (#_OTAllocInContext endpoint #$T_BIND #$T_ADDR errP *null-ptr*)
           71                     )
           72               (check-ot-error-return :alloc)
           73               (setf (ot-conn-bindret conn) 
           74                     #-carbon-compat (#_OTAlloc endpoint #$T_BIND #$T_ADDR errP)
           75                     #+carbon-compat (#_OTAllocInContext endpoint #$T_BIND #$T_ADDR errP *null-ptr*)
           76                     )
           77               (check-ot-error-return :alloc)
           78               (setf (ot-conn-options conn) 
           79                     #-carbon-compat (#_OTAlloc endpoint #$T_OPTMGMT #$T_OPT errP)
           80                     #+carbon-compat (#_OTAllocInContext endpoint #$T_OPTMGMT #$T_OPT errP *null-ptr*)
           81                     )
           82               (check-ot-error-return :alloc))
           83             ;; BIND to local address (for UDP server)
           84             (when local-port ; local-address
           85               (let* ((host (or local-address (local-interface-ip-address)))
           86                      (port (tcp-service-port-number local-port))
           87                      (localaddress `(:tcp ,host ,port))
           88                      (bindreq (ot-conn-bindreq conn))
           89                      (bindret (ot-conn-bindret conn)))
           90                 (let* ((netbuf (pref bindreq :tbind.addr)))
           91                   (declare (dynamic-extent netbuf))
           92                   (setf (pref netbuf :tnetbuf.len) (record-length :inetaddress)
           93                         (pref bindreq :tbind.qlen) 5)       ; arbitrary qlen
           94                   (#_OTInitInetAddress (pref netbuf :tnetbuf.buf) port host)
           95                   (setf (pref context :ot-context.completed) nil)
           96                   (unless (= (setq err (#_OTBind endpoint bindreq bindret)) #$kOTNoError)
           97                     (ot-error err :bind)))
           98                 (setf (ot-conn-local-address conn) localaddress)))
           99             conn)
          100         (ot-error err :create)))))
          101 
          102 (defun make-TUnitData (endpoint)
          103   "create the send/recv buffer for UDP sockets"
          104   (let ((err #$kOTNoError))
          105     (rlet ((errP :osstatus))
          106       (macrolet ((check-ot-error-return (error-context)
          107                    `(unless (eql (setq err (pref errP :osstatus)) #$kOTNoError)
          108                       (values (ot-error err ,error-context)))))
          109         (let ((udata #-carbon-compat (#_OTAlloc endpoint #$T_UNITDATA #$T_ALL errP)
          110                      #+carbon-compat (#_OTAllocInContext endpoint #$T_UNITDATA #$T_ALL errP *null-ptr*)))
          111           (check-ot-error-return :alloc)
          112           udata)))))
          113 
          114 (defun send-message (conn data buffer size host port &optional (offset 0))
          115   ;; prepare dest address
          116   (let ((addr (pref data :tunitdata.addr)))
          117     (declare (dynamic-extent addr))
          118     (setf (pref addr :tnetbuf.len) (record-length :inetaddress))
          119     (#_OTInitInetAddress (pref addr :tnetbuf.buf) port host))
          120   ;; prepare data buffer
          121   (let* ((udata (pref data :tunitdata.udata))
          122          (outptr (pref udata :tnetbuf.buf)))
          123     (declare (dynamic-extent udata))
          124     (%copy-ivector-to-ptr buffer offset outptr 0 size)
          125     (setf (pref udata :tnetbuf.len) size))
          126   ;; send the packet
          127   (let* ((endpoint (ot-conn-endpoint conn))
          128          (result (#_OTSndUData endpoint data)))
          129     (the fixnum result)))
          130 
          131 (defun receive-message (conn data buffer length)
          132   (let* ((endpoint (ot-conn-endpoint conn))
          133          (err (#_OTRcvUData endpoint data *null-ptr*)))
          134     (if (eql err #$kOTNoError)
          135         (let* (;(addr (pref data :tunitdata.addr))
          136                (udata (pref data :tunitdata.udata))
          137                (inptr (pref udata :tnetbuf.buf))
          138                (read-bytes (pref udata :tnetbuf.len))
          139                (buffer (or buffer (make-array read-bytes :element-type '(unsigned-byte 8))))
          140                (length (or length (length buffer)))
          141                (actual-size (min read-bytes length)))
          142           (%copy-ptr-to-ivector inptr 0 buffer 0 actual-size)
          143           (values buffer
          144                   actual-size
          145                   0 0)) ; TODO: retrieve address and port
          146       (ot-error err :receive)))) ; TODO: use OTRcvUDErr instead