Commit d373fbbb authored by Alexandre's avatar Alexandre
Browse files

[DoH] Use HTTP/2 streams. Addresses #8

parent 674a3376
......@@ -385,6 +385,7 @@ class ConnectionDoT(Connection):
request.check_response()
return request
class CurlHandle():
def __init__(self, connect=None, forceIPv4=False, forceIPv6=False,
verbose=verbose, insecure=insecure):
......@@ -406,6 +407,15 @@ class CurlHandle():
self.handle.setopt(pycurl.CONNECT_TO, [f'::{repraddress}:443',])
self.handle.setopt(pycurl.HTTPHEADER, ["Content-type: application/dns-message"])
def close(self):
self.handle.close()
def setopt(self, key, value):
self.handle.setopt(key, value)
def perform(self):
self.handle.perform()
def reset_opt_default(self):
opts = {
pycurl.NOBODY: False,
......@@ -416,6 +426,34 @@ class CurlHandle():
for opt, value in opts.items():
self.handle.setopt(opt, value)
def prepare(self, connection, request):
if not connection.multistreams:
self.reset_opt_default()
if request.post:
self.prepare_post(connection, request)
elif request.head:
self.prepare_head(connection, request)
else:
self.prepare_get(connection, request)
request.buffer = io.BytesIO()
self.handle.setopt(pycurl.WRITEDATA, request.buffer)
def prepare_get(self, connection, request):
self.handle.setopt(pycurl.HTTPGET, True)
dns_req = base64.urlsafe_b64encode(request.data).decode('UTF8').rstrip('=')
self.handle.setopt(pycurl.URL, connection.server + ("?dns=%s" % dns_req))
def prepare_post(self, connection, request):
request.post = True
self.handle.setopt(pycurl.POST, True)
self.handle.setopt(pycurl.POSTFIELDS, request.data)
self.handle.setopt(pycurl.URL, connection.server)
def prepare_head(self, connection, request):
request.head = True
self.prepare_get(connection, request)
self.handle.setopt(pycurl.NOBODY, True)
class ConnectionDoH(Connection):
def __init__(self, server, servername=None, connect=None, forceIPv4=False, forceIPv6=False,
......@@ -426,70 +464,54 @@ class ConnectionDoH(Connection):
self.url = server
self.connect = connect
self.multistreams = multistreams
def create_handle(self):
if not self.multistreams:
self.curl = CurlHandle(self.connect, self.forceIPv4, self.forceIPv6, self.verbose, self.insecure)
if self.multistreams:
self.multi = self.create_multi()
else:
self.curl = CurlHandle(self.connect, self.forceIPv4, self.forceIPv6, self.verbose, self.insecure)
return
self.curl = pycurl.Curl() # TODO: use CurlMulti()
# http://pycurl.io/docs/latest/curlmultiobject.html
# but first we have to
# setopt somewhere else because
# most options are not for
# the multi handle but for
# each stream handle.
self.curl_handle = self.create_handle()
def end(self):
self.curl.handle.close()
def create_handle(self):
return CurlHandle(self.connect, self.forceIPv4, self.forceIPv6, self.verbose, self.insecure)
def reset_opt_default(self):
self.curl.reset_opt_default()
def create_multi(self):
multi = pycurl.CurlMulti()
multi.setopt(pycurl.M_MAX_HOST_CONNECTIONS, 1)
return multi
def prepare(self, request):
try:
self.curl.reset_opt_default()
except AttributeError:
self.create_handle()
if request.post:
self.prepare_post(request)
elif request.head:
self.prepare_head(request)
def end(self):
if not self.multistreams:
self.curl_handle.close()
else:
self.prepare_get(request)
def prepare_get(self, request):
self.curl.handle.setopt(pycurl.HTTPGET, True)
dns_req = base64.urlsafe_b64encode(request.data).decode('UTF8').rstrip('=')
self.curl.handle.setopt(pycurl.URL, self.server + ("?dns=%s" % dns_req))
def prepare_post(self, request):
request.post = True
self.curl.handle.setopt(pycurl.POST, True)
self.curl.handle.setopt(pycurl.POSTFIELDS, request.data)
self.curl.handle.setopt(pycurl.URL, self.server)
def prepare_head(self, request):
request.head = True
self.prepare_get(request)
self.curl.handle.setopt(pycurl.NOBODY, True)
# TODO: remove and close handles
self.multi.close()
def perform(self, request):
request.buffer = io.BytesIO()
self.curl.handle.setopt(pycurl.WRITEDATA, request.buffer)
self.curl.handle.perform()
self.curl_handle.setopt(pycurl.WRITEDATA, request.buffer)
self.curl_handle.perform()
def perform_multi(self):
while 1:
ret, num_handles = self.multi.perform()
if ret != pycurl.E_CALL_MULTI_PERFORM:
break
while num_handles:
ret = self.multi.select(1.0)
if ret == -1:
continue
while 1:
ret, num_handles = self.multi.perform()
if ret != pycurl.E_CALL_MULTI_PERFORM:
break
def send(self, request):
request.to_wire()
self.prepare(request)
self.perform(request)
def receive(self, request):
handle = request.handle.handle
body = request.buffer.getvalue()
body_size = len(body)
http_code = self.curl.handle.getinfo(pycurl.RESPONSE_CODE)
content_type = self.curl.handle.getinfo(pycurl.CONTENT_TYPE)
http_code = handle.getinfo(pycurl.RESPONSE_CODE)
content_type = handle.getinfo(pycurl.CONTENT_TYPE)
request.response = body
request.response_size = body_size
request.rcode = http_code
......@@ -504,11 +526,17 @@ class ConnectionDoH(Connection):
request = RequestDoH(qname, qtype, want_dnssec=dnssec, use_edns=edns)
request.head = head
request.post = post
request.to_wire()
if synchronous:
request.handle = self.curl_handle
else:
request.handle = self.create_handle()
request.handle.prepare(self, request)
if synchronous:
self.send_and_receive(request)
request.check_response()
else:
self.send(request)
self.multi.add_handle(request.handle.handle)
return request
......@@ -761,6 +789,7 @@ for i in range (0, tests):
if delay is not None:
time.sleep(delay)
if multistreams:
conn.perform_multi()
print("")
result = read_results(conn, pending)
for j in result:
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment