From 09621f53716240c6a48cac7f09193a43b0ad305b Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sun, 26 Jan 2020 01:18:34 +0000 Subject: [PATCH] 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. --- lisp/dns/client.lisp | 173 +++++++++++++++++++++++++++++++++++++++++ lisp/dns/default.nix | 7 +- lisp/dns/package.lisp | 4 + lisp/dns/resolver.lisp | 29 ------- 4 files changed, 182 insertions(+), 31 deletions(-) create mode 100644 lisp/dns/client.lisp create mode 100644 lisp/dns/package.lisp delete mode 100644 lisp/dns/resolver.lisp diff --git a/lisp/dns/client.lisp b/lisp/dns/client.lisp new file mode 100644 index 000000000..0f355589d --- /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 c41b02f97..134540b2b 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 000000000..639d9994a --- /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 774be525c..000000000 --- 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"))