source: branches/home/psmith/restructure/src/io/async-socket.lisp

Last change on this file was 104, checked in by psmith, 18 years ago

Improved error-handling on %socket

File size: 6.9 KB
Line 
1#|
2Copyright (c) 2006 Risto Laakso
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions
7are met:
81. Redistributions of source code must retain the above copyright
9   notice, this list of conditions and the following disclaimer.
102. Redistributions in binary form must reproduce the above copyright
11   notice, this list of conditions and the following disclaimer in the
12   documentation and/or other materials provided with the distribution.
133. The name of the author may not be used to endorse or promote products
14   derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26|#
27(in-package :nio)
28
29(declaim (optimize (debug 3) (speed 3) (space 0)))
30
31;;;; FFI
32
33(defconstant +af-inet+ 2)
34(defconstant +af-inet6+ 30)
35
36(defconstant +sock-stream+ 1)
37(defconstant +sock-dgram+ 2)
38
39#+(or darwin macosx freebsd)
40(defcstruct sockaddr-in
41  (len :uint8)
42  (family :uint8)
43  (port :uint16)
44  (addr :uint32)
45  (zero :char :count 8))
46
47#+linux
48(defcstruct sockaddr-in
49  (family :uint16)
50  (port :uint16)
51  (addr :uint32)
52  (zero :char :count 8))
53
54(defconstant +sockaddr-in-len+ #.(+ 1 1 2 4 8))
55
56(defcstruct sockaddr-in6
57  (len :uint8)
58  (family :uint8)
59  (port :uint16)
60  (flowinfo :uint32)
61  (addr :uint16 :count 8)
62  (scope-id :uint32))
63
64(defconstant +sockaddr-in6-len+ #.(+ 1 1 2 4 16 4))
65
66(defcfun ("socket" %socket) :int
67  (domain :int)
68  (type :int)
69  (protocol :int))
70
71(defcfun ("inet_pton" %inet-pton) :int
72  (af :int)
73  (src :string)
74  (dst :pointer))
75
76(defcfun ("htons" %htons) :uint16
77  (host-value :uint16))
78
79(defcfun ("bind" %bind) :int
80  (socket :int)
81  (sockaddr :pointer)
82  (socklent :long))
83
84(defcfun ("listen" %listen) :int
85  (socket :int)
86  (backlog :int))
87
88(defcfun ("accept" %accept) :int
89  (socket :int)
90  (sockaddr :pointer)
91  (socklen :pointer))
92
93(defcfun ("connect" %connect) :int
94  (socket :int)
95  (sockaddr :pointer)
96  (socklent :int))
97
98
99(define-condition fd-error (error)
100  ((error-number :initarg :error)))
101
102
103
104;;TODO put backlog on config
105(defun start-listen (socket-fd &optional (backlog 1000))
106  (%listen socket-fd backlog))
107
108
109;;;; IPv4
110
111(defun make-inet-socket (&optional (type :tcp))
112 (let ((fd (%socket +af-inet+ (ecase type (:tcp +sock-stream+) (:udp +sock-dgram+)) 0)))
113   (when (eql fd -1)
114     (perror)
115#+nio-debug     (format-log t "async-socket::make-inet-socket - %socket failed (errno ~A)~%" )
116     (error (make-instance 'fd-error :error (get-errno))))
117   fd))
118
119
120(defun init-inet-socket(sa port addr)
121
122    (memzero sa +sockaddr-in-len+)
123
124    ;; init struct
125    #+(or darwin macosx freebsd)
126    (setf (foreign-slot-value sa 'sockaddr-in 'len) +sockaddr-in-len+)
127
128    (setf (foreign-slot-value sa 'sockaddr-in 'port) (%htons port)
129          (foreign-slot-value sa 'sockaddr-in 'family) +af-inet+)
130
131    ;; set addr
132    (if (/= (%inet-pton +af-inet+ addr (foreign-slot-pointer sa 'sockaddr-in 'addr)) 1)
133        (error "inet_pton: Bad address ~A!" addr)))
134
135
136(defun bind-inet-socket (socket-fd port &optional (addr "127.0.0.1"))
137  (with-foreign-object (sa 'sockaddr-in)
138    (init-inet-socket sa port addr)
139
140    ;; bind
141    (if (= (%bind socket-fd sa +sockaddr-in-len+) 0)
142        t
143        nil)))
144
145(defun connect-inet-socket (socket-fd node)
146      (format-log t "async-socket:connect-inet-socket called with ~A, and ~A~%" socket-fd node)
147  (with-foreign-object (sa 'sockaddr-in)
148    (init-inet-socket sa (remote-port node) (remote-host node))
149    (let ((res (%connect socket-fd sa +sockaddr-in-len+)))
150      (format-log t "async-socket:connect-inet-socket library connect call returned ~A, and errno ~A~%" res (get-errno))
151      (if (= res -1)
152          nil
153          t))))
154
155
156;;;; IPv6
157
158(defun make-inet6-socket (&optional (type :tcp))
159  (%socket +af-inet6+ (ecase type (:tcp +sock-stream+) (:udp +sock-dgram+)) 0))
160
161(defun bind-inet6-socket (socket-fd port &optional (addr "::1"))
162  (with-foreign-object (sa 'sockaddr-in6)
163
164    (memzero sa +sockaddr-in6-len+)
165
166    ;; init struct
167    (setf (foreign-slot-value sa 'sockaddr-in6 'len) +sockaddr-in6-len+
168          (foreign-slot-value sa 'sockaddr-in6 'port) (%htons port)
169          (foreign-slot-value sa 'sockaddr-in6 'family) +af-inet6+)
170
171    ;; set addr
172    (if (/= (%inet-pton +af-inet6+ addr (foreign-slot-pointer sa 'sockaddr-in6 'addr)) 1)
173        (error "inet_pton: Bad address ~A!" addr))
174
175    ;; bind
176    (if (= (%bind socket-fd sa +sockaddr-in6-len+) 0)
177        t
178        nil)))
179
180
181
182;;;; SOCKET I/O
183
184(defun socket-accept (socket-fd connection-type)
185  "Accept connection from SOCKET-FD. Allocates and returns socket structure denoting the connection."
186
187  (flet ((parse-inet6-addr (addr) 
188           (let ((client-addr (foreign-slot-value addr 'sockaddr-in6 'addr)))
189             (loop for i from 0 to 7 
190                for hex = (mem-aref client-addr :unsigned-short i)
191                collect hex into res
192                finally (return res))))
193
194         (parse-inet4-addr (addr)
195           (let ((client-addr (foreign-slot-value addr 'sockaddr-in 'addr)))
196             (loop for i from 0 to 3
197                for hex = (logand (ash client-addr (- (* i 8))) #xFF)
198                collect hex into res
199                finally (return (nreverse res))))))
200
201    (with-foreign-object (addr 'sockaddr-in6)
202
203      (let ((len (foreign-alloc :unsigned-long :initial-element +sockaddr-in6-len+)))
204
205        ;; accept connection
206#+nio-debug (format-log t "async-socket::socket-accept - calling %accept~%")
207      (let* ((res (%accept socket-fd addr len)) 
208             (async-fd (create-state-machine connection-type res res (node nil nil))))
209
210#+nio-debug (format-log t "async-socket::socket-accept - create async-fd ~A~%" async-fd)
211
212        (unless (< res 0)
213          (let ((len-value (mem-ref len :unsigned-int)))
214
215            ;; parse sockaddr struct for remote client info
216
217            (with-slots (family remote-host remote-port) (socket async-fd)
218             
219              (cond
220                ((= len-value +sockaddr-in6-len+)
221                 (setf family :inet6
222                       remote-port (foreign-slot-value addr 'sockaddr-in6 'port)
223                       remote-host (parse-inet6-addr addr)))
224
225                ((= len-value +sockaddr-in-len+)
226                 (setf family :inet4
227                       remote-port (foreign-slot-value addr 'sockaddr-in 'port)
228                       remote-host (parse-inet4-addr addr)))))
229
230
231            (foreign-free len)
232            (if (>= res 0) async-fd nil)
233            )))))))
234
235
236
237(defun test-socket-connect(close)
238         (format t "Got fd's :")
239  (loop for i from 1 to 1025 do
240       (let ((fd (make-inet-socket)))
241         (format t "~A " fd)
242         (when (and close fd)
243           (close-fd fd)))))
Note: See TracBrowser for help on using the repository browser.