From 8f805a29d10ca7080aec0f07feca49fc22a631a3 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sun, 26 Jan 2020 19:58:52 +0000 Subject: [PATCH] feat(lisp/dns): Use new DNS deserialiser in dns:lookup-generic This enables arbitrary DNS lookups (with the caveat that RRDATAs are currently not deserialised into a record-type-specific format). An error condition has been defined for error-responses from the HTTP server which provides interactive restarts for attempting a new call with different parameters. --- lisp/dns/client.lisp | 204 +++++++++++-------------------------------- 1 file changed, 52 insertions(+), 152 deletions(-) diff --git a/lisp/dns/client.lisp b/lisp/dns/client.lisp index 0f355589d..2dbe9ff31 100644 --- a/lisp/dns/client.lisp +++ b/lisp/dns/client.lisp @@ -4,19 +4,65 @@ (in-package #:dns) ;; The DoH client is configured with a URI Template [RFC6570] -(defvar *doh-base-url* "https://dns.google/dns-query" +(defvar *doh-base-url* "https://dns.google/resolve" "Base URL of the service providing DNS-over-HTTP(S). Defaults to the Google-hosted API.") -(defun lookup-generic (name type) - (multiple-value-bind (stream) - (drakma:http-request *doh-base-url* +(define-condition doh-error (error) + ((query-name :initarg :query-name + :reader doh-error-query-name + :type string) + (query-type :initarg :query-type + :reader doh-error-query-type + :type string) + (doh-url :initarg :doh-url + :reader doh-error-doh-url + :type string) + (status-code :initarg :status-code + :reader doh-error-status-code + :type integer) + (response-body :initarg :response-body + :reader doh-error-response-body + :type (or nil (vector (unsigned-byte 8)) string))) + + (:report (lambda (condition stream) + (let ((url (doh-error-doh-url condition)) + (status (doh-error-status-code condition)) + (body (doh-error-response-body condition))) + (format stream "DoH service at '~A' responded with non-success (~A): ~%~%~A" + url status body))))) + +(defun lookup-generic (name type &key (doh-url *doh-base-url*)) + (multiple-value-bind (body status) + (drakma:http-request doh-url :decode-content t - :want-stream t + ;; TODO(tazjin): Figure out why 'want-stream' doesn't work :parameters `(("type" . ,type) ("name" . ,name) ("ct" . "application/dns-message"))) - (read-binary 'dns-message stream))) + (if (= 200 status) + (read-binary 'dns-message (flexi-streams:make-in-memory-input-stream body)) + + (restart-case (error 'doh-error + :query-name name + :query-type type + :doh-url doh-url + :status-code status + :response-body body) + (call-with-other-name (new-name) + :interactive (lambda () (list (the string (read)))) + :test (lambda (c) (typep c 'doh-error)) + (lookup-generic new-name type :doh-url doh-url)) + + (call-with-other-type (new-type) + :interactive (lambda () (list (the string (read)))) + :test (lambda (c) (typep c 'doh-error)) + (lookup-generic name new-type :doh-url doh-url)) + + (call-with-other-url (new-url) + :interactive (lambda () (list (the string (read)))) + :test (lambda (c) (typep c 'doh-error)) + (lookup-generic name type :doh-url new-url)))))) (defun lookup-txt (name) "Look up the TXT records at NAME." @@ -25,149 +71,3 @@ (defun lookup-mx (name) "Look up the MX records at NAME." (lookup-generic name "MX")) - - -;; The URI Template defined in this document is processed without any -;; variables when the HTTP method is POST. When the HTTP method is GET, -;; the single variable "dns" is defined as the content of the DNS -;; request (as described in Section 6), encoded with base64url -;; [RFC4648]. - -;; When using the POST method, the DNS query is included as the message -;; body of the HTTP request, and the Content-Type request header field -;; indicates the media type of the message. POSTed requests are -;; generally smaller than their GET equivalents. - -;; Using the GET method is friendlier to many HTTP cache -;; implementations. - -;; The DoH client SHOULD include an HTTP Accept request header field to -;; indicate what type of content can be understood in response. -;; Irrespective of the value of the Accept request header field, the -;; client MUST be prepared to process "application/dns-message" (as -;; described in Section 6) responses but MAY also process other DNS- -;; related media types it receives. - -;; In order to maximize HTTP cache friendliness, DoH clients using media -;; formats that include the ID field from the DNS message header, such -;; as "application/dns-message", SHOULD use a DNS ID of 0 in every DNS -;; request. HTTP correlates the request and response, thus eliminating -;; the need for the ID in a media type such as "application/dns- -;; message". The use of a varying DNS ID can cause semantically -;; equivalent DNS queries to be cached separately. - -;; DoH clients can use HTTP/2 padding and compression [RFC7540] in the -;; same way that other HTTP/2 clients use (or don't use) them. - -;; 4.1.1. HTTP Request Examples - -;; These examples use HTTP/2-style formatting from [RFC7540]. - -;; These examples use a DoH service with a URI Template of -;; "https://dnsserver.example.net/dns-query{?dns}" to resolve IN A -;; records. - -;; The requests are represented as bodies with media type "application/ -;; dns-message". - -;; The first example request uses GET to request "www.example.com". - -;; :method = GET -;; :scheme = https -;; :authority = dnsserver.example.net -;; :path = /dns-query?dns=AAABAAABAAAAAAAAA3d3dwdleGFtcGxlA2NvbQAAAQAB -;; accept = application/dns-message - -;; Finally, a GET-based query for "a.62characterlabel-makes-base64url- -;; distinct-from-standard-base64.example.com" is shown as an example to -;; emphasize that the encoding alphabet of base64url is different than -;; regular base64 and that padding is omitted. - -;; The only response type defined in this document is "application/dns- -;; message", but it is possible that other response formats will be -;; defined in the future. A DoH server MUST be able to process -;; "application/dns-message" request messages. - -;; Each DNS request-response pair is mapped to one HTTP exchange. - -;; DNS response codes indicate either success or failure for the DNS -;; query. A successful HTTP response with a 2xx status code (see -;; Section 6.3 of [RFC7231]) is used for any valid DNS response, - -;; HTTP responses with non-successful HTTP status codes do not contain -;; replies to the original DNS question in the HTTP request. DoH -;; clients need to use the same semantic processing of non-successful -;; HTTP status codes as other HTTP clients. - -;; 4.2.2. HTTP Response Example - -;; This is an example response for a query for the IN AAAA records for -;; "www.example.com" with recursion turned on. The response bears one -;; answer record with an address of 2001:db8:abcd:12:1:2:3:4 and a TTL -;; of 3709 seconds. - -;; :status = 200 -;; content-type = application/dns-message -;; content-length = 61 -;; cache-control = max-age=3709 - -;; <61 bytes represented by the following hex encoding> -;; 00 00 81 80 00 01 00 01 00 00 00 00 03 77 77 77 -;; 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 00 1c 00 -;; 01 c0 0c 00 1c 00 01 00 00 0e 7d 00 10 20 01 0d -;; b8 ab cd 00 12 00 01 00 02 00 03 00 04 - -;; This protocol MUST be used with the https URI scheme [RFC7230]. - -;; In particular, DoH servers SHOULD assign an explicit HTTP freshness -;; lifetime (see Section 4.2 of [RFC7234]) so that the DoH client is -;; more likely to use fresh DNS data. This requirement is due to HTTP -;; caches being able to assign their own heuristic freshness (such as -;; that described in Section 4.2.2 of [RFC7234]), which would take -;; control of the cache contents out of the hands of the DoH server. - - -;; The assigned freshness lifetime of a DoH HTTP response MUST be less -;; than or equal to the smallest TTL in the Answer section of the DNS -;; response. A freshness lifetime equal to the smallest TTL in the -;; Answer section is RECOMMENDED. For example, if a HTTP response -;; carries three RRsets with TTLs of 30, 600, and 300, the HTTP -;; freshness lifetime should be 30 seconds (which could be specified as -;; "Cache-Control: max-age=30"). This requirement helps prevent expired -;; RRsets in messages in an HTTP cache from unintentionally being -;; served. - -;; If the DNS response has no records in the Answer section, and the DNS -;; response has an SOA record in the Authority section, the response -;; freshness lifetime MUST NOT be greater than the MINIMUM field from -;; that SOA record (see [RFC2308]). - -;; DoH clients MUST account for the Age response header field's value -;; [RFC7234] when calculating the DNS TTL of a response. For example, -;; if an RRset is received with a DNS TTL of 600, but the Age header -;; field indicates that the response has been cached for 250 seconds, -;; the remaining lifetime of the RRset is 350 seconds. This requirement -;; applies to both DoH client HTTP caches and DoH client DNS caches. - -;; Those features were introduced to HTTP in HTTP/2 [RFC7540]. -;; Earlier versions of HTTP are capable of conveying the semantic -;; requirements of DoH but may result in very poor performance. - -;; In order to maximize interoperability, DoH clients and DoH servers -;; MUST support the "application/dns-message" media type. - -;; The data payload for the "application/dns-message" media type is a -;; single message of the DNS on-the-wire format defined in Section 4.2.1 -;; of [RFC1035], which in turn refers to the full wire format defined in -;; Section 4.1 of that RFC. - -;; This media type restricts the maximum size of the DNS message to -;; 65535 bytes. - -;; When using the GET method, the data payload for this media type MUST -;; be encoded with base64url [RFC4648] and then provided as a variable -;; named "dns" to the URI Template expansion. Padding characters for -;; base64url MUST NOT be included. - -;; When using the POST method, the data payload for this media type MUST -;; NOT be encoded and is used directly as the HTTP message body.