Improve PRINT-OBJECT for DATAGRAM sockets.
[iolib.git] / sockets / address.lisp
blob610c2cf1304c275961a2f28582df6e555fb6eb5e
1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Indent-tabs-mode: NIL -*-
2 ;;;
3 ;;; address.lisp --- IP address classes.
4 ;;;
5 ;;; Copyright (C) 2006-2007, Stelian Ionescu <sionescu@common-lisp.net>
6 ;;;
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
13 ;;;
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.
18 ;;;
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 ;;;; Class Definitions
28 (defclass address ()
29 ((name :initarg :name :reader address-name :type vector))
30 (:documentation "Base class for all socket address classes."))
32 (defclass inet-address (address) ()
33 (:documentation "Base class for IPv4 and IPv6 addresses."))
35 (defclass ipv4-address (inet-address) ()
36 (:documentation "IPv4 address. Its low-level representation
37 can be accessed as vector of type IPV4-ARRAY through the
38 ADDRESS-NAME reader."))
40 (defclass ipv6-address (inet-address) ()
41 (:documentation "IPv6 address. Its low-level representation
42 can be accessed as vector of type IPV6-ARRAY through the
43 ADDRESS-NAME reader."))
45 (defclass local-address (address)
46 ((abstract :initform nil :initarg :abstract
47 :reader abstract-address-p :type boolean))
48 (:documentation "UNIX socket address."))
50 ;;;; Conversion functions
52 (defun integer-to-dotted (integer)
53 "Convert a 32-bit unsigned integer to a dotted string."
54 (check-type integer ub32)
55 (format nil "~A.~A.~A.~A"
56 (ldb (byte 8 24) integer)
57 (ldb (byte 8 16) integer)
58 (ldb (byte 8 8) integer)
59 (ldb (byte 8 0) integer)))
61 (defun dotted-to-vector (address)
62 "Convert a dotted IPv4 address to a (simple-array (unsigned-byte 8) 4)."
63 (check-type address string)
64 (let ((addr (make-array 4 :element-type 'ub8 :initial-element 0))
65 (split (split-sequence #\. address :count 5)))
66 (flet ((set-array-value (index str)
67 (setf (aref addr index)
68 (or (parse-number-or-nil str :ub8)
69 (error 'parse-error)))))
70 (let ((len (length split)))
71 (unless (<= 1 len 4)
72 (error 'parse-error))
73 (set-array-value 3 (nth (1- len) split))
74 (loop :for n :in split
75 :for index :below (1- len)
76 :do (set-array-value index n))))
77 (values addr)))
79 (defun dotted-to-integer (address)
80 "Convert a dotted IPv4 address to a 32-bit unsigned integer."
81 (vector-to-integer (dotted-to-vector address)))
83 (defun vector-to-dotted (vector)
84 "Convert an 4-element vector to a dotted string."
85 (coercef vector 'ipv4-array)
86 (let ((*print-pretty* nil))
87 (with-output-to-string (s)
88 (princ (aref vector 0) s) (princ #\. s)
89 (princ (aref vector 1) s) (princ #\. s)
90 (princ (aref vector 2) s) (princ #\. s)
91 (princ (aref vector 3) s))))
93 ;;; TODO: add tests against inet_pton(). Optimize if necessary.
94 ;;; <http://java.sun.com/javase/6/docs/api/java/net/Inet6Address.html#format>
95 (defun colon-separated-to-vector (string)
96 "Convert a colon-separated IPv6 address to a (simple-array ub16 8)."
97 (check-type string string)
98 (when (< (length string) 2)
99 (error 'parse-error))
100 (flet ((handle-trailing-and-leading-colons (string)
101 (let ((start 0)
102 (end (length string))
103 (start-i 0)
104 (trailing-colon-p nil)
105 (tokens-from-leading-or-trailing-zeros 0))
106 (when (char= #\: (char string 0))
107 (incf start)
108 (unless (char= #\: (char string 1))
109 (setq start-i 1)
110 (setq tokens-from-leading-or-trailing-zeros 1)))
111 (when (char= #\: (char string (- end 1)))
112 (setq trailing-colon-p t)
113 (unless (char= #\: (char string (- end 2)))
114 (incf tokens-from-leading-or-trailing-zeros))
115 (decf end))
116 (values start end start-i trailing-colon-p
117 tokens-from-leading-or-trailing-zeros)))
118 (emptyp (string)
119 (= 0 (length string)))
120 ;; we need to use this instead of dotted-to-vector because
121 ;; abbreviated IPv4 addresses are invalid in this context.
122 (ipv4-string-to-ub16-list (string)
123 (let ((tokens (split-sequence #\. string)))
124 (when (= (length tokens) 4)
125 (let ((ipv4 (map 'vector
126 (lambda (string)
127 (let ((x (ignore-errors
128 (parse-integer string))))
129 (if (or (null x) (not (<= 0 x #xff)))
130 (error 'parse-error)
131 x)))
132 tokens)))
133 (list (dpb (aref ipv4 0) (byte 8 8) (aref ipv4 1))
134 (dpb (aref ipv4 2) (byte 8 8) (aref ipv4 3)))))))
135 (parse-hex-ub16 (string)
136 (let ((x (ignore-errors (parse-integer string :radix 16))))
137 (if (or (null x) (not (<= 0 x #xffff)))
138 (error 'parse-error)
139 x))))
140 (multiple-value-bind (start end start-i trailing-colon-p extra-tokens)
141 (handle-trailing-and-leading-colons string)
142 (let* ((vector (make-array 8 :element-type 'ub16 :initial-element 0))
143 (tokens (split-sequence #\: string :start start :end end))
144 (empty-tokens (count-if #'emptyp tokens))
145 (token-count (+ (length tokens) extra-tokens)))
146 (unless trailing-colon-p
147 (let ((ipv4 (ipv4-string-to-ub16-list (car (last tokens)))))
148 (when ipv4
149 (incf token-count)
150 (setq tokens (nconc (butlast tokens) ipv4)))))
151 (when (or (> token-count 8) (> empty-tokens 1)
152 (and (zerop empty-tokens) (/= token-count 8)))
153 (error 'parse-error))
154 (loop for i from start-i and token in tokens do
155 (cond
156 ((integerp token) (setf (aref vector i) token))
157 ((emptyp token) (incf i (- 8 token-count)))
158 (t (setf (aref vector i) (parse-hex-ub16 token)))))
159 vector))))
161 (defun ipv4-on-ipv6-mapped-vector-p (vector)
162 (and (dotimes (i 5 t)
163 (when (plusp (aref vector i))
164 (return nil)))
165 (= (aref vector 5) #xffff)))
167 (defun princ-ipv4-on-ipv6-mapped-address (vector s)
168 (princ "::ffff:" s)
169 (let ((*print-base* 10) (*print-pretty* nil))
170 (princ (ldb (byte 8 8) (aref vector 6)) s) (princ #\. s)
171 (princ (ldb (byte 8 0) (aref vector 6)) s) (princ #\. s)
172 (princ (ldb (byte 8 8) (aref vector 7)) s) (princ #\. s)
173 (princ (ldb (byte 8 0) (aref vector 7)) s)))
175 (defun vector-to-colon-separated (vector &optional (case :downcase))
176 "Convert an 8-element vector to a colon-separated IPv6
177 address. CASE may be :DOWNCASE or :UPCASE."
178 (coercef vector 'ipv6-array)
179 (check-type case (member :upcase :downcase))
180 (let ((s (make-string-output-stream)))
181 (flet ((find-zeros ()
182 (let ((start (position 0 vector :start 1 :end 7)))
183 (when start
184 (values start
185 (position-if #'plusp vector :start start :end 7)))))
186 (princ-subvec (start end)
187 (loop :for i :from start :below end
188 :do (princ (aref vector i) s) (princ #\: s))))
189 (cond
190 ((ipv4-on-ipv6-mapped-vector-p vector)
191 (princ-ipv4-on-ipv6-mapped-address vector s))
193 (let ((*print-base* 16) (*print-pretty* nil))
194 (when (plusp (aref vector 0)) (princ (aref vector 0) s))
195 (princ #\: s)
196 (multiple-value-bind (start end) (find-zeros)
197 (cond (start (princ-subvec 1 start)
198 (princ #\: s)
199 (when end (princ-subvec end 7)))
200 (t (princ-subvec 1 7))))
201 (when (plusp (aref vector 7)) (princ (aref vector 7) s))))))
202 (let ((str (get-output-stream-string s)))
203 (ecase case
204 (:downcase (nstring-downcase str))
205 (:upcase (nstring-upcase str))))))
207 (defmacro ignore-parse-errors (&body body)
208 ;; return first value only
209 `(values (ignore-some-conditions (parse-error) ,@body)))
211 (defun string-address-to-vector (address)
212 "Convert a string address (dotted or colon-separated) to a vector address.
213 If the string is not a valid address, return NIL."
214 (or (ignore-parse-errors (dotted-to-vector address))
215 (ignore-parse-errors (colon-separated-to-vector address))))
217 (defun address-to-vector (address)
218 "Convert any representation of an internet address to a vector.
219 Allowed inputs are: unsigned 32-bit integers, strings, vectors
220 and INET-ADDRESS objects. If the address is valid, two values
221 are returned: the vector and the address type (:IPV4 or IPV6),
222 otherwise NIL is returned."
223 (let (vector
224 addr-type)
225 (typecase address
226 (number (and (ignore-parse-errors
227 (setf vector (integer-to-vector address)))
228 (setf addr-type :ipv4)))
229 (string (cond
230 ((ignore-parse-errors (setf vector (dotted-to-vector address)))
231 (setf addr-type :ipv4))
232 ((ignore-parse-errors
233 (setf vector (colon-separated-to-vector address)))
234 (setf addr-type :ipv6))))
235 ((vector * 4) (and (ignore-parse-errors
236 (setf vector (coerce address 'ipv4-array)))
237 (setf addr-type :ipv4)))
238 ((vector * 8) (and (ignore-parse-errors
239 (setf vector (coerce address 'ipv6-array)))
240 (setf addr-type :ipv6)))
241 (ipv4-address (setf vector (address-name address)
242 addr-type :ipv4))
243 (ipv6-address (setf vector (address-name address)
244 addr-type :ipv6)))
245 (when vector
246 (values vector addr-type))))
248 (defun ensure-address (address &optional (family :internet))
249 "If FAMILY is :LOCAL, a LOCAL-ADDRESS is instantiated with
250 ADDRESS as its NAME slot. If FAMILY is :INTERNET, an appropriate
251 subtype of INET-ADDRESS is instantiated after guessing the
252 address type through ADDRESS-TO-VECTOR. If the address is not
253 valid, a CL:PARSE-ERROR is signalled.
255 When ADDRESS is already an instance of the ADDRESS class, a check
256 is made to see if it matches the FAMILY argument and it is
257 returned unmodified."
258 (ecase family
259 (:internet
260 (typecase address
261 (address (check-type address inet-address) address)
262 (t (make-address (or (address-to-vector address)
263 (error 'parse-error))))))
264 (:local
265 (etypecase address
266 (string (make-instance 'local-address :name address))
267 (address (check-type address local-address) address)))))
269 ;;;; Print Methods
271 (defgeneric address-to-string (address)
272 (:documentation "Returns a textual presentation of ADDRESS."))
274 (defmethod address-to-string ((address ipv4-address))
275 (vector-to-dotted (address-name address)))
277 (defmethod address-to-string ((address ipv6-address))
278 (vector-to-colon-separated (address-name address)))
280 (defmethod address-to-string ((address local-address))
281 (if (abstract-address-p address)
282 "<unknown socket>"
283 (address-name address)))
285 (defmethod print-object ((address ipv4-address) stream)
286 (print-unreadable-object (address stream :type nil :identity nil)
287 (format stream "IPv4 address: ~A" (address-to-string address))))
289 (defmethod print-object ((address ipv6-address) stream)
290 (print-unreadable-object (address stream :type nil :identity nil)
291 (format stream "IPv6 address: ~A" (address-to-string address))))
293 (defmethod print-object ((address local-address) stream)
294 (print-unreadable-object (address stream :type nil :identity nil)
295 (format stream "Unix socket address: ~A. Abstract: ~:[no~;yes~]"
296 (address-to-string address) (abstract-address-p address))))
298 ;;;; Equality Methods
300 (defun vector-equal (v1 v2)
301 (and (= (length v1) (length v2))
302 (every #'eql v1 v2)))
304 (defgeneric address= (addr1 addr2)
305 (:documentation "Returns T if both arguments are the same socket address."))
307 (defmethod address= ((addr1 inet-address) (addr2 inet-address))
308 (vector-equal (address-name addr1) (address-name addr2)))
310 (defmethod address= ((addr1 local-address) (addr2 local-address))
311 (equal (address-name addr1) (address-name addr2)))
313 (defun address-equal-p (addr1 addr2 &optional (family :internet))
314 "Returns T if both arguments are designators for the same socket address."
315 (address= (ensure-address addr1 family)
316 (ensure-address addr2 family)))
318 ;;;; Copy Methods
320 (defgeneric copy-address (address)
321 (:documentation
322 "Returns a copy of ADDRESS which is ADDRESS= to the original."))
324 (defmethod copy-address ((addr ipv4-address))
325 (make-instance 'ipv4-address :name (copy-seq (address-name addr))))
327 (defmethod copy-address ((addr ipv6-address))
328 (make-instance 'ipv6-address :name (copy-seq (address-name addr))))
330 (defmethod copy-address ((addr local-address))
331 (make-instance 'local-address
332 :name (copy-seq (address-name addr))
333 :abstract (abstract-address-p addr)))
335 ;;; Returns an IPv6 address by mapping ADDR onto it.
336 (defun map-ipv4-address-to-ipv6 (address)
337 (make-instance 'ipv6-address
338 :name (map-ipv4-vector-to-ipv6 (address-name address))))
340 (defun map-ipv6-address-to-ipv4 (address)
341 (assert (ipv6-ipv4-mapped-p address))
342 (make-instance 'ipv4-address
343 :name (map-ipv6-vector-to-ipv4 (address-name address))))
345 ;;;; Constructor
347 (defun make-address (name)
348 "Constructs an ADDRESS object. NAME should be of type
349 IPV4-ARRAY, IPV6-ARRAY or STRING in which case an instance of
350 IPV4-ADDRESS, IPV6-ADDRESS or LOCAL-ADDRESS, respectively, will
351 be created. Otherwise, a TYPE-ERROR is signalled. See also
352 ENSURE-ADDRESS."
353 (cond
354 ((ignore-errors (coercef name 'ipv4-array))
355 (make-instance 'ipv4-address :name name))
356 ((ignore-errors (coercef name 'ipv6-array))
357 (make-instance 'ipv6-address :name name))
358 ((stringp name) (make-instance 'local-address :name name))
359 (t (error 'type-error :datum name
360 :expected-type '(or string ipv4-array ipv6-array)))))