1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Indent-tabs-mode: NIL -*-
3 ;;; make-socket.lisp --- Socket creation.
5 ;;; Copyright (C) 2006-2008, 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 (defun create-socket (family type connect external-format
&optional fd
)
27 (make-instance (select-socket-class family type connect
:default
)
28 :family family
:file-descriptor fd
29 :external-format external-format
))
31 (defmacro with-close-on-error
((var value
) &body body
)
32 "Bind VAR to VALUE, execute BODY as implicit PROGN and return VAR.
33 On error call CLOSE with :ABORT T on VAR."
34 (with-gensyms (errorp)
35 `(let ((,var
,value
) (,errorp t
))
37 (multiple-value-prog1 (locally ,@body
,var
) (setf ,errorp nil
))
38 (when (and ,var
,errorp
) (close ,var
:abort t
))))))
40 (defmacro with-guard-again-non-list-args-and-destructuring-bind-errors
41 (form args
&body body
)
43 (handler-case (progn ,@body
)
44 (error (err) `(error ,err
)))
47 (define-symbol-macro +default-host
+
48 (if *ipv6
* +ipv6-unspecified
+ +ipv4-unspecified
+))
50 ;;; Internet Stream Active Socket creation
52 (defun %%make-internet-stream-active-socket
(family ef keepalive nodelay reuse-address
53 local-host local-port remote-host remote-port
)
54 (let ((local-port (ensure-numerical-service local-port
))
55 (remote-port (ensure-numerical-service remote-port
)))
56 (with-close-on-error (socket (create-socket family
:stream
:active ef
))
57 (when keepalive
(setf (socket-option socket
:keep-alive
) t
))
58 (when nodelay
(setf (socket-option socket
:tcp-nodelay
) t
))
60 (bind-address socket
(convert-or-lookup-inet-address local-host
)
62 :reuse-address reuse-address
))
63 (when (plusp remote-port
)
64 (connect socket
(convert-or-lookup-inet-address remote-host
)
65 :port remote-port
)))))
67 (defun %make-internet-stream-active-socket
(args family ef
)
68 (destructuring-bind (&key keepalive nodelay
(reuse-address t
)
69 local-host
(local-port 0)
70 (remote-host +default-host
+) (remote-port 0))
72 (%%make-internet-stream-active-socket family ef keepalive nodelay reuse-address
73 local-host local-port remote-host remote-port
)))
75 (define-compiler-macro %make-internet-stream-active-socket
(&whole form args family ef
)
76 (with-guard-again-non-list-args-and-destructuring-bind-errors
78 (destructuring-bind (&key keepalive nodelay
(reuse-address t
)
79 local-host
(local-port 0)
80 (remote-host +default-host
+) (remote-port 0))
82 `(%%make-internet-stream-active-socket
,family
,ef
,keepalive
,nodelay
,reuse-address
83 ,local-host
,local-port
,remote-host
,remote-port
))))
85 ;;; Internet Stream Passive Socket creation
87 (defun %%make-internet-stream-passive-socket
(family ef interface reuse-address
88 local-host local-port backlog
)
89 (let ((local-port (ensure-numerical-service local-port
)))
90 (with-close-on-error (socket (create-socket family
:stream
:passive ef
))
93 (setf (socket-option socket
:bind-to-device
) interface
))
94 (bind-address socket
(convert-or-lookup-inet-address local-host
)
96 :reuse-address reuse-address
)
97 (socket-listen socket
:backlog backlog
)))))
99 (defun %make-internet-stream-passive-socket
(args family ef
)
100 (destructuring-bind (&key interface
(reuse-address t
)
101 (local-host +default-host
+) (local-port 0)
102 (backlog *default-backlog-size
*))
104 (%%make-internet-stream-passive-socket family ef interface reuse-address
105 local-host local-port backlog
)))
107 (define-compiler-macro %make-internet-stream-passive-socket
(&whole form args family ef
)
108 (with-guard-again-non-list-args-and-destructuring-bind-errors
110 (destructuring-bind (&key interface
(reuse-address t
)
111 (local-host +default-host
+) (local-port 0)
112 (backlog *default-backlog-size
*))
114 `(%%make-internet-stream-passive-socket
,family
,ef
,interface
,reuse-address
115 ,local-host
,local-port
,backlog
))))
117 ;;; Local Stream Active Socket creation
119 (defun %%make-local-stream-active-socket
(family ef local-filename remote-filename
)
120 (with-close-on-error (socket (create-socket family
:stream
:active ef
))
122 (bind-address socket
(ensure-address local-filename
:local
)))
123 (when remote-filename
124 (connect socket
(ensure-address remote-filename
:local
)))))
126 (defun %make-local-stream-active-socket
(args family ef
)
127 (destructuring-bind (&key local-filename remote-filename
)
129 (%%make-local-stream-active-socket family ef local-filename remote-filename
)))
131 (define-compiler-macro %make-local-stream-active-socket
(&whole form args family ef
)
132 (with-guard-again-non-list-args-and-destructuring-bind-errors
134 (destructuring-bind (&key local-filename remote-filename
)
136 `(%%make-local-stream-active-socket
,family
,ef
,local-filename
,remote-filename
))))
138 ;;; Local Stream Passive Socket creation
140 (defun %%make-local-stream-passive-socket
(family ef local-filename reuse-address backlog
)
141 (with-close-on-error (socket (create-socket family
:stream
:passive ef
))
143 (bind-address socket
(ensure-address local-filename
:local
)
144 :reuse-address reuse-address
)
145 (socket-listen socket
:backlog backlog
))))
147 (defun %make-local-stream-passive-socket
(args family ef
)
148 (destructuring-bind (&key local-filename
(reuse-address t
)
149 (backlog *default-backlog-size
*))
151 (%%make-local-stream-passive-socket family ef local-filename reuse-address backlog
)))
153 (define-compiler-macro %make-local-stream-passive-socket
(&whole form args family ef
)
154 (with-guard-again-non-list-args-and-destructuring-bind-errors
156 (destructuring-bind (&key local-filename
(reuse-address t
)
157 (backlog *default-backlog-size
*))
159 `(%%make-local-stream-passive-socket
,family
,ef
,local-filename
,reuse-address
,backlog
))))
161 ;;; Internet Datagram Socket creation
163 (defun %%make-internet-datagram-socket
(family ef broadcast interface reuse-address
164 local-host local-port remote-host remote-port
)
165 (let ((local-port (ensure-numerical-service local-port
))
166 (remote-port (ensure-numerical-service remote-port
)))
167 (with-close-on-error (socket (create-socket family
:datagram
:active ef
))
168 (when broadcast
(setf (socket-option socket
:broadcast
) t
))
170 (bind-address socket
(convert-or-lookup-inet-address local-host
)
172 :reuse-address reuse-address
)
174 (setf (socket-option socket
:bind-to-device
) interface
)))
175 (when (plusp remote-port
)
176 (connect socket
(convert-or-lookup-inet-address remote-host
)
177 :port remote-port
)))))
179 (defun %make-internet-datagram-socket
(args family ef
)
180 (destructuring-bind (&key broadcast interface
(reuse-address t
)
181 local-host
(local-port 0)
182 (remote-host +default-host
+) (remote-port 0))
184 (%%make-internet-datagram-socket family ef broadcast interface reuse-address
185 local-host local-port remote-host remote-port
)))
187 (define-compiler-macro %make-internet-datagram-socket
(&whole form args family ef
)
188 (with-guard-again-non-list-args-and-destructuring-bind-errors
190 (destructuring-bind (&key broadcast interface
(reuse-address t
)
191 local-host
(local-port 0)
192 (remote-host +default-host
+) (remote-port 0))
194 `(%%make-internet-datagram-socket
,family
,ef
,broadcast
,interface
,reuse-address
195 ,local-host
,local-port
,remote-host
,remote-port
))))
197 ;;; Local Datagram Socket creation
199 (defun %%make-local-datagram-socket
(family ef local-filename remote-filename
)
200 (with-close-on-error (socket (create-socket family
:datagram
:active ef
))
202 (bind-address socket
(ensure-address local-filename
:local
)))
203 (when remote-filename
204 (connect socket
(ensure-address remote-filename
:local
)))))
206 (defun %make-local-datagram-socket
(args family ef
)
207 (destructuring-bind (&key local-filename remote-filename
)
209 (%%make-local-datagram-socket family ef local-filename remote-filename
)))
211 (define-compiler-macro %make-local-datagram-socket
(&whole form args family ef
)
212 (with-guard-again-non-list-args-and-destructuring-bind-errors
214 (destructuring-bind (&key local-filename remote-filename
)
216 `(%%make-local-datagram-socket
,family
,ef
,local-filename
,remote-filename
))))
220 (defun make-socket (&rest args
&key
(family :internet
) (type :stream
)
221 (connect :active
) (ipv6 *ipv6
*)
222 (external-format :default
) &allow-other-keys
)
223 "Creates a socket instance of the appropriate subclass of SOCKET."
224 (check-type family
(member :internet
:local
:ipv4
:ipv6
) "one of :INTERNET, :LOCAL, :IPV4 or :IPV6")
225 (check-type type
(member :stream
:datagram
) "either :STREAM or :DATAGRAM")
226 (check-type connect
(member :active
:passive
) "either :ACTIVE or :PASSIVE")
227 (let ((args (remove-from-plist args
:family
:type
:connect
:external-format
:ipv6
)))
229 (:internet
(setf family
+default-inet-family
+))
230 (:ipv4
(setf ipv6 nil
)))
232 (multiple-value-case (family type connect
)
233 (((:ipv4
:ipv6
) :stream
:active
)
234 (%make-internet-stream-active-socket args family external-format
))
235 (((:ipv4
:ipv6
) :stream
:passive
)
236 (%make-internet-stream-passive-socket args family external-format
))
237 ((:local
:stream
:active
)
238 (%make-local-stream-active-socket args
:local external-format
))
239 ((:local
:stream
:passive
)
240 (%make-local-stream-passive-socket args
:local external-format
))
241 (((:ipv4
:ipv6
) :datagram
)
242 (%make-internet-datagram-socket args family external-format
))
244 (%make-local-datagram-socket args
:local external-format
))))))
246 (define-compiler-macro make-socket
(&whole form
&rest args
&key
(family :internet
) (type :stream
)
247 (connect :active
) (ipv6 '*ipv6
* ipv6p
)
248 (external-format :default
) &allow-other-keys
)
250 ((and (constantp family
) (constantp type
) (constantp connect
))
251 (check-type family
(member :internet
:local
:ipv4
:ipv6
) "one of :INTERNET, :LOCAL, :IPV4 or :IPV6")
252 (check-type type
(member :stream
:datagram
) "either :STREAM or :DATAGRAM")
253 (check-type connect
(member :active
:passive
) "either :ACTIVE or :PASSIVE")
254 (let ((lower-function
255 (multiple-value-case (family type connect
)
256 (((:ipv4
:ipv6
:internet
) :stream
:active
) '%make-internet-stream-active-socket
)
257 (((:ipv4
:ipv6
:internet
) :stream
:passive
) '%make-internet-stream-passive-socket
)
258 ((:local
:stream
:active
) '%make-local-stream-active-socket
)
259 ((:local
:stream
:passive
) '%make-local-stream-passive-socket
)
260 (((:ipv4
:ipv6
:internet
) :datagram
) '%make-internet-datagram-socket
)
261 ((:local
:datagram
) '%make-local-datagram-socket
)))
262 (newargs (remove-from-plist args
:family
:type
:connect
:external-format
:ipv6
)))
264 (:internet
(setf family
'+default-inet-family
+))
265 (:ipv4
(setf ipv6 nil
)))
266 (let ((expansion `(,lower-function
(list ,@newargs
) ,family
,external-format
)))
267 (if (or ipv6p
(eq :ipv4 family
))
268 `(let ((*ipv6
* ,ipv6
)) ,expansion
)
272 (defmacro with-open-socket
((var &rest args
) &body body
)
273 "VAR is bound to a socket created by passing ARGS to
274 MAKE-SOCKET and BODY is executed as implicit PROGN. The socket
275 is automatically closed upon exit."
276 `(with-open-stream (,var
(make-socket ,@args
)) ,@body
))
278 (defmacro with-accept-connection
((var passive-socket
&rest args
) &body body
)
279 "VAR is bound to a socket created by passing PASSIVE-SOCKET and ARGS to
280 ACCEPT-CONNECTION and BODY is executed as implicit PROGN. The socket
281 is automatically closed upon exit."
282 `(with-open-stream (,var
(accept-connection ,passive-socket
,@args
)) ,@body
))
284 ;;; MAKE-SOCKET-STREAM
286 (defun get-address-family (fd)
287 (with-sockaddr-storage (ss)
288 (with-socklen (size size-of-sockaddr-storage
)
289 (%getsockname fd ss size
)
290 (foreign-slot-value ss
'sockaddr-storage
'family
))))
292 (defun make-socket-stream (fd &key
(external-format :default
) (errorp t
))
293 "Creates an active stream socket instance of the appropriate subclass of SOCKET using `FD'.
294 The address family of the sockets is automatically discovered using OS functions. If `FD' is
295 an invalid socket descriptor and `ERRORP' is not NIL a condition subtype of POSIX-ERROR
296 is signaled, otherwise two values are returned: NIL and the specific condition object."
297 (flet ((%make-socket-stream
()
298 (let ((family (switch ((get-address-family fd
) :test
#'=)
302 (create-socket family
:stream
:active external-format fd
))))
304 (%make-socket-stream
)
305 (ignore-some-conditions (posix-error)
306 (%make-socket-stream
)))))