1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; indent-tabs-mode: nil -*-
3 ;;; --- Socket creation.
6 (in-package :iolib.sockets
)
8 (eval-when (:compile-toplevel
:load-toplevel
:execute
)
9 (defvar *socket-type-map
*
10 '(((:ipv4
:stream
:active
:default
) . socket-stream-internet-active
)
11 ((:ipv6
:stream
:active
:default
) . socket-stream-internet-active
)
12 ((:ipv4
:stream
:passive
:default
) . socket-stream-internet-passive
)
13 ((:ipv6
:stream
:passive
:default
) . socket-stream-internet-passive
)
14 ((:local
:stream
:active
:default
) . socket-stream-local-active
)
15 ((:local
:stream
:passive
:default
) . socket-stream-local-passive
)
16 ((:local
:datagram
:active
:default
) . socket-datagram-local-active
)
17 ((:ipv4
:datagram
:active
:default
) . socket-datagram-internet-active
)
18 ((:ipv6
:datagram
:active
:default
) . socket-datagram-internet-active
)))
20 ;; FIXME: should match :default to whatever protocol is the default.
21 (defun select-socket-class (address-family type connect protocol
)
22 (or (cdr (assoc (list address-family type connect protocol
) *socket-type-map
*
24 (error "No socket class found !!"))))
26 (defun create-socket (family type connect external-format
&key
27 fd input-buffer-size output-buffer-size
)
28 (make-instance (select-socket-class family type connect
:default
)
29 :address-family family
:file-descriptor fd
30 :external-format external-format
31 :input-buffer-size input-buffer-size
32 :output-buffer-size output-buffer-size
))
34 (define-compiler-macro create-socket
(&whole form
&environment env
35 family type connect external-format
36 &key fd input-buffer-size output-buffer-size
)
38 ((and (constantp family env
) (constantp type env
) (constantp connect env
))
39 `(make-instance ',(select-socket-class family type connect
:default
)
40 :address-family
,family
:file-descriptor
,fd
41 :external-format
,external-format
42 :input-buffer-size
,input-buffer-size
43 :output-buffer-size
,output-buffer-size
))
46 (defmacro with-close-on-error
((var value
) &body body
)
47 "Bind `VAR' to `VALUE' and execute `BODY' as implicit PROGN.
48 If a non-local exit occurs during the execution of `BODY',
49 call CLOSE with :ABORT T on `VAR'."
51 (unwind-protect-case () ,@body
52 (:abort
(close ,var
:abort t
)))))
54 (defmacro %create-internet-socket
(family &rest args
)
56 (:ipv4
(create-socket :ipv4
,@args
))
57 (:ipv6
(create-socket :ipv6
,@args
))))
59 (defmacro with-guard-against-non-list-args-and-destructuring-bind-errors
60 (form args
&body body
)
62 (handler-case (progn ,@body
)
63 (error (err) `(error ,err
)))
66 (eval-when (:compile-toplevel
:load-toplevel
:execute
)
67 (defun make-first-level-name (family type connect
)
68 (format-symbol :iolib.sockets
"%~A-~A-~A-~A-~A" :make family type connect
:socket
)))
70 (defmacro define-socket-creator
((socket-family socket-type socket-connect
)
71 (family ef key
&rest args
) &body body
)
72 (assert (eql '&key key
))
73 (flet ((maybe-quote-default-value (arg)
74 (cond ((symbolp arg
) arg
)
75 ((consp arg
) (list (first arg
) `(quote ,(second arg
))))))
77 (cond ((symbolp arg
) arg
)
78 ((consp arg
) (first arg
))))
80 `(list (quote ,(car form
)) ,@(cdr form
))))
81 (let* ((arg-names (mapcar #'arg-name args
))
82 (first-level-function (make-first-level-name socket-family socket-type socket-connect
))
83 (second-level-function (format-symbol t
"%~A" first-level-function
))
84 (first-level-body `(,second-level-function family ef
,@arg-names
)))
86 (declaim (inline ,second-level-function
))
87 (defun ,second-level-function
(,family
,ef
,@arg-names
) ,@body
)
88 (defun ,first-level-function
(arguments family ef
)
89 (destructuring-bind (&key
,@args
) arguments
,first-level-body
))
90 (define-compiler-macro ,first-level-function
(&whole form arguments family ef
)
91 (with-guard-against-non-list-args-and-destructuring-bind-errors
93 ;; Must quote default values in order for them not to be evaluated
94 ;; in the compilation environment
95 (destructuring-bind (&key
,@(mapcar #'maybe-quote-default-value args
))
97 ,(quotify first-level-body
))))))))
99 ;;; Internet Stream Active Socket creation
101 (defun %%init-internet-stream-active-socket
(socket keepalive nodelay reuse-address
102 local-host local-port remote-host remote-port
)
103 (setf (socket-option socket
:no-sigpipe
:if-does-not-exist nil
) t
)
104 (when keepalive
(setf (socket-option socket
:keep-alive
) t
))
105 (when nodelay
(setf (socket-option socket
:tcp-nodelay
) t
))
107 (bind-address socket
(ensure-hostname local-host
)
109 :reuse-address reuse-address
))
111 (connect socket
(ensure-hostname remote-host
)
115 (define-socket-creator (:internet
:stream
:active
)
116 (family ef
&key keepalive nodelay
(reuse-address t
)
117 local-host local-port remote-host remote-port
118 input-buffer-size output-buffer-size
)
119 (with-close-on-error (socket (%create-internet-socket family
:stream
:active ef
120 :input-buffer-size input-buffer-size
121 :output-buffer-size output-buffer-size
))
122 (%%init-internet-stream-active-socket socket keepalive nodelay reuse-address
123 local-host
(or local-port
0) remote-host remote-port
)))
125 ;;; Internet Stream Passive Socket creation
127 (defun %%init-internet-stream-passive-socket
(socket interface reuse-address
128 local-host local-port backlog
)
131 (setf (socket-option socket
:bind-to-device
) interface
))
132 (bind-address socket
(ensure-hostname local-host
)
134 :reuse-address reuse-address
)
135 (listen-on socket
:backlog backlog
))
138 (define-socket-creator (:internet
:stream
:passive
)
139 (family ef
&key interface
(reuse-address t
)
140 local-host local-port backlog
)
141 (with-close-on-error (socket (%create-internet-socket family
:stream
:passive ef
))
142 (%%init-internet-stream-passive-socket socket interface reuse-address
143 local-host
(or local-port
0)
144 (or backlog
*default-backlog-size
*))))
146 ;;; Local Stream Active Socket creation
148 (defun %%init-local-stream-active-socket
(socket local-filename remote-filename
)
149 (setf (socket-option socket
:no-sigpipe
:if-does-not-exist nil
) t
)
151 (bind-address socket
(ensure-address local-filename
:family
:local
)))
152 (when remote-filename
153 (connect socket
(ensure-address remote-filename
:family
:local
)))
156 (define-socket-creator (:local
:stream
:active
)
157 (family ef
&key local-filename remote-filename
158 input-buffer-size output-buffer-size
)
159 (declare (ignore family
))
160 (with-close-on-error (socket (create-socket :local
:stream
:active ef
161 :input-buffer-size input-buffer-size
162 :output-buffer-size output-buffer-size
))
163 (%%init-local-stream-active-socket socket local-filename remote-filename
)))
165 ;;; Local Stream Passive Socket creation
167 (defun %%init-local-stream-passive-socket
(socket local-filename reuse-address backlog
)
169 (bind-address socket
(ensure-address local-filename
:family
:local
)
170 :reuse-address reuse-address
)
171 (listen-on socket
:backlog backlog
))
174 (define-socket-creator (:local
:stream
:passive
)
175 (family ef
&key local-filename
(reuse-address t
) backlog
)
176 (declare (ignore family
))
177 (with-close-on-error (socket (create-socket :local
:stream
:passive ef
))
178 (%%init-local-stream-passive-socket socket local-filename reuse-address
179 (or backlog
*default-backlog-size
*))))
181 ;;; Internet Datagram Socket creation
183 (defun %%init-internet-datagram-active-socket
(socket broadcast interface reuse-address
184 local-host local-port remote-host remote-port
)
185 (setf (socket-option socket
:no-sigpipe
:if-does-not-exist nil
) t
)
186 (when broadcast
(setf (socket-option socket
:broadcast
) t
))
188 (bind-address socket
(ensure-hostname local-host
)
190 :reuse-address reuse-address
)
192 (setf (socket-option socket
:bind-to-device
) interface
)))
194 (connect socket
(ensure-hostname remote-host
)
198 (define-socket-creator (:internet
:datagram
:active
)
199 (family ef
&key broadcast interface
(reuse-address t
)
200 local-host local-port remote-host remote-port
)
201 (with-close-on-error (socket (%create-internet-socket family
:datagram
:active ef
))
202 (%%init-internet-datagram-active-socket socket broadcast interface reuse-address
203 local-host
(or local-port
0)
204 remote-host
(or remote-port
0))))
206 ;;; Local Datagram Socket creation
208 (defun %%init-local-datagram-active-socket
(socket local-filename remote-filename
)
209 (setf (socket-option socket
:no-sigpipe
:if-does-not-exist nil
) t
)
211 (bind-address socket
(ensure-address local-filename
:family
:local
)))
212 (when remote-filename
213 (connect socket
(ensure-address remote-filename
:family
:local
)))
216 (define-socket-creator (:local
:datagram
:active
)
217 (family ef
&key local-filename remote-filename
)
218 (declare (ignore family
))
219 (with-close-on-error (socket (create-socket :local
:datagram
:active ef
))
220 (%%init-local-datagram-active-socket socket local-filename remote-filename
)))
224 (defmethod make-socket (&rest args
&key
(address-family :internet
) (type :stream
)
225 (connect :active
) (ipv6 *ipv6
*)
226 (external-format :default
) &allow-other-keys
)
227 (when (eql :file address-family
) (setf address-family
:local
))
228 (check-type address-family
(member :internet
:local
:ipv4
:ipv6
) "one of :INTERNET, :LOCAL(or :FILE), :IPV4 or :IPV6")
229 (check-type type
(member :stream
:datagram
) "either :STREAM or :DATAGRAM")
230 (check-type connect
(member :active
:passive
) "either :ACTIVE or :PASSIVE")
231 (let ((args (remove-from-plist args
:address-family
:type
:connect
:external-format
:ipv6
)))
232 (when (eql :ipv4 address-family
) (setf ipv6 nil
))
234 (when (eql :internet address-family
) (setf address-family
+default-inet-address-family
+))
235 (multiple-value-case ((address-family type connect
))
236 (((:ipv4
:ipv6
) :stream
:active
)
237 (%make-internet-stream-active-socket args address-family external-format
))
238 (((:ipv4
:ipv6
) :stream
:passive
)
239 (%make-internet-stream-passive-socket args address-family external-format
))
240 ((:local
:stream
:active
)
241 (%make-local-stream-active-socket args
:local external-format
))
242 ((:local
:stream
:passive
)
243 (%make-local-stream-passive-socket args
:local external-format
))
244 (((:ipv4
:ipv6
) :datagram
)
245 (%make-internet-datagram-active-socket args address-family external-format
))
247 (%make-local-datagram-active-socket args
:local external-format
))))))
249 (define-compiler-macro make-socket
(&whole form
&environment env
&rest args
250 &key
(address-family :internet
) (type :stream
)
251 (connect :active
) (ipv6 '*ipv6
* ipv6p
)
252 (external-format :default
) &allow-other-keys
)
253 (when (eql :file address-family
) (setf address-family
:local
))
255 ((and (constantp address-family env
) (constantp type env
) (constantp connect env
))
256 (check-type address-family
(member :internet
:local
:ipv4
:ipv6
) "one of :INTERNET, :LOCAL(or :FILE), :IPV4 or :IPV6")
257 (check-type type
(member :stream
:datagram
) "either :STREAM or :DATAGRAM")
258 (check-type connect
(member :active
:passive
) "either :ACTIVE or :PASSIVE")
259 (let* ((family (if (member address-family
'(:ipv4
:ipv6
)) :internet address-family
))
260 (lower-function (make-first-level-name family type connect
))
261 (newargs (remove-from-plist args
:address-family
:type
:connect
:external-format
:ipv6
)))
263 (:internet
(setf address-family
'+default-inet-address-family
+))
264 (:ipv4
(setf ipv6 nil ipv6p t
)))
265 (let ((expansion `(,lower-function
(list ,@newargs
) ,address-family
,external-format
)))
266 (if ipv6p
`(let ((*ipv6
* ,ipv6
)) ,expansion
) expansion
))))
269 (defmacro with-open-socket
((var &rest args
) &body body
)
270 "Bind VAR to a socket created by passing ARGS to MAKE-SOCKET and execute BODY as implicit PROGN.
271 The socket is automatically closed upon exit."
272 `(with-open-stream (,var
(make-socket ,@args
)) ,@body
))
274 (defmacro with-accept-connection
((var passive-socket
&rest args
) &body body
)
275 "Bind VAR to a socket created by passing PASSIVE-SOCKET and ARGS to ACCEPT-CONNECTION and execute BODY as implicit PROGN.
276 The socket is automatically closed upon exit."
277 `(with-open-stream (,var
(accept-connection ,passive-socket
,@args
)) ,@body
))
279 ;;; MAKE-SOCKET-FROM-FD
281 ;;; FIXME: must come up with a way to find out
282 ;;; whether a socket is active or passive
283 (defmethod make-socket-from-fd ((fd integer
) &key
(connect :active
) (external-format :default
)
284 input-buffer-size output-buffer-size
)
285 (flet ((%get-address-family
(fd)
286 (with-sockaddr-storage-and-socklen (ss size
)
287 (%getsockname fd ss size
)
288 (eswitch ((foreign-slot-value ss
'sockaddr-storage
'family
) :test
#'=)
293 (eswitch ((get-socket-option-int fd sol-socket so-type
) :test
#'=)
294 (sock-stream :stream
)
295 (sock-dgram :datagram
))))
296 (create-socket (%get-address-family fd
)
298 connect external-format
:fd fd
299 :input-buffer-size input-buffer-size
300 :output-buffer-size output-buffer-size
)))
304 (defmethod make-socket-pair (&key
(type :stream
) (protocol :default
) (external-format :default
)
305 input-buffer-size output-buffer-size
)
306 (flet ((%make-socket-pair
(fd)
307 (make-socket-from-fd fd
:external-format external-format
308 :input-buffer-size input-buffer-size
309 :output-buffer-size output-buffer-size
)))
310 (multiple-value-bind (fd1 fd2
)
311 (multiple-value-call #'%socketpair
312 (translate-make-socket-keywords-to-constants :local type protocol
))
313 (values (%make-socket-pair fd1
)
314 (%make-socket-pair fd2
)))))
316 ;;; SEND/RECEIVE-FILE-DESCRIPTOR
318 (defun call-with-buffers-for-fd-passing (fn)
319 (with-foreign-object (msg 'msghdr
)
320 (isys:bzero msg size-of-msghdr
)
321 (with-foreign-pointer (buffer #.
(isys:cmsg.space size-of-int
) buffer-size
)
322 (isys:bzero buffer buffer-size
)
323 (with-foreign-slots ((control controllen
) msg msghdr
)
325 controllen buffer-size
)
326 (let ((cmsg (isys:cmsg.firsthdr msg
)))
327 (with-foreign-slots ((len level type
) cmsg cmsghdr
)
328 (setf len
(isys:cmsg.len size-of-int
)
331 (funcall fn msg cmsg
)))))))
333 (defmacro with-buffers-for-fd-passing
((msg-var cmsg-var
) &body body
)
334 `(call-with-buffers-for-fd-passing (lambda (,msg-var
,cmsg-var
) ,@body
)))
336 (defmethod send-file-descriptor ((socket local-socket
) file-descriptor
)
337 (with-buffers-for-fd-passing (msg cmsg
)
338 (let ((data (isys:cmsg.data cmsg
)))
339 (setf (mem-aref data
:int
) file-descriptor
)
340 (%sendmsg
(fd-of socket
) msg
0)
343 (defmethod receive-file-descriptor ((socket local-socket
))
344 (with-buffers-for-fd-passing (msg cmsg
)
345 (let ((data (isys:cmsg.data cmsg
)))
346 (%recvmsg
(fd-of socket
) msg
0)
347 (mem-aref data
:int
))))