1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; indent-tabs-mode: nil -*-
3 ;;; --- Socket creation.
6 (in-package :net.sockets
)
8 (defun create-socket (family type connect external-format
&key
9 fd input-buffer-size output-buffer-size
)
10 (make-instance (select-socket-class family type connect
:default
)
11 :address-family family
:file-descriptor fd
12 :external-format external-format
13 :input-buffer-size input-buffer-size
14 :output-buffer-size output-buffer-size
))
16 (define-compiler-macro create-socket
(&whole form family type connect external-format
17 &key fd input-buffer-size output-buffer-size
)
19 ((and (constantp family
) (constantp type
) (constantp connect
))
20 `(make-instance ',(select-socket-class family type connect
:default
)
21 :address-family
,family
:file-descriptor
,fd
22 :external-format
,external-format
23 :input-buffer-size
,input-buffer-size
24 :output-buffer-size
,output-buffer-size
))
27 (defmacro with-close-on-error
((var value
) &body body
)
28 "Bind `VAR' to `VALUE', execute `BODY' as implicit PROGN and return `VAR'.
29 If a non-local exit occurs during the execution of `BODY' call CLOSE with :ABORT T on `VAR'."
30 (with-gensyms (errorp)
31 `(let ((,var
,value
) (,errorp t
))
33 (multiple-value-prog1 (locally ,@body
,var
) (setf ,errorp nil
))
34 (when (and ,var
,errorp
) (close ,var
:abort t
))))))
36 (defmacro %create-internet-socket
(family &rest args
)
38 (:ipv4
(create-socket :ipv4
,@args
))
39 (:ipv6
(create-socket :ipv6
,@args
))))
41 (defmacro with-guard-against-non-list-args-and-destructuring-bind-errors
42 (form args
&body body
)
44 (handler-case (progn ,@body
)
45 (error (err) `(error ,err
)))
48 ;;; Internet Stream Active Socket creation
50 (defun %%init-internet-stream-active-socket
(socket keepalive nodelay reuse-address
51 local-host local-port remote-host remote-port
)
52 (let ((local-port (ensure-numerical-service local-port
))
53 (remote-port (ensure-numerical-service remote-port
)))
54 #+freebsd
(setf (socket-option socket
:no-sigpipe
) t
)
55 (when keepalive
(setf (socket-option socket
:keep-alive
) t
))
56 (when nodelay
(setf (socket-option socket
:tcp-nodelay
) t
))
58 (bind-address socket
(ensure-hostname local-host
)
60 :reuse-address reuse-address
))
61 (when (plusp remote-port
)
62 (connect socket
(ensure-hostname remote-host
)
65 (declaim (inline %%make-internet-stream-active-socket
))
66 (defun %%make-internet-stream-active-socket
(family ef keepalive nodelay reuse-address
67 local-host local-port remote-host remote-port
68 input-buffer-size output-buffer-size
)
69 (with-close-on-error (socket (%create-internet-socket family
:stream
:active ef
70 :input-buffer-size input-buffer-size
71 :output-buffer-size output-buffer-size
))
72 (%%init-internet-stream-active-socket socket keepalive nodelay reuse-address
73 local-host local-port remote-host remote-port
)))
75 (defun %make-internet-stream-active-socket
(args family ef
)
76 (destructuring-bind (&key keepalive nodelay
(reuse-address t
)
77 local-host
(local-port 0)
78 (remote-host +any-host
+) (remote-port 0)
79 input-buffer-size output-buffer-size
)
81 (%%make-internet-stream-active-socket family ef keepalive nodelay reuse-address
82 local-host local-port remote-host remote-port
83 input-buffer-size output-buffer-size
)))
85 (define-compiler-macro %make-internet-stream-active-socket
(&whole form args family ef
)
86 (with-guard-against-non-list-args-and-destructuring-bind-errors
88 (destructuring-bind (&key keepalive nodelay
(reuse-address t
)
89 local-host
(local-port 0)
90 (remote-host +any-host
+) (remote-port 0)
91 input-buffer-size output-buffer-size
)
93 `(%%make-internet-stream-active-socket
,family
,ef
,keepalive
,nodelay
,reuse-address
94 ,local-host
,local-port
,remote-host
,remote-port
95 ,input-buffer-size
,output-buffer-size
))))
97 ;;; Internet Stream Passive Socket creation
99 (defun %%init-internet-stream-passive-socket
(socket interface reuse-address
100 local-host local-port backlog
)
101 #-linux
(declare (ignore interface
))
102 (let ((local-port (ensure-numerical-service local-port
)))
106 (setf (socket-option socket
:bind-to-device
) interface
))
107 (bind-address socket
(ensure-hostname local-host
)
109 :reuse-address reuse-address
)
110 (listen-on socket
:backlog backlog
))))
112 (declaim (inline %%make-internet-stream-passive-socket
))
113 (defun %%make-internet-stream-passive-socket
(family ef interface reuse-address
114 local-host local-port backlog
)
115 (with-close-on-error (socket (%create-internet-socket family
:stream
:passive ef
))
116 (%%init-internet-stream-passive-socket socket interface reuse-address
117 local-host local-port backlog
)))
119 (defun %make-internet-stream-passive-socket
(args family ef
)
120 (destructuring-bind (&key interface
(reuse-address t
)
121 (local-host +any-host
+) (local-port 0)
122 (backlog *default-backlog-size
*))
124 (%%make-internet-stream-passive-socket family ef interface reuse-address
125 local-host local-port backlog
)))
127 (define-compiler-macro %make-internet-stream-passive-socket
(&whole form args family ef
)
128 (with-guard-against-non-list-args-and-destructuring-bind-errors
130 (destructuring-bind (&key interface
(reuse-address t
)
131 (local-host +any-host
+) (local-port 0)
132 (backlog *default-backlog-size
*))
134 `(%%make-internet-stream-passive-socket
,family
,ef
,interface
,reuse-address
135 ,local-host
,local-port
,backlog
))))
137 ;;; Local Stream Active Socket creation
139 (defun %%init-local-stream-active-socket
(socket local-filename remote-filename
)
140 #+freebsd
(setf (socket-option socket
:no-sigpipe
) t
)
142 (bind-address socket
(ensure-address local-filename
:family
:local
)))
143 (when remote-filename
144 (connect socket
(ensure-address remote-filename
:family
:local
))))
146 (declaim (inline %%make-local-stream-active-socket
))
147 (defun %%make-local-stream-active-socket
(family ef local-filename remote-filename
148 input-buffer-size output-buffer-size
)
149 (declare (ignore family
))
150 (with-close-on-error (socket (create-socket :local
:stream
:active ef
151 :input-buffer-size input-buffer-size
152 :output-buffer-size output-buffer-size
))
153 (%%init-local-stream-active-socket socket local-filename remote-filename
)))
155 (defun %make-local-stream-active-socket
(args family ef
)
156 (destructuring-bind (&key local-filename remote-filename
157 input-buffer-size output-buffer-size
)
159 (%%make-local-stream-active-socket family ef local-filename remote-filename
160 input-buffer-size output-buffer-size
)))
162 (define-compiler-macro %make-local-stream-active-socket
(&whole form args family ef
)
163 (with-guard-against-non-list-args-and-destructuring-bind-errors
165 (destructuring-bind (&key local-filename remote-filename
166 input-buffer-size output-buffer-size
)
168 `(%%make-local-stream-active-socket
,family
,ef
,local-filename
,remote-filename
169 ,input-buffer-size
,output-buffer-size
))))
171 ;;; Local Stream Passive Socket creation
173 (defun %%init-local-stream-passive-socket
(socket local-filename reuse-address backlog
)
175 (bind-address socket
(ensure-address local-filename
:family
:local
)
176 :reuse-address reuse-address
)
177 (listen-on socket
:backlog backlog
)))
179 (declaim (inline %%make-local-stream-passive-socket
))
180 (defun %%make-local-stream-passive-socket
(family ef local-filename reuse-address backlog
)
181 (declare (ignore family
))
182 (with-close-on-error (socket (create-socket :local
:stream
:passive ef
))
183 (%%init-local-stream-passive-socket socket local-filename reuse-address backlog
)))
185 (defun %make-local-stream-passive-socket
(args family ef
)
186 (destructuring-bind (&key local-filename
(reuse-address t
)
187 (backlog *default-backlog-size
*))
189 (%%make-local-stream-passive-socket family ef local-filename reuse-address backlog
)))
191 (define-compiler-macro %make-local-stream-passive-socket
(&whole form args family ef
)
192 (with-guard-against-non-list-args-and-destructuring-bind-errors
194 (destructuring-bind (&key local-filename
(reuse-address t
)
195 (backlog *default-backlog-size
*))
197 `(%%make-local-stream-passive-socket
,family
,ef
,local-filename
,reuse-address
,backlog
))))
199 ;;; Internet Datagram Socket creation
201 (defun %%init-internet-datagram-socket
(socket broadcast interface reuse-address
202 local-host local-port remote-host remote-port
)
203 #-linux
(declare (ignore interface
))
204 #+freebsd
(setf (socket-option socket
:no-sigpipe
) t
)
205 (let ((local-port (ensure-numerical-service local-port
))
206 (remote-port (ensure-numerical-service remote-port
)))
207 (when broadcast
(setf (socket-option socket
:broadcast
) t
))
209 (bind-address socket
(ensure-hostname local-host
)
211 :reuse-address reuse-address
)
214 (setf (socket-option socket
:bind-to-device
) interface
)))
215 (when (plusp remote-port
)
216 (connect socket
(ensure-hostname remote-host
)
217 :port remote-port
))))
219 (declaim (inline %%make-internet-datagram-socket
))
220 (defun %%make-internet-datagram-socket
(family ef broadcast interface reuse-address
221 local-host local-port remote-host remote-port
)
222 (with-close-on-error (socket (%create-internet-socket family
:datagram
:active ef
))
223 (%%init-internet-datagram-socket socket broadcast interface reuse-address
224 local-host local-port remote-host remote-port
)))
226 (defun %make-internet-datagram-socket
(args family ef
)
227 (destructuring-bind (&key broadcast interface
(reuse-address t
)
228 local-host
(local-port 0)
229 (remote-host +any-host
+) (remote-port 0))
231 (%%make-internet-datagram-socket family ef broadcast interface reuse-address
232 local-host local-port remote-host remote-port
)))
234 (define-compiler-macro %make-internet-datagram-socket
(&whole form args family ef
)
235 (with-guard-against-non-list-args-and-destructuring-bind-errors
237 (destructuring-bind (&key broadcast interface
(reuse-address t
)
238 local-host
(local-port 0)
239 (remote-host +any-host
+) (remote-port 0))
241 `(%%make-internet-datagram-socket
,family
,ef
,broadcast
,interface
,reuse-address
242 ,local-host
,local-port
,remote-host
,remote-port
))))
244 ;;; Local Datagram Socket creation
246 (defun %%init-local-datagram-socket
(socket local-filename remote-filename
)
247 #+freebsd
(setf (socket-option socket
:no-sigpipe
) t
)
249 (bind-address socket
(ensure-address local-filename
:family
:local
)))
250 (when remote-filename
251 (connect socket
(ensure-address remote-filename
:family
:local
))))
253 (declaim (inline %%make-local-datagram-socket
))
254 (defun %%make-local-datagram-socket
(family ef local-filename remote-filename
)
255 (declare (ignore family
))
256 (with-close-on-error (socket (create-socket :local
:datagram
:active ef
))
257 (%%init-local-datagram-socket socket local-filename remote-filename
)))
259 (defun %make-local-datagram-socket
(args family ef
)
260 (destructuring-bind (&key local-filename remote-filename
)
262 (%%make-local-datagram-socket family ef local-filename remote-filename
)))
264 (define-compiler-macro %make-local-datagram-socket
(&whole form args family ef
)
265 (with-guard-against-non-list-args-and-destructuring-bind-errors
267 (destructuring-bind (&key local-filename remote-filename
)
269 `(%%make-local-datagram-socket
,family
,ef
,local-filename
,remote-filename
))))
273 (defun make-socket (&rest args
&key
(address-family :internet
) (type :stream
)
274 (connect :active
) (ipv6 *ipv6
*)
275 (external-format :default
) &allow-other-keys
)
276 "Creates a socket instance of the appropriate subclass of SOCKET."
277 (check-type address-family
(member :internet
:local
:ipv4
:ipv6
) "one of :INTERNET, :LOCAL, :IPV4 or :IPV6")
278 (check-type type
(member :stream
:datagram
) "either :STREAM or :DATAGRAM")
279 (check-type connect
(member :active
:passive
) "either :ACTIVE or :PASSIVE")
280 (let ((args (remove-from-plist args
:address-family
:type
:connect
:external-format
:ipv6
)))
281 (when (eq :ipv4 address-family
) (setf ipv6 nil
))
283 (when (eq :internet address-family
) (setf address-family
+default-inet-address-family
+))
284 (multiple-value-case ((address-family type connect
) :test
#'eq
)
285 (((:ipv4
:ipv6
) :stream
:active
)
286 (%make-internet-stream-active-socket args address-family external-format
))
287 (((:ipv4
:ipv6
) :stream
:passive
)
288 (%make-internet-stream-passive-socket args address-family external-format
))
289 ((:local
:stream
:active
)
290 (%make-local-stream-active-socket args
:local external-format
))
291 ((:local
:stream
:passive
)
292 (%make-local-stream-passive-socket args
:local external-format
))
293 (((:ipv4
:ipv6
) :datagram
)
294 (%make-internet-datagram-socket args address-family external-format
))
296 (%make-local-datagram-socket args
:local external-format
))))))
298 (define-compiler-macro make-socket
(&whole form
&rest args
&key
(address-family :internet
) (type :stream
)
299 (connect :active
) (ipv6 '*ipv6
* ipv6p
)
300 (external-format :default
) &allow-other-keys
)
302 ((and (constantp address-family
) (constantp type
) (constantp connect
))
303 (check-type address-family
(member :internet
:local
:ipv4
:ipv6
) "one of :INTERNET, :LOCAL, :IPV4 or :IPV6")
304 (check-type type
(member :stream
:datagram
) "either :STREAM or :DATAGRAM")
305 (check-type connect
(member :active
:passive
) "either :ACTIVE or :PASSIVE")
306 (let ((lower-function
307 (multiple-value-case ((address-family type connect
) :test
#'eq
)
308 (((:ipv4
:ipv6
:internet
) :stream
:active
) '%make-internet-stream-active-socket
)
309 (((:ipv4
:ipv6
:internet
) :stream
:passive
) '%make-internet-stream-passive-socket
)
310 ((:local
:stream
:active
) '%make-local-stream-active-socket
)
311 ((:local
:stream
:passive
) '%make-local-stream-passive-socket
)
312 (((:ipv4
:ipv6
:internet
) :datagram
) '%make-internet-datagram-socket
)
313 ((:local
:datagram
) '%make-local-datagram-socket
)))
314 (newargs (remove-from-plist args
:address-family
:type
:connect
:external-format
:ipv6
)))
315 (multiple-value-case (address-family)
316 (:internet
(setf address-family
'+default-inet-address-family
+))
317 (:ipv4
(setf ipv6 nil
)))
318 (let ((expansion `(,lower-function
(list ,@newargs
) ,address-family
,external-format
)))
319 (if (or ipv6p
(eq :ipv4 address-family
))
320 `(let ((*ipv6
* ,ipv6
)) ,expansion
)
324 (defmacro with-open-socket
((var &rest args
) &body body
)
325 "VAR is bound to a socket created by passing ARGS to
326 MAKE-SOCKET and BODY is executed as implicit PROGN. The socket
327 is automatically closed upon exit."
328 `(with-open-stream (,var
(make-socket ,@args
)) ,@body
))
330 (defmacro with-accept-connection
((var passive-socket
&rest args
) &body body
)
331 "VAR is bound to a socket created by passing PASSIVE-SOCKET and ARGS to
332 ACCEPT-CONNECTION and BODY is executed as implicit PROGN. The socket
333 is automatically closed upon exit."
334 `(with-open-stream (,var
(accept-connection ,passive-socket
,@args
)) ,@body
))
336 ;;; MAKE-SOCKET-FROM-FD
338 ;;; FIXME: must come up with a way to find out
339 ;;; whether a socket is active or passive
340 (defun make-socket-from-fd (fd &key
(connect :active
) (external-format :default
)
341 input-buffer-size output-buffer-size
)
342 "Creates an socket instance of the appropriate subclass of SOCKET using `FD'.
343 The connection type of the socket must be specified(:ACTIVE or :PASSIVE).
344 The address family and type of the socket is automatically discovered using OS functions. Buffer sizes
345 for the new socket can also be specified using `INPUT-BUFFER-SIZE' and `OUTPUT-BUFFER-SIZE'."
346 (flet ((%get-address-family
(fd)
347 (with-sockaddr-storage-and-socklen (ss size
)
348 (%getsockname fd ss size
)
349 (foreign-slot-value ss
'sockaddr-storage
'family
)
350 (eswitch ((foreign-slot-value ss
'sockaddr-storage
'family
) :test
#'=)
355 (eswitch ((get-socket-option-int fd sol-socket so-type
) :test
#'=)
356 (sock-stream :stream
)
357 (sock-dgram :datagram
))))
358 (create-socket (%get-address-family fd
)
360 connect external-format
:fd fd
361 :input-buffer-size input-buffer-size
362 :output-buffer-size output-buffer-size
)))
366 (defun make-socket-pair (&key
(type :stream
) (protocol :default
) (external-format :default
)
367 input-buffer-size output-buffer-size
)
368 "Creates a pair of sockets connected to each other.
369 The socket type can be either :STREAM or :DATAGRAM. Currently OSes can only create :LOCAL sockets this way.
370 Buffer sizes for the new sockets can also be specified using `INPUT-BUFFER-SIZE' and `OUTPUT-BUFFER-SIZE'."
371 (flet ((%make-socket-pair
(fd)
372 (make-socket-from-fd fd
:external-format external-format
373 :input-buffer-size input-buffer-size
374 :output-buffer-size output-buffer-size
)))
375 (multiple-value-bind (fd1 fd2
)
376 (multiple-value-call #'%socketpair
377 (translate-make-socket-keywords-to-constants :local type protocol
))
378 (values (%make-socket-pair fd1
)
379 (%make-socket-pair fd2
)))))
381 ;;; SEND/RECEIVE-FILE-DESCRIPTOR
383 (defun call-with-buffers-for-fd-passing (fn)
384 (with-foreign-object (msg 'msghdr
)
385 (bzero msg size-of-msghdr
)
386 (with-foreign-pointer (buffer (%cmsg-space size-of-int
) buffer-size
)
387 (bzero buffer buffer-size
)
388 (with-foreign-slots ((control controllen
) msg msghdr
)
390 controllen buffer-size
)
391 (let ((cmsg (%cmsg-firsthdr msg
)))
392 (with-foreign-slots ((len level type
) cmsg cmsghdr
)
393 (setf len
(%cmsg-len size-of-int
)
396 (funcall fn msg cmsg
)))))))
398 (defmacro with-buffers-for-fd-passing
((msg-var cmsg-var
) &body body
)
399 `(call-with-buffers-for-fd-passing #'(lambda (,msg-var
,cmsg-var
) ,@body
)))
401 (defmethod send-file-descriptor ((socket local-socket
) file-descriptor
)
402 (with-buffers-for-fd-passing (msg cmsg
)
403 (let ((data (%cmsg-data cmsg
)))
404 (setf (mem-ref data
:int
) file-descriptor
)
405 (%sendmsg
(fd-of socket
) msg
0)
408 (defmethod receive-file-descriptor ((socket local-socket
))
409 (with-buffers-for-fd-passing (msg cmsg
)
410 (let ((data (%cmsg-data cmsg
)))
411 (%recvmsg
(fd-of socket
) msg
0)
412 (mem-ref data
:int
))))