diff options
| author | Bob Halley <halley@dnspython.org> | 2020-07-13 06:31:24 -0700 |
|---|---|---|
| committer | Bob Halley <halley@dnspython.org> | 2020-07-13 06:31:24 -0700 |
| commit | a7e71aa9b0ebf43c8f997adc66b45069506afa6e (patch) | |
| tree | 6a0fff36b6a6ab8af8dd3bd6772f2b4b7e5a7f87 /examples | |
| parent | 868c660fc9487862cbb8b79bafdfd3209c54d4ad (diff) | |
| download | dnspython-a7e71aa9b0ebf43c8f997adc66b45069506afa6e.tar.gz | |
Add DoH JSON example.
Diffstat (limited to 'examples')
| -rwxr-xr-x | examples/doh-json.py | 92 |
1 files changed, 92 insertions, 0 deletions
diff --git a/examples/doh-json.py b/examples/doh-json.py new file mode 100755 index 0000000..8cfe1b0 --- /dev/null +++ b/examples/doh-json.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 + +import copy +import json +import requests + +import dns.flags +import dns.message +import dns.resolver +import dns.rdataclass +import dns.rdatatype + +# This shows how to convert to/from dnspython's message object and the +# DNS-over-HTTPS (DoH) JSON form used by Google and Cloudflare, and +# described here: +# +# https://developers.google.com/speed/public-dns/docs/doh/json +# +# There's no need to do this for DoH as dnspython supports the +# standard RFC 8484 protocol which all DoH providers implement. The +# conversion to/from JSON is useful, however, so we show a way to do +# it. +# +# "simple" below means "simple python data types", i.e. things made of +# combinations of dictionaries, lists, strings, and numbers. + +def make_rr(simple, rdata): + csimple = copy.copy(simple) + csimple['data'] = rdata.to_text() + return csimple + +def flatten_rrset(rrs): + simple = { + 'name': str(rrs.name), + 'type': rrs.rdtype, + } + if len(rrs) > 0: + simple['TTL'] = rrs.ttl + return [make_rr(simple, rdata) for rdata in rrs] + else: + return [simple] + +def to_doh_simple(message): + simple = { + 'Status': message.rcode() + } + for f in dns.flags.Flag: + if f != dns.flags.Flag.AA and f != dns.flags.Flag.QR: + # DoH JSON doesn't need AA and omits it. DoH JSON is only + # used in replies so the QR flag is implied. + simple[f.name] = (message.flags & f) != 0 + for i, s in enumerate(message.sections): + k = dns.message.MessageSection.to_text(i).title() + simple[k] = [] + for rrs in s: + simple[k].extend(flatten_rrset(rrs)) + # we don't encode the ecs_client_subnet field + return simple + +def from_doh_simple(simple, add_qr=False): + message = dns.message.QueryMessage() + flags = 0 + for f in dns.flags.Flag: + if simple.get(f.name, False): + flags |= f + if add_qr: # QR is implied + flags |= dns.flags.QR + message.flags = flags + message.set_rcode(simple.get('Status', 0)) + for i, sn in enumerate(dns.message.MessageSection): + rr_list = simple.get(sn.name.title(), []) + for rr in rr_list: + rdtype = dns.rdatatype.RdataType(rr['type']) + rrs = message.find_rrset(i, dns.name.from_text(rr['name']), + dns.rdataclass.IN, rdtype, + create=True) + if 'data' in rr: + rrs.add(dns.rdata.from_text(dns.rdataclass.IN, rdtype, + rr['data']), rr.get('TTL', 0)) + # we don't decode the ecs_client_subnet field + return message + + +a = dns.resolver.resolve('www.dnspython.org', 'a') +p = to_doh_simple(a.response) +print(json.dumps(p, indent=4)) +response = requests.get('https://dns.google/resolve?', verify=True, + params={'name': 'www.dnspython.org', + 'type': 1}) +p = json.loads(response.text) +m = from_doh_simple(p, True) +print(m) |
