about summary refs log tree commit diff
path: root/lisp
diff options
context:
space:
mode:
authorVincent Ambo <tazjin@google.com>2020-01-26T01·18+0000
committerVincent Ambo <tazjin@google.com>2020-01-26T01·20+0000
commit09621f53716240c6a48cac7f09193a43b0ad305b (patch)
tree5d32bb2f4c6626a417bee511b5e9304b58385474 /lisp
parent29e1de2fd248ec4e152d5b0c83514fa57bcb0a33 (diff)
refactor(lisp/dns): Split package into multiple files
Adds a package definition file and moves the current client into
client.lisp

Note that the client is not working at all at this commit as this is a
work-in-progress snapshot.
Diffstat (limited to 'lisp')
-rw-r--r--lisp/dns/client.lisp173
-rw-r--r--lisp/dns/default.nix7
-rw-r--r--lisp/dns/package.lisp4
-rw-r--r--lisp/dns/resolver.lisp29
4 files changed, 182 insertions, 31 deletions
diff --git a/lisp/dns/client.lisp b/lisp/dns/client.lisp
new file mode 100644
index 0000000000..0f355589db
--- /dev/null
+++ b/lisp/dns/client.lisp
@@ -0,0 +1,173 @@
+;; Implementation of a DoH-client, see RFC 8484 (DNS Queries over
+;; HTTPS (DoH))
+
+(in-package #:dns)
+
+;;    The DoH client is configured with a URI Template [RFC6570]
+(defvar *doh-base-url* "https://dns.google/dns-query"
+  "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*
+                           :decode-content t
+                           :want-stream t
+                           :parameters `(("type" . ,type)
+                                         ("name" . ,name)
+                                         ("ct" . "application/dns-message")))
+    (read-binary 'dns-message stream)))
+
+(defun lookup-txt (name)
+  "Look up the TXT records at NAME."
+  (lookup-generic name "TXT"))
+
+(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.
diff --git a/lisp/dns/default.nix b/lisp/dns/default.nix
index c41b02f97c..134540b2be 100644
--- a/lisp/dns/default.nix
+++ b/lisp/dns/default.nix
@@ -4,12 +4,15 @@ pkgs.nix.buildLisp.library {
   name = "dns";
 
   deps = with pkgs.third_party.lisp; [
+    drakma
+    lisp-binary
+    iterate
     alexandria
     cl-json
-    drakma
   ];
 
   srcs = [
-    ./resolver.lisp
+    ./package.lisp
+    ./client.lisp
   ];
 }
diff --git a/lisp/dns/package.lisp b/lisp/dns/package.lisp
new file mode 100644
index 0000000000..639d9994aa
--- /dev/null
+++ b/lisp/dns/package.lisp
@@ -0,0 +1,4 @@
+(defpackage #:dns
+  (:documentation "Simple DNS resolver in Common Lisp")
+  (:use #:cl #:iterate #:lisp-binary)
+  (:export #:lookup-txt #:lookup-mx))
diff --git a/lisp/dns/resolver.lisp b/lisp/dns/resolver.lisp
deleted file mode 100644
index 774be525cb..0000000000
--- a/lisp/dns/resolver.lisp
+++ /dev/null
@@ -1,29 +0,0 @@
-;; Initial implementation is a simple client for
-;; https://developers.google.com/speed/public-dns/docs/doh/json
-
-(defpackage #:dns
-  (:documentation "Simple DNS resolver in Common Lisp")
-  (:use #:cl)
-  (:export #:lookup-txt #:lookup-mx))
-
-(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 (body)
-      (drakma:http-request *doh-base-url*
-                           :decode-content t
-                           :want-stream t
-                           :parameters `(("type" . ,type)
-                                         ("name" . ,name)
-                                         ("ct" . "application/x-javascript")))
-    (cl-json:decode-json body)))
-
-(defun lookup-txt (name)
-  "Look up the TXT records at NAME."
-  (lookup-generic name "TXT"))
-
-(defun lookup-mx (name)
-  "Look up the MX records at NAME."
-  (lookup-generic name "MX"))