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-grovel::foreign-nullp 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.
40 (assert (or (= length
4) (= length
8)))
41 (naturalize-unsigned-byte-8-array ad
4))
45 (naturalize-unsigned-byte-8-array ad length
)))
48 (sb-alien:cast ad sb-alien
:c-string
))))))
49 (make-instance 'host-ent
50 :name
(sockint::hostent-name h
)
51 :type
(sockint::hostent-type h
)
53 :addresses addresses
)))
55 (defun naturalize-unsigned-byte-8-array (array length
)
56 (let ((addr (make-array 4 :element-type
'(unsigned-byte 8))))
58 (setf (elt addr i
) (sb-alien:deref array i
)))
63 (defun get-host-by-name (host-name)
64 "Returns a HOST-ENT instance for HOST-NAME or signals a NAME-SERVICE-ERROR.
65 HOST-NAME may also be an IP address in dotted quad notation or some other
66 weird stuff - see gethostbyname(3) or getaddrinfo(3) for the details."
67 #+sb-bsd-sockets-addrinfo
68 (get-address-info host-name
)
69 #-sb-bsd-sockets-addrinfo
70 (make-host-ent (sockint::gethostbyname host-name
)))
72 (defun get-host-by-address (address)
73 "Returns a HOST-ENT instance for ADDRESS, which should be a vector of
74 (integer 0 255), or signals a NAME-SERVICE-ERROR. See gethostbyaddr(3)
75 or gethostinfo(3) for details."
76 #+sb-bsd-sockets-addrinfo
77 (get-name-info address
)
78 #-sb-bsd-sockets-addrinfo
79 (sockint::with-in-addr packed-addr
()
80 (let ((addr-vector (coerce address
'vector
)))
81 (loop for i from
0 below
(length addr-vector
)
82 do
(setf (sb-alien:deref
(sockint::in-addr-addr packed-addr
) i
)
84 (make-host-ent (sockint::gethostbyaddr packed-addr
88 ;;; Emulate the above two functions with getaddrinfo / getnameinfo
90 #+sb-bsd-sockets-addrinfo
91 (defun get-address-info (node)
92 (sb-alien:with-alien
((res (* (* sockint
::addrinfo
)) :local
93 (sb-alien:make-alien
(* sockint
::addrinfo
))))
94 (let ((err (sockint::getaddrinfo node nil nil res
)))
96 (let ((host-ent (make-instance 'host-ent
98 :type sockint
::af-inet
101 (loop for sap
= (sb-alien:deref res
) then
(sockint::addrinfo-next info
)
102 until
(sb-alien::null-alien sap
)
103 for info
= (sb-alien:cast sap
(* sockint
::addrinfo
))
104 ;; Only handle AF_INET currently.
105 do
(when (eq (sockint::addrinfo-family info
) sockint
::af-inet
)
106 (let* ((sockaddr (sockint::addrinfo-addr info
))
107 (address (sockint::sockaddr-in-addr sockaddr
)))
108 ;; The same effective result can be multiple time
109 ;; in the list, with different socktypes. Only record
110 ;; each address once.
111 (setf (slot-value host-ent
'addresses
)
112 (adjoin (naturalize-unsigned-byte-8-array address
114 (host-ent-addresses host-ent
)
116 (sockint::free-addrinfo
(sb-alien:deref res
))
118 (addrinfo-error "getaddrinfo" err
)))))
120 (defconstant ni-max-host
1025)
122 #+sb-bsd-sockets-addrinfo
123 (defun get-name-info (address)
124 (assert (= (length address
) 4))
125 (sockint::with-sockaddr-in sockaddr
()
126 (sb-alien:with-alien
((host-buf (array char
#.ni-max-host
)))
127 (setf (sockint::sockaddr-in-family sockaddr
) sockint
::af-inet
)
129 (setf (sb-alien:deref
(sockint::sockaddr-in-addr sockaddr
) i
)
131 (let ((err (sockint::getnameinfo
(sb-alien:alien-sap sockaddr
)
132 (sb-alien:alien-size sockint
::sockaddr-in
:bytes
)
133 (sb-alien:cast host-buf
(* char
)) ni-max-host
135 sockint
::ni-namereqd
)))
137 (make-instance 'host-ent
138 :name
(sb-alien::c-string-to-string
139 (sb-alien:alien-sap host-buf
)
140 (sb-impl::default-external-format
)
142 :type sockint
::af-inet
144 :addresses
(list address
))
145 (addrinfo-error "getnameinfo" err
))))))
149 (defvar *name-service-errno
* 0
150 "The value of h_errno, after it's been fetched from Unix-land by calling
151 GET-NAME-SERVICE-ERRNO")
153 (defun name-service-error (where &optional errno
)
154 ;; There was a dummy docstring here for the texinfo extractor, but I
155 ;; see no reason for this to be documented in the manual, and removed
157 (let ((*name-service-errno
* (get-name-service-errno errno
)))
158 ;; Comment next to NETDB_INTERNAL in netdb.h says "See errno.".
159 ;; This special case treatment hasn't actually been tested yet.
161 (if (= *name-service-errno
* sockint
::NETDB-INTERNAL
)
164 (condition-for-name-service-errno *name-service-errno
*)))
165 (error condition
:errno
*name-service-errno
* :syscall where
)))))
167 (defun addrinfo-error (where error-code
)
168 (let ((condition (condition-for-name-service-error-code error-code
)))
169 (error condition
:error-code error-code
:syscall where
)))
171 (define-condition name-service-error
(condition)
172 ((errno :initform nil
:initarg
:errno
:reader name-service-error-errno
)
173 (error-code :initform nil
:initarg
:error-code
174 :reader name-service-error-error-code
)
175 (symbol :initform nil
:initarg
:symbol
:reader name-service-error-symbol
)
176 (syscall :initform
"an unknown location" :initarg
:syscall
:reader name-service-error-syscall
))
177 (:report
(lambda (c s
)
178 (let* ((errno (name-service-error-errno c
))
179 (error-code (name-service-error-error-code c
)))
180 (format s
"Name service error in \"~A\": ~A (~A)"
181 (name-service-error-syscall c
)
182 (or (name-service-error-symbol c
)
185 (get-name-service-error-message errno error-code
))))))
187 (defparameter *conditions-for-name-service-errno
* nil
)
188 ;; getaddrinfo and getnameinfo return an error code, rather than using
189 ;; h_errno. While on Linux there's no overlap between their possible
190 ;; values, this doesn't seem to be guaranteed on all systems.
191 (defparameter *conditions-for-name-service-error-code
* nil
)
193 ;; Define a special name-service-error for variour error cases, and associate
194 ;; them with the matching h_errno / error code.
195 (defmacro define-name-service-condition
(errno-symbol error-code-symbol name
)
197 (define-condition ,name
(name-service-error)
198 ((errno-symbol :reader name-service-error-errno-symbol
199 :initform
(quote ,errno-symbol
))
200 (error-code-symbol :reader name-service-error-error-code-symbol
201 :initform
(quote ,error-code-symbol
))))
202 (push (cons ,errno-symbol
(quote ,name
))
203 *conditions-for-name-service-errno
*)
204 #+sb-bsd-sockets-addrinfo
205 (push (cons ,error-code-symbol
(quote ,name
))
206 *conditions-for-name-service-error-code
*)))
209 (define-name-service-condition
210 sockint
::NETDB-INTERNAL
211 nil
;; Doesn't map directly to any getaddrinfo error code
212 netdb-internal-error
)
214 (define-name-service-condition
215 sockint
::NETDB-SUCCESS
216 nil
;; Doesn't map directly to any getaddrinfo error code
218 (define-name-service-condition
219 sockint
::HOST-NOT-FOUND
221 host-not-found-error
)
222 (define-name-service-condition
226 (define-name-service-condition
230 (define-name-service-condition
231 sockint
::NO-ADDRESS
;; Also defined as NO-DATA, with the same value
232 #-freebsd sockint
::EAI-NODATA
#+freebsd nil
235 (defun condition-for-name-service-errno (err)
236 (or (cdr (assoc err
*conditions-for-name-service-errno
* :test
#'eql
))
237 'name-service-error
))
239 (defun condition-for-name-service-error-code (err)
240 (or (cdr (assoc err
*conditions-for-name-service-error-code
* :test
#'eql
))
241 'name-service-error
))
243 (defun get-name-service-errno (&optional errno
)
244 (setf *name-service-errno
*
246 (sb-alien:alien-funcall
248 (sb-alien:extern-alien
"get_h_errno" (function integer
))
250 (sb-alien:extern-alien
"WSAGetLastError" (function integer
))))))
252 (defun get-name-service-error-message (errno error-code
)
255 (sockint::h-strerror errno
)
256 (sockint::gai-strerror error-code
)))