tOpenTransportUDP.lisp - clic - Clic is an command line interactive client for gopher written in Common LISP
(HTM) git clone git://bitreich.org/clic/ git://hg6vgqziawt5s4dj.onion/clic/
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) Tags
(DIR) LICENSE
---
tOpenTransportUDP.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