Fix SO_BINDTODEVICE socket option, add keyword argument :INTERFACE to MAKE-SOCKET.
[iolib.git] / sockets / socket-methods.lisp
blobfc6724dc577e9fcdca10fd9e5f0f0a61f784ed2e
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-class (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 (t (lookup-protocol protocol)))))
57 (values sf st sp)))
59 (defmethod socket-fd ((socket socket))
60 (fd-of socket))
62 (defmethod (setf socket-fd) (fd (socket socket))
63 (setf (fd-of socket) fd))
65 (defmethod initialize-instance :after ((socket socket)
66 &key file-descriptor family type
67 (protocol :default))
68 (with-accessors ((fd fd-of) (fam socket-family) (proto socket-protocol))
69 socket
70 (setf fd (or file-descriptor
71 (multiple-value-bind (sf st sp)
72 (translate-make-socket-keywords-to-constants
73 family type protocol)
74 (socket sf st sp))))
75 (setf fam family
76 proto protocol)))
78 (defmethod (setf external-format-of) (external-format (socket passive-socket))
79 (setf (slot-value socket 'external-format)
80 (babel:ensure-external-format external-format)))
82 (defmethod initialize-instance :after ((socket passive-socket)
83 &key external-format)
84 (setf (external-format-of socket) external-format))
86 (defmethod socket-type ((socket stream-socket))
87 :stream)
89 (defmethod socket-type ((socket datagram-socket))
90 :datagram)
92 ;;;; Printing
94 (defun sock-fam (socket)
95 (ecase (socket-family socket)
96 (:ipv4 "IPv4")
97 (:ipv6 "IPv6")))
99 (defmethod print-object ((socket socket-stream-internet-active) stream)
100 (print-unreadable-object (socket stream :identity t)
101 (format stream "active ~A stream socket" (sock-fam socket))
102 (if (socket-connected-p socket)
103 (multiple-value-bind (addr port) (remote-name socket)
104 (format stream " connected to ~A/~A"
105 (address-to-string addr) port))
106 (format stream ", ~:[closed~;unconnected~]" (fd-of socket)))))
108 (defmethod print-object ((socket socket-stream-internet-passive) stream)
109 (print-unreadable-object (socket stream :identity t)
110 (format stream "passive ~A stream socket" (sock-fam socket))
111 (if (socket-bound-p socket)
112 (multiple-value-bind (addr port) (local-name socket)
113 (format stream " ~:[bound to~;waiting @~] ~A/~A"
114 (socket-listening-p socket)
115 (address-to-string addr) port))
116 (format stream ", ~:[closed~;unbound~]" (fd-of socket)))))
118 (defmethod print-object ((socket socket-stream-local-active) stream)
119 (print-unreadable-object (socket stream :identity t)
120 (format stream "active local stream socket")
121 (if (socket-connected-p socket)
122 (format stream " connected to ~S"
123 (address-to-string (remote-address socket)))
124 (format stream ", ~:[closed~;unconnected~]" (fd-of socket)))))
126 (defmethod print-object ((socket socket-stream-local-passive) stream)
127 (print-unreadable-object (socket stream :identity t)
128 (format stream "passive local stream socket")
129 (if (socket-bound-p socket)
130 (format stream " ~:[bound to~;waiting @~] ~S"
131 (socket-listening-p socket)
132 (address-to-string (local-address socket)))
133 (format stream ", ~:[closed~;unbound~]" (fd-of socket)))))
135 (defmethod print-object ((socket socket-datagram-local-active) stream)
136 (print-unreadable-object (socket stream :identity t)
137 (format stream "local datagram socket")
138 (if (socket-connected-p socket)
139 (format stream " connected to ~S"
140 (address-to-string (remote-address socket)))
141 (if (fd-of socket)
142 (format stream " waiting @ ~S" (address-to-string (local-address socket)))
143 (format stream ", closed" )))))
145 (defmethod print-object ((socket socket-datagram-internet-active) stream)
146 (print-unreadable-object (socket stream :identity t)
147 (format stream "~A datagram socket" (sock-fam socket))
148 (if (socket-connected-p socket)
149 (multiple-value-bind (addr port) (remote-name socket)
150 (format stream " connected to ~A/~A"
151 (address-to-string addr) port))
152 (if (fd-of socket)
153 (multiple-value-bind (addr port) (local-name socket)
154 (format stream " waiting @ ~A/~A"
155 (address-to-string addr) port))
156 (format stream ", closed" )))))
158 ;;;; CLOSE
160 (defmethod close :around ((socket socket) &key abort)
161 (declare (ignore abort))
162 (call-next-method)
163 (when (fd-of socket)
164 (with-socket-error-filter
165 (nix:close (fd-of socket))))
166 (setf (fd-of socket) nil
167 (slot-value socket 'bound) nil)
168 (values socket))
170 (defmethod close :around ((socket passive-socket) &key abort)
171 (declare (ignore abort))
172 (call-next-method)
173 (setf (slot-value socket 'listening) nil)
174 (values socket))
176 (defmethod close ((socket socket) &key abort)
177 (declare (ignore socket abort)))
179 (defmethod socket-open-p ((socket socket))
180 (when (fd-of socket)
181 (handler-case
182 (with-foreign-object (ss 'sockaddr-storage)
183 (bzero ss size-of-sockaddr-storage)
184 (with-socklen (size size-of-sockaddr-storage)
185 (getsockname (fd-of socket) ss size)
187 (nix:ebadf () nil)
188 #+freebsd (nix:econnreset () nil))))
190 ;;;; GETSOCKNAME
192 (defmethod local-name ((socket socket))
193 (with-foreign-object (ss 'sockaddr-storage)
194 (bzero ss size-of-sockaddr-storage)
195 (with-socklen (size size-of-sockaddr-storage)
196 (getsockname (fd-of socket) ss size)
197 (sockaddr-storage->sockaddr ss))))
199 (defmethod local-address ((socket socket))
200 (nth-value 0 (local-name socket)))
202 (defmethod local-port ((socket internet-socket))
203 (nth-value 1 (local-name socket)))
205 ;;;; GETPEERNAME
207 (defmethod remote-name ((socket socket))
208 (with-foreign-object (ss 'sockaddr-storage)
209 (bzero ss size-of-sockaddr-storage)
210 (with-socklen (size size-of-sockaddr-storage)
211 (getpeername (fd-of socket) ss size)
212 (sockaddr-storage->sockaddr ss))))
214 (defmethod remote-address ((socket socket))
215 (nth-value 0 (remote-name socket)))
217 (defmethod remote-port ((socket internet-socket))
218 (nth-value 1 (remote-name socket)))
220 ;;;; BIND
222 (defmethod bind-address :before ((socket internet-socket) address
223 &key (reuse-address t))
224 (declare (ignore address))
225 (when reuse-address
226 (set-socket-option socket :reuse-address :value t)))
228 (defun bind-ipv4-address (fd address port)
229 (with-sockaddr-in (sin address port)
230 (bind fd sin size-of-sockaddr-in)))
232 (defun bind-ipv6-address (fd address port)
233 (with-sockaddr-in6 (sin6 address port)
234 (bind fd sin6 size-of-sockaddr-in6)))
236 (defmethod bind-address ((socket internet-socket) (address ipv4-address)
237 &key (port 0))
238 (if (eql (socket-family socket) :ipv6)
239 (bind-ipv6-address (fd-of socket)
240 (map-ipv4-vector-to-ipv6 (address-name address))
241 port)
242 (bind-ipv4-address (fd-of socket) (address-name address) port))
243 (values socket))
245 (defmethod bind-address ((socket internet-socket) (address ipv6-address)
246 &key (port 0))
247 (bind-ipv6-address (fd-of socket) (address-name address) port)
248 (values socket))
250 (defmethod bind-address ((socket local-socket) (address local-address) &key)
251 (with-sockaddr-un (sun (address-name address))
252 (bind (fd-of socket) sun size-of-sockaddr-un))
253 (values socket))
255 (defmethod bind-address :after ((socket socket) (address address) &key)
256 (setf (slot-value socket 'bound) t))
258 ;;;; LISTEN
260 (defmethod socket-listen ((socket passive-socket) &key backlog)
261 (unless backlog (setf backlog (min *default-backlog-size*
262 +max-backlog-size+)))
263 (check-type backlog unsigned-byte "a non-negative integer")
264 (listen (fd-of socket) backlog)
265 (setf (slot-value socket 'listening) t)
266 (values socket))
268 (defmethod socket-listen ((socket active-socket) &key backlog)
269 (declare (ignore backlog))
270 (error "You can't listen on active sockets."))
272 ;;;; ACCEPT
274 (defmethod accept-connection ((socket active-socket) &key external-format)
275 (declare (ignore external-format))
276 (error "You can't accept connections on active sockets."))
278 (defmethod accept-connection ((socket passive-socket) &key external-format)
279 (flet ((make-client-socket (fd)
280 (make-instance (active-class socket)
281 :external-format (or external-format
282 (external-format-of socket))
283 :file-descriptor fd)))
284 (with-foreign-object (ss 'sockaddr-storage)
285 (bzero ss size-of-sockaddr-storage)
286 (with-socklen (size size-of-sockaddr-storage)
287 (handler-case
288 (make-client-socket (accept (fd-of socket) ss size))
289 (nix:ewouldblock ()))))))
291 ;;;; CONNECT
293 #+freebsd
294 (defmethod connect :before ((socket active-socket) sockaddr &key)
295 (declare (ignore sockaddr))
296 (set-socket-option socket :no-sigpipe :value t))
298 (defun ipv4-connect (fd address port)
299 (with-sockaddr-in (sin address port)
300 (%connect fd sin size-of-sockaddr-in)))
302 (defun ipv6-connect (fd address port)
303 (with-sockaddr-in6 (sin6 address port)
304 (%connect fd sin6 size-of-sockaddr-in6)))
306 (defmethod connect ((socket internet-socket) (address ipv4-address)
307 &key (port 0))
308 (if (eql (socket-family socket) :ipv6)
309 (ipv6-connect (fd-of socket)
310 (map-ipv4-vector-to-ipv6 (address-name address))
311 port)
312 (ipv4-connect (fd-of socket) (address-name address) port))
313 (values socket))
315 (defmethod connect ((socket internet-socket) (address ipv6-address)
316 &key (port 0))
317 (ipv6-connect (fd-of socket) (address-name address) port)
318 (values socket))
320 (defmethod connect ((socket local-socket) (address local-address) &key)
321 (with-sockaddr-un (sun (address-name address))
322 (%connect (fd-of socket) sun size-of-sockaddr-un))
323 (values socket))
325 (defmethod connect ((socket passive-socket) address &key)
326 (declare (ignore address))
327 (error "You cannot connect passive sockets."))
329 (defmethod socket-connected-p ((socket socket))
330 (when (fd-of socket)
331 (handler-case
332 (with-foreign-object (ss 'sockaddr-storage)
333 (bzero ss size-of-sockaddr-storage)
334 (with-socklen (size size-of-sockaddr-storage)
335 (getpeername (fd-of socket) ss size)
337 (socket-not-connected-error () nil))))
339 ;;;; SHUTDOWN
341 (defmethod shutdown ((socket active-socket) direction)
342 (check-type direction (member :read :write :read-write)
343 "one of :READ, :WRITE or :READ-WRITE")
344 (%shutdown (fd-of socket)
345 (ecase direction
346 (:read shut-rd)
347 (:write shut-wr)
348 (:read-write shut-rdwr)))
349 (values socket))
351 (defmethod shutdown ((socket passive-socket) direction)
352 (declare (ignore direction))
353 (error "You cannot shut down passive sockets."))
355 ;;;; SEND
357 (eval-when (:compile-toplevel :load-toplevel :execute)
358 (defun compute-flags (flags args)
359 (loop :with flag-combination := 0
360 :for cons :on args :by #'cddr
361 :for flag := (car cons)
362 :for val := (cadr cons)
363 :for const := (cdr (assoc flag flags))
364 :when const :do
365 (when (not (constantp val)) (return-from compute-flags))
366 (setf flag-combination (logior flag-combination const))
367 :finally (return flag-combination)))
369 (defmacro define-socket-flag (place name value platform)
370 (let ((val (cond ((or (not platform)
371 (featurep platform)) value)
372 ((not (featurep platform)) 0))))
373 `(push (cons ,name ,val) ,place))))
375 (eval-when (:compile-toplevel :load-toplevel :execute)
376 (defparameter *sendmsg-flags* nil)
378 (defmacro define-sendmsg-flags (&rest forms)
379 (flet ((dflag (form)
380 (destructuring-bind (name value &optional platform) form
381 `(define-socket-flag *sendmsg-flags* ,name ,value ,platform))))
382 `(progn
383 ,@(mapcar #'dflag forms))))
385 (define-sendmsg-flags
386 (:end-of-record msg-eor (:not :windows))
387 (:dont-route msg-dontroute)
388 (:dont-wait msg-dontwait (:not :windows))
389 (:no-signal msg-nosignal (:not (:or :darwin :windows)))
390 (:out-of-band msg-oob)
391 (:more msg-more :linux)
392 (:confirm msg-confirm :linux)))
394 (defun %normalize-send-buffer (buff start end ef)
395 (check-bounds buff start end)
396 (etypecase buff
397 (ub8-sarray (values buff start (- end start)))
398 (ub8-vector (values (coerce buff 'ub8-sarray)
399 start (- end start)))
400 (string (values (%to-octets buff ef start end)
401 0 (- end start)))
402 (vector (values (coerce buff 'ub8-sarray)
403 start (- end start)))))
405 (defun %socket-send (buffer socket start end remote-address remote-port flags)
406 (when (typep socket 'passive-socket)
407 (error "You cannot send data on a passive socket."))
408 (check-type start unsigned-byte "a non-negative integer")
409 (check-type end (or unsigned-byte null) "a non-negative integer or NIL")
410 (check-type remote-address (or address null) "a network address or NIL")
411 (check-type remote-port tcp-port "a valid TCP port number")
412 (when (and (ipv4-address-p remote-address)
413 (eq (socket-family socket) :ipv6))
414 (setf remote-address (map-ipv4-address-to-ipv6 remote-address)))
415 (multiple-value-bind (buff start-offset bufflen)
416 (%normalize-send-buffer buffer start end (external-format-of socket))
417 (with-foreign-object (ss 'sockaddr-storage)
418 (bzero ss size-of-sockaddr-storage)
419 (when remote-address
420 (sockaddr->sockaddr-storage ss remote-address remote-port))
421 (with-pointer-to-vector-data (buff-sap buff)
422 (incf-pointer buff-sap start-offset)
423 (sendto (fd-of socket) buff-sap bufflen flags
424 (if remote-address ss (null-pointer))
425 (if remote-address size-of-sockaddr-storage 0))))))
427 (defmethod socket-send ((buffer array) (socket active-socket) &rest args
428 &key (start 0) end remote-address (remote-port 0) &allow-other-keys)
429 (%socket-send buffer socket start end remote-address remote-port
430 (compute-flags *sendmsg-flags* args)))
432 (define-compiler-macro socket-send (&whole form buffer socket &rest args
433 &key (start 0) end remote-address (remote-port 0)
434 &allow-other-keys)
435 (let ((flags (compute-flags *sendmsg-flags* args)))
436 (cond (flags `(%socket-send ,buffer ,socket ,start ,end
437 ,remote-address ,remote-port ,flags))
438 (t form))))
440 ;;;; RECV
442 (eval-when (:compile-toplevel :load-toplevel :execute)
443 (defparameter *recvfrom-flags* nil)
445 (defmacro define-recvfrom-flags (&rest forms)
446 (flet ((dflag (form)
447 (destructuring-bind (name value &optional platform) form
448 `(define-socket-flag *recvfrom-flags* ,name ,value ,platform))))
449 `(progn
450 ,@(mapcar #'dflag forms))))
452 (define-recvfrom-flags
453 (:out-of-band msg-oob)
454 (:peek msg-peek)
455 (:wait-all msg-waitall (:not :windows))
456 (:dont-wait msg-dontwait (:not :windows))
457 (:no-signal msg-nosignal (:not (:or :darwin :windows)))))
459 (defun %normalize-receive-buffer (buff start end)
460 (check-bounds buff start end)
461 (etypecase buff
462 ((simple-array ub8 (*)) (values buff start (- end start)))))
464 (defun %socket-receive-bytes (buffer ss fd flags start end)
465 (multiple-value-bind (buff start-offset bufflen)
466 (%normalize-receive-buffer buffer start end)
467 (with-socklen (size size-of-sockaddr-storage)
468 (bzero ss size-of-sockaddr-storage)
469 (with-pointer-to-vector-data (buff-sap buff)
470 (incf-pointer buff-sap start-offset)
471 (recvfrom fd buff-sap bufflen flags ss size)))))
473 (declaim (inline %socket-receive-stream-socket))
474 (defun %socket-receive-stream-socket (buffer socket start end flags)
475 (with-foreign-object (ss 'sockaddr-storage)
476 (let ((bytes-received (%socket-receive-bytes buffer ss (fd-of socket) flags
477 start end)))
478 (values buffer bytes-received))))
480 (declaim (inline %socket-receive-datagram-socket))
481 (defun %socket-receive-datagram-socket (buffer socket start end flags)
482 (with-foreign-object (ss 'sockaddr-storage)
483 (let ((bytes-received (%socket-receive-bytes buffer ss (fd-of socket) flags
484 start end)))
485 (multiple-value-bind (remote-address remote-port)
486 (sockaddr-storage->sockaddr ss)
487 (values buffer bytes-received remote-address remote-port)))))
489 (defun %socket-receive (buffer socket start end flags)
490 (when (typep socket 'passive-socket)
491 (error "You cannot receive data from a passive socket."))
492 (etypecase socket
493 (stream-socket (%socket-receive-stream-socket
494 buffer socket start end flags))
495 (datagram-socket (%socket-receive-datagram-socket
496 buffer socket start end flags))))
498 (defmethod socket-receive ((buffer array) (socket active-socket)
499 &rest args &key (start 0) end &allow-other-keys)
500 (%socket-receive buffer socket start end
501 (compute-flags *recvfrom-flags* args)))
503 (define-compiler-macro socket-receive (&whole form buffer socket &rest args
504 &key (start 0) end &allow-other-keys)
505 (let ((flags (compute-flags *recvfrom-flags* args)))
506 (cond (flags `(%socket-receive ,buffer ,socket ,start ,end ,flags))
507 (t form))))
509 ;;;; Datagram Sockets
511 (defmethod disconnect :before ((socket active-socket))
512 (unless (typep socket 'datagram-socket)
513 (error "You can only disconnect active datagram sockets.")))
515 (defmethod disconnect ((socket datagram-socket))
516 (with-foreign-object (sin 'sockaddr-in)
517 (bzero sin size-of-sockaddr-in)
518 (setf (foreign-slot-value sin 'sockaddr-in 'addr) af-unspec)
519 (%connect (fd-of socket) sin size-of-sockaddr-in)))