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 (defun make-host-ent (h &optional errno
)
23 (when (sb-alien:null-alien h
)
24 (name-service-error "gethostbyname" errno
))
25 (let* ((length (sockint::hostent-length h
))
26 (aliases (loop for i
= 0 then
(1+ i
)
27 for al
= (sb-alien:deref
(sockint::hostent-aliases h
) i
)
31 (loop for i
= 0 then
(1+ i
)
32 for ad
= (sb-alien:deref
(sockint::hostent-addresses h
) i
)
33 until
(sb-alien:null-alien ad
)
34 collect
(ecase (sockint::hostent-type h
)
36 ;; CLH: Work around x86-64 darwin bug here.
37 ;; The length is reported as 8, when it should be 4.
38 ;; FIXME: this is rumored to be fixed in 10.5
41 (assert (or (= length
4) (= length
8)))
42 (naturalize-unsigned-byte-8-array ad
4))
46 (naturalize-unsigned-byte-8-array ad length
)))
49 (sb-alien:cast ad sb-alien
:c-string
))))))
50 (make-instance 'host-ent
51 :name
(sockint::hostent-name h
)
52 :type
(sockint::hostent-type h
)
54 :addresses addresses
)))
56 (defun naturalize-unsigned-byte-8-array (array length
)
57 (let ((addr (make-array 4 :element-type
'(unsigned-byte 8))))
59 (setf (elt addr i
) (sb-alien:deref array i
)))
64 (defun get-host-by-name (host-name)
65 "Returns a HOST-ENT instance for HOST-NAME or signals a NAME-SERVICE-ERROR.
66 HOST-NAME may also be an IP address in dotted quad notation or some other
67 weird stuff - see gethostbyname(3) or getaddrinfo(3) for the details."
68 #+sb-bsd-sockets-addrinfo
69 (get-address-info host-name
)
70 #-sb-bsd-sockets-addrinfo
71 (make-host-ent (sockint::gethostbyname host-name
)))
73 (defun get-host-by-address (address)
74 "Returns a HOST-ENT instance for ADDRESS, which should be a vector of
75 (integer 0 255), or signals a NAME-SERVICE-ERROR. See gethostbyaddr(3)
76 or gethostinfo(3) for details."
77 #+sb-bsd-sockets-addrinfo
78 (get-name-info address
)
79 #-sb-bsd-sockets-addrinfo
80 (sockint::with-in-addr packed-addr
()
81 (let ((addr-vector (coerce address
'vector
)))
82 (loop for i from
0 below
(length addr-vector
)
83 do
(setf (sb-alien:deref
(sockint::in-addr-addr packed-addr
) i
)
85 (make-host-ent (sockint::gethostbyaddr packed-addr
89 ;;; Emulate the above two functions with getaddrinfo / getnameinfo
91 #+sb-bsd-sockets-addrinfo
92 (defun get-address-info (node)
93 (sb-alien:with-alien
((res (* (* sockint
::addrinfo
)) :local
94 (sb-alien:make-alien
(* sockint
::addrinfo
))))
95 (let ((err (sockint::getaddrinfo node nil nil res
)))
97 (let ((host-ent (make-instance 'host-ent
99 :type sockint
::af-inet
102 (loop for sap
= (sb-alien:deref res
) then
(sockint::addrinfo-next info
)
103 until
(sb-alien::null-alien sap
)
104 for info
= (sb-alien:cast sap
(* sockint
::addrinfo
))
105 ;; Only handle AF_INET currently.
106 do
(when (eq (sockint::addrinfo-family info
) sockint
::af-inet
)
107 (let* ((sockaddr (sockint::addrinfo-addr info
))
108 (address (sockint::sockaddr-in-addr sockaddr
)))
109 ;; The same effective result can be multiple time
110 ;; in the list, with different socktypes. Only record
111 ;; each address once.
112 (setf (slot-value host-ent
'addresses
)
113 (adjoin (naturalize-unsigned-byte-8-array address
115 (host-ent-addresses host-ent
)
117 (sockint::free-addrinfo
(sb-alien:deref res
))
119 (addrinfo-error "getaddrinfo" err
)))))
121 (defconstant ni-max-host
1025)
123 #+sb-bsd-sockets-addrinfo
124 (defun get-name-info (address)
125 (assert (= (length address
) 4))
126 (sockint::with-sockaddr-in sockaddr
()
127 (sb-alien:with-alien
((host-buf (array char
#.ni-max-host
)))
128 #+darwin
(setf (sockint::sockaddr-in-len sockaddr
) 16)
129 (setf (sockint::sockaddr-in-family sockaddr
) sockint
::af-inet
)
131 (setf (sb-alien:deref
(sockint::sockaddr-in-addr sockaddr
) i
)
133 (let ((err (sockint::getnameinfo
(sb-alien:alien-sap sockaddr
)
134 (sb-alien:alien-size sockint
::sockaddr-in
:bytes
)
135 (sb-alien:cast host-buf
(* char
)) ni-max-host
137 sockint
::ni-namereqd
)))
139 (make-instance 'host-ent
140 :name
(sb-alien::c-string-to-string
141 (sb-alien:alien-sap host-buf
)
142 (sb-impl::default-external-format
)
144 :type sockint
::af-inet
146 :addresses
(list address
))
147 (addrinfo-error "getnameinfo" err
))))))
151 (defvar *name-service-errno
* 0
152 "The value of h_errno, after it's been fetched from Unix-land by calling
153 GET-NAME-SERVICE-ERRNO")
155 (defun name-service-error (where &optional errno
)
156 ;; There was a dummy docstring here for the texinfo extractor, but I
157 ;; see no reason for this to be documented in the manual, and removed
159 (let ((*name-service-errno
* (get-name-service-errno errno
)))
160 ;; Comment next to NETDB_INTERNAL in netdb.h says "See errno.".
161 ;; This special case treatment hasn't actually been tested yet.
163 (if (= *name-service-errno
* sockint
::NETDB-INTERNAL
)
166 (condition-for-name-service-errno *name-service-errno
*)))
167 (error condition
:errno
*name-service-errno
* :syscall where
)))))
169 (defun addrinfo-error (where error-code
)
170 (let ((condition (condition-for-name-service-error-code error-code
)))
171 (error condition
:error-code error-code
:syscall where
)))
173 (define-condition name-service-error
(error)
174 ((errno :initform nil
:initarg
:errno
:reader name-service-error-errno
)
175 (error-code :initform nil
:initarg
:error-code
176 :reader name-service-error-error-code
)
177 (symbol :initform nil
:initarg
:symbol
:reader name-service-error-symbol
)
178 (syscall :initform
"an unknown location" :initarg
:syscall
:reader name-service-error-syscall
))
179 (:report
(lambda (c s
)
180 (let* ((errno (name-service-error-errno c
))
181 (error-code (name-service-error-error-code c
)))
182 (format s
"Name service error in \"~A\": ~A (~A)"
183 (name-service-error-syscall c
)
184 (or (name-service-error-symbol c
)
187 (get-name-service-error-message errno error-code
))))))
189 (defparameter *conditions-for-name-service-errno
* nil
)
190 ;; getaddrinfo and getnameinfo return an error code, rather than using
191 ;; h_errno. While on Linux there's no overlap between their possible
192 ;; values, this doesn't seem to be guaranteed on all systems.
193 (defparameter *conditions-for-name-service-error-code
* nil
)
195 ;; Define a special name-service-error for variour error cases, and associate
196 ;; them with the matching h_errno / error code.
197 (defmacro define-name-service-condition
(errno-symbol error-code-symbol name
)
199 (define-condition ,name
(name-service-error)
200 ((errno-symbol :reader name-service-error-errno-symbol
201 :initform
(quote ,errno-symbol
))
202 (error-code-symbol :reader name-service-error-error-code-symbol
203 :initform
(quote ,error-code-symbol
))))
204 (push (cons ,errno-symbol
(quote ,name
))
205 *conditions-for-name-service-errno
*)
206 #+sb-bsd-sockets-addrinfo
207 (push (cons ,error-code-symbol
(quote ,name
))
208 *conditions-for-name-service-error-code
*)))
211 (define-name-service-condition
212 sockint
::NETDB-INTERNAL
213 nil
;; Doesn't map directly to any getaddrinfo error code
214 netdb-internal-error
)
216 (define-name-service-condition
217 sockint
::NETDB-SUCCESS
218 nil
;; Doesn't map directly to any getaddrinfo error code
220 (define-name-service-condition
221 sockint
::HOST-NOT-FOUND
223 host-not-found-error
)
224 (define-name-service-condition
228 (define-name-service-condition
232 (define-name-service-condition
233 ;; Also defined as NO-DATA, with the same value
235 ;; getaddrinfo() as of RFC 3493 can no longer distinguish between
236 ;; host no found and address not found
240 (defun condition-for-name-service-errno (err)
241 (or (cdr (assoc err
*conditions-for-name-service-errno
* :test
#'eql
))
242 'name-service-error
))
244 (defun condition-for-name-service-error-code (err)
245 (or (cdr (assoc err
*conditions-for-name-service-error-code
* :test
#'eql
))
246 'name-service-error
))
248 (defun get-name-service-errno (&optional errno
)
249 (setf *name-service-errno
*
251 (sb-alien:alien-funcall
253 (sb-alien:extern-alien
"get_h_errno" (function integer
))
255 (sb-alien:extern-alien
"WSAGetLastError" (function integer
))))))
257 (defun get-name-service-error-message (errno error-code
)
260 (sockint::h-strerror errno
)
261 (sockint::gai-strerror error-code
)))