1 (in-package :sb-bsd-sockets)
4 ((name :initarg :name :reader host-ent-name
5 :documentation "The name of the host")
6 ;; Deliberately not documented, since this isn't very useful,
7 ;; and the data isn't available when using getaddrinfo(). Unfortunately
9 (aliases :initarg :aliases :reader host-ent-aliases)
10 ;; presently always AF_INET. Not exported.
11 (address-type :initarg :type :reader host-ent-address-type)
12 (addresses :initarg :addresses :reader host-ent-addresses
13 :documentation "A list of addresses for this host."))
14 (:documentation "This class represents the results of an address lookup."))
16 (defgeneric host-ent-address (host-ent)
17 (:documentation "Returns some valid address for HOST-ENT."))
19 (defmethod host-ent-address ((host-ent host-ent))
20 (car (host-ent-addresses host-ent)))
22 #-sb-bsd-sockets-addrinfo
23 (defun make-host-ent (h &optional errno)
24 (when (sb-alien:null-alien h)
25 (name-service-error "gethostbyname" errno))
26 (let* ((length (sockint::hostent-length h))
27 (aliases (loop for i = 0 then (1+ i)
28 for al = (sb-alien:deref (sockint::hostent-aliases h) i)
32 (loop for i = 0 then (1+ i)
33 for ad = (sb-alien:deref (sockint::hostent-addresses h) i)
34 until (sb-alien:null-alien ad)
35 collect (ecase (sockint::hostent-type h)
37 ;; CLH: Work around x86-64 darwin bug here.
38 ;; The length is reported as 8, when it should be 4.
39 ;; FIXME: this is rumored to be fixed in 10.5
42 (assert (or (= length 4) (= length 8)))
43 (naturalize-unsigned-byte-8-array ad 4))
47 (naturalize-unsigned-byte-8-array ad length)))
50 (sb-alien:cast ad sb-alien:c-string))))))
51 (make-instance 'host-ent
52 :name (sockint::hostent-name h)
53 :type (sockint::hostent-type h)
55 :addresses addresses)))
57 (declaim (inline naturalize-unsigned-byte-8-array))
58 (defun naturalize-unsigned-byte-8-array (array length)
59 (let ((addr (make-array 4 :element-type '(unsigned-byte 8))))
61 (setf (elt addr i) (sb-alien:deref array i)))
66 #-sb-bsd-sockets-addrinfo
67 (sb-ext:defglobal **gethostby-lock** (sb-thread:make-mutex :name "gethostby lock"))
69 (defun get-host-by-name (host-name)
70 "Returns a HOST-ENT instance for HOST-NAME or signals a NAME-SERVICE-ERROR.
71 HOST-NAME may also be an IP address in dotted quad notation or some other
72 weird stuff - see gethostbyname(3) or getaddrinfo(3) for the details."
73 #+sb-bsd-sockets-addrinfo
74 (get-address-info host-name)
75 #-sb-bsd-sockets-addrinfo
76 (sb-thread::with-system-mutex (**gethostby-lock** :allow-with-interrupts t)
77 (make-host-ent (sockint::gethostbyname host-name))))
79 (defun get-host-by-address (address)
80 "Returns a HOST-ENT instance for ADDRESS, which should be a vector of
81 (integer 0 255), or signals a NAME-SERVICE-ERROR. See gethostbyaddr(3)
82 or gethostinfo(3) for details."
83 #+sb-bsd-sockets-addrinfo
84 (get-name-info address)
85 #-sb-bsd-sockets-addrinfo
86 (sb-thread::with-system-mutex (**gethostby-lock** :allow-with-interrupts t)
87 (sockint::with-in-addr packed-addr ()
88 (let ((addr-vector (coerce address 'vector)))
89 (loop for i from 0 below (length addr-vector)
90 do (setf (sb-alien:deref (sockint::in-addr-addr packed-addr) i)
92 (make-host-ent (sockint::gethostbyaddr packed-addr
94 sockint::af-inet))))))
96 ;;; Emulate the above two functions with getaddrinfo / getnameinfo
98 #+sb-bsd-sockets-addrinfo
99 (declaim (inline get-address-info))
100 #+sb-bsd-sockets-addrinfo
101 (defun get-address-info (node)
102 (declare (optimize speed))
103 (sb-alien:with-alien ((info (* sockint::addrinfo)))
104 (let* ((err (sockint::getaddrinfo node nil nil (sb-alien:addr info)))
107 (let ((host-ent (make-instance 'host-ent
109 :type sockint::af-inet
112 (loop until (sb-alien::null-alien info)
113 ;; Only handle AF_INET currently.
115 (when (eq (sockint::addrinfo-family info) sockint::af-inet)
116 (let* ((sockaddr (sockint::addrinfo-addr info))
117 (address (sockint::sockaddr-in-addr sockaddr)))
118 ;; The same effective result can be multiple time
119 ;; in the list, with different socktypes. Only record
120 ;; each address once.
121 (setf (slot-value host-ent 'addresses)
122 (adjoin (naturalize-unsigned-byte-8-array address
124 (host-ent-addresses host-ent)
126 (setf info (sockint::addrinfo-next info)))
127 (sockint::freeaddrinfo to-free)
129 (addrinfo-error "getaddrinfo" err)))))
131 (defconstant ni-max-host 1025)
133 #+sb-bsd-sockets-addrinfo
134 (declaim (inline get-name-info))
135 #+sb-bsd-sockets-addrinfo
136 (defun get-name-info (address)
137 (declare (optimize speed)
139 (assert (= (length address) 4))
140 (sockint::with-sockaddr-in sockaddr ()
141 (sb-alien:with-alien ((host-buf (array char #.ni-max-host)))
142 #+darwin (setf (sockint::sockaddr-in-len sockaddr) 16)
143 (setf (sockint::sockaddr-in-family sockaddr) sockint::af-inet)
145 (setf (sb-alien:deref (sockint::sockaddr-in-addr sockaddr) i)
147 (let ((err (sockint::getnameinfo sockaddr
148 (sb-alien:alien-size sockint::sockaddr-in :bytes)
149 (sb-alien:cast host-buf (* char)) ni-max-host
151 sockint::ni-namereqd)))
153 (make-instance 'host-ent
154 :name (sb-alien::c-string-to-string
155 (sb-alien:alien-sap host-buf)
156 (sb-impl::default-external-format)
158 :type sockint::af-inet
160 :addresses (list address))
161 (addrinfo-error "getnameinfo" err))))))
165 (defvar *name-service-errno* 0
166 "The value of h_errno, after it's been fetched from Unix-land by calling
167 GET-NAME-SERVICE-ERRNO")
169 (defun name-service-error (where &optional errno)
170 ;; There was a dummy docstring here for the texinfo extractor, but I
171 ;; see no reason for this to be documented in the manual, and removed
173 (let ((*name-service-errno* (get-name-service-errno errno)))
174 ;; Comment next to NETDB_INTERNAL in netdb.h says "See errno.".
175 ;; This special case treatment hasn't actually been tested yet.
176 (if (and #-win32 (= *name-service-errno* sockint::NETDB-INTERNAL))
179 (condition-for-name-service-errno *name-service-errno*)))
180 (error condition :errno *name-service-errno* :syscall where)))))
182 (defun addrinfo-error (where error-code)
183 (let ((condition (condition-for-name-service-error-code error-code)))
184 (error condition :error-code error-code :syscall where)))
186 (define-condition name-service-error (error)
187 ((errno :initform nil :initarg :errno :reader name-service-error-errno)
188 (error-code :initform nil :initarg :error-code
189 :reader name-service-error-error-code)
190 (symbol :initform nil :initarg :symbol :reader name-service-error-symbol)
191 (syscall :initform "an unknown location" :initarg :syscall :reader name-service-error-syscall))
192 (:report (lambda (c s)
193 (let* ((errno (name-service-error-errno c))
194 (error-code (name-service-error-error-code c)))
195 (format s "Name service error in \"~A\": ~A (~A)"
196 (name-service-error-syscall c)
197 (or (name-service-error-symbol c)
200 (get-name-service-error-message errno error-code))))))
202 (defparameter *conditions-for-name-service-errno* nil)
203 ;; getaddrinfo and getnameinfo return an error code, rather than using
204 ;; h_errno. While on Linux there's no overlap between their possible
205 ;; values, this doesn't seem to be guaranteed on all systems.
206 (defparameter *conditions-for-name-service-error-code* nil)
208 ;; Define a special name-service-error for variour error cases, and associate
209 ;; them with the matching h_errno / error code.
210 (defmacro define-name-service-condition (errno-symbol error-code-symbol name)
212 (define-condition ,name (name-service-error)
213 ((errno-symbol :reader name-service-error-errno-symbol
214 :initform (quote ,errno-symbol))
215 (error-code-symbol :reader name-service-error-error-code-symbol
216 :initform (quote ,error-code-symbol))))
217 (push (cons ,errno-symbol (quote ,name))
218 *conditions-for-name-service-errno*)
219 #+sb-bsd-sockets-addrinfo
220 (push (cons ,error-code-symbol (quote ,name))
221 *conditions-for-name-service-error-code*)))
224 (define-name-service-condition
225 sockint::NETDB-INTERNAL
226 nil ;; Doesn't map directly to any getaddrinfo error code
227 netdb-internal-error)
229 (define-name-service-condition
230 sockint::NETDB-SUCCESS
231 nil ;; Doesn't map directly to any getaddrinfo error code
233 (define-name-service-condition
234 sockint::HOST-NOT-FOUND
236 host-not-found-error)
237 (define-name-service-condition
241 (define-name-service-condition
245 (define-name-service-condition
246 ;; Also defined as NO-DATA, with the same value
248 ;; getaddrinfo() as of RFC 3493 can no longer distinguish between
249 ;; host no found and address not found
253 (defun condition-for-name-service-errno (err)
254 (or (cdr (assoc err *conditions-for-name-service-errno* :test #'eql))
255 'name-service-error))
257 (defun condition-for-name-service-error-code (err)
258 (or (cdr (assoc err *conditions-for-name-service-error-code* :test #'eql))
259 'name-service-error))
261 (defun get-name-service-errno (&optional errno)
262 (setf *name-service-errno*
264 (sb-alien:alien-funcall
266 (sb-alien:extern-alien "get_h_errno" (function integer))
268 (sb-alien:extern-alien "WSAGetLastError" (function integer))))))
270 (defun get-name-service-error-message (errno error-code)
273 (sockint::h-strerror errno)
274 (sockint::gai-strerror error-code)))