COMMON-NET: Added a recursive DNS resolver.
[lisp-utils.git] / dns.lisp
CommitLineData
267b03c0
FT
1;;;; DNS implementation for COMMON-NET
2
3(in-package :common-net)
4
5(defstruct dns-packet
6 (txid (random 65536) :type (unsigned-byte 16))
7 (is-response nil)
8 (opcode :query :type (member :query :iquery :status))
9 (authoritative nil)
10 (truncated nil)
11 (recurse nil)
12 (will-recurse nil)
13 (resp-code :success :type (member :success :format-error :server-failure :name-error :not-implemented :refused))
14 (queries '() :type list)
15 (answers '() :type list)
16 (authority '() :type list)
17 (additional '() :type list))
18
19(defclass resource-query ()
20 ((name :initarg :name)
21 (type :initarg :type)))
22
23(defclass resource-record ()
24 ((name :initarg :name)
25 (ttl :initarg :ttl)))
26
27(defvar *rr-coding-types* '())
28
29(defmacro define-rr-type (name class type slots)
30 (let ((format (mapcar #'(lambda (slot)
31 (list* (if (listp (car slot))
32 (caar slot)
33 (car slot))
34 (cdr slot)))
35 slots))
28d289c5
FT
36 (slot-desc (mapcar #'(lambda (slot)
37 (let ((name (car slot)))
38 `(,name :initarg ,(intern (symbol-name name) (find-package :keyword))))) slots)))
267b03c0
FT
39 `(progn
40 (defclass ,name (resource-record) ,slot-desc)
41 (setf *rr-coding-types* (cons '(,name (,class ,type) ,format)
42 (remove ',name *rr-coding-types* :key #'car))))))
43
44(define-rr-type a-record #x1 #x1
45 ((address ipv4-address)))
46(define-rr-type ns-record #x1 #x2
47 ((ns-name domain-name)))
48(define-rr-type cname-record #x1 #x5
49 ((cname domain-name)))
50(define-rr-type soa-record #x1 #x6
51 ((mname domain-name)
52 (rname domain-name)
53 (serial uint-32)
54 (refresh uint-32)
55 (retry uint-32)
56 (expire uint-32)))
57(define-rr-type ptr-record #x1 #xc
58 ((pointed domain-name)))
59(define-rr-type mx-record #x1 #xf
60 ((prio uint-16)
61 (mail-host domain-name)))
62(define-rr-type txt-record #x1 #x10
63 ((text text)))
64(define-rr-type aaaa-record #x1 #x1c
65 ((address ipv6-address)))
66(define-rr-type srv-record #x1 #x21
67 ((prio uint-16)
68 (weigth uint-16)
69 (port uint-16)
70 (host-name domain-name)))
71
72;;; Packet decoding logic
73
74(defstruct dns-decode-state
75 (packet nil :type (array (unsigned-byte 8)))
76 (pos 0 :type (mod 65536))
77 (prev-names '() :type list))
78
79(define-condition dns-error (error) ())
80(define-condition dns-decode-error (dns-error)
81 ((packet :initarg :packet)))
82(define-condition simple-dns-decode-error (dns-decode-error simple-error) ())
83
84(defun simple-dns-decode-error (packet format &rest args)
85 (error 'simple-dns-decode-error :packet packet :format-control format :format-argument args))
86
87(defun decode-uint-8 (buf)
88 (declare (type dns-decode-state buf))
89 (with-slots (packet pos) buf
90 (when (< (- (length packet) pos) 1)
91 (simple-dns-decode-error buf "DNS packet is too short (wanted a 8-bit number)."))
92 (prog1 (aref packet pos)
93 (incf pos))))
94
95(defun decode-uint-16 (buf)
96 (declare (type dns-decode-state buf))
97 (with-slots (packet pos) buf
98 (when (< (- (length packet) pos) 2)
99 (simple-dns-decode-error buf "DNS packet is too short (wanted a 16-bit number)."))
100 (prog1
101 (+ (* (aref packet pos) 256)
102 (aref packet (1+ pos)))
103 (incf pos 2))))
104
105(defun decode-uint-32 (buf)
106 (declare (type dns-decode-state buf))
107 (with-slots (packet pos) buf
108 (when (< (- (length packet) pos) 4)
109 (simple-dns-decode-error buf "DNS packet is too short (wanted a 32-bit number)."))
110 (prog1
111 (+ (* (aref packet pos) #x1000000)
112 (* (aref packet (+ pos 1)) #x10000)
113 (* (aref packet (+ pos 2)) #x100)
114 (aref packet (+ pos 3)))
426521e7 115 (incf pos 4))))
267b03c0
FT
116
117(defun decode-domain-name (buf)
118 (declare (type dns-decode-state buf))
426521e7
FT
119 (labels ((decode-label ()
120 (let* ((orig-off (dns-decode-state-pos buf))
121 (len (decode-uint-8 buf)))
122 (case (ldb (byte 2 6) len)
123 ((0)
124 (if (zerop len)
125 '()
126 (with-slots (packet pos) buf
127 (let* ((label (prog1
128 (handler-bind
129 ((charcode:coding-error
130 (lambda (c)
131 (declare (ignore c))
132 (simple-dns-decode-error buf "DNS label was not ASCII."))))
133 (charcode:decode-string (subseq packet
134 pos (+ pos len))
135 :ascii))
136 (incf pos len)))
137 (decoded (append (list label) (decode-label))))
138 (push (cons orig-off decoded) (slot-value buf 'prev-names))
139 decoded))))
d2505410 140 ((3) (let* ((offset (+ (* 256 (ldb (byte 6 0) len))
426521e7
FT
141 (decode-uint-8 buf)))
142 (prev (assoc offset (dns-decode-state-prev-names buf))))
143 (unless prev
144 (simple-dns-decode-error buf "Domain name label pointed to non-label position."))
145 (cdr prev)))
146 (t (simple-dns-decode-error buf "Illegal DNS label flags: ~D" (ldb (byte 2 6) len)))))))
147 (decode-label)))
267b03c0
FT
148
149(defun decode-dns-query (buf)
150 (declare (type dns-decode-state buf))
151 (let* ((name (decode-domain-name buf))
152 (type (decode-uint-16 buf))
153 (class (decode-uint-16 buf))
154 (desc (find (list class type) *rr-coding-types* :key 'second :test 'equal)))
155 (if desc
156 (make-instance 'resource-query :name name :type (first desc))
157 (progn (warn "Unknown DNS RR type: ~D, ~D" class type)
158 nil))))
159
160(defun decode-dns-record (buf)
161 (declare (type dns-decode-state buf))
162 (let* ((name (decode-domain-name buf))
163 (type (decode-uint-16 buf))
164 (class (decode-uint-16 buf))
165 (ttl (decode-uint-32 buf))
166 (dlen (decode-uint-16 buf))
167 (desc (find (list class type) *rr-coding-types* :key 'second :test 'equal)))
168 (when (< (length (dns-decode-state-packet buf))
169 (+ (dns-decode-state-pos buf) dlen))
170 (simple-dns-decode-error buf "Not enough data left in DNS packet to decode indicated RR data length."))
171 (if desc
172 (let ((orig-off (dns-decode-state-pos buf))
173 (rr (make-instance (first desc)
174 :name name
175 :ttl ttl)))
176 (dolist (slot-desc (third desc))
177 (destructuring-bind (slot-name type) slot-desc
178 (setf (slot-value rr slot-name)
179 (with-slots (packet pos) buf
180 (ecase type
181 ((uint-16) (decode-uint-16 buf))
182 ((uint-32) (decode-uint-32 buf))
183 ((domain-name) (decode-domain-name buf))
184 ((text)
185 (let ((len (decode-uint-8 buf)))
186 (prog1 (subseq packet pos (+ pos len))
187 (incf pos len))))
188 ((ipv4-address)
189 (prog1 (make-instance 'ipv4-host-address :host-bytes (subseq packet pos (+ pos 4)))
190 (incf pos 4)))
191 ((ipv6-address)
192 (prog1 (make-instance 'ipv6-host-address :host-bytes (subseq packet pos (+ pos 16)))
193 (incf pos 16))))))))
194 (unless (= (dns-decode-state-pos buf) (+ orig-off dlen))
195 (simple-dns-decode-error buf "DNS RR data length did not match up with actual decoded data."))
196 rr)
197 (progn (warn "Unknown DNS RR type: ~D, ~D" class type)
198 (incf (dns-decode-state-pos buf) dlen)
199 nil))))
200
201(defun decode-dns-packet (buf)
202 (declare (type dns-decode-state buf))
203 (let* ((txid (decode-uint-16 buf))
204 (flags (decode-uint-16 buf))
205 (qnum (decode-uint-16 buf))
206 (ansnum (decode-uint-16 buf))
207 (autnum (decode-uint-16 buf))
208 (auxnum (decode-uint-16 buf))
209 (packet (make-dns-packet :txid txid
210 :is-response (ldb-test (byte 1 15) flags)
211 :opcode (case (ldb (byte 4 11) flags)
212 ((0) :query)
213 ((1) :iquery)
214 ((2) :status)
215 (t (simple-dns-decode-error buf "Unknown DNS opcode: ~D" (ldb (byte 4 11) flags))))
216 :authoritative (ldb-test (byte 1 10) flags)
217 :truncated (ldb-test (byte 1 9) flags)
218 :recurse (ldb-test (byte 1 8) flags)
219 :will-recurse (ldb-test (byte 1 7) flags)
220 :resp-code (case (ldb (byte 4 0) flags)
221 ((0) :success)
222 ((1) :format-error)
223 ((2) :server-failure)
224 ((3) :name-error)
225 ((4) :not-implemented)
226 ((5) :refused)
227 (t (simple-dns-decode-error buf "Unknown DNS response code: ~D" (ldb (byte 4 0) flags)))))))
228 (with-slots (queries answers authority additional) packet
229 (dotimes (i qnum)
230 (setf queries (append queries (list (decode-dns-query buf)))))
231 (dotimes (i ansnum)
232 (setf answers (append answers (list (decode-dns-record buf)))))
233 (dotimes (i autnum)
234 (setf authority (append authority (list (decode-dns-record buf)))))
235 (dotimes (i auxnum)
236 (setf additional (append additional (list (decode-dns-record buf))))))
237 packet))
238
239(defun dns-decode (packet)
240 (decode-dns-packet (make-dns-decode-state :packet packet)))
241
242;;; Packet encoding logic
243
244(defstruct dns-encode-state
245 (packet-buf (make-array '(512) :element-type '(unsigned-byte 8) :adjustable t :fill-pointer 0) :type (array (unsigned-byte 8)))
246 (prev-names '() :type list))
247
248(defun encode-uint-8 (buf num)
249 (declare (type dns-encode-state buf)
250 (type (unsigned-byte 8) num))
251 (with-slots (packet-buf) buf
252 (vector-push-extend num packet-buf)))
253
254(defun encode-uint-16 (buf num)
255 (declare (type dns-encode-state buf)
256 (type (unsigned-byte 16) num))
257 (with-slots (packet-buf) buf
258 (vector-push-extend (ldb (byte 8 8) num) packet-buf)
259 (vector-push-extend (ldb (byte 8 0) num) packet-buf)))
260
261(defun encode-uint-32 (buf num)
262 (declare (type dns-encode-state buf)
263 (type (unsigned-byte 32) num))
264 (with-slots (packet-buf) buf
265 (vector-push-extend (ldb (byte 8 24) num) packet-buf)
266 (vector-push-extend (ldb (byte 8 16) num) packet-buf)
267 (vector-push-extend (ldb (byte 8 8) num) packet-buf)
268 (vector-push-extend (ldb (byte 8 0) num) packet-buf)))
269
270(defun encode-bytes (buf bytes)
271 (declare (type dns-encode-state buf)
272 (type (array (unsigned-byte 8)) bytes))
273 (with-slots (packet-buf) buf
274 (dotimes (i (length bytes) (values))
275 (vector-push-extend (elt bytes i) packet-buf))))
276
277(defun encode-domain-name (buf name)
278 (declare (type dns-encode-state buf)
279 (type list name))
280 (with-slots (packet-buf prev-names) buf
281 (labels ((encode-label (name)
282 (let ((prev (find name prev-names :key 'first :test 'equal)))
283 (cond ((null name)
284 (encode-uint-8 buf 0))
285 (prev
286 (encode-uint-16 buf (+ #xc000 (cdr prev))))
287 (t
288 (when (< (length packet-buf) 16384)
289 (push (cons name (length packet-buf)) prev-names))
290 (let ((encoded (charcode:encode-string (car name) :ascii)))
291 (unless (< (length encoded) 64)
292 (error "DNS labels cannot exceed 63 octets in length: ~S" (car name)))
293 (encode-uint-8 buf (length encoded))
294 (encode-bytes buf encoded))
295 (encode-label (cdr name)))))))
296 (encode-label name))))
297
298(defun encode-dns-query (buf query)
299 (declare (type dns-encode-state buf)
300 (type resource-query query))
301 (let ((desc (find (slot-value query 'type) *rr-coding-types* :key 'first)))
302 (encode-domain-name buf (slot-value query 'name))
303 (encode-uint-16 buf (second (second desc)))
304 (encode-uint-16 buf (first (second desc)))))
305
306(defun encode-dns-record (buf record)
307 (declare (type dns-encode-state buf)
308 (type resource-record record))
309 (let ((desc (find (class-name (class-of record)) *rr-coding-types* :key 'first)))
310 (encode-domain-name buf (slot-value record 'name))
311 (encode-uint-16 buf (second (second desc)))
312 (encode-uint-16 buf (first (second desc)))
313 (encode-uint-32 buf (slot-value record 'ttl))
314 (with-slots (packet-buf) buf
315 (let ((orig-off (length packet-buf)))
316 (encode-uint-16 buf 0)
317 (dolist (slot-desc (third desc))
318 (destructuring-bind (slot-name type) slot-desc
319 (let ((val (slot-value record slot-name)))
320 (ecase type
321 ((uint-16) (encode-uint-16 buf val))
322 ((uint-32) (encode-uint-32 buf val))
323 ((domain-name) (encode-domain-name buf val))
324 ((text) (let ((data (etypecase val
325 (string (charcode:encode-string val :ascii))
326 ((array (unsigned-byte 8)) val))))
327 (unless (< (length data) 256)
328 (error "DNS text data length cannot exceed 255 octets."))
329 (encode-uint-8 buf (length data))
330 (encode-bytes buf data)))
331 ((ipv4-address)
332 (check-type val ipv4-host-address)
333 (encode-bytes buf (slot-value val 'host-bytes)))
334 ((ipv6-address)
335 (check-type val ipv6-host-address)
336 (encode-bytes buf (slot-value val 'host-bytes)))))))
337 (let ((dlen (- (length packet-buf) orig-off)))
338 (setf (aref packet-buf orig-off) (ldb (byte 8 8) dlen)
339 (aref packet-buf (1+ orig-off)) (ldb (byte 8 0) dlen)))))))
340
341(defun encode-dns-packet (buf packet)
342 (declare (type dns-encode-state buf)
343 (type dns-packet packet))
344 (with-slots (txid is-response opcode authoritative truncated
345 recurse will-recurse resp-code
346 queries answers authority additional) packet
347 (encode-uint-16 buf txid)
348 (let ((flags 0))
349 (setf (ldb (byte 1 15) flags) (if is-response 1 0)
350 (ldb (byte 4 11) flags) (ecase opcode
351 ((:query) 0)
352 ((:iquery) 1)
353 ((:status) 2))
354 (ldb (byte 1 10) flags) (if authoritative 1 0)
355 (ldb (byte 1 9) flags) (if truncated 1 0)
356 (ldb (byte 1 8) flags) (if recurse 1 0)
357 (ldb (byte 1 7) flags) (if will-recurse 1 0)
358 (ldb (byte 4 0) flags) (ecase resp-code
359 ((:success) 0)
360 ((:format-error) 1)
361 ((:server-failure) 2)
362 ((:name-error) 3)
363 ((:not-implemented) 4)
364 ((:refused) 5)))
365 (encode-uint-16 buf flags))
366 (encode-uint-16 buf (length queries))
367 (encode-uint-16 buf (length answers))
368 (encode-uint-16 buf (length authority))
369 (encode-uint-16 buf (length additional))
370 (dolist (query queries)
371 (encode-dns-query buf query))
372 (dolist (rr answers)
373 (encode-dns-record buf rr))
374 (dolist (rr authority)
375 (encode-dns-record buf rr))
376 (dolist (rr additional)
377 (encode-dns-record buf rr)))
378 (values))
379
380(defun dns-encode (packet)
381 (check-type packet dns-packet)
382 (let ((buf (make-dns-encode-state)))
383 (encode-dns-packet buf packet)
384 (slot-value buf 'packet-buf)))
b466cd48
FT
385
386;;; DN format
387
388(defun parse-domain-name (name)
389 (declare (type string name))
390 (let ((l '())
391 (p 0))
392 (loop (let ((p2 (position #\. name :start p)))
393 (if p2
394 (if (= p2 (1- (length name)))
395 (return (values l t))
396 (setf l (append l (list (subseq name p p2)))
397 p (1+ p2)))
398 (return (values (append l (list (subseq name p))) nil)))))))
399
400(defun unparse-domain-name (name)
401 (declare (type list name))
402 (let ((buf nil))
403 (dolist (label name buf)
404 (setf buf (if buf
405 (concatenate 'string buf "." label)
406 label)))))
407
0818ef99
FT
408;;; Basic communication
409
d62b2326
FT
410(define-condition name-server-timeout (dns-error)
411 ((server :initarg :server)))
412
0818ef99
FT
413(defun dns-do-request (server packet)
414 (declare (type address server)
415 (type dns-packet packet))
416 (with-connection (sk server)
417 (socket-send sk (dns-encode packet))
418 (loop
419 (let ((resp (dns-decode (socket-recv sk))))
420 (when (= (dns-packet-txid resp)
421 (dns-packet-txid packet))
422 (return resp))))))
423
424(defun dns-std-request (queries &key (txid (random 65536)) (recurse t))
425 (let ((qlist (map 'list #'(lambda (o)
426 (let ((name (first o))
427 (type (second o)))
428 (make-instance 'resource-query
429 :name (etypecase name
430 (string (parse-domain-name name))
431 (list name))
432 :type type)))
433 queries)))
434 (make-dns-packet :txid txid
435 :recurse recurse
436 :queries qlist)))
437
28d289c5
FT
438;;; RR caching
439
440(defstruct domain-cache-entry
d62b2326 441 (expire nil :type (or number null))
28d289c5
FT
442 (records '() :type list))
443
d62b2326
FT
444(defun domain-cache-key (name type)
445 (list name (etypecase type
446 (symbol type)
447 (resource-record (class-name (class-of type))))))
448
449(defun domain-cache-key-rr (record)
450 (declare (type resource-record record))
451 (list (slot-value record 'name) (class-name (class-of record))))
452
28d289c5 453(defun domain-cache-get-entry (cache name type &optional create)
d62b2326 454 (let* ((key (domain-cache-key name type))
28d289c5 455 (cur (gethash key cache)))
d62b2326
FT
456 (when (and cur (or (eq create :clear)
457 (let ((expire (domain-cache-entry-expire cur)))
458 (and expire
459 (> (/ (get-internal-real-time) internal-time-units-per-second)
460 expire)))))
461 (remhash key cache)
462 (setf cur nil))
28d289c5
FT
463 (cond (cur cur)
464 (create
465 (setf (gethash key cache) (make-domain-cache-entry))))))
466
467(defun domain-cache-put (cache record)
468 (with-slots (name ttl) record
469 (let ((entry (domain-cache-get-entry cache name record t)))
470 (push record (domain-cache-entry-records entry)))))
471
d62b2326
FT
472(defun dns-cache-records (cache records)
473 (loop (unless records (return))
474 (let* ((key (domain-cache-key-rr (car records)))
475 (matching (remove key records :test-not 'equal :key #'domain-cache-key-rr))
476 (ttl (block no-expire
477 (+ (/ (get-internal-real-time) internal-time-units-per-second)
478 (apply 'min (mapcar #'(lambda (rr)
479 (with-slots (ttl) rr
480 (if ttl ttl (return-from no-expire nil))))
481 matching)))))
482 (entry (make-domain-cache-entry :expire ttl :records matching)))
483 (setf (gethash key cache) entry
484 records (set-difference records matching)))))
485
486(defun dns-cache-response (cache packet)
487 (let ((records (append (dns-packet-answers packet)
488 (dns-packet-authority packet)
489 (dns-packet-additional packet))))
490 (flet ((on-root (rr)
491 (equal (slot-value rr 'name) '())))
492 (when (some #'on-root records)
493 (warn "DNS packet purports to contain RRs on the root zone.")
494 (setf records (delete-if #'on-root records))))
495 (when (dns-packet-authoritative packet)
496 (dolist (rq (dns-packet-queries packet))
497 (with-slots (name type) rq
498 (unless (equal name '())
499 (let ((key (domain-cache-key name type)))
500 (unless (find key records :test 'equal :key #'domain-cache-key-rr)
501 (let ((entry (domain-cache-get-entry cache name type :clear)))
502 (setf (domain-cache-entry-expire entry)
503 (+ (/ (get-internal-real-time) internal-time-units-per-second)
504 60))))))))) ; XXX: Or something. It needs
505 ; to last for the query in
506 ; progress, at least. One
507 ; should probably look at an
508 ; SOA RR, if there is one.
509 (dns-cache-records cache records)))
510
28d289c5
FT
511(defun make-domain-cache ()
512 (let ((table (make-hash-table :test 'equal)))
513 (dolist (server (labels ((ipv4 (address)
514 (make-instance 'ipv4-host-address :host-string address)))
515 `(("a.root-servers.net" ,(ipv4 "198.41.0.4"))
516 ("b.root-servers.net" ,(ipv4 "192.228.79.201"))
517 ("c.root-servers.net" ,(ipv4 "192.33.4.12"))
518 ("d.root-servers.net" ,(ipv4 "128.8.10.90"))
519 ("e.root-servers.net" ,(ipv4 "192.203.230.10"))
520 ("f.root-servers.net" ,(ipv4 "192.5.5.241"))
521 ("g.root-servers.net" ,(ipv4 "192.112.36.4"))
522 ("h.root-servers.net" ,(ipv4 "128.63.2.53"))
523 ("i.root-servers.net" ,(ipv4 "192.36.148.17"))
524 ("j.root-servers.net" ,(ipv4 "192.58.128.30"))
525 ("k.root-servers.net" ,(ipv4 "193.0.14.129"))
526 ("l.root-servers.net" ,(ipv4 "199.7.83.42"))
527 ("m.root-servers.net" ,(ipv4 "202.12.27.33")))))
528 (let ((parsed (parse-domain-name (first server))))
529 (domain-cache-put table (make-instance 'ns-record :name '() :ttl nil :ns-name parsed))
d62b2326
FT
530 ;; Ensure that the cache is initialized at least with empty
531 ;; lists, so that the resolver doesn't try to resolve the root
532 ;; servers.
533 (domain-cache-get-entry table parsed 'a-record t)
534 (domain-cache-get-entry table parsed 'aaaa-record t)
535
28d289c5
FT
536 (dolist (address (cdr server))
537 (domain-cache-put table (etypecase address
538 (ipv4-host-address (make-instance 'a-record :name parsed :ttl nil :address address)))))))
539 table))
540
541;;; Resolver
542
543(defstruct resolver-config
544 (cache (make-domain-cache))
545 (default-domains '() :type list)
546 (help-servers '() :type list))
547
548(defun initialize-default-resolver ()
549 #+unix (with-open-file (s #p"/etc/resolv.conf" :if-does-not-exist nil)
550 (when s
551 (let ((cfg (make-resolver-config)))
552 (labels ((whitespace-p (char)
553 (declare (type character char))
554 (or (char= char #\space)
555 (char= char #\tab)))
556 (split-line (line)
557 (let ((l '())
558 (p 0))
559 (loop (let* ((p1 (or (position-if-not #'whitespace-p line :start p)
560 (return l)))
561 (p2 (position-if #'whitespace-p line :start p1)))
562 (if p2
563 (setf l (append l (list (subseq line p1 p2)))
564 p p2)
565 (progn (setf l (append l (list (subseq line p1 p2))))
566 (return l))))))))
567 (let ((domain nil)
568 (search '()))
569 (loop (let ((line (read-line s nil nil)))
570 (unless line (return))
571 (let ((line (split-line line)))
572 (when line
573 (cond ((equal (car line) "nameserver")
574 (push (make-instance 'ipv4-address :host-string (second line))
575 (resolver-config-help-servers cfg)))
576 ((equal (car line) "search")
577 (setf search (append search (cdr line))))
578 ((equal (car line) "domain")
579 (setf domain (second line))))))))
580 (setf (resolver-config-default-domains cfg)
581 (or search (and domain (list domain)))))
582 cfg))))
583 #-unix nil)
584
d62b2326 585(defvar *dns-resolver-config* (initialize-default-resolver))
28d289c5 586
d62b2326 587(defgeneric dns-server-address-for-record (record))
6c36ab4a
FT
588(defmethod dns-server-address-for-record ((record a-record))
589 (make-instance 'udp4-address
590 :host-address (slot-value record 'address)
591 :port 53))
592(defmethod dns-server-address-for-record ((record aaaa-record))
593 (make-instance 'udp6-address
594 :host-address (slot-value record 'address)
595 :port 53))
596
597(define-condition dns-resolver-condition (condition)
598 ((query-name :initarg :query-name)
599 (query-type :initarg :query-type)
600 (config :initarg :config)))
601
602(define-condition dns-resolver-error (dns-error dns-resolver-condition) ())
603(define-condition domain-not-found-error (dns-resolver-error) ()
604 (:report (lambda (c s)
605 (with-slots (query-name) c
606 (format s "No name servers found for domain name ~A." query-name)))))
607(define-condition dns-name-error (dns-error dns-resolver-condition) ()
608 (:report (lambda (c s)
609 (with-slots (query-name) c
610 (format s "The domain name ~A does not exist." query-name)))))
611
612(define-condition dns-resolver-querying (dns-resolver-condition)
613 ((server :initarg :server)))
614
615(define-condition dns-resolver-got-resp (dns-resolver-condition)
616 ((server :initarg :server)
617 (response :initarg :response)))
618
619(define-condition dns-resolver-help (dns-resolver-condition) ())
620(define-condition dns-resolver-recursing (dns-resolver-condition) ())
621
622(define-condition dns-resolver-following-cname (dns-resolver-condition)
623 ((cname-rr :initarg :cname-rr)))
624
625(defun dns-resolve-name (name types &key (require-all t) (config *dns-resolver-config*))
626 (let ((name (etypecase name
627 (list name)
628 (string (parse-domain-name name))))
629 (types (etypecase types
630 (list types)
631 (symbol (list types))))
632 (cache (resolver-config-cache config)))
633 (flet ((check-cache ()
634 (let ((cn-entry (domain-cache-get-entry cache name 'cname-record)))
635 (when (and cn-entry (domain-cache-entry-records cn-entry))
636 (let ((record (car (domain-cache-entry-records cn-entry))))
637 (signal 'dns-resolver-following-cname :cname-rr record
638 :query-name (unparse-domain-name name) :query-type types
639 :config config)
640 (return-from dns-resolve-name
641 (dns-resolve-name (slot-value record 'cname) types :config config)))))
642 (block skip
643 (let ((records '())
644 (got-some nil))
645 (dolist (type types)
646 (let ((entry (domain-cache-get-entry cache name type)))
647 (cond (entry
648 (setf records (append records (domain-cache-entry-records entry))
649 got-some t))
650 (require-all
651 (return-from skip)))))
652 (when got-some
653 (return-from dns-resolve-name (values records name))))))
654 (nearest-known-servers (name)
655 (labels ((check1 (name)
656 (let ((entry (domain-cache-get-entry cache name 'ns-record)))
657 (cond ((and entry (domain-cache-entry-records entry))
658 (values (domain-cache-entry-records entry) name))
659 (name (check1 (cdr name)))
660 (t (values '() name))))))
661 (check1 name)))
662 (do-request (server)
663 (signal 'dns-resolver-querying :server server
664 :query-name (unparse-domain-name name) :query-type types
665 :config config)
666 (handler-case
667 (let ((resp (dns-do-request (udp-address-for server 53)
668 (dns-std-request (mapcar #'(lambda (type)
669 `(,name ,type))
670 types)))))
671 (signal 'dns-resolver-got-resp :server server :response resp
672 :query-name (unparse-domain-name name) :query-type types
673 :config config)
674 (dns-cache-response cache resp)
675 (with-slots (resp-code) resp
676 (when (eq (dns-packet-resp-code resp) :name-error)
677 (error 'dns-name-error :query-name (unparse-domain-name name) :query-type types
678 :config config))
679 (eq resp-code :success)))
680 (name-server-timeout () nil))))
681 (check-cache)
682 (signal 'dns-resolver-help :query-name (unparse-domain-name name) :query-type types
683 :config config)
684 (dolist (help-server (resolver-config-help-servers config))
685 (do-request help-server)
686 (check-cache))
687 (signal 'dns-resolver-recursing :query-name (unparse-domain-name name) :query-type types
688 :config config)
689 (let ((checked-domains '()))
690 (loop (multiple-value-bind (servers domain)
691 (nearest-known-servers name)
692 (unless servers
693 (error 'domain-not-found-error :query-name (unparse-domain-name name) :query-type types
694 :config config))
695 (if (find domain checked-domains :test 'equal)
696 (error 'domain-not-found-error :query-name (unparse-domain-name name) :query-type types
697 :config config)
698 (push domain checked-domains))
699 (macrolet ((dolist-random ((var list) &body body)
700 (let ((copy (gensym "COPY")))
701 `(let ((,copy ,list))
702 (loop (unless ,copy (return))
703 (let ((,var (elt ,list (random (length ,list)))))
704 (setf ,copy (remove ,var ,copy))
705 ,@body))))))
706 (block found-server
707 (dolist-random (record servers)
708 (let* ((server (slot-value record 'ns-name)))
709 (dolist-random (record (handler-case
710 (dns-resolve-name server '(a-record aaaa-record) :require-all nil :config config)
711 (dns-resolver-error () '())))
712 (when (do-request (dns-server-address-for-record record))
713 (return-from found-server))))))
714 (check-cache))))))))
28d289c5 715
b466cd48
FT
716;;; Misc.
717
718(defmethod print-object ((q resource-query) stream)
719 (with-slots (name type) q
720 (if *print-readably*
721 (format stream "~A: ~A" type (unparse-domain-name name))
722 (format stream "#<~S ~S ~S>" 'resource-query type (unparse-domain-name name)))))
723