From: Filip Š Date: Sat, 28 Sep 2019 15:43:48 +0000 (+0200) Subject: Add support for DoH X-Git-Tag: v2.0.0rc1~345^2~8 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8e8f7129a4a2c2b1a5873996773eb2b4da0c4452;p=thirdparty%2Fdnspython.git Add support for DoH --- diff --git a/dns/query.py b/dns/query.py index 20c953cb..62b50143 100644 --- a/dns/query.py +++ b/dns/query.py @@ -19,6 +19,7 @@ from __future__ import generators +import urllib.request import errno import select import socket @@ -189,6 +190,27 @@ def _destination_and_source(af, where, port, source, source_port): return (af, destination, source) +def doh(query, nameserver): + """Return the response obtained after sending a query via DoH. + + *query*, a ``dns.message.Message`` containing the query to send + + *nameserver*, a ``str`` containing the nameserver URL. + + Returns a ``dns.message.Message``. + """ + + wirequery = query.to_wire() + headers = { + 'Accept': 'application/dns-message', + 'Content-Type': 'application/dns-message', + } + + request = urllib.request.Request(nameserver, data=wirequery, headers=headers) + response = urllib.request.urlopen(request).read() + + return dns.message.from_wire(response) + def send_udp(sock, what, destination, expiration=None): """Send a DNS message to the specified UDP socket. diff --git a/dns/query.pyi b/dns/query.pyi index fe5ef826..ccd4b3fe 100644 --- a/dns/query.pyi +++ b/dns/query.pyi @@ -1,5 +1,8 @@ from typing import Optional, Union, Dict, Generator, Any from . import message, tsig, rdatatype, rdataclass, name, message +def doh(query : message.Message, nameserver : str) -> message.Message: + pass + def tcp(q : message.Message, where : str, timeout : float = None, port=53, af : Optional[int] = None, source : Optional[str] = None, source_port : int = 0, one_rr_per_rrset=False) -> message.Message: pass diff --git a/dns/resolver.py b/dns/resolver.py index cecc3749..04c6f6f1 100644 --- a/dns/resolver.py +++ b/dns/resolver.py @@ -17,6 +17,7 @@ """DNS stub resolver.""" +from urllib.parse import urlparse import socket import sys import time @@ -897,29 +898,34 @@ class Resolver(object): for nameserver in nameservers[:]: timeout = self._compute_timeout(start, lifetime) port = self.nameserver_ports.get(nameserver, self.port) + protocol = urlparse(nameserver).scheme try: - tcp_attempt = tcp - if tcp: - response = dns.query.tcp(request, nameserver, - timeout, port, - source=source, - source_port=source_port) + if protocol == 'https': + tcp_attempt = True + response = dns.query.doh(request, nameserver) else: - try: - response = dns.query.udp(request, nameserver, + tcp_attempt = tcp + if tcp: + response = dns.query.tcp(request, nameserver, timeout, port, source=source, - source_port=\ - source_port) - except dns.message.Truncated: - # Response truncated; retry with TCP. - tcp_attempt = True - timeout = self._compute_timeout(start, lifetime) - response = \ - dns.query.tcp(request, nameserver, - timeout, port, - source=source, - source_port=source_port) + source_port=source_port) + else: + try: + response = dns.query.udp(request, nameserver, + timeout, port, + source=source, + source_port=\ + source_port) + except dns.message.Truncated: + # Response truncated; retry with TCP. + tcp_attempt = True + timeout = self._compute_timeout(start, lifetime) + response = \ + dns.query.tcp(request, nameserver, + timeout, port, + source=source, + source_port=source_port) except (socket.error, dns.exception.Timeout) as ex: # # Communication failure or timeout. Go to the