1 ;;;; -*- Mode: Lisp; indent-tabs-mode: nil -*-
3 ;;; --- iolib.sockets test suite.
6 (in-package :iolib-tests
)
8 (in-suite :iolib.sockets
)
10 (iolib.base
:enable-literal-reader
)
12 (defparameter *echo-address
* (lookup-hostname "deneb.cddr.org"))
13 (defparameter *echo-port
* 7)
14 (defparameter *echo-timeout
* 2)
19 (test (address-to-vector.1 :compile-at
:definition-time
)
20 (is (equalp (address-to-vector "127.0.0.1")
21 (values #(127 0 0 1) :ipv4
))))
23 ;;; and an address with bit 8 set on some octets
24 (test (address-to-vector.2 :compile-at
:definition-time
)
25 (is (equalp (address-to-vector "242.1.211.3")
26 (values #(242 1 211 3) :ipv4
))))
28 (test (address-to-vector.3 :compile-at
:definition-time
)
29 (is (equalp (address-to-vector "::")
30 (values #(0 0 0 0 0 0 0 0) :ipv6
))))
32 ;;; RT: used to return the PARSE-ERROR as a secondary value.
33 (test (string-address-to-vector.1 :compile-at
:definition-time
)
34 (is-false (string-address-to-vector "256.0.0.1")))
36 ;;; RT: should only ignore PARSE-ERRORs.
37 (test (string-address-to-vector.2 :compile-at
:definition-time
)
39 (string-address-to-vector 'not-a-string
)))
41 ;;; RT: should signal a PARSE-ERROR when given an invalid string.
42 (test (ensure-address.1 :compile-at
:definition-time
)
44 (ensure-address "ff0x::114")))
47 (test (ensure-address.2 :compile-at
:definition-time
)
49 (ensure-address "127.0.256.1")))
51 (test (ensure-address.3 :compile-at
:definition-time
)
53 (or (ensure-address "ff0x::114" :errorp nil
)
54 (ensure-address "127.0.256.1" :errorp nil
))))
56 (test (integer-to-dotted-and-back :compile-at
:definition-time
)
58 (every (lambda (s) (string= s
(integer-to-dotted (dotted-to-integer s
))))
59 '("0.0.255.0" "0.255.255.0" "0.255.255.1"))))
61 (test (integer-to-dotted.1 :compile-at
:definition-time
)
62 (is (string= (integer-to-dotted 0)
65 (test (integer-to-dotted.2 :compile-at
:definition-time
)
66 (is (string= (integer-to-dotted +max-ipv4-value
+)
69 (test (integer-to-dotted.3 :compile-at
:definition-time
)
71 (integer-to-dotted (1+ +max-ipv4-value
+))))
73 (test (integer-to-dotted.4 :compile-at
:definition-time
)
75 (integer-to-dotted -
1)))
77 (test (dotted-to-vector.1 :compile-at
:definition-time
)
78 (is (equalp (mapcar #'dotted-to-vector
'("255.255.255.255" "0.0.0.0" "127.0.0.1"))
79 '(#(255 255 255 255) #(0 0 0 0) #(127 0 0 1)))))
81 (test (dotted-to-vector.2 :compile-at
:definition-time
)
83 (dotted-to-vector "127.0.0.0.0")))
85 (test (dotted-to-vector.3 :compile-at
:definition-time
)
87 (dotted-to-vector 'not-a-string
)))
89 (test (vector-to-dotted.1 :compile-at
:definition-time
)
90 (is (equalp (mapcar #'vector-to-dotted
'(#(255 255 255 255) #(0 0 0 0) (127 0 0 1)))
91 '("255.255.255.255" "0.0.0.0" "127.0.0.1"))))
93 (test (vector-to-dotted.2 :compile-at
:definition-time
)
95 (vector-to-dotted #(127 0 0 256))))
97 (test (address-to-string.1 :compile-at
:definition-time
)
98 (is (equalp (mapcar (lambda (x) (address-to-string (make-address x
)))
99 '(#(127 0 0 1) #(255 255 255 255) #(0 0 0 0 0 0 0 0)
100 #(0 0 0 0 0 0 0 1) #(1 0 0 0 0 0 0 0)))
101 '("127.0.0.1" "255.255.255.255" "::" "::1" "1::"))))
103 (test (vector-to-colon-separated.1 :compile-at
:definition-time
)
104 (is (equalp (let ((ip #(0 0 0 255 255 255 0 0)))
105 (values (vector-to-colon-separated ip
)
106 (vector-to-colon-separated ip
:downcase
)
107 (vector-to-colon-separated ip
:upcase
)))
108 (values "::ff:ff:ff:0:" "::ff:ff:ff:0:" "::FF:FF:FF:0:"))))
110 (test (vector-to-colon-separated.2 :compile-at
:definition-time
)
111 (is (string= (vector-to-colon-separated #(1 2 3 4 5 0 6 7))
114 (test (vector-to-colon-separated.3 :compile-at
:definition-time
)
115 (is (string= (vector-to-colon-separated #(0 2 3 4 5 0 6 7))
118 (test (vector-to-colon-separated.4 :compile-at
:definition-time
)
119 (is (string= (vector-to-colon-separated #(1 2 3 4 5 0 6 0))
122 (test (colon-separated-to-vector.1 :compile-at
:definition-time
)
123 (is (equalp (mapcar #'colon-separated-to-vector
124 '(":ff::ff:" "::" "::1" "1::" ":2:3:4:5:6:7:8" "1:2:3:4:5:6:7:"
125 ":1::2:" "::127.0.0.1" ":1::127.0.0.1"))
126 '(#(0 #xff
0 0 0 0 #xff
0)
133 #(0 0 0 0 0 0 #x7f00
1)
134 #(0 1 0 0 0 0 #x7f00
1)))))
136 (test (address=.1 :compile-at
:definition-time
)
137 (is-true (address= +ipv4-loopback
+ (make-address #(127 0 0 1)))))
139 (test (address=.2 :compile-at
:definition-time
)
140 (is-true (address= +ipv6-loopback
+ (ensure-address "::1"))))
142 (test (copy-address.1 :compile-at
:definition-time
)
143 (is-true (loop for designator in
(list "127.0.0.1" +max-ipv4-value
+ "::" "::1")
144 for addr1
= (ensure-address designator
)
145 for addr2
= (ensure-address designator
)
146 for addr3
= (copy-address addr1
)
147 always
(and (address= addr1 addr2
)
148 (address= addr1 addr3
)
149 (address= addr2 addr3
)))))
151 (test (make-address.1 :compile-at
:definition-time
)
153 (make-address 'not-a-valid-designator
)))
155 (test (address.unspecified
.1 :compile-at
:definition-time
)
156 (is-true (every #'inet-address-unspecified-p
157 (mapcar #'ensure-address
'("0.0.0.0" "::" "0:0:0:0:0:0:0:0")))))
159 (test (address.loopback
.1 :compile-at
:definition-time
)
160 (is-true (every #'inet-address-loopback-p
161 (mapcar #'ensure-address
'("127.0.0.1" "::1" "0:0::1")))))
163 (test (address.multicast
.1 :compile-at
:definition-time
)
164 (is-true (every #'inet-address-multicast-p
165 (mapcar #'ensure-address
166 '("224.0.0.0" "224.1.2.3" "232.0.0.127" "239.255.255.255"
167 "ff02::2" "ff0a::114" "ff05::1:3")))))
169 (test (address.ipv6-ipv4-mapped
.1 :compile-at
:definition-time
)
170 (is-true (ipv6-ipv4-mapped-p (ensure-address "::ffff:127.0.0.1"))))
172 (test (address.ipv6.1
:compile-at
:definition-time
)
173 (is (equalp (address-to-vector "::1.2.3.4")
174 (values #(0 0 0 0 0 0 #x0102
#x0304
) :ipv6
))))
178 (defparameter *google-ns
*
179 (list #/ip
/8.8.8.8 #/ip
/8.8.4.4))
181 #-no-internet-available
182 (test (lookup-hostname.1 :compile-at
:definition-time
)
183 (is (equalp (multiple-value-bind (address addresses truename
)
184 (lookup-hostname "a.root-servers.net" :ipv6 nil
:ns
*google-ns
*)
185 (declare (ignore addresses
))
186 (values (address-equal-p address
#(198 41 0 4))
188 (values t
"a.root-servers.net"))))
190 #-no-internet-available
191 (test (lookup-hostname.2 :compile-at
:definition-time
)
192 (is-true (string= (nth-value 2 (lookup-hostname #(198 41 0 4) :ns
*google-ns
*))
193 "a.root-servers.net")))
195 (test (lookup-hostname.3 :compile-at
:definition-time
)
196 (signals resolver-no-name-error
197 (lookup-hostname "foo.tninkpad.telent.net." :ns
*google-ns
*)))
199 (test (lookup-hostname.4 :compile-at
:definition-time
)
200 (is-true (address-equal-p (lookup-hostname #(127 0 0 1) :ipv6 nil
:ns
*google-ns
*)
203 (test (lookup-hostname.5 :compile-at
:definition-time
)
205 (lookup-hostname #(127 0 0) :ns
*google-ns
*)))
207 (test (lookup-hostname.6 :compile-at
:definition-time
)
208 (signals resolver-no-name-error
209 (lookup-hostname #(127 0 0 1) :ipv6
:ipv6
:ns
*google-ns
*)))
213 (test (lookup-service.1 :compile-at
:definition-time
)
214 (is (equalp (lookup-service "ssh")
215 (values 22 "ssh" :tcp
))))
217 (test (lookup-service.2 :compile-at
:definition-time
)
218 (is (equalp (lookup-service 53 :udp
)
219 (values 53 "domain" :udp
))))
221 ;;; looks up a reserved service port
222 (test (lookup-service.3 :compile-at
:definition-time
)
223 ;; TODO: check for a more specific error.
224 (signals unknown-service
225 (lookup-service 1024)))
229 (test (lookup-protocol.1 :compile-at
:definition-time
)
230 (is (equalp (multiple-value-bind (number name
)
231 (lookup-protocol "tcp")
232 (values number name
))
235 (test (lookup-protocol.2 :compile-at
:definition-time
)
236 (is (equalp (multiple-value-bind (number name
)
237 (lookup-protocol "udp")
238 (values number name
))
241 (test (lookup-protocol.3 :compile-at
:definition-time
)
242 (signals unknown-protocol
243 (lookup-protocol "nonexistent-protocol")))
245 (test (lookup-protocol.4 :compile-at
:definition-time
)
246 (is-true (string= (nth-value 1 (lookup-protocol 6))
249 ;;;; Network Interfaces
251 (test (list-network-interfaces.1 :compile-at
:definition-time
)
252 (is-true (<= 1 (length (list-network-interfaces)))))
254 (test (network-interfaces.1 :compile-at
:definition-time
)
256 (flet ((nif-equal (if1 if2
)
257 (check-type if1 cons
)
258 (check-type if2 cons
)
259 (and (string= (interface-name if1
) (interface-name if2
))
260 (eql (interface-index if1
) (interface-index if2
)))))
261 (loop for nif in
(list-network-interfaces)
262 always
(and (nif-equal nif
(lookup-interface (interface-name nif
)))
263 (nif-equal nif
(lookup-interface (interface-index nif
))))))))
267 ;;; RT: don't accept unknown keyword arguments, such as typos.
270 (make-socket :this-kw-arg-doesnt-exist t
)))
272 (test (make-socket.2 :compile-at
:definition-time
)
273 (is (equalp (with-open-socket (s :address-family
:ipv4
)
274 (values (socket-connected-p s
)
276 (> (socket-os-fd s
) 1)
277 (socket-address-family s
)
278 (socket-protocol s
)))
279 (values nil t t
:ipv4
:default
)))) ; why isn't it :TCP?
281 (test (make-socket.3 :compile-at
:definition-time
)
282 (is-true (with-open-socket (s :address-family
:ipv4
)
285 ;;; Given the functions we've got so far, if you can think of a better
286 ;;; way to make sure the bind succeeded than trying it twice, let me
287 ;;; know. 1974 has no special significance, unless you're the same age
289 (test (inet.socket-bind
.1 :compile-at
:definition-time
)
290 (signals socket-address-in-use-error
291 (with-open-socket (s1 :address-family
:ipv4
:connect
:passive
292 :local-host
#(127 0 0 1) :local-port
1974)
293 (with-open-socket (s2 :address-family
:ipv4
:connect
:passive
294 :local-host
#(127 0 0 1) :local-port
1974)
297 (test (sockopt.1 :compile-at
:definition-time
)
298 (is-true (with-open-socket (s :address-family
:ipv4
)
299 (setf (socket-option s
:reuse-address
) t
)
300 (socket-option s
:reuse-address
))))
302 ;;; Like READ-SEQUENCE, but returns early if the full quantity of data
303 ;;; isn't there to be read. Blocks if no input at all.
304 (defun read-buf-nonblock (buffer stream
)
305 (let ((eof (gensym)))
307 (c (read-char stream nil eof
)
308 (read-char-no-hang stream nil eof
)))
309 ((or (>= i
(length buffer
)) (not c
) (eq c eof
)) i
)
310 (setf (elt buffer i
) c
))))
312 (test (simple-tcp-client :compile-at
:definition-time
)
314 (with-open-socket (s :remote-host
*echo-address
* :remote-port
*echo-port
*
315 :address-family
:ipv4
)
316 (setf (socket-option s
:receive-timeout
) *echo-timeout
*)
317 (let ((data (make-string 200)))
318 (format s
"here is some text")
320 (let ((data (subseq data
0 (read-buf-nonblock data s
))))
321 ;; (format t "~&Got ~S back from TCP echo server~%" data)
322 (> (length data
) 0))))))
324 (test (sockaddr-return-type :compile-at
:definition-time
)
326 (with-open-socket (s :remote-host
*echo-address
* :remote-port
*echo-port
*
327 :address-family
:ipv4
)
328 (setf (socket-option s
:receive-timeout
) *echo-timeout
*)
329 (and (ipv4-address-p (remote-host s
))
330 (numberp (remote-port s
))))))
332 ;;; We don't support streams with UDP sockets ATM. But when we do,
333 ;;; let's add a similar test using stream functions.
335 ;;; FIXME: figure out why this test blocks with the inetd services on
336 ;;; my machines, on both Darwin and Linux/x86-64. Works with
337 ;;; echo-server.c though --luis
338 (test (simple-udp-client.1 :compile-at
:definition-time
)
340 (with-open-socket (s :remote-host
*echo-address
* :remote-port
*echo-port
*
341 :type
:datagram
:address-family
:ipv4
)
342 (send-to s
#(1 2 3 4 5))
343 (let ((nbytes (nth-value 1 (handler-bind ((isys:ewouldblock
345 (invoke-restart (find-restart 'retry-syscall e
)
347 (receive-from s
:size
200)))))
350 (test (simple-udp-client.2 :compile-at
:definition-time
)
352 (with-open-socket (s :type
:datagram
:address-family
:ipv4
)
353 (send-to s
#(1 2 3 4 5)
354 :remote-host
*echo-address
*
355 :remote-port
*echo-port
*)
356 (let ((nbytes (nth-value 1 (handler-bind ((isys:ewouldblock
358 (invoke-restart (find-restart 'retry-syscall e
)
360 (receive-from s
:size
200)))))
363 (test (simple-local-sockets :compile-at
:definition-time
)
364 (is (string= (let ((file (namestring
365 (make-pathname :name
"local-socket" :type nil
367 (asdf:system-definition-pathname
368 (asdf:find-system
'#:iolib-tests
)))))))
369 (ignore-errors (delete-file file
))
370 (with-open-socket (p :address-family
:local
:connect
:passive
:local-filename file
)
371 (with-open-socket (a :address-family
:local
:remote-filename file
)
372 (format a
"local socket test")
374 (let ((s (accept-connection p
)))
377 (delete-file file
)))))
378 "local socket test")))
380 (defmacro with-http-stream
((var host port request
) &body body
)
381 `(with-open-socket (,var
:address-family
:ipv4
:remote-host
,host
:remote-port
,port
)
382 (format ,var
,(concatenate 'string request
" HTTP/1.0~%~%"))
386 #-no-internet-available
387 (test (simple-http-client :compile-at
:definition-time
)
389 (with-http-stream (s "www.google.com" 80 "HEAD /")
390 (let ((data (make-string 200)))
391 (setf data
(subseq data
0 (read-buf-nonblock data s
)))
393 (> (length data
) 0)))))
395 #-no-internet-available
396 (test (sockopt-receive-buffer :compile-at
:definition-time
)
397 ;; on Linux x86, the receive buffer size appears to be doubled in the
398 ;; kernel: we set a size of x and then getsockopt() returns 2x.
399 ;; This is why we compare with >= instead of =
401 (with-http-stream (s "www.google.com" 80 "HEAD/")
402 (setf (socket-option s
:receive-buffer
) 1975)
403 (let ((data (make-string 200)))
404 (setf data
(subseq data
0 (read-buf-nonblock data s
)))
405 (and (> (length data
) 0)
406 (>= (socket-option s
:receive-buffer
) 1975))))))
408 (test (socket-open-p.1 :compile-at
:definition-time
)
409 (is-true (with-open-socket (s)
412 (test (socket-open-p.2 :compile-at
:definition-time
)
413 (is-true (with-open-socket (s :remote-host
*echo-address
* :remote-port
*echo-port
*
414 :address-family
:ipv4
)
417 (test (socket-open-p.3 :compile-at
:definition-time
)
418 (is-false (with-open-socket (s)
422 (test (socket-open-p.4 :compile-at
:definition-time
)
423 (is-false (with-open-socket (s :remote-host
*echo-address
* :remote-port
*echo-port
*
424 :address-family
:ipv4
)
428 (test (open-stream-p.1 :compile-at
:definition-time
)
429 (is-true (with-open-socket (s)
432 (test (open-stream-p.2 :compile-at
:definition-time
)
433 (is-true (with-open-socket (s :remote-host
*echo-address
* :remote-port
*echo-port
*
434 :address-family
:ipv4
)
437 (test (open-stream-p.3 :compile-at
:definition-time
)
438 (is-false (with-open-socket (s)
442 (test (open-stream-p.4 :compile-at
:definition-time
)
443 (is-false (with-open-socket (s :remote-host
*echo-address
* :remote-port
*echo-port
*
444 :address-family
:ipv4
)
448 ;;; we don't have an automatic test for some of this yet. There's no
449 ;;; simple way to run servers and have something automatically connect
450 ;;; to them as client, unless we spawn external programs. Then we
451 ;;; have to start telling people what external programs they should
452 ;;; have installed. Which, eventually, we will, but not just yet
454 ;;; to check with this: can display packets from multiple peers
455 ;;; peer address is shown correctly for each packet
456 ;;; packet length is correct
457 ;;; long (>500 byte) packets have the full length shown (doesn't work)
459 (defun udp-server (port)
460 (with-open-socket (s :type
:datagram
:local-port port
)
462 (multiple-value-bind (buf len address port
)
463 (receive-from s
:size
500)
464 (format t
"Received ~A bytes from ~A:~A - ~A ~%"
465 len address port
(subseq buf
0 (min 10 len
)))))))