Mercurial > core / lisp/lib/net/codec/dns.lisp
changeset 698: |
96958d3eb5b0 |
parent: |
bcf772e86fbc
|
author: |
Richard Westhaver <ellis@rwest.io> |
date: |
Fri, 04 Oct 2024 22:04:59 -0400 |
permissions: |
-rw-r--r-- |
description: |
fixes |
1 ;;; lib/net/codec/dns.lisp --- DNS Record utils 3 ;; from: https://github.com/Shinmera/dns-client 6 (in-package :net/codec/dns) 10 (define-condition dns-condition () 13 (define-condition dns-server-failure (error dns-condition) 14 ((dns-server :initarg :dns-server :reader dns-server) 15 (response-code :initarg :response-code :reader response-code)) 16 (:report (lambda (c s) (format s "DNS server ~% ~a~%responded with failure code ~d~@[~% ~a~]" 17 (dns-server c) (response-code c) (response-code-name (response-code c)))))) 19 (defun response-code-name (code) 27 (6 :name-should-not-exist) 28 (7 :set-should-not-exist) 29 (8 :set-does-not-exist) 32 (11 :type-not-implemented) 34 (17 :key-not-recognised) 42 (defmacro with-dns-error-handling (&body body) 43 `(handler-bind ((dns-server-failure 45 (unless (find (response-code e) '(1 3 6 7 8)) 49 ;;; Note: we assume that we never cross byte boundaries when accessing bits. 50 (defmacro with-decoding ((octets start &optional (pos (gensym "POS"))) &body body) 53 (prog1 (logbitp (* 8 (rem ,pos 1)) (aref ,octets (floor ,pos))) 56 (prog1 (ldb (byte 4 (* 8 (rem ,pos 1))) (aref ,octets (floor ,pos))) 59 (prog1 (aref ,octets (floor ,pos)) 61 (int16 () ;; big-endian 62 (prog1 (+ (ash (aref ,octets (+ 0 (floor ,pos))) 8) 63 (ash (aref ,octets (+ 1 (floor ,pos))) 0)) 66 (prog1 (+ (ash (aref ,octets (+ 0 (floor ,pos))) 24) 67 (ash (aref ,octets (+ 1 (floor ,pos))) 16) 68 (ash (aref ,octets (+ 2 (floor ,pos))) 8) 69 (ash (aref ,octets (+ 3 (floor ,pos))) 0)) 71 (declare (ignorable #'int1 #'int4 #'int8 #'int16 #'int32)) 74 (defmacro with-encoding ((octets start &optional (pos (gensym "POS"))) &body body) 77 (let ((octet (aref ,octets (floor ,pos)))) 78 (setf (ldb (byte 1 (* 8 (rem ,pos 1))) octet) 83 (setf (aref ,octets (floor ,pos)) octet) 86 (let ((octet (aref ,octets (floor ,pos)))) 87 (setf (ldb (byte 4 (* 8 (rem ,pos 1))) octet) value) 88 (setf (aref ,octets (floor ,pos)) octet) 91 (setf (aref ,octets (floor ,pos)) value) 93 (int16 (value) ;; big-endian 94 (setf (aref ,octets (+ 0 ,pos)) (ldb (byte 8 8) value)) 95 (setf (aref ,octets (+ 1 ,pos)) (ldb (byte 8 0) value)) 97 (int32 (value) ;; big-endian 98 (setf (aref ,octets (+ 0 ,pos)) (ldb (byte 8 24) value)) 99 (setf (aref ,octets (+ 1 ,pos)) (ldb (byte 8 16) value)) 100 (setf (aref ,octets (+ 2 ,pos)) (ldb (byte 8 8) value)) 101 (setf (aref ,octets (+ 3 ,pos)) (ldb (byte 8 0) value)) 103 (declare (ignorable #'int1 #'int4 #'int8 #'int16 #'int32)) 106 (defmacro maybe-set ((octets offset) &body calls) 107 `(with-encoding (,octets ,offset pos) 108 ,@(loop for (func value) in calls 111 (incf pos ,(ecase func 119 (defparameter *record-type-table* 217 (defun record-type-id (record-type &optional (error T)) 218 (or (second (find record-type *record-type-table* :key #'first :test #'string-equal)) 219 (when error "No such record type ~s" record-type))) 221 (defun id-record-type (id) 222 (or (first (find id *record-type-table* :key #'second :test #'=)) 225 (defun encode-host (name octets offset) 227 (name (encode-domain name))) 229 (setf (aref octets (+ offset start)) (- end start)) 230 (loop for i from (1+ start) to end 231 do (setf (aref octets (+ offset i)) (char-code (char-downcase (char name (1- i)))))) 232 (setf start (1+ end)))) 233 (loop for i from 0 below (length name) 234 do (when (char= #\. (char name i)) 236 finally (finish (length name))) 237 (setf (aref octets (+ offset start)) 0) 238 (+ offset start 1)))) 240 (defun decode-host* (string) 242 while (< i (length string)) 243 do (let ((jump (char-code (char string i)))) 244 (setf (char string i) #\.) 247 (if (string/= "" string) 251 (defun decode-host (octets offset start) 252 (loop with i = offset 255 with stream = (make-string-output-stream) 256 until (= 0 (aref octets i)) 257 ;; Handle label compression jump 258 do (cond ((<= 192 (aref octets i)) 260 (- (+ (* 256 (aref octets i)) (aref octets (1+ i))) 261 #b1100000000000001))) 265 (write-char (code-char (aref octets i)) stream))) 269 finally (return (values (decode-host* (get-output-stream-string stream)) (1+ pos))))) 271 (defun decode-header (octets offset) 272 (with-decoding (octets offset pos) 273 (values (list :id (int16) 274 :recursion-desired (int1) 275 :truncated-message (int1) 276 :authorative-answer (int1) 279 :response-code (int4) 280 :checking-disabled (int1) 281 :authenticated-data (int1) 283 :recursion-available (int1) 284 :question-count (int16) 285 :answer-count (int16) 286 :authority-count (int16) 287 :additional-count (int16)) 290 (defun encode-header (octets offset &key id recursion-desired truncated-message authorative-answer operation reply-p response-code checking-disabled authenticated-data z-reserved recursion-available question-count answer-count authority-count additional-count) 291 (maybe-set (octets offset) 293 (int1 recursion-desired) 294 (int1 truncated-message) 295 (int1 authorative-answer) 299 (int1 checking-disabled) 300 (int1 authenticated-data) 302 (int1 recursion-available) 303 (int16 question-count) 305 (int16 authority-count) 306 (int16 additional-count))) 308 (defun encode-query (octets offset hostname &key type class) 309 (let ((type (etypecase type 310 ((or string symbol) (record-type-id type)) 311 ((unsigned-byte 16) type)))) 312 (setf offset (encode-host hostname octets offset)) 313 (maybe-set (octets offset) 317 (defun decode-query (octets offset) 318 (with-decoding (octets offset pos) 319 (values (list :type (id-record-type (int16)) 323 (defun decode-data (octets offset) 324 (with-decoding (octets offset pos) 325 (values (list :type (id-record-type (int16)) 331 (defgeneric decode-record-payload (type octets start end)) 333 (defmethod decode-record-payload (type octets start end) 334 (subseq octets start end)) 336 (defmethod decode-record-payload ((type (eql :A)) octets start end) 337 (format NIL "~d.~d.~d.~d" 338 (aref octets (+ 0 start)) 339 (aref octets (+ 1 start)) 340 (aref octets (+ 2 start)) 341 (aref octets (+ 3 start)))) 343 (defmethod decode-record-payload ((type (eql :AAAA)) octets start end) 344 (usocket:vector-to-ipv6-host octets)) 346 (defmethod decode-record-payload ((type (eql :TXT)) octets start end) 347 (decode-host octets start 0)) 349 (defmethod decode-record-payload ((type (eql :URI)) octets start end) 350 (decode-host octets start 0)) 352 (defmethod decode-record-payload ((type (eql :CNAME)) octets start end) 353 (decode-host octets start 0)) 355 (defmethod decode-record-payload ((type (eql :PTR)) octets start end) 356 (decode-host octets start 0)) 358 ;; TODO: decode more. 360 (defmethod decode-record-payload ((type (eql :MX)) octets start end) 361 (with-decoding (octets start pos) 362 (list :priority (int16) 363 :name (decode-host octets pos 0)))) 365 (defmethod decode-record-payload ((type (eql :SOA)) octets start end) 366 (multiple-value-bind (mname pos) (decode-host octets start 0) 367 (multiple-value-bind (rname pos) (decode-host octets pos 0) 368 (with-decoding (octets pos) 375 :minimum (int32)))))) 377 (defun decode-record (octets offset) 378 (multiple-value-bind (data pos) (decode-data octets offset) 379 (setf (getf data :data) (decode-record-payload (getf data :type) octets pos (+ pos (getf data :length)))) 380 (values data (+ pos (getf data :length))))) 382 (defun decode-response (server octets offset limit) 383 (declare (ignorable limit)) 384 ;; FIXME: Implement buffer limiting. 385 (multiple-value-bind (header pos) (decode-header octets offset) 386 (when (< 0 (getf header :response-code)) 387 (error 'dns-server-failure :dns-server server :response-code (getf header :response-code))) 388 (let ((record-offset pos)) 390 (multiple-value-bind (name pos) (decode-host octets record-offset offset) 391 (multiple-value-bind (query pos) (funcall fun octets pos) 392 (setf record-offset pos) 393 (setf (getf query :name) name) 396 (loop repeat (getf header :question-count) 397 collect (decode #'decode-query)) 399 (loop repeat (getf header :answer-count) 400 collect (decode #'decode-record)) 402 (loop repeat (getf header :authority-count) 403 collect (decode #'decode-record)) 405 (loop repeat (getf header :additional-count) 406 collect (decode #'decode-record))