Introduce class named-address for protocol addresses with an explicit endpoint
[iolib.git] / src / sockets / address.lisp
blob985254348bd87c8878e811f29223a9dacc4f19e7
1 ;;;; -*- Mode: Lisp; indent-tabs-mode: nil -*-
2 ;;;
3 ;;; --- IP address classes and main methods.
4 ;;;
6 (in-package :iolib.sockets)
8 ;;;; Class Definitions
10 (defclass address ()
12 (:documentation "Base class for all socket address classes."))
14 (defclass named-address ()
15 ((name :initarg :name :reader address-name :type vector))
16 (:documentation "Base class for socket address with a name."))
18 (defclass inet-address (named-address) ()
19 (:documentation "Base class for IPv4 and IPv6 addresses."))
21 (defclass ipv4-address (inet-address) ()
22 (:documentation "IPv4 address. Its low-level representation
23 can be accessed as vector of type IPV4-ARRAY through the
24 ADDRESS-NAME reader."))
26 (defclass ipv6-address (inet-address) ()
27 (:documentation "IPv6 address. Its low-level representation
28 can be accessed as vector of type IPV6-ARRAY through the
29 ADDRESS-NAME reader."))
31 (defclass local-address (named-address)
32 ((abstract :initform nil :initarg :abstract
33 :reader abstract-address-p :type boolean))
34 (:documentation "UNIX socket address."))
35 (unset-method-docstring #'abstract-address-p () '(local-address))
36 (set-function-docstring 'abstract-address-p "Return T if ADDRESS is a LOCAL-ADDRESS that lives in the abstract namespace.")
38 (defmethod initialize-instance :after ((address local-address) &key)
39 (with-slots (name) address
40 (etypecase name
41 (string t)
42 (pathname (setf name (namestring name))))))
44 (defmethod make-load-form ((address inet-address) &optional env)
45 (declare (ignore env))
46 `(make-instance ,(class-of address)
47 :name ,(address-name address)))
49 (defmethod make-load-form ((address local-address) &optional env)
50 (declare (ignore env))
51 `(make-instance ,(class-of address)
52 :name ,(address-name address)
53 :abstract ,(abstract-address-p address)))
55 ;;;; Conversion functions for SOCKADDR_* structs
57 (defun sockaddr-in->sockaddr (sin)
58 (with-foreign-slots ((addr port) sin sockaddr-in)
59 (values (make-instance 'ipv4-address
60 :name (integer-to-vector (ntohl addr)))
61 (ntohs port))))
63 (defun sockaddr-in6->sockaddr (sin6)
64 (with-foreign-slots ((addr port) sin6 sockaddr-in6)
65 (values (make-instance 'ipv6-address
66 :name (in6-addr-to-ipv6-array addr))
67 (ntohs port))))
69 (defun parse-un-path (path)
70 (foreign-string-to-lisp path :max-chars (1- unix-path-max)))
72 (defun sockaddr-un->sockaddr (sun)
73 (with-foreign-slots ((path) sun sockaddr-un)
74 (multiple-value-bind (name abstract)
75 (if (zerop (mem-aref path :uint8 0))
76 (values (parse-un-path (inc-pointer path 1)) t)
77 (values (parse-un-path path) nil))
78 (make-instance 'local-address :name name :abstract abstract))))
80 (defun sockaddr-nl->sockaddr (snl)
81 (with-foreign-slots ((groups port) snl sockaddr-nl)
82 (values (make-instance 'netlink-address :multicast-groups groups)
83 port)))
85 (defun sockaddr-storage->sockaddr (ss)
86 (with-foreign-slots ((family) ss sockaddr-storage)
87 (switch (family :test #'=)
88 (af-inet (sockaddr-in->sockaddr ss))
89 (af-inet6 (sockaddr-in6->sockaddr ss))
90 (af-local (sockaddr-un->sockaddr ss))
91 (af-netlink (sockaddr-nl->sockaddr ss)))))
93 (defun sockaddr->sockaddr-storage (ss sockaddr &optional (port 0))
94 (etypecase sockaddr
95 (ipv4-address (make-sockaddr-in ss (address-name sockaddr) port))
96 (ipv6-address (make-sockaddr-in6 ss (address-name sockaddr) port))
97 (local-address (make-sockaddr-un ss (address-name sockaddr)
98 (abstract-address-p sockaddr)))
99 (netlink-address (make-sockaddr-nl ss (multicast-groups sockaddr) port))))
101 (defun sockaddr-size (ss)
102 (with-foreign-slots ((family) ss sockaddr-storage)
103 (switch (family :test #'=)
104 (af-inet (isys:sizeof 'sockaddr-in))
105 (af-inet6 (isys:sizeof 'sockaddr-in6))
106 (af-local (isys:sizeof 'sockaddr-un))
107 (af-netlink (isys:sizeof 'sockaddr-nl)))))
109 ;;;; Conversion functions
111 (defun integer-to-dotted (integer)
112 "Convert an (UNSIGNED-BYTE 32) IPv4 address to a dotted string."
113 (check-type integer ub32 "an '(unsigned-byte 32)")
114 (let ((*print-pretty* nil) (*print-base* 10))
115 (format nil "~A.~A.~A.~A"
116 (ldb (byte 8 24) integer)
117 (ldb (byte 8 16) integer)
118 (ldb (byte 8 8) integer)
119 (ldb (byte 8 0) integer))))
121 (defun dotted-to-vector (address)
122 "Convert a dotted IPv4 address to a (SIMPLE-ARRAY (UNSIGNED-BYTE 8) 4)."
123 (check-type address string "a string")
124 (let ((addr (make-array 4 :element-type 'ub8 :initial-element 0))
125 (split (split-sequence #\. address :count 5)))
126 (flet ((set-array-value (index str)
127 (setf (aref addr index)
128 (ensure-number str :type 'ub8))))
129 (let ((len (length split)))
130 (unless (<= 1 len 4)
131 (error 'parse-error))
132 (set-array-value 3 (nth (1- len) split))
133 (loop :for n :in split
134 :for index :below (1- len)
135 :do (set-array-value index n))))
136 (values addr)))
138 (defun dotted-to-integer (address)
139 "Convert a dotted IPv4 address to an (UNSIGNED-BYTE 32)."
140 (vector-to-integer (dotted-to-vector address)))
142 (defun vector-to-dotted (vector)
143 "Convert an 4-element vector to a dotted string."
144 (coercef vector 'ipv4-array)
145 (let ((*print-pretty* nil) (*print-base* 10))
146 (with-output-to-string (s)
147 (princ (aref vector 0) s) (princ #\. s)
148 (princ (aref vector 1) s) (princ #\. s)
149 (princ (aref vector 2) s) (princ #\. s)
150 (princ (aref vector 3) s))))
152 ;;; TODO: add tests against inet_pton(). Optimize if necessary.
153 ;;; <http://java.sun.com/javase/6/docs/api/java/net/Inet6Address.html#format>
154 (defun colon-separated-to-vector (string)
155 "Convert a colon-separated IPv6 address to a (SIMPLE-ARRAY (UNSIGNED-BYTE 16) 8)."
156 (check-type string string "a string")
157 (when (< (length string) 2)
158 (error 'parse-error))
159 (flet ((handle-trailing-and-leading-colons (string)
160 (let ((start 0)
161 (end (length string))
162 (start-i 0)
163 (trailing-colon-p nil)
164 (tokens-from-leading-or-trailing-zeros 0))
165 (when (char= #\: (char string 0))
166 (incf start)
167 (unless (char= #\: (char string 1))
168 (setq start-i 1)
169 (setq tokens-from-leading-or-trailing-zeros 1)))
170 (when (char= #\: (char string (- end 1)))
171 (setq trailing-colon-p t)
172 (unless (char= #\: (char string (- end 2)))
173 (incf tokens-from-leading-or-trailing-zeros))
174 (decf end))
175 (values start end start-i trailing-colon-p
176 tokens-from-leading-or-trailing-zeros)))
177 ;; we need to use this instead of dotted-to-vector because
178 ;; abbreviated IPv4 addresses are invalid in this context.
179 (ipv4-string-to-ub16-list (string)
180 (let ((tokens (split-sequence #\. string)))
181 (when (= (length tokens) 4)
182 (let ((ipv4 (map 'vector
183 (lambda (string)
184 (let ((x (ignore-errors
185 (parse-integer string))))
186 (if (or (null x) (not (<= 0 x #xff)))
187 (error 'parse-error)
188 x)))
189 tokens)))
190 (list (dpb (aref ipv4 0) (byte 8 8) (aref ipv4 1))
191 (dpb (aref ipv4 2) (byte 8 8) (aref ipv4 3)))))))
192 (parse-hex-ub16 (string)
193 (ensure-number string :type 'ub16 :radix 16)))
194 (multiple-value-bind (start end start-i trailing-colon-p extra-tokens)
195 (handle-trailing-and-leading-colons string)
196 (let* ((vector (make-array 8 :element-type 'ub16 :initial-element 0))
197 (tokens (split-sequence #\: string :start start :end end))
198 (empty-tokens (count-if #'emptyp tokens))
199 (token-count (+ (length tokens) extra-tokens)))
200 (unless trailing-colon-p
201 (let ((ipv4 (ipv4-string-to-ub16-list (lastcar tokens))))
202 (when ipv4
203 (incf token-count)
204 (setq tokens (nconc (butlast tokens) ipv4)))))
205 (when (or (> token-count 8) (> empty-tokens 1)
206 (and (zerop empty-tokens) (/= token-count 8)))
207 (error 'parse-error))
208 (loop for i from start-i and token in tokens do
209 (cond
210 ((integerp token) (setf (aref vector i) token))
211 ((emptyp token) (incf i (- 8 token-count)))
212 (t (setf (aref vector i) (parse-hex-ub16 token)))))
213 vector))))
215 (defun ipv4-on-ipv6-mapped-vector-p (vector)
216 (and (dotimes (i 5 t)
217 (when (plusp (aref vector i))
218 (return nil)))
219 (= (aref vector 5) #xffff)))
221 (defun princ-ipv4-on-ipv6-mapped-address (vector s)
222 (princ "::ffff:" s)
223 (let ((*print-base* 10) (*print-pretty* nil))
224 (princ (ldb (byte 8 8) (aref vector 6)) s) (princ #\. s)
225 (princ (ldb (byte 8 0) (aref vector 6)) s) (princ #\. s)
226 (princ (ldb (byte 8 8) (aref vector 7)) s) (princ #\. s)
227 (princ (ldb (byte 8 0) (aref vector 7)) s)))
229 (defun vector-to-colon-separated (vector &optional (case :downcase))
230 "Convert an (SIMPLE-ARRAY (UNSIGNED-BYTE 16) 8) to a colon-separated IPv6
231 address. CASE may be :DOWNCASE or :UPCASE."
232 (coercef vector 'ipv6-array)
233 (check-type case (member :upcase :downcase) "either :UPCASE or :DOWNCASE")
234 (let ((s (make-string-output-stream)))
235 (flet ((find-zeros ()
236 (let ((start (position 0 vector :start 1 :end 7)))
237 (when start
238 (values start
239 (position-if #'plusp vector :start start :end 7)))))
240 (princ-subvec (start end)
241 (loop :for i :from start :below end
242 :do (princ (aref vector i) s) (princ #\: s))))
243 (cond
244 ((ipv4-on-ipv6-mapped-vector-p vector)
245 (princ-ipv4-on-ipv6-mapped-address vector s))
247 (let ((*print-base* 16) (*print-pretty* nil))
248 (when (plusp (aref vector 0)) (princ (aref vector 0) s))
249 (princ #\: s)
250 (multiple-value-bind (start end) (find-zeros)
251 (cond (start (princ-subvec 1 start)
252 (princ #\: s)
253 (when end (princ-subvec end 7)))
254 (t (princ-subvec 1 7))))
255 (when (plusp (aref vector 7)) (princ (aref vector 7) s))))))
256 (let ((str (get-output-stream-string s)))
257 (ecase case
258 (:downcase (nstring-downcase str))
259 (:upcase (nstring-upcase str))))))
261 (defmacro ignore-parse-errors (&body body)
262 ;; return first value only
263 `(values (ignore-some-conditions (parse-error) ,@body)))
265 (defun string-address-to-vector (address)
266 "Convert a string address (dotted or colon-separated) to a vector address.
267 If the string is not a valid address, return NIL."
268 (or (ignore-parse-errors (dotted-to-vector address))
269 (ignore-parse-errors (colon-separated-to-vector address))))
271 (defun address-to-vector (address)
272 "Convert any representation of an internet address to a vector.
273 Allowed inputs are: unsigned 32-bit integers, strings, vectors
274 and INET-ADDRESS objects. If the address is valid, two values
275 are returned: the vector and the address type (:IPV4 or IPV6),
276 otherwise NIL is returned."
277 (let (vector addr-type)
278 (typecase address
279 (number (and (ignore-parse-errors
280 (setf vector (integer-to-vector address)))
281 (setf addr-type :ipv4)))
282 (string (cond
283 ((ignore-parse-errors (setf vector (dotted-to-vector address)))
284 (setf addr-type :ipv4))
285 ((ignore-parse-errors
286 (setf vector (colon-separated-to-vector address)))
287 (setf addr-type :ipv6))))
288 ((vector * 4) (and (ignore-parse-errors
289 (setf vector (coerce address 'ipv4-array)))
290 (setf addr-type :ipv4)))
291 ((vector * 8) (and (ignore-parse-errors
292 (setf vector (coerce address 'ipv6-array)))
293 (setf addr-type :ipv6)))
294 (ipv4-address (setf vector (copy-seq (address-name address))
295 addr-type :ipv4))
296 (ipv6-address (setf vector (copy-seq (address-name address))
297 addr-type :ipv6)))
298 (when vector
299 (values vector addr-type))))
301 (defun ensure-address (address &key (family :internet) abstract (errorp t))
302 "If FAMILY is :LOCAL, a LOCAL-ADDRESS is instantiated with
303 ADDRESS as its NAME slot. If FAMILY is :INTERNET, an appropriate
304 subtype of INET-ADDRESS is instantiated after guessing the
305 address type through ADDRESS-TO-VECTOR. If the address is invalid
306 and ERRORP is not NIL, then a CL:PARSE-ERROR is signalled,
307 otherwise NIL is returned.
309 When ADDRESS is already an instance of the ADDRESS class, a check
310 is made to see if it matches the FAMILY argument and it is
311 returned unmodified."
312 (ecase family
313 (:internet
314 (typecase address
315 (address (cond
316 (errorp
317 (check-type address inet-address "an INET address"))
318 ((not (typep address 'inet-address))
319 (return* nil)))
320 address)
321 (t (let ((vector (address-to-vector address)))
322 (cond
323 (vector (make-address vector))
324 (errorp (error 'parse-error)))))))
325 (:local
326 (etypecase address
327 (string (make-instance 'local-address :name address :abstract abstract))
328 (address (cond
329 (errorp
330 (check-type address local-address "a local address"))
331 ((not (typep address 'local-address))
332 (return* nil)))
333 address)))))
335 ;;;; Print Methods
337 (defgeneric address-to-string (address)
338 (:documentation "Returns a textual presentation of ADDRESS."))
340 (defmethod address-to-string ((address ipv4-address))
341 (vector-to-dotted (address-name address)))
343 (defmethod address-to-string ((address ipv6-address))
344 (vector-to-colon-separated (address-name address)))
346 (defmethod address-to-string ((address local-address))
347 (format nil "~:[~;@~]~S" (abstract-address-p address)
348 (address-name address)))
350 (defmethod print-object ((address inet-address) stream)
351 (let ((namestring (address-to-string address)))
352 (if (or *print-readably* *print-escape*)
353 (format stream "#/~S/~A" 'ip namestring)
354 (write-string namestring stream))))
356 (defmethod print-object ((address local-address) stream)
357 (print-unreadable-object (address stream :type nil :identity nil)
358 (format stream "Unix socket address: ~A"
359 (address-to-string address))))
361 ;;;; Reader Macro
363 (define-literal-reader ip (stream)
364 (loop :with sstr := (make-string-output-stream)
365 :for char := (read-char stream nil nil)
366 :while char
367 :do (cond ((or (digit-char-p char 16)
368 (member char '(#\. #\:) :test #'char=))
369 (write-char char sstr))
371 (unread-char char stream)
372 (loop-finish)))
373 :finally (return (or (ensure-address (get-output-stream-string sstr)
374 :errorp nil)
375 (error 'reader-error :stream stream)))))
377 ;;;; Equality Methods
379 (defun vector-equal (v1 v2)
380 (and (= (length v1) (length v2))
381 (every #'eql v1 v2)))
383 (defgeneric address= (addr1 addr2)
384 (:documentation "Returns T if both arguments are the same socket address."))
386 (defmethod address= ((addr1 inet-address) (addr2 inet-address))
387 (vector-equal (address-name addr1) (address-name addr2)))
389 (defmethod address= ((addr1 local-address) (addr2 local-address))
390 (equal (address-name addr1) (address-name addr2)))
392 (defun address-equal-p (addr1 addr2 &optional (family :internet))
393 "Returns T if both arguments are designators for the same socket address."
394 (address= (ensure-address addr1 :family family)
395 (ensure-address addr2 :family family)))
397 ;;;; Copy Methods
399 (defgeneric copy-address (address)
400 (:documentation
401 "Returns a copy of ADDRESS which is ADDRESS= to the original."))
403 (defmethod copy-address ((addr ipv4-address))
404 (make-instance 'ipv4-address :name (copy-seq (address-name addr))))
406 (defmethod copy-address ((addr ipv6-address))
407 (make-instance 'ipv6-address :name (copy-seq (address-name addr))))
409 (defmethod copy-address ((addr local-address))
410 (make-instance 'local-address
411 :name (copy-seq (address-name addr))
412 :abstract (abstract-address-p addr)))
414 (defun map-ipv4-address-to-ipv6 (address)
415 "Returns an IPv6 address by mapping ADDRESS onto it."
416 (make-instance 'ipv6-address
417 :name (map-ipv4-vector-to-ipv6 (address-name address))))
419 (defun map-ipv6-address-to-ipv4 (address)
420 "Extracts the IPv4 part of an IPv6-mapped IPv4 address.
421 Signals an error if ADDRESS is not an IPv6-mapped IPv4 address."
422 (assert (ipv6-ipv4-mapped-p address) (address)
423 "Not an IPv6-mapped IPv4 address: ~A" address)
424 (make-instance 'ipv4-address
425 :name (map-ipv6-vector-to-ipv4 (address-name address))))
427 ;;;; Constructor
429 (defun make-address (name)
430 "Constructs an ADDRESS object. NAME should be of type
431 IPV4-ARRAY, IPV6-ARRAY or STRING in which case an instance of
432 IPV4-ADDRESS, IPV6-ADDRESS or LOCAL-ADDRESS, respectively, will
433 be created. Otherwise, a TYPE-ERROR is signalled. See also
434 ENSURE-ADDRESS."
435 (cond
436 ((ignore-errors (coercef name 'ipv4-array))
437 (make-instance 'ipv4-address :name name))
438 ((ignore-errors (coercef name 'ipv6-array))
439 (make-instance 'ipv6-address :name name))
440 ((stringp name) (make-instance 'local-address :name name))
441 (t (error 'type-error :datum name
442 :expected-type '(or string ipv4-array ipv6-array)))))