about summary refs log tree commit diff
path: root/lisp
diff options
context:
space:
mode:
authorVincent Ambo <tazjin@google.com>2020-01-26T19·58+0000
committerVincent Ambo <tazjin@google.com>2020-01-26T19·58+0000
commit8f805a29d10ca7080aec0f07feca49fc22a631a3 (patch)
tree9d7db51f7280a2c534272749beb474c2c7db0ca3 /lisp
parent1440fc0dd722dded073888c9bc3bd5101774309d (diff)
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.
Diffstat (limited to 'lisp')
-rw-r--r--lisp/dns/client.lisp204
1 files changed, 52 insertions, 152 deletions
diff --git a/lisp/dns/client.lisp b/lisp/dns/client.lisp
index 0f355589db..2dbe9ff31d 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.