GithubHelp home page GithubHelp logo

facebookarchive / doh-proxy Goto Github PK

View Code? Open in Web Editor NEW
463.0 32.0 78.0 196 KB

A proof of concept DNS-Over-HTTPS proxy implementing https://datatracker.ietf.org/doc/draft-ietf-doh-dns-over-https/

Home Page: https://facebookexperimental.github.io/doh-proxy/

License: Other

Python 99.78% Makefile 0.22%

doh-proxy's Introduction

DNS Over HTTPS Proxy

Test Code Base GitHub Super-Linter PyPI version

A set of python 3 scripts that supports proxying DNS over HTTPS as specified in the IETF Draft draft-ietf-doh-dns-over-https.

DOH provides a way to run encrypted DNS over HTTPS, a protocol which can freely traverse firewalls when other encrypted mechanism may be blocked.

The project comes with a set of 4 tools:

  • doh-proxy: A service that receives DOH queries over HTTP2 and forwards them to a recursive resolver.
  • doh-httpproxy: Like doh-proxy but uses HTTP instead of HTTP2. The main intent is to run this behind a reverse proxy.
  • doh-stub: A service that listens for DNS queries and forwards them to a DOH server.
  • doh-client: A tool to perform a test DNS query against DOH server.

See the CONTRIBUTING file for how to help out.

DOH Proxy was created during IETF Hackathon 100 as a proof-of-concept and is not used at Facebook.

You are welcome to use it, but be aware that support is limited and best-effort.

Installing

To install an already packaged version directly from PyPi:

$ pip3 install doh-proxy

Usage

doh-proxy

doh-proxy is a stand alone server answering DOH request. The proxy does not do DNS recursion itself and rather forward the query to a full-featured DNS recursive server or DNS caching server.

By running doh-proxy, you can get and end-to-end DOH solution with minimal setup.

$ sudo doh-proxy \
    --upstream-resolver=::1 \
    --certfile=./fullchain.pem \
    --keyfile=./privkey.pem

doh-httpproxy

doh-httpproxy is designed to be running behind a reverse proxy. In this setup a reverse proxy such as NGINX would be handling the HTTPS/HTTP2 requests from the DOH clients and will forward them to doh-httpproxy backends.

While this setup requires more upfront setup, it allows running DOH proxy unprivileged and on multiple cores.

$ doh-httpproxy \
    --upstream-resolver=::1 \
    --port 8080 \
    --listen-address ::1

doh-httpproxy now also supports TLS, that you can enable passing the args --certfile and --keyfile (just like doh-proxy)

doh-stub

doh-stub is the piece of software that you would run on the clients. By providing a local DNS server, doh-stub will forward the DNS requests it receives to a DOH server using an encrypted link.

You can start a stub resolver with:

$ doh-stub \
    --listen-port 5553 \
    --listen-address ::1 \
    --domain foo.bar \
    --remote-address ::1

and query it.

$ dig @::1 -p 5553 example.com

doh-client

doh-client is just a test cli that can be used to quickly send a request to a DOH server and dump the returned answer.

$ doh-client  \
    --domain dns.dnsoverhttps.net \
    --qname sigfail.verteiltesysteme.net \
    --dnssec
id 37762
opcode QUERY
rcode SERVFAIL
flags QR RD RA
edns 0
eflags DO
payload 4096
;QUESTION
sigfail.verteiltesysteme.net. IN AAAA
;ANSWER
;AUTHORITY
;ADDITIONAL

$ doh-client  \
    --domain dns.dnsoverhttps.net \
    --qname sigok.verteiltesysteme.net \
    --dnssec
id 49772
opcode QUERY
rcode NOERROR
flags QR RD RA AD
edns 0
eflags DO
payload 4096
;QUESTION
sigok.verteiltesysteme.net. IN AAAA
;ANSWER
sigok.verteiltesysteme.net. 60 IN AAAA 2001:638:501:8efc::139
sigok.verteiltesysteme.net. 60 IN RRSIG AAAA 5 3 60 20180130030002 20171031030002 30665 verteiltesysteme.net. O7QgNZFBu3fULvBXwM39apv5nMehh51f mLOVEsC8qZUyxIbxo4eDLQt0JvPoPpFH 5TbWdlm/jxq5x2/Kjw7yUdpohhiNmdoD Op7Y+RyHbf676FoC5Zko9uOAB7Pp8ERz qiT0QPt1ec12bM0XKQigfp+2Hy9wUuSN QmAzXS2s75k=
;AUTHORITY
;ADDITIONAL

Development

Requirements

  • python >= 3.5
  • aiohttp
  • aioh2
  • dnspython

Building

DOH Proxy uses Python'setuptools to manage dependencies and build.

To install its dependencies:

$ python3 setup.py develop
# Due to GH #63
$ pip install git+https://github.com/URenko/aioh2#egg=aioh2

To build:

$ python3 setup.py build

To run unittests:

$ python3 setup.py test

To run the linter:

DOH Proxy uses GitHub Action Super-Linter to lint the code. In order to validate your code locally, it is possible to run Super-Linter locally using the following comand line from within the repository:

docker run -e RUN_LOCAL=true  -e VALIDATE_PYTHON_PYLINT=false \
    -e FILTER_REGX_INCLUDE='(dohproxy|test)/.*.py' \
    -v $(pwd):/tmp/lint \
     --rm github/super-linter:v3

From within the root of the repository, you can test the proxy, stub and client respectively by using the following commands:

$ sudo PYTHONPATH=. ./dohproxy/proxy.py ...
$ PYTHONPATH=. ./dohproxy/httpproxy.py ...
$ PYTHONPATH=. ./dohproxy/stub.py ...
$ PYTHONPATH=. ./dohproxy/client.py ...

License

DOH Proxy is BSD-licensed.

doh-proxy's People

Contributors

ameyah avatar bagder avatar chantra avatar crolfe avatar eraymitrani avatar jedisct1 avatar jpmens avatar netbsd8 avatar newez avatar prajay avatar rfinnie avatar tiran avatar xetherealx avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

doh-proxy's Issues

Connection leak if asyncio TimeoutError happens

This is another leak as mentioned in #66
https://github.com/facebookexperimental/doh-proxy/blob/999a2ae9825919aa4196a917dafa75fb20ec80d6/dohproxy/server_protocol.py#L53-L57
https://github.com/facebookexperimental/doh-proxy/blob/999a2ae9825919aa4196a917dafa75fb20ec80d6/dohproxy/server_protocol.py#L62-L66
Both create_datagram_endpoint and create_connection return a tuple of transport, protocol. You need to capture the transport reference, so that when you hit an asyncio.TimeoutError you can properly run transport.close(). If you don't do this and are sending lots of queries, eventually you'll hit your ulimit -n maximum open files limit from your system. This can be solved by passing the transport object into _try_query and then closing it in
https://github.com/facebookexperimental/doh-proxy/blob/999a2ae9825919aa4196a917dafa75fb20ec80d6/dohproxy/server_protocol.py#L73-L75

[httpproxy] support X-Forwarded-For header

Currently, the HTTP DOH Proxy does not handle X-Forwarded-For header. As it is designed to be running behind a reverse proxy, it will lose the original requestor IP which can be inconvenient in logs for instance or in the future if we want to be able to forward some ECS information.

We should handle this in the httpproxy code and make it available universally. aiohttp can handle it using a middleware: https://aiohttp-remotes.readthedocs.io/en/stable/api.html#x-forwarded

and https://aiohttp-remotes.readthedocs.io/en/stable/api.html#setup to set it up.

doh-httpproxy inside docker container: aiohttp_remotes.exceptions.UntrustedIP

Hi! I'd like to use doh-httpproxy as a docker container. My docker file looks like this.

FROM python:latest

RUN apt-get update \
	&& apt-get -y upgrade \
	&& apt-get -y install dnsutils \
	&& apt-get -y install git \
#	&& pip3 install doh-proxy \
	&& pip3 install git+https://github.com/facebookexperimental/doh-proxy.git

EXPOSE 9000

ENTRYPOINT [ "doh-httpproxy", "--upstream-resolver=8.8.8.8", "--port", "9000", "--listen-address", "0.0.0.0" ]
  • Build with: docker build . --tag doh
  • Run with: docker run -p 127.0.0.1:9000:9000 -it doh

In front of that I have a Nginx Reverse Proxy with HTTPS. If I now test this with doh-stub, I get the following error from doh-httpproxy.

root@doh01 /home/doh # docker run -p 127.0.0.1:9000:9000 -it doh
======== Running on http://0.0.0.0:9000 ========
(Press CTRL+C to quit)
--- Logging error ---
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/site-packages/aiohttp_remotes/x_forwarded.py", line 96, in middleware
    ip = remote_ip(self._trusted, ips)
  File "/usr/local/lib/python3.7/site-packages/aiohttp_remotes/utils.py", line 54, in remote_ip
    check_ip(tr, ip)
  File "/usr/local/lib/python3.7/site-packages/aiohttp_remotes/utils.py", line 67, in check_ip
    raise UntrustedIP(ip, trusted)
aiohttp_remotes.exceptions.UntrustedIP: (IPv4Address('172.17.0.1'), [IPv6Address('::1'), IPv4Address('127.0.0.1')])

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.7/logging/__init__.py", line 983, in emit
    msg = self.format(record)
  File "/usr/local/lib/python3.7/logging/__init__.py", line 829, in format
    return fmt.format(record)
  File "/usr/local/lib/python3.7/logging/__init__.py", line 569, in format
    record.message = record.getMessage()
  File "/usr/local/lib/python3.7/logging/__init__.py", line 331, in getMessage
    msg = msg % self.args
KeyError: 'expected'
Call stack:
  File "/usr/local/bin/doh-httpproxy", line 11, in <module>
    sys.exit(main())
  File "/usr/local/lib/python3.7/site-packages/dohproxy/httpproxy.py", line 152, in main
    app, host=args.listen_address, port=args.port, ssl_context=ssl_context)
  File "/usr/local/lib/python3.7/site-packages/aiohttp/web.py", line 120, in run_app
    loop.run_forever()
  File "/usr/local/lib/python3.7/asyncio/base_events.py", line 523, in run_forever
    self._run_once()
  File "/usr/local/lib/python3.7/asyncio/base_events.py", line 1758, in _run_once
    handle._run()
  File "/usr/local/lib/python3.7/asyncio/events.py", line 88, in _run
    self._context.run(self._callback, *self._args)
  File "/usr/local/lib/python3.7/site-packages/aiohttp/web_protocol.py", line 390, in start
    resp = await self._request_handler(request)
  File "/usr/local/lib/python3.7/site-packages/aiohttp/web_app.py", line 366, in _handle
    resp = await handler(request)
  File "/usr/local/lib/python3.7/site-packages/aiohttp/web_middlewares.py", line 106, in impl
    return await handler(request)
  File "/usr/local/lib/python3.7/site-packages/aiohttp_remotes/x_forwarded.py", line 114, in middleware
    exc.log(request)
  File "/usr/local/lib/python3.7/site-packages/aiohttp_remotes/exceptions.py", line 94, in log
    logger.error(msg, context, extra=extra)
Message: 'Untrusted IP: %(ip)s, trusted: %(expected)s'
Arguments: {'ip': IPv4Address('172.17.0.1'), 'trusted': [IPv6Address('::1'), IPv4Address('127.0.0.1')]}
--- Logging error ---
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/site-packages/aiohttp_remotes/x_forwarded.py", line 96, in middleware
    ip = remote_ip(self._trusted, ips)
  File "/usr/local/lib/python3.7/site-packages/aiohttp_remotes/utils.py", line 54, in remote_ip
    check_ip(tr, ip)
  File "/usr/local/lib/python3.7/site-packages/aiohttp_remotes/utils.py", line 67, in check_ip
    raise UntrustedIP(ip, trusted)
aiohttp_remotes.exceptions.UntrustedIP: (IPv4Address('172.17.0.1'), [IPv6Address('::1'), IPv4Address('127.0.0.1')])

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.7/logging/__init__.py", line 983, in emit
    msg = self.format(record)
  File "/usr/local/lib/python3.7/logging/__init__.py", line 829, in format
    return fmt.format(record)
  File "/usr/local/lib/python3.7/logging/__init__.py", line 569, in format
    record.message = record.getMessage()
  File "/usr/local/lib/python3.7/logging/__init__.py", line 331, in getMessage
    msg = msg % self.args
KeyError: 'expected'
Call stack:
  File "/usr/local/bin/doh-httpproxy", line 11, in <module>
    sys.exit(main())
  File "/usr/local/lib/python3.7/site-packages/dohproxy/httpproxy.py", line 152, in main
    app, host=args.listen_address, port=args.port, ssl_context=ssl_context)
  File "/usr/local/lib/python3.7/site-packages/aiohttp/web.py", line 120, in run_app
    loop.run_forever()
  File "/usr/local/lib/python3.7/asyncio/base_events.py", line 523, in run_forever
    self._run_once()
  File "/usr/local/lib/python3.7/asyncio/base_events.py", line 1758, in _run_once
    handle._run()
  File "/usr/local/lib/python3.7/asyncio/events.py", line 88, in _run
    self._context.run(self._callback, *self._args)
  File "/usr/local/lib/python3.7/site-packages/aiohttp/web_protocol.py", line 390, in start
    resp = await self._request_handler(request)
  File "/usr/local/lib/python3.7/site-packages/aiohttp/web_app.py", line 366, in _handle
    resp = await handler(request)
  File "/usr/local/lib/python3.7/site-packages/aiohttp/web_middlewares.py", line 106, in impl
    return await handler(request)
  File "/usr/local/lib/python3.7/site-packages/aiohttp_remotes/x_forwarded.py", line 114, in middleware
    exc.log(request)
  File "/usr/local/lib/python3.7/site-packages/aiohttp_remotes/exceptions.py", line 95, in log
    logger.error(msg, context, extra=extra)
Message: 'Untrusted IP: %(ip)s, trusted: %(expected)s'
Arguments: {'ip': IPv4Address('172.17.0.1'), 'trusted': [IPv6Address('::1'), IPv4Address('127.0.0.1')]}

I am not good at Python, but the following seems to be important to me:

aiohttp_remotes.exceptions.UntrustedIP: (IPv4Address('172.17.0.1'), [IPv6Address('::1'), IPv4Address('127.0.0.1')])

AIOHTTP only allow requests from localhost.

Is this a bug of doh-httpproxy or a configuration error of mine?

[proxy] Provide ECS info to upstream resolver

Currently, the resolver that doh-proxy uses will only know that the query is coming from the doh-proxy server itself.
There is cases when it can be desired to provide Client subnet info to the upstream resolver.

This should include an option for both v4 and v6 ala unbound max-client-subnet-ipv6 and max-client-subnet-ipv4 with a default of 0 (disabled).

If ECS is provided in the original request, we may want to leave it as is for now, if it is not, we would add it to the payload and take it out on the reply to the client over the HTTPS side.

connection timeout for TCP queries

Hi there!

I noticed that try_tcp in dohproxy/server_protocol.py does not set a timeout when attempting create a connection with the configured resolver. Should the configured resolver be in a state where it is not accepting TCP connections, then it is possible that requests could be stuck waiting indefinitely for a connection to be established. This appears to be easy enough to resolve if we wrap create_connection with asyncio.wait_for, and use the existing timeout value.

If this is a change you are interested in, please let me know and I will send a PR.

[httpproxy] handle HEAD

aiohttp's add_get enable HEAD by default. The way the code is written, we check if it is a GET request, else assume it is POST.
there is no "body" and we return a 400

asyncio.async invalid syntax error

run in python3.7 alpine docker,

/ # doh-stub
Traceback (most recent call last):
File "/usr/local/bin/doh-stub", line 5, in
from dohproxy.stub import main
File "/usr/local/lib/python3.7/site-packages/dohproxy/stub.py", line 11, in
from dohproxy import client_protocol, utils
File "/usr/local/lib/python3.7/site-packages/dohproxy/client_protocol.py", line 10, in
import aioh2
File "/usr/local/lib/python3.7/site-packages/aioh2/init.py", line 2, in
from .helper import *
File "/usr/local/lib/python3.7/site-packages/aioh2/helper.py", line 89
async_task = asyncio.async
^
SyntaxError: invalid syntax

Permit to communicate with the resolver using a unix socket

Hi guys,
it would be great if on the doh-proxy we could set something like this
--upstream-resolver=unix:/tmp/whathever

This way we could communicate with a local dns resolver without rewriting the source IP of the original dns query. Otherwise the log of the dns resolver will report always 127.0.0.1 as source IP or whatever IP was set on the doh-proxy.
In other words it would be great to transform doh-proxy in a transparent proxy.

Any suggestion?

thx!

Enhancement to support ECH standard for SNI

Dear Team,

Thanks for the amazing work you put into doh-proxy! It has been an integral part of our network structure! We were using doh-proxy for ESNI among others extensively. However Firefox and Cloudflare are removing support for ESNI in favor of the new ECH standard. I know it's really early on the specifications of the protocol but I would like to know if this is something you are looking into and upgrading doh-proxy to support ECH?

Thanks in advance for taking the time to consider this.
Best Regards

aioh2 not compatible with Python 3.7

doh-proxy depends on aioh2, which uses the keyword (in Python 3.7) async.

$  ~ doh-client  \
    --domain dns.dnsoverhttps.net \
    --qname sigfail.verteiltesysteme.net \
    --dnssec
Traceback (most recent call last):
  File "/home/pi/.local/bin/doh-client", line 5, in <module>
    from dohproxy.client import main
  File "/home/pi/.local/lib/python3.7/site-packages/dohproxy/client.py", line 12, in <module>
    from dohproxy import client_protocol, utils
  File "/home/pi/.local/lib/python3.7/site-packages/dohproxy/client_protocol.py", line 10, in <module>
    import aioh2
  File "/home/pi/.local/lib/python3.7/site-packages/aioh2/__init__.py", line 2, in <module>
    from .helper import *
  File "/home/pi/.local/lib/python3.7/site-packages/aioh2/helper.py", line 89
    async_task = asyncio.async

[httpproxy] loglevel (--level) is ignored in some parts of the code

ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:645)

I used the following command to create my private and public key :

openssl req -newkey rsa:2048 -nodes -keyout doh.key -x509 -days 365 -out doh.pem

And used this command to run doh-proxy(160.98.31.165 is the IP of my server)
sudo doh-proxy --upstream-resolver=1.1.1.1 --certfile=/home/adriano/projet/doh.pem --keyfile=/home/adriano/projet/doh.key --listen-address=160.98.31.165

When I try to make a request with doh-client :
sudo doh-client --qname heai-fr.ch --domain 160.98.31.165
I get the this error:

ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:645)

When I try to check my SSL connection, with:
openssl s_client -connect 160.98.31.165:443
It works
Does anybody know how to fix the error?

priority.priority.TooManyStreamsError

When holding the same connection and requests are going through, eventually, we will hit this exception from priority library:

priority.priority.TooManyStreamsError: Refusing to insert 1001 streams into priority tree at once

When the connection ends, it does not look like aioh2 releases it which I think is the source of the issue for now.
see decentfox/aioh2#16

I believe I have a work around for that, but it would be better to find a better solution.

[client] query google's doh failed

try the following command and get errors:

doh-client --domain dns.google.com --uri "resolve?" --qname www.youtube.com
2019-03-30 12:58:32,947: DEBUG: Opening connection to dns.google.com
2019-03-30 12:58:34,004: DEBUG: Query parameters: {'dns': 'AAABAAABAAAAAAAAA3d3dwd5b3V0dWJlA2NvbQAAHAAB'}
2019-03-30 12:58:34,005: DEBUG: Stream ID: 1 / Total streams: 0
2019-03-30 12:58:34,156: DEBUG: Response headers: [(':status', '404'), ('content-type', 'text/html; charset=UTF-8'), ('referrer-policy', 'no-referrer'), ('content-length', '1561'), ('date', 'Sat, 30 Mar 2019 04:58:34 GMT'), ('alt-svc', 'quic=":443"; ma=2592000; v="46,44,43,39"')]
2019-03-30 12:58:34,160: ERROR: Exception in callback _SelectorSocketTransport._read_ready()
handle: <Handle _SelectorSocketTransport._read_ready()>
Traceback (most recent call last):
File "D:\Soft\Python3\Lib\asyncio\events.py", line 127, in _run
self._callback(*self._args)
File "D:\Soft\Python3\Lib\asyncio\selector_events.py", line 731, in _read_ready
self._protocol.data_received(data)
File "D:\Soft\Python3\Lib\asyncio\sslproto.py", line 516, in data_received
self._app_protocol.data_received(chunk)
File "d:\tools\dos.venv\lib\site-packages\aioh2\protocol.py", line 259, in data_received
self._event_received(event)
File "d:\tools\dos.venv\lib\site-packages\aioh2\protocol.py", line 268, in _event_received
self._event_handlerstype(event)
KeyError: <class 'h2.events.PingReceived'>
Traceback (most recent call last):
File "D:\Soft\Python3\Lib\runpy.py", line 193, in _run_module_as_main
"main", mod_spec)
File "D:\Soft\Python3\Lib\runpy.py", line 85, in run_code
exec(code, run_globals)
File "D:\tools\dos.venv\Scripts\doh-client.exe_main
.py", line 9, in
File "d:\tools\dos.venv\lib\site-packages\dohproxy\client.py", line 63, in main
main_sync(args)
File "d:\tools\dos.venv\lib\site-packages\dohproxy\client.py", line 58, in main_sync
loop.run_until_complete(client.make_request(None, build_query(args)))
File "D:\Soft\Python3\Lib\asyncio\base_events.py", line 466, in run_until_complete
return future.result()
File "d:\tools\dos.venv\lib\site-packages\dohproxy\client_protocol.py", line 140, in make_request
dnsr = self.on_message_received(stream_id, resp)
File "d:\tools\dos.venv\lib\site-packages\dohproxy\client_protocol.py", line 66, in on_message_received
return dns.message.from_wire(msg)
File "d:\tools\dos.venv\lib\site-packages\dns\message.py", line 823, in from_wire
reader.read()
File "d:\tools\dos.venv\lib\site-packages\dns\message.py", line 746, in read
self._get_question(qcount)
File "d:\tools\dos.venv\lib\site-packages\dns\message.py", line 621, in _get_question
(qname, used) = dns.name.from_wire(self.wire, self.current)
File "d:\tools\dos.venv\lib\site-packages\dns\name.py", line 988, in from_wire
raise BadLabelType
dns.name.BadLabelType: The label type in DNS name wire format is unknown.

ValueError: IPv6 addresses are 16 bytes long

We've got lots of these in the logs:

 ERROR: Exception in callback _SelectorDatagramTransport._read_ready()
 handle: <Handle _SelectorDatagramTransport._read_ready()>
 Traceback (most recent call last):
   File "/usr/lib/python3.7/asyncio/events.py", line 88, in _run
     self._context.run(self._callback, *self._args)
   File "/usr/lib/python3.7/asyncio/selector_events.py", line 962, in _read_ready
     self._protocol.datagram_received(data, addr)
   File "/usr/local/lib/python3.7/dist-packages/dohproxy/server_protocol.py", line 143, in datagram_received
     dnsr = dns.message.from_wire(data)
   File "/usr/local/lib/python3.7/dist-packages/dns/message.py", line 823, in from_wire
     reader.read()
   File "/usr/local/lib/python3.7/dist-packages/dns/message.py", line 749, in read
     self._get_section(self.message.answer, ancount)
   File "/usr/local/lib/python3.7/dist-packages/dns/message.py", line 723, in _get_section
     self.message.origin)
   File "/usr/local/lib/python3.7/dist-packages/dns/rdata.py", line 424, in from_wire
     return cls.from_wire(rdclass, rdtype, wire, current, rdlen, origin)
   File "/usr/local/lib/python3.7/dist-packages/dns/rdtypes/IN/AAAA.py", line 54, in from_wire
     wire[current: current + rdlen])
   File "/usr/local/lib/python3.7/dist-packages/dns/inet.py", line 78, in inet_ntop
     return dns.ipv6.inet_ntoa(address)
   File "/usr/local/lib/python3.7/dist-packages/dns/ipv6.py", line 39, in inet_ntoa
     raise ValueError("IPv6 addresses are 16 bytes long")

Request stuck

I used this command to run my doh-server:
sudo doh-proxy --upstream-resolver=1.1.1.1 --certfile=/../fullchain.pem --keyfile=/etc/.../privkey.pem --listen-address 160.98.31.165
And I try with my client this one:
sudo doh-client --qname 160.98.31.165 --domain heia-fr.ch
But my request is stuck like that :

2018-12-18 18:01:15,388: DEBUG: Opening connection to heia-fr.ch
2018-12-18 18:01:15,443: DEBUG: Query parameters: {'dns': 'AAABAAABAAAAAAAAAzE2MAI5OAIzMQMxNjUAABwAAQ'}
2018-12-18 18:01:15,445: DEBUG: Stream ID: 1 / Total streams: 0

And I can wait for ever.
Can anyone help me?
Thanks

Cannot bind if no --listen-address parameter

I am running the stub in a docker container where the internal IP address sometimes changes. I would like the stub to listen on any IP addresses.
If I omit the --listen-address parameter, the stub won't start with the error:

OSError: [Errno 99] Address not available

If I bind to ::1, obviously the docker-proxy won't be able to talk to it.
If I add --listen-address 172.17.0.12, it works flawlessy.

Can I use any wildcard in the --listen-address parameter? The best would be if the default value would be to bind to all interfaces.

[proxy] improve logging

Logging is terrible at the moment. We should make this better and at least reasonably readable by default.

doh-stub does not work on Windows 10

I am trying to use doh-stub to relay DNS traffic to Cloudflare (or any other DoH provider) using the following command:
doh-stub --listen-port 5553 --listen-address 127.0.0.1 --domain cloudflare-dns.com

and trying to test it using nslookup:
nslookup -port=5553 google.com 127.0.0.1

I am getting:
*** UnKnown can't find google.com: No response from server

No firewall rule, No EDR, No Antivirus, ... . However, it seems the doh-stub does not listening to the port; thus not responding.

OS Name: Microsoft Windows 10 Education
OS Version: 10.0.17134 N/A Build 17134

Python 3.6.4

I tried on another system (same OS and python), same result.

I used the following command to install:
pip install doh-proxy

Cannot run doh-stub on ArchLinux, NPN extension not supported

[ling@archlinux Accesser]$ doh-stub \
>     --listen-port 5553 \
>     --listen-address ::1 \
>     --domain foo.bar \
>     --remote-address ::1
2019-04-24 21:03:57,586:     INFO: Starting UDP server
2019-04-24 21:03:57,586:    DEBUG: Opening connection to foo.bar
Traceback (most recent call last):
  File "/usr/bin/doh-stub", line 10, in <module>
    sys.exit(main())
  File "/usr/lib/python3.7/site-packages/dohproxy/stub.py", line 40, in main
    loop.run_until_complete(proto.setup_client())
  File "/usr/lib/python3.7/asyncio/base_events.py", line 584, in run_until_complete
    return future.result()
  File "/usr/lib/python3.7/site-packages/dohproxy/client_protocol.py", line 34, in setup_client
    cafile=self.args.cafile
  File "/usr/lib/python3.7/site-packages/dohproxy/utils.py", line 131, in create_custom_ssl_context
    sslctx.set_npn_protocols(constants.DOH_H2_NPN_PROTOCOLS)
  File "/usr/lib/python3.7/ssl.py", line 434, in set_npn_protocols
    self._set_npn_protocols(protos)
NotImplementedError: The NPN extension requires OpenSSL 1.0.1 or later.

'NoneType' object is not subscriptable

in on_answer occasionally we cannot get the client ip address - line:

        clientip = self.transport.get_extra_info('peername')[0]

Not too sure why this would be but as I think it's only for logging it probably makes sense to only try doing this if we are logging anyway?

future: <Task finished name='Task-670' coro=<H2Protocol.resolve() done, defined at /usr/lib/python3.8/site-packages/doh_proxy-0.0.9-py3.8.egg/dohproxy/proxy.py:207> exception=TypeError("'NoneType' object is not subscriptable")>
Traceback (most recent call last):
  File "/usr/lib/python3.8/site-packages/doh_proxy-0.0.9-py3.8.egg/dohproxy/proxy.py", line 216, in resolve
    self.on_answer(stream_id, dnsr=dnsr)   
  File "/usr/lib/python3.8/site-packages/doh_proxy-0.0.9-py3.8.egg/dohproxy/proxy.py", line 188, in on_answer
    clientip = self.transport.get_extra_info('peername')[0]
TypeError: 'NoneType' object is not subscriptable 

[proxy] support multiple up streams

Currently the doh proxies will use one and only 1 upstream resolver. It could be useful to have a pool of upstream IPs to use and also potentially handle retries between those.

Sent with GitHawk

[httpproxy] Exception in callback _SelectorDatagramTransport._read_ready()

setup as described in https://facebookexperimental.github.io/doh-proxy/tutorials/nginx-dohhttpproxy-unbound-centos7.html but on Debian

running from git 149c97d
Python 3.5.3

The setup works but the logs contain many occurrences of:

ERROR: Exception in callback _SelectorDatagramTransport._read_ready()
handle: <Handle _SelectorDatagramTransport._read_ready()>
Traceback (most recent call last):
  File "/usr/lib/python3.5/asyncio/events.py", line 126, in _run
    self._callback(*self._args)
  File "/usr/lib/python3.5/asyncio/selector_events.py", line 1077, in _read_ready
    self._protocol.datagram_received(data, addr)
  File "/usr/local/lib/python3.5/dist-packages/dohproxy/server_protocol.py", line 139, in datagram_received
    self.receive_helper(dnsr)
  File "/usr/local/lib/python3.5/dist-packages/dohproxy/server_protocol.py", line 128, in receive_helper
    self.fut.set_result(dnsr)
  File "/usr/lib/python3.5/asyncio/futures.py", line 348, in set_result
    raise InvalidStateError('{}: {!r}'.format(self._state, self))
asyncio.futures.InvalidStateError: CANCELLED: <Future cancelled>

POST support in doh-httpproxy?

Hi!

Would it be possible to support POST requests in httpproxy? That would be great!

I naively tried to add a route, but I'm not familiar with aiohttp and reading the body seems to be a bit trickier.

Support draft-13

@bagder provided #43 which makes doh-proxy draft-13 compliant.

In 3d4f1e1 I have fixed the test to accommodate for the change.

A couple of more things need to happen:

  • update changelog
  • update docs
  • more e2e testing
  • finally release a new package

Python 3.7 support?

~ ❯ python3 --version
Python 3.7.3
~ ❯  doh-client --domain doh.seby.io --port 8443 --qname example.com --qtype A
Traceback (most recent call last):
  File "/usr/local/bin/doh-client", line 6, in <module>
    from dohproxy.client import main
  File "/usr/local/lib/python3.7/site-packages/dohproxy/client.py", line 12, in <module>
    from dohproxy import client_protocol, utils
  File "/usr/local/lib/python3.7/site-packages/dohproxy/client_protocol.py", line 10, in <module>
    import aioh2
  File "/usr/local/lib/python3.7/site-packages/aioh2/__init__.py", line 2, in <module>
    from .helper import *
  File "/usr/local/lib/python3.7/site-packages/aioh2/helper.py", line 89
    async_task = asyncio.async
                             ^
SyntaxError: invalid syntax

A quick search brought me here: pymodbus-dev/pymodbus#319

doh-stub mode Runtime error

Hello, I observed the log. Occasionally, after a few days of running, there will be such a warning, and the connection will be cut off.
<<ERROR: Fatal read error on socket transport>>
Query the next google, most people say it is a problem with python itself.
What do you think?
I hope you can check this question, thank you!

Unhandled exception ConnectionResetError: Cannot write to closing transport

doh-httpproxy running on 9f943a4
we got this in the logs:

 ERROR: Unhandled exception
 Traceback (most recent call last):
   File "/usr/local/lib/python3.7/dist-packages/aiohttp/web_protocol.py", line 447, in start
     await resp.prepare(request)
   File "/usr/local/lib/python3.7/dist-packages/aiohttp/web_response.py", line 353, in prepare
     return await self._start(request)
   File "/usr/local/lib/python3.7/dist-packages/aiohttp/web_response.py", line 667, in _start
     return await super()._start(request)
   File "/usr/local/lib/python3.7/dist-packages/aiohttp/web_response.py", line 410, in _start
     await writer.write_headers(status_line, headers)
   File "/usr/local/lib/python3.7/dist-packages/aiohttp/http_writer.py", line 112, in write_headers
     self._write(buf)
   File "/usr/local/lib/python3.7/dist-packages/aiohttp/http_writer.py", line 67, in _write
     raise ConnectionResetError('Cannot write to closing transport')
 ConnectionResetError: Cannot write to closing transport

firefox + doh-proxy

looks like doh-proxy ceases to serve requests from mozilla after 5-10 minutes of usage
When firefox configured for TRR-only mode nothing can be resolved anymore until doh-proxy is restarted.
When firefox configure for TRR=2 mode, it silently stop using TRR resolver

doh-proxy listening on IPv4

After using the command
doh-proxy -h

I see nowhere that I can configure IPv4 adresses.
How can I change my server to listen only in IPv4?
Thanks

[doh-httpproxy] --trusted mandatory ?

Hi,
After upgrading to latest version in pip to get the new draft-13 compliance (that's needed for Firefox =<62 it seems), doh-httpproxy starts up as usual, but no longer want to serve DOH queries with error message:

2018-10-03 13:32:08,515:    ERROR: Too many X-Forwarded-For values: [IPv4Address('127.0.0.1')], expected 2
2018-10-03 13:37:36,262:    ERROR: Too many X-Forwarded-For values: [IPv4Address('127.0.0.1')], expected 2

Is --trusted mandatory now? (If I supply that, it starts working as normal) #49
Anyway, I think 127.0.0.1 and ::1 are sane defaults even without having to supply --trusted

check how we handle "refused" reply

It seems that when the proxy receives a "refused" reply, it is not forwarded to the stub.

To reproduce:

dig @stub example.com +nord

or +norecurse depending on dig version.

Too many open files

In the light of the errors bellow we were wondering what amount of open files is doh-httpproxy expected to require.

The logs bellow already generated 50GB of logs at one point.

 Traceback (most recent call last):
   File "/usr/local/lib/python3.7/dist-packages/aiohttp/web_protocol.py", line 418, in start
     resp = await task
   File "/usr/local/lib/python3.7/dist-packages/aiohttp/web_app.py", line 458, in _handle
     resp = await handler(request)
   File "/usr/local/lib/python3.7/dist-packages/aiohttp/web_middlewares.py", line 119, in impl
     return await handler(request)
   File "/usr/local/lib/python3.7/dist-packages/aiohttp_remotes/x_forwarded.py", line 111, in middleware
     return await handler(request)
   File "/usr/local/lib/python3.7/dist-packages/dohproxy/httpproxy.py", line 68, in doh1handler
     return await request.app.resolve(request, dnsq)
   File "/usr/local/lib/python3.7/dist-packages/dohproxy/httpproxy.py", line 81, in resolve
     dnsr = await dnsclient.query(dnsq, clientip)
   File "/usr/local/lib/python3.7/dist-packages/dohproxy/server_protocol.py", line 45, in query
     dnsr = await self.query_udp(dnsq, clientip, timeout=timeout)
   File "/usr/local/lib/python3.7/dist-packages/dohproxy/server_protocol.py", line 56, in query_udp
     remote_addr=(self.upstream_resolver, self.upstream_port))
   File "/usr/lib/python3.7/asyncio/base_events.py", line 1245, in create_datagram_endpoint
     raise exceptions[0]
   File "/usr/lib/python3.7/asyncio/base_events.py", line 1218, in create_datagram_endpoint
     family=family, type=socket.SOCK_DGRAM, proto=proto)
   File "/usr/lib/python3.7/socket.py", line 151, in __init__
     _socket.socket.__init__(self, family, type, proto, fileno)
 OSError: [Errno 24] Too many open files
ERROR: socket.accept() out of system resource
 socket: <socket.socket fd=6, family=AddressFamily.AF_INET6, type=SocketKind.SOCK_STREAM, proto=6, laddr=(::1, 8084, 0, 0)>
 Traceback (most recent call last):
   File "/usr/lib/python3.7/asyncio/selector_events.py", line 156, in _accept_connection
   File "/usr/lib/python3.7/socket.py", line 212, in accept
     fd, addr = self._accept()
 OSError: [Errno 24] Too many open files

[httpproxy] support for TLS

Currently dohproxy/httpproxy.py does not support HTTPS. It was a quick way to get it running behind nginx for instance. We should still provide a way to use HTTPS behind the reverse proxy and the doh-proxies.
To do that, proxy_parser_base can be changed to make --cert-file and keyfile optional in case of httpproxy.

[proxy] logging hooks

python logging is good enough to perform some troubleshooting, but it can be desirable to have more flexible logging handler like logstash... for instance.

The goal here is to provide a logging api and some sample logging classes.

doh-stub & doh-client crash on Debian 10

While doh-httpproxy runs fine on Debian 10, both doh-stub and doh-client crash with this error:

root@alkurah:~# doh-stub Traceback (most recent call last): File "/usr/local/bin/doh-stub", line 6, in <module> from dohproxy.stub import main File "/usr/local/lib/python3.7/dist-packages/dohproxy/stub.py", line 11, in <module> from dohproxy import client_protocol, utils File "/usr/local/lib/python3.7/dist-packages/dohproxy/client_protocol.py", line 10, in <module> import aioh2 File "/usr/local/lib/python3.7/dist-packages/aioh2/__init__.py", line 2, in <module> from .helper import * File "/usr/local/lib/python3.7/dist-packages/aioh2/helper.py", line 89 async_task = asyncio.async ^ SyntaxError: invalid syntax
Installation was successful:
root@alkurah:~# pip3 install doh-proxy Collecting doh-proxy Using cached https://files.pythonhosted.org/packages/46/eb/e6aab2f014069f5e136fe1e3ce82928a5ab2ab17b7ab54a129e7a8f8ee04/doh_proxy-0.0.9-py3-none-any.whl Requirement already satisfied: dnspython in /usr/local/lib/python3.7/dist-packages (from doh-proxy) (1.16.0) Requirement already satisfied: aiohttp>=2.3.0 in /usr/local/lib/python3.7/dist-packages (from doh-proxy) (3.6.2) Requirement already satisfied: aioh2>=0.2.1 in /usr/local/lib/python3.7/dist-packages (from doh-proxy) (0.2.2) Requirement already satisfied: aiohttp-remotes>=0.1.2 in /usr/local/lib/python3.7/dist-packages (from doh-proxy) (0.1.2) Requirement already satisfied: yarl<2.0,>=1.0 in /usr/local/lib/python3.7/dist-packages (from aiohttp>=2.3.0->doh-proxy) (1.3.0) Requirement already satisfied: async-timeout<4.0,>=3.0 in /usr/local/lib/python3.7/dist-packages (from aiohttp>=2.3.0->doh-proxy) (3.0.1) Requirement already satisfied: chardet<4.0,>=2.0 in /usr/lib/python3/dist-packages (from aiohttp>=2.3.0->doh-proxy) (3.0.4) Requirement already satisfied: attrs>=17.3.0 in /usr/local/lib/python3.7/dist-packages (from aiohttp>=2.3.0->doh-proxy) (19.3.0) Requirement already satisfied: multidict<5.0,>=4.5 in /usr/local/lib/python3.7/dist-packages (from aiohttp>=2.3.0->doh-proxy) (4.5.2) Requirement already satisfied: h2<4,>=3 in /usr/local/lib/python3.7/dist-packages (from aioh2>=0.2.1->doh-proxy) (3.1.1) Requirement already satisfied: priority==1.3.0 in /usr/local/lib/python3.7/dist-packages (from aioh2>=0.2.1->doh-proxy) (1.3.0) Requirement already satisfied: idna>=2.0 in /usr/lib/python3/dist-packages (from yarl<2.0,>=1.0->aiohttp>=2.3.0->doh-proxy) (2.6) Requirement already satisfied: hpack<4,>=2.3 in /usr/local/lib/python3.7/dist-packages (from h2<4,>=3->aioh2>=0.2.1->doh-proxy) (3.0.0) Requirement already satisfied: hyperframe<6,>=5.2.0 in /usr/local/lib/python3.7/dist-packages (from h2<4,>=3->aioh2>=0.2.1->doh-proxy) (5.2.0) Installing collected packages: doh-proxy Successfully installed doh-proxy-0.0.9

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.