Initial checkin of common-net.
[lisp-utils.git] / net-sb-bsd.lisp
CommitLineData
dfa6197c
FT
1(in-package :common-net)
2
3(defclass sbcl-socket (socket)
4 ((sb-socket :initarg :sb-socket :type sb-bsd-sockets:socket)))
5(defclass sbcl-listen-socket (listen-socket sbcl-socket) ())
6(defclass sbcl-stream-socket (stream-socket sbcl-socket) ())
7(defclass sbcl-datagram-socket (datagram-socket sbcl-socket) ())
8
9(defmacro with-sb-socket ((var socket) &body body)
10 (let ((success (gensym "SUCCESS")))
11 `(let ((,var ,socket)
12 (,success nil))
13 (unwind-protect
14 (multiple-value-prog1
15 (progn ,@body)
16 (setf ,success t))
17 (unless ,success
18 (sb-bsd-sockets:socket-close ,var))))))
19
20(defun map-sbcl-to-address (sk address)
21 (etypecase sk
22 (sb-bsd-sockets:inet-socket
23 (let ((host (first address))
24 (port (second address)))
25 (make-instance (ecase (sb-bsd-sockets:socket-type sk)
26 ((:stream) 'tcp-address)
27 ((:datagram) 'udp-address))
28 :host (if (every #'zerop host)
29 nil
30 (make-instance 'ipv4-address :bytes host))
31 :port port)))))
32
33(defun map-address-to-sbcl (sk address)
34 (etypecase sk
35 (sb-bsd-sockets:inet-socket
36 (etypecase address
37 (inet-port-address
38 (with-slots (host port) address
39 (list (etypecase host
40 (null #(0 0 0 0))
41 (ipv4-address (slot-value host 'bytes)))
42 port)))))
43 (sb-bsd-sockets:local-socket
44 (etypecase address
45 (local-address
46 (namestring (slot-value address 'path)))))))
47
48(defun sbcl-socket-type-and-args (address)
49 (etypecase address
50 (inet-port-address
51 (let ((type (etypecase address
52 (tcp-address :stream)
53 (udp-address :datagram))))
54 (with-slots (host port) address
55 (etypecase host
56 (null
57 ;; This should probably be changed to use IPv6 when SBCL
58 ;; supports it. At least on Linux, since it supports
59 ;; v4-mapping, but it is less clear what to do on the
60 ;; BSDs.
61 (list 'sb-bsd-sockets:inet-socket :type type))
62 (ipv4-address
63 (list 'sb-bsd-sockets:inet-socket :type type))
64 (ipv6-address
65 (error "SBCL does not support IPv6."))))))
66 (inet-host-address
67 (error "SBCL does not support raw sockets."))
68 (local-stream-address
69 (list 'sb-bsd-sockets:local-socket :type :stream))
70 (local-seq-address
71 (error "SBCL does not support Unix seqpacket sockets."))
72 (local-datagram-address
73 (list 'sb-bsd-sockets:local-socket :type :datagram))))
74
75(defun sb-bsd-socket-for-address (address)
76 (apply #'make-instance (sbcl-socket-type-and-args address)))
77
78(defun check-not-closed (socket)
79 (declare (type sbcl-socket socket))
80 (when (null (slot-value socket 'sb-socket))
81 (error 'socket-closed :socket socket)))
82
83(define-condition wrapped-socket-error (error socket-condition)
84 ((cause :initarg :cause))
85 (:report (lambda (c s)
86 (princ (slot-value c 'cause) s))))
87
88(defun map-sb-bsd-error (socket c)
89 (cond ((eql (sb-bsd-sockets::socket-error-errno c) 32) ; EPIPE
90 (error 'socket-disconnected :socket socket))
91 ((eql (sb-bsd-sockets::socket-error-errno c) 104) ; ECONNRESET
92 (error 'socket-disconnected :socket socket))
93 (t (error 'wrapped-socket-error :socket socket :cause c))))
94
95(defmacro map-sb-bsd-errors ((socket) &body body)
96 (let ((c (gensym "C")))
97 `(handler-bind ((sb-bsd-sockets:socket-error (lambda (,c) (map-sb-bsd-error ,socket ,c))))
98 ,@body)))
99
100(defmacro retry-loop ((format-string &rest format-args) &body body)
101 `(loop (with-simple-restart (retry ,format-string ,@format-args)
102 (return ,@body))))
103
104(defmethod close-socket ((socket sbcl-socket))
105 (with-slots (sb-socket) socket
106 (unless (null sb-socket)
107 (sb-bsd-sockets:socket-close sb-socket)
108 (setf sb-socket nil))))
109
110(defmethod socket-open-p ((socket sbcl-socket))
111 (if (slot-value socket 'sb-socket) t nil))
112
113(defmethod socket-local-address ((socket sbcl-socket))
114 (check-not-closed socket)
115 (with-slots (sb-socket) socket
116 (map-sbcl-to-address sb-socket (multiple-value-list (sb-bsd-sockets:socket-name sb-socket)))))
117
118(defmethod socket-remote-address ((socket sbcl-socket))
119 (check-not-closed socket)
120 (with-slots (sb-socket) socket
121 (map-sbcl-to-address sb-socket (multiple-value-list (sb-bsd-sockets:socket-peername sb-socket)))))
122
123(defmethod socket-send ((socket sbcl-socket) buf &key (start 0) (end (length buf)) no-hang)
124 (check-not-closed socket)
125 (let ((result (map-sb-bsd-errors (socket)
126 (retry-loop ("Retry the send operation.")
127 (sb-bsd-sockets:socket-send (slot-value socket 'sb-socket)
128 (if (= start 0)
129 buf
130 (subseq buf start end))
131 (- end start)
132 :nosignal t
133 :dontwait no-hang)))))
134 (etypecase result
135 (null 0)
136 (integer result))))
137
138(defmethod socket-send-to ((socket sbcl-socket) buf destination &key (start 0) (end (length buf)) from no-hang)
139 (check-not-closed socket)
140 (when from
141 (error "SB-BSD-THREADS does not support specifying the source address of individual packets."))
142 (let ((result (map-sb-bsd-errors (socket)
143 (retry-loop ("Retry the send operation.")
144 (sb-bsd-sockets:socket-send (slot-value socket 'sb-socket)
145 (if (= start 0)
146 buf
147 (subseq buf start end))
148 (- end start)
149 :address (map-address-to-sbcl socket destination)
150 :nosignal t
151 :dontwait no-hang)))))
152 (etypecase result
153 (null 0)
154 (integer result))))
155
156(defmethod socket-recv-into ((socket sbcl-socket) buf &key (start 0) (end (length buf)) no-hang)
157 (check-not-closed socket)
158 (check-type buf sequence)
159 (let* ((direct (and (= start 0) (typep buf '(array (unsigned-byte 8)))))
160 (readbuf (if direct
161 buf
162 (make-array (list (- end start)) :element-type '(unsigned-byte 8))))
163 (ret-list (multiple-value-list
164 (map-sb-bsd-errors (socket)
165 (retry-loop ("Try receiving again.")
166 (sb-bsd-sockets:socket-receive (slot-value socket 'sb-socket)
167 readbuf
168 (- end start)
169 :dontwait no-hang
170 :element-type '(unsigned-byte 8))))))
171 (len (second ret-list))
172 (addr-list (cddr ret-list)))
173 (etypecase len
174 (null (values nil nil))
175 (integer
176 (unless direct
177 (replace buf readbuf :start1 start :end2 len))
178 (values len (map-sbcl-to-address (slot-value socket 'sb-socket) addr-list))))))
179
180(defmethod bind-to-address ((address tcp-address))
181 (make-instance 'sbcl-listen-socket
182 :sb-socket (with-sb-socket (sk (sb-bsd-socket-for-address address))
183 (handler-bind
184 ((sb-bsd-sockets:address-in-use-error (lambda (c)
185 (declare (ignore c))
186 (error 'address-busy :address address))))
187 (retry-loop ("Try binding again.")
188 (apply #'sb-bsd-sockets:socket-bind sk (map-address-to-sbcl sk address))))
189 (sb-bsd-sockets:socket-listen sk 64)
190 sk)))
191
192(defmethod connect-to-address ((remote tcp-address) &key local)
193 (typecase local
194 (string (setf local (resolve-address local))))
195 (make-instance 'sbcl-stream-socket
196 :sb-socket (with-sb-socket (sk (sb-bsd-socket-for-address (if local local remote)))
197 (if local
198 (handler-case
199 (apply #'sb-bsd-sockets:socket-bind sk (map-address-to-sbcl sk local))
200 (sb-bsd-sockets:address-in-use-error ()
201 (error 'address-busy :address local))))
202 (retry-loop ("Retry connection.")
203 (handler-bind
204 ((sb-bsd-sockets:connection-refused-error (lambda (c)
205 (declare (ignore c))
206 (error 'connection-refused :address remote))))
207 (apply #'sb-bsd-sockets:socket-connect sk (map-address-to-sbcl sk remote))))
208 sk)))
209
210(defmethod bind-to-address ((address udp-address))
211 (make-instance 'sbcl-datagram-socket
212 :sb-socket (with-sb-socket (sk (sb-bsd-socket-for-address address))
213 (handler-case
214 (apply #'sb-bsd-sockets:socket-bind sk (map-address-to-sbcl sk address))
215 (sb-bsd-sockets:address-in-use-error ()
216 (error 'address-busy :address address)))
217 sk)))
218
219(defmethod connect-to-address ((remote udp-address) &key local)
220 (typecase local
221 (string (setf local (resolve-address local))))
222 (make-instance 'sbcl-datagram-socket
223 :sb-socket (with-sb-socket (sk (sb-bsd-socket-for-address (if local local remote)))
224 (if local
225 (handler-case
226 (apply #'sb-bsd-sockets:socket-bind sk (map-address-to-sbcl sk local))
227 (sb-bsd-sockets:address-in-use-error ()
228 (error 'address-busy :address local))))
229 (apply #'sb-bsd-sockets:socket-connect sk (map-address-to-sbcl sk remote))
230 sk)))
231
232(defmethod accept ((socket sbcl-listen-socket))
233 (check-not-closed socket)
234 (let* ((ret-list (multiple-value-list (sb-bsd-sockets:socket-accept (slot-value socket 'sb-socket))))
235 (sk (first ret-list))
236 (addr-list (rest ret-list)))
237 (with-sb-socket (sk sk)
238 (values (make-instance 'sbcl-stream-socket :sb-socket sk)
239 (map-sbcl-to-address sk addr-list)))))