Windows port. Lots of rough edges, passes the most important tests.
[iolib.git] / sockets / socket-methods.lisp
blobf1107a48be2843a9b1dbf8c1fd1b0882175e3a06
1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Indent-tabs-mode: NIL -*-
2 ;;;
3 ;;; socket-methods.lisp --- Various socket methods.
4 ;;;
5 ;;; Copyright (C) 2006-2007, Stelian Ionescu <sionescu@common-lisp.net>
6 ;;;
7 ;;; This code is free software; you can redistribute it and/or
8 ;;; modify it under the terms of the version 2.1 of
9 ;;; the GNU Lesser General Public License as published by
10 ;;; the Free Software Foundation, as clarified by the
11 ;;; preamble found here:
12 ;;; http://opensource.franz.com/preamble.html
13 ;;;
14 ;;; This program is distributed in the hope that it will be useful,
15 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;;; GNU General Public License for more details.
18 ;;;
19 ;;; You should have received a copy of the GNU Lesser General
20 ;;; Public License along with this library; if not, write to the
21 ;;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
22 ;;; Boston, MA 02110-1301, USA
24 (in-package :net.sockets)
26 (defvar *socket-type-map*
27 '(((:ipv4 :stream :active :default) . socket-stream-internet-active)
28 ((:ipv6 :stream :active :default) . socket-stream-internet-active)
29 ((:ipv4 :stream :passive :default) . socket-stream-internet-passive)
30 ((:ipv6 :stream :passive :default) . socket-stream-internet-passive)
31 ((:local :stream :active :default) . socket-stream-local-active)
32 ((:local :stream :passive :default) . socket-stream-local-passive)
33 ((:local :datagram :active :default) . socket-datagram-local-active)
34 ((:ipv4 :datagram :active :default) . socket-datagram-internet-active)
35 ((:ipv6 :datagram :active :default) . socket-datagram-internet-active)))
37 ;;; FIXME: should match :default to whatever protocol is the default.
38 (defun select-socket-type (family type connect protocol)
39 (or (cdr (assoc (list family type connect protocol) *socket-type-map*
40 :test #'equal))
41 (error "No socket class found !!")))
43 ;;;; Shared Initialization
45 (defun translate-make-socket-keywords-to-constants (family type protocol)
46 (let ((sf (ecase family
47 (:ipv4 af-inet)
48 (:ipv6 af-inet6)
49 (:local af-local)))
50 (st (ecase type
51 (:stream sock-stream)
52 (:datagram sock-dgram)))
53 (sp (cond
54 ((integerp protocol) protocol)
55 ((eql protocol :default) 0)
56 ((keywordp protocol)
57 (protocol-number
58 (lookup-protocol (string-downcase (string protocol))))))))
59 (values sf st sp)))
61 (defmethod socket-fd ((socket socket))
62 (fd-of socket))
64 (defmethod (setf socket-fd) (fd (socket socket))
65 (setf (fd-of socket) fd))
67 ;; TODO: we should add some sort of finalizer here to avoid leaking
68 ;; sockets FDs and buffers. Something along these lines:
69 ;; (when finalize
70 ;; (trivial-garbage:finalize socket (lambda () (close socket))))
72 ;; However SBCL's semantics don't allow this, since that reference to
73 ;; the socket will prevent it from being garbage collected. So we'd
74 ;; need to get all necessary information into a closure or something
75 ;; (foreign pointers, FDs, etc) in order to do that closing.
76 (defmethod shared-initialize :after ((socket socket) slot-names
77 &key file-descriptor family type
78 (protocol :default))
79 (declare (ignore slot-names))
80 (when (socket-open-p socket)
81 (close socket))
82 (with-accessors ((fd fd-of) (fam socket-family) (proto socket-protocol))
83 socket
84 (setf fd (or file-descriptor
85 (multiple-value-bind (sf st sp)
86 (translate-make-socket-keywords-to-constants
87 family type protocol)
88 (socket sf st sp))))
89 (setf fam family
90 proto protocol)))
92 (defmethod (setf external-format-of) (external-format (socket passive-socket))
93 (setf (slot-value socket 'external-format)
94 (babel:ensure-external-format external-format)))
96 (defmethod shared-initialize :after ((socket passive-socket) slot-names
97 &key external-format)
98 (declare (ignore slot-names))
99 (setf (external-format-of socket) external-format))
101 (defmethod socket-type ((socket stream-socket))
102 :stream)
104 (defmethod socket-type ((socket datagram-socket))
105 :datagram)
107 ;;;; Printing
109 (defun sock-fam (socket)
110 (ecase (socket-family socket)
111 (:ipv4 "IPv4")
112 (:ipv6 "IPv6")))
114 (defmethod print-object ((socket socket-stream-internet-active) stream)
115 (print-unreadable-object (socket stream :identity t)
116 (format stream "active ~A stream socket" (sock-fam socket))
117 (if (socket-connected-p socket)
118 (multiple-value-bind (addr port) (remote-name socket)
119 (format stream " connected to ~A/~A"
120 (address-to-string addr) port))
121 (format stream ", ~:[closed~;unconnected~]" (fd-of socket)))))
123 (defmethod print-object ((socket socket-stream-internet-passive) stream)
124 (print-unreadable-object (socket stream :identity t)
125 (format stream "passive ~A stream socket" (sock-fam socket))
126 (if (socket-bound-p socket)
127 (multiple-value-bind (addr port) (local-name socket)
128 (format stream " ~:[bound to~;waiting @~] ~A/~A"
129 (socket-listening-p socket)
130 (address-to-string addr) port))
131 (format stream ", ~:[closed~;unbound~]" (fd-of socket)))))
133 (defmethod print-object ((socket socket-stream-local-active) stream)
134 (print-unreadable-object (socket stream :identity t)
135 (format stream "active local stream socket")
136 (if (socket-connected-p socket)
137 (format stream " connected to ~A"
138 (address-to-string (remote-address socket)))
139 (format stream ", ~:[closed~;unconnected~]" (fd-of socket)))))
141 (defmethod print-object ((socket socket-stream-local-passive) stream)
142 (print-unreadable-object (socket stream :identity t)
143 (format stream "passive local stream socket")
144 (if (socket-bound-p socket)
145 (format stream " ~:[bound to~;waiting @~] ~A"
146 (socket-listening-p socket)
147 (address-to-string (local-address socket)))
148 (format stream ", ~:[closed~;unbound~]" (fd-of socket)))))
150 (defmethod print-object ((socket socket-datagram-local-active) stream)
151 (print-unreadable-object (socket stream :identity t)
152 (format stream "local datagram socket")
153 (if (socket-connected-p socket)
154 (format stream " connected to ~A"
155 (address-to-string (remote-address socket)))
156 (format stream ", ~:[closed~;unconnected~]" (fd-of socket)))))
158 (defmethod print-object ((socket socket-datagram-internet-active) stream)
159 (print-unreadable-object (socket stream :identity t)
160 (format stream "~A datagram socket" (sock-fam socket))
161 (if (socket-connected-p socket)
162 (multiple-value-bind (addr port) (remote-name socket)
163 (format stream " connected to ~A/~A"
164 (address-to-string addr) port))
165 (format stream ", ~:[closed~;unconnected~]" (fd-of socket)))))
167 ;;;; CLOSE
169 (defmethod close :around ((socket socket) &key abort)
170 (declare (ignore abort))
171 (call-next-method)
172 (when (fd-of socket)
173 (with-socket-error-filter
174 (nix:close (fd-of socket))))
175 (setf (fd-of socket) nil
176 (slot-value socket 'bound) nil)
177 (values socket))
179 (defmethod close :around ((socket passive-socket) &key abort)
180 (declare (ignore abort))
181 (call-next-method)
182 (setf (slot-value socket 'listening) nil)
183 (values socket))
185 (defmethod close ((socket socket) &key abort)
186 (declare (ignore socket abort)))
188 (defmethod socket-open-p ((socket socket))
189 (when (fd-of socket)
190 (handler-case
191 (with-foreign-object (ss 'sockaddr-storage)
192 (bzero ss size-of-sockaddr-storage)
193 (with-socklen (size size-of-sockaddr-storage)
194 (getsockname (fd-of socket) ss size)
196 (nix:ebadf ())
197 #+freebsd (nix:econnreset ()))))
199 ;;;; GETSOCKNAME
201 (defmethod local-name ((socket socket))
202 (with-foreign-object (ss 'sockaddr-storage)
203 (bzero ss size-of-sockaddr-storage)
204 (with-socklen (size size-of-sockaddr-storage)
205 (getsockname (fd-of socket) ss size)
206 (sockaddr-storage->sockaddr ss))))
208 (defmethod local-address ((socket socket))
209 (nth-value 0 (local-name socket)))
211 (defmethod local-port ((socket internet-socket))
212 (nth-value 1 (local-name socket)))
214 ;;;; GETPEERNAME
216 (defmethod remote-name ((socket socket))
217 (with-foreign-object (ss 'sockaddr-storage)
218 (bzero ss size-of-sockaddr-storage)
219 (with-socklen (size size-of-sockaddr-storage)
220 (getpeername (fd-of socket) ss size)
221 (sockaddr-storage->sockaddr ss))))
223 (defmethod remote-address ((socket socket))
224 (nth-value 0 (remote-name socket)))
226 (defmethod remote-port ((socket internet-socket))
227 (nth-value 1 (remote-name socket)))
229 ;;;; BIND
231 (defmethod bind-address :before ((socket internet-socket) address
232 &key (reuse-address t))
233 (declare (ignore address))
234 (when reuse-address
235 (set-socket-option socket :reuse-address :value t)))
237 (defun bind-ipv4-address (fd address port)
238 (with-sockaddr-in (sin address port)
239 (bind fd sin size-of-sockaddr-in)))
241 (defun bind-ipv6-address (fd address port)
242 (with-sockaddr-in6 (sin6 address port)
243 (bind fd sin6 size-of-sockaddr-in6)))
245 (defmethod bind-address ((socket internet-socket) (address ipv4-address)
246 &key (port 0))
247 (if (eql (socket-family socket) :ipv6)
248 (bind-ipv6-address (fd-of socket)
249 (map-ipv4-vector-to-ipv6 (address-name address))
250 port)
251 (bind-ipv4-address (fd-of socket) (address-name address) port))
252 socket)
254 (defmethod bind-address ((socket internet-socket) (address ipv6-address)
255 &key (port 0))
256 (bind-ipv6-address (fd-of socket) (address-name address) port)
257 socket)
259 (defmethod bind-address ((socket local-socket) (address local-address) &key)
260 #+windows (error "This platform does not support local sockets.")
261 #-windows
262 (with-sockaddr-un (sun (address-name address))
263 (bind (fd-of socket) sun size-of-sockaddr-un))
264 socket)
266 (defmethod bind-address :after ((socket socket) (address address) &key)
267 (setf (slot-value socket 'bound) t))
269 ;;;; LISTEN
271 (defmethod socket-listen ((socket passive-socket) &key backlog)
272 (unless backlog (setf backlog (min *default-backlog-size*
273 +max-backlog-size+)))
274 (check-type backlog unsigned-byte "a non-negative integer")
275 (listen (fd-of socket) backlog)
276 (setf (slot-value socket 'listening) t)
277 (values socket))
279 (defmethod socket-listen ((socket active-socket) &key backlog)
280 (declare (ignore backlog))
281 (error "You can't listen on active sockets."))
283 ;;;; ACCEPT
285 (defmethod accept-connection ((socket active-socket))
286 (error "You can't accept connections on active sockets."))
288 (defmethod accept-connection ((socket passive-socket))
289 (flet ((make-client-socket (fd)
290 (make-instance (active-class socket)
291 :external-format (external-format-of socket)
292 :file-descriptor fd)))
293 (with-foreign-object (ss 'sockaddr-storage)
294 (bzero ss size-of-sockaddr-storage)
295 (with-socklen (size size-of-sockaddr-storage)
296 (handler-case
297 (make-client-socket (accept (fd-of socket) ss size))
298 (nix:ewouldblock ()))))))
300 ;;;; CONNECT
302 #+freebsd
303 (defmethod connect :before ((socket active-socket) sockaddr &key)
304 (declare (ignore sockaddr))
305 (set-socket-option socket :no-sigpipe :value t))
307 (defun ipv4-connect (fd address port)
308 (with-sockaddr-in (sin address port)
309 (%connect fd sin size-of-sockaddr-in)))
311 (defun ipv6-connect (fd address port)
312 (with-sockaddr-in6 (sin6 address port)
313 (%connect fd sin6 size-of-sockaddr-in6)))
315 (defmethod connect ((socket internet-socket) (address ipv4-address)
316 &key (port 0))
317 (if (eql (socket-family socket) :ipv6)
318 (ipv6-connect (fd-of socket)
319 (map-ipv4-vector-to-ipv6 (address-name address))
320 port)
321 (ipv4-connect (fd-of socket) (address-name address) port))
322 (values socket))
324 (defmethod connect ((socket internet-socket) (address ipv6-address)
325 &key (port 0))
326 (ipv6-connect (fd-of socket) (address-name address) port)
327 (values socket))
329 (defmethod connect ((socket local-socket) (address local-address) &key)
330 #+windows
331 (error "This platform does not support local sockets.")
332 #-windows
333 (with-sockaddr-un (sun (address-name address))
334 (%connect (fd-of socket) sun size-of-sockaddr-un))
335 (values socket))
337 (defmethod connect ((socket passive-socket) address &key)
338 (declare (ignore address))
339 (error "You cannot connect passive sockets."))
341 (defmethod socket-connected-p ((socket socket))
342 (when (fd-of socket)
343 (handler-case
344 (with-foreign-object (ss 'sockaddr-storage)
345 (bzero ss size-of-sockaddr-storage)
346 (with-socklen (size size-of-sockaddr-storage)
347 (getpeername (fd-of socket) ss size)
349 (socket-not-connected-error () nil))))
351 ;;;; SHUTDOWN
353 (defmethod shutdown ((socket active-socket) direction)
354 (check-type direction (member :read :write :read-write)
355 "valid direction specifier")
356 (%shutdown (fd-of socket)
357 (ecase direction
358 (:read shut-rd)
359 (:write shut-wr)
360 (:read-write shut-rdwr)))
361 (values socket))
363 (defmethod shutdown ((socket passive-socket) direction)
364 (declare (ignore direction))
365 (error "You cannot shut down passive sockets."))
367 ;;;; SEND
369 (defun %normalize-send-buffer (buff start end ef)
370 (check-bounds buff start end)
371 (etypecase buff
372 (ub8-sarray (values buff start (- end start)))
373 (ub8-vector (values (coerce buff 'ub8-sarray)
374 start (- end start)))
375 (string (values (%to-octets buff ef start end)
376 0 (- end start)))))
378 (defmethod socket-send ((buffer array) (socket active-socket)
379 &key (start 0) end remote-address remote-port
380 end-of-record dont-route dont-wait no-signal
381 out-of-band #+linux more #+linux confirm)
382 #+darwin (declare (ignore no-signal)) ; better warn?
383 #+windows (declare (ignore dont-wait no-signal end-of-record)) ; ditto
384 (check-type start unsigned-byte
385 "a non-negative unsigned integer")
386 (check-type end (or unsigned-byte null)
387 "a non-negative unsigned integer or NIL")
388 (when (or remote-port remote-address)
389 (check-type remote-address address "a network address")
390 (check-type remote-port (unsigned-byte 16) "a valid IP port number"))
391 (let ((flags (logior #-windows (if end-of-record msg-eor 0)
392 (if dont-route msg-dontroute 0)
393 #-windows (if dont-wait msg-dontwait 0)
394 #-(or darwin windows) (if no-signal msg-nosignal 0)
395 (if out-of-band msg-oob 0)
396 #+linux (if more msg-more 0)
397 #+linux (if confirm msg-confirm 0))))
398 (when (and (ipv4-address-p remote-address)
399 (eql (socket-family socket) :ipv6))
400 (setf remote-address (map-ipv4-address-to-ipv6 remote-address)))
401 (multiple-value-bind (buff start-offset bufflen)
402 (%normalize-send-buffer buffer start end (external-format-of socket))
403 (with-foreign-object (ss 'sockaddr-storage)
404 (bzero ss size-of-sockaddr-storage)
405 (when remote-address
406 (sockaddr->sockaddr-storage ss remote-address remote-port))
407 (with-pointer-to-vector-data (buff-sap buff)
408 (incf-pointer buff-sap start-offset)
409 (sendto (fd-of socket) buff-sap bufflen flags
410 (if remote-address ss (null-pointer))
411 (if remote-address size-of-sockaddr-storage 0)))))))
413 (defmethod socket-send (buffer (socket passive-socket) &key)
414 (declare (ignore buffer))
415 (error "You cannot send data on a passive socket."))
417 ;;;; RECV
419 (defun %normalize-receive-buffer (buff start end)
420 (check-bounds buff start end)
421 (etypecase buff
422 ((simple-array ub8 (*)) (values buff start (- end start)))))
424 (defun calc-recvfrom-flags (out-of-band peek wait-all dont-wait no-signal)
425 #+darwin (declare (ignore no-signal)) ; better warn?
426 #+windows (declare (ignore wait-all dont-wait no-signal)) ; ditto
427 (logior (if out-of-band msg-oob 0)
428 (if peek msg-peek 0)
429 #-windows (if wait-all msg-waitall 0)
430 #-windows (if dont-wait msg-dontwait 0)
431 #-(or windows darwin) (if no-signal msg-nosignal 0)))
433 (defun %do-recvfrom (buffer ss fd flags start end)
434 (multiple-value-bind (buff start-offset bufflen)
435 (%normalize-receive-buffer buffer start end)
436 (with-socklen (size size-of-sockaddr-storage)
437 (bzero ss size-of-sockaddr-storage)
438 (with-pointer-to-vector-data (buff-sap buff)
439 (incf-pointer buff-sap start-offset)
440 (recvfrom fd buff-sap bufflen flags ss size)))))
442 (defmethod socket-receive ((buffer array) (socket stream-socket) &key (start 0)
443 end out-of-band peek wait-all dont-wait no-signal)
444 (with-foreign-object (ss 'sockaddr-storage)
445 (let* ((flags (calc-recvfrom-flags out-of-band peek wait-all
446 dont-wait no-signal))
447 (bytes-received (%do-recvfrom buffer ss (fd-of socket) flags
448 start end)))
449 (values buffer bytes-received))))
451 (defmethod socket-receive ((buffer array) (socket datagram-socket)
452 &key (start 0) end out-of-band peek wait-all
453 dont-wait no-signal)
454 (with-foreign-object (ss 'sockaddr-storage)
455 (let* ((flags (calc-recvfrom-flags out-of-band peek wait-all dont-wait
456 no-signal))
457 (bytes-received (%do-recvfrom buffer ss (fd-of socket) flags
458 start end)))
459 (multiple-value-bind (remote-address remote-port)
460 (sockaddr-storage->sockaddr ss)
461 (values buffer bytes-received remote-address remote-port)))))
463 (defmethod socket-receive (buffer (socket passive-socket) &key)
464 (declare (ignore buffer))
465 (error "You cannot receive data from a passive socket."))
467 ;;;; Datagram Sockets
469 (defmethod disconnect :before ((socket active-socket))
470 (unless (typep socket 'datagram-socket)
471 (error "You can only disconnect active datagram sockets.")))
473 (defmethod disconnect ((socket datagram-socket))
474 (with-foreign-object (sin 'sockaddr-in)
475 (bzero sin size-of-sockaddr-in)
476 (setf (foreign-slot-value sin 'sockaddr-in 'addr) af-unspec)
477 (%connect (fd-of socket) sin size-of-sockaddr-in)))