1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Indent-tabs-mode: NIL -*-
3 ;;; socket-methods.lisp --- Various socket methods.
5 ;;; Copyright (C) 2006-2007, Stelian Ionescu <sionescu@common-lisp.net>
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
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.
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
*
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
52 (:datagram sock-dgram
)))
54 ((integerp protocol
) protocol
)
55 ((eql protocol
:default
) 0)
58 (lookup-protocol (string-downcase (string protocol
))))))))
61 (defmethod socket-fd ((socket 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:
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
79 (declare (ignore slot-names
))
80 (when (socket-open-p socket
)
82 (with-accessors ((fd fd-of
) (fam socket-family
) (proto socket-protocol
))
84 (setf fd
(or file-descriptor
85 (multiple-value-bind (sf st sp
)
86 (translate-make-socket-keywords-to-constants
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
98 (declare (ignore slot-names
))
99 (setf (external-format-of socket
) external-format
))
101 (defmethod socket-type ((socket stream-socket
))
104 (defmethod socket-type ((socket datagram-socket
))
109 (defun sock-fam (socket)
110 (ecase (socket-family socket
)
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
)))))
169 (defmethod close :around
((socket socket
) &key abort
)
170 (declare (ignore abort
))
173 (with-socket-error-filter
174 (nix:close
(fd-of socket
))))
175 (setf (fd-of socket
) nil
176 (slot-value socket
'bound
) nil
)
179 (defmethod close :around
((socket passive-socket
) &key abort
)
180 (declare (ignore abort
))
182 (setf (slot-value socket
'listening
) nil
)
185 (defmethod close ((socket socket
) &key abort
)
186 (declare (ignore socket abort
)))
188 (defmethod socket-open-p ((socket socket
))
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
)
197 #+freebsd
(nix:econnreset
()))))
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
)))
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
)))
231 (defmethod bind-address :before
((socket internet-socket
) address
232 &key
(reuse-address t
))
233 (declare (ignore 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
)
247 (if (eql (socket-family socket
) :ipv6
)
248 (bind-ipv6-address (fd-of socket
)
249 (map-ipv4-vector-to-ipv6 (address-name address
))
251 (bind-ipv4-address (fd-of socket
) (address-name address
) port
))
254 (defmethod bind-address ((socket internet-socket
) (address ipv6-address
)
256 (bind-ipv6-address (fd-of socket
) (address-name address
) port
)
259 (defmethod bind-address ((socket local-socket
) (address local-address
) &key
)
260 #+windows
(error "This platform does not support local sockets.")
262 (with-sockaddr-un (sun (address-name address
))
263 (bind (fd-of socket
) sun size-of-sockaddr-un
))
266 (defmethod bind-address :after
((socket socket
) (address address
) &key
)
267 (setf (slot-value socket
'bound
) t
))
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
)
279 (defmethod socket-listen ((socket active-socket
) &key backlog
)
280 (declare (ignore backlog
))
281 (error "You can't listen on active sockets."))
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
)
297 (make-client-socket (accept (fd-of socket
) ss size
))
298 (nix:ewouldblock
()))))))
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
)
317 (if (eql (socket-family socket
) :ipv6
)
318 (ipv6-connect (fd-of socket
)
319 (map-ipv4-vector-to-ipv6 (address-name address
))
321 (ipv4-connect (fd-of socket
) (address-name address
) port
))
324 (defmethod connect ((socket internet-socket
) (address ipv6-address
)
326 (ipv6-connect (fd-of socket
) (address-name address
) port
)
329 (defmethod connect ((socket local-socket
) (address local-address
) &key
)
331 (error "This platform does not support local sockets.")
333 (with-sockaddr-un (sun (address-name address
))
334 (%connect
(fd-of socket
) sun size-of-sockaddr-un
))
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
))
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
))))
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
)
360 (:read-write shut-rdwr
)))
363 (defmethod shutdown ((socket passive-socket
) direction
)
364 (declare (ignore direction
))
365 (error "You cannot shut down passive sockets."))
369 (defun %normalize-send-buffer
(buff start end ef
)
370 (check-bounds buff start end
)
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
)
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
)
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."))
419 (defun %normalize-receive-buffer
(buff start end
)
420 (check-bounds buff start end
)
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)
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
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
454 (with-foreign-object (ss 'sockaddr-storage
)
455 (let* ((flags (calc-recvfrom-flags out-of-band peek wait-all dont-wait
457 (bytes-received (%do-recvfrom buffer ss
(fd-of socket
) flags
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
)))