File: OpenTransportUDP.lisp

package info (click to toggle)
acl2 8.0dfsg-1
  • links: PTS
  • area: main
  • in suites: buster
  • size: 226,956 kB
  • sloc: lisp: 2,678,900; ansic: 6,101; perl: 5,816; xml: 3,586; cpp: 2,624; ruby: 2,576; makefile: 2,443; sh: 2,312; python: 778; yacc: 764; ml: 763; awk: 260; csh: 186; php: 171; lex: 165; tcl: 44; java: 41; asm: 23; haskell: 17
file content (146 lines) | stat: -rw-r--r-- 6,398 bytes parent folder | download | duplicates (10)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
;;;-*-Mode: LISP; Package: CCL -*-
;;
;;; OpenTransportUDP.lisp
;;; Copyright 2012 Chun Tian (binghe) <binghe.lisp@gmail.com>

;;; UDP extension to OpenTransport.lisp (with some TCP patches)

(in-package "CCL")

(eval-when (:compile-toplevel :load-toplevel :execute)
  (require :opentransport))

;; MCL Issue 28: Passive TCP streams should be able to listen to the loopback interface
;; see http://code.google.com/p/mcl/issues/detail?id=28 for details

(defparameter *passive-interface-address* NIL
  "Address to use for passive connections - optionally bind to loopback address while opening a tcp stream")

(advise local-interface-ip-address
  (or *passive-interface-address* (:do-it))
  :when :around :name 'override-local-interface-ip-address)

;; MCL Issue 29: Passive TCP connections on OS assigned ports
;; see http://code.google.com/p/mcl/issues/detail?id=29 for details
(advise ot-conn-tcp-passive-connect
  (destructuring-bind (conn port &optional (allow-reuse t)) arglist
    (declare (ignore allow-reuse))
    (if (eql port #$kOTAnyInetAddress)
	;; Avoids registering a proxy for port 0 but instead registers one for the true port:
	(multiple-value-bind (proxy result)
	    (let* ((*opentransport-class-proxies* NIL) ; makes ot-find-proxy return NIL
		   (result (:do-it)) ;; pushes onto *opentransport-class-proxies*
		   (proxy (prog1
			      (pop *opentransport-class-proxies*)
			    (assert (not *opentransport-class-proxies*))))
		   (context (cdr proxy))
		   (tmpconn (make-ot-conn :context context 
					  :endpoint (pref context :ot-context.ref)))
		   (localaddress (ot-conn-tcp-get-addresses tmpconn)))
	      (declare (dynamic-extent tmpconn))
	      ;; replace original set in body of function
	      (setf (ot-conn-local-address conn) localaddress)
	      (values
	       (cons localaddress context)
	       result))
	  ;; need to be outside local binding of *opentransport-class-proxies* 
	  (without-interrupts
	      (push proxy *opentransport-class-proxies*))
	  result)
	(:do-it)))
  :when :around :name 'ot-conn-tcp-passive-connect-any-address)

(defun open-udp-socket (&key local-address local-port)
  (init-opentransport)
  (let (endpoint ; TODO: opentransport-alloc-endpoint-from-freelist
	(err #$kOTNoError)
	(configptr (ot-cloned-configuration traps::$kUDPName)))
    (rlet ((errP :osstatus))
      (setq endpoint #+carbon-compat (#_OTOpenEndpointInContext configptr 0 (%null-ptr) errP *null-ptr*)
	             #-carbon-compat (#_OTOpenEndpoint configptr 0 (%null-ptr) errP)
	    err (pref errP :osstatus))
      (if (eql err #$kOTNoError)
	  (let* ((context (ot-make-endpoint-context endpoint nil nil)) ; no notifier, not minimal
		 (conn (make-ot-conn :context context :endpoint endpoint)))
	    (macrolet ((check-ot-error-return (error-context)
			 `(unless (eql (setq err (pref errP :osstatus)) #$kOTNoError)
			    (values (ot-error err ,error-context)))))
	      (setf (ot-conn-bindreq conn) 
		    #-carbon-compat (#_OTAlloc endpoint #$T_BIND #$T_ADDR errP)
		    #+carbon-compat (#_OTAllocInContext endpoint #$T_BIND #$T_ADDR errP *null-ptr*)
		    )
	      (check-ot-error-return :alloc)
	      (setf (ot-conn-bindret conn) 
		    #-carbon-compat (#_OTAlloc endpoint #$T_BIND #$T_ADDR errP)
		    #+carbon-compat (#_OTAllocInContext endpoint #$T_BIND #$T_ADDR errP *null-ptr*)
		    )
	      (check-ot-error-return :alloc)
	      (setf (ot-conn-options conn) 
		    #-carbon-compat (#_OTAlloc endpoint #$T_OPTMGMT #$T_OPT errP)
		    #+carbon-compat (#_OTAllocInContext endpoint #$T_OPTMGMT #$T_OPT errP *null-ptr*)
		    )
	      (check-ot-error-return :alloc))
	    ;; BIND to local address (for UDP server)
	    (when local-port ; local-address
	      (let* ((host (or local-address (local-interface-ip-address)))
		     (port (tcp-service-port-number local-port))
		     (localaddress `(:tcp ,host ,port))
		     (bindreq (ot-conn-bindreq conn))
		     (bindret (ot-conn-bindret conn)))
		(let* ((netbuf (pref bindreq :tbind.addr)))
		  (declare (dynamic-extent netbuf))
		  (setf (pref netbuf :tnetbuf.len) (record-length :inetaddress)
			(pref bindreq :tbind.qlen) 5)       ; arbitrary qlen
		  (#_OTInitInetAddress (pref netbuf :tnetbuf.buf) port host)
		  (setf (pref context :ot-context.completed) nil)
		  (unless (= (setq err (#_OTBind endpoint bindreq bindret)) #$kOTNoError)
		    (ot-error err :bind)))
		(setf (ot-conn-local-address conn) localaddress)))
	    conn)
	(ot-error err :create)))))

(defun make-TUnitData (endpoint)
  "create the send/recv buffer for UDP sockets"
  (let ((err #$kOTNoError))
    (rlet ((errP :osstatus))
      (macrolet ((check-ot-error-return (error-context)
		   `(unless (eql (setq err (pref errP :osstatus)) #$kOTNoError)
		      (values (ot-error err ,error-context)))))
	(let ((udata #-carbon-compat (#_OTAlloc endpoint #$T_UNITDATA #$T_ALL errP)
		     #+carbon-compat (#_OTAllocInContext endpoint #$T_UNITDATA #$T_ALL errP *null-ptr*)))
	  (check-ot-error-return :alloc)
	  udata)))))

(defun send-message (conn data buffer size host port &optional (offset 0))
  ;; prepare dest address
  (let ((addr (pref data :tunitdata.addr)))
    (declare (dynamic-extent addr))
    (setf (pref addr :tnetbuf.len) (record-length :inetaddress))
    (#_OTInitInetAddress (pref addr :tnetbuf.buf) port host))
  ;; prepare data buffer
  (let* ((udata (pref data :tunitdata.udata))
	 (outptr (pref udata :tnetbuf.buf)))
    (declare (dynamic-extent udata))
    (%copy-ivector-to-ptr buffer offset outptr 0 size)
    (setf (pref udata :tnetbuf.len) size))
  ;; send the packet
  (let* ((endpoint (ot-conn-endpoint conn))
	 (result (#_OTSndUData endpoint data)))
    (the fixnum result)))

(defun receive-message (conn data buffer length)
  (let* ((endpoint (ot-conn-endpoint conn))
	 (err (#_OTRcvUData endpoint data *null-ptr*)))
    (if (eql err #$kOTNoError)
	(let* (;(addr (pref data :tunitdata.addr))
	       (udata (pref data :tunitdata.udata))
	       (inptr (pref udata :tnetbuf.buf))
	       (read-bytes (pref udata :tnetbuf.len))
	       (buffer (or buffer (make-array read-bytes :element-type '(unsigned-byte 8))))
	       (length (or length (length buffer)))
	       (actual-size (min read-bytes length)))
	  (%copy-ptr-to-ivector inptr 0 buffer 0 actual-size)
	  (values buffer
		  actual-size
		  0 0)) ; TODO: retrieve address and port
      (ot-error err :receive)))) ; TODO: use OTRcvUDErr instead