From 065c0b810d218117f092e67dae087017a9006800 Mon Sep 17 00:00:00 2001 From: Miguel Cuartin Date: Tue, 16 Feb 2021 16:51:48 -0500 Subject: [PATCH] add Dockerfile --- .gitignore | 4 +- Dockerfile | 8 ++++ dnproxy.py | 47 -------------------- dnproxy.yml | 0 requirements.txt | 7 +++ src/dnproxy.py | 111 +++++++++++++++++++++++++++++++++++++++++++++++ src/dnproxy.yml | 6 +++ 7 files changed, 135 insertions(+), 48 deletions(-) create mode 100644 Dockerfile delete mode 100644 dnproxy.py delete mode 100644 dnproxy.yml create mode 100644 src/dnproxy.py create mode 100644 src/dnproxy.yml diff --git a/.gitignore b/.gitignore index f5e96db..90c9145 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -venv \ No newline at end of file +venv +*.log +socket \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f4109c1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,8 @@ +FROM python:3.8 +WORKDIR /code +COPY requirements.txt . +RUN pip install -r requirements.txt +COPY src/ . +EXPOSE 53/tcp +EXPOSE 53/udp +CMD [ "python", "./dnproxy.py" ] \ No newline at end of file diff --git a/dnproxy.py b/dnproxy.py deleted file mode 100644 index 2316413..0000000 --- a/dnproxy.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env python3 - -import logging as l -import socket - -from pyfiglet import Figlet - -import dns.flags -import dns.message -import dns.rdataclass -import dns.rdatatype -import dns.name -import dns.query - -from typing import cast - -# General config -l.basicConfig(level=l.INFO) -f = Figlet(font='slant') - -l.info(f.renderText('dnproxy')) - -# General Parameters -HOST = '127.0.0.1' -PORT = 53 -UPSTREAM_IP = '1.1.1.1' -TLS_HOSTNAME = 'cloudflare-dns.com' - - -with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s: - s.bind((HOST, PORT)) - l.info(f"Listening on {HOST}:{PORT}") - - while True: - (wire, host) = s.recvfrom(1024) - q = dns.message.from_wire(wire) - l.info(f'Question {q.id}:{q.question}') - - try: - r = dns.query.tls(q, UPSTREAM_IP, server_hostname=TLS_HOSTNAME) - l.info(f'Response {r.id}:{r.answer}') - - except KeyError: - l.error("error") - - wire = r.to_wire() - s.sendto(wire, host) \ No newline at end of file diff --git a/dnproxy.yml b/dnproxy.yml deleted file mode 100644 index e69de29..0000000 diff --git a/requirements.txt b/requirements.txt index 76e8b1c..e338d1b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,8 +4,15 @@ chardet==4.0.0 cryptography==3.4.5 dnspython==2.1.0 idna==2.10 +lxml==4.6.2 pycparser==2.20 pyfiglet==0.8.post1 +pytils==0.3 +PyYAML==5.4.1 requests==2.25.1 requests-toolbelt==0.9.1 +selectors==0.0.14 +six==1.15.0 urllib3==1.26.3 +user-agent==0.1.9 +weblib==0.1.30 diff --git a/src/dnproxy.py b/src/dnproxy.py new file mode 100644 index 0000000..aa8d2f1 --- /dev/null +++ b/src/dnproxy.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 + +import sys +import socket +import selectors +import types +import yaml +import logging +from dns import message, query +from pyfiglet import Figlet +import binascii +import time + +# General config +f = Figlet(font='slant') + +# Load Config +with open("dnproxy.yml", "r") as configfile: + cfg = yaml.load(configfile, Loader=yaml.FullLoader) + +# create logger +logger = logging.getLogger('dnproxy') +logger.setLevel(logging.DEBUG) +ch = logging.StreamHandler() +ch.setLevel(logging.INFO) +formatter = logging.Formatter("%(asctime)s - %(name)s - [%(levelname)s] - %(message)s ") # I am printing thread id here +ch.setFormatter(formatter) +logger.addHandler(ch) + +# Init +logger.info(f.renderText('dnproxy')) + +HOST = cfg['dnproxy']['host'] +PORT = cfg['dnproxy']['port'] +UPSTREAM_IP = cfg['upstream']['ip'] +TLS_HOSTNAME = cfg['upstream']['hostname'] + +sel = selectors.DefaultSelector() + +def receive_message(sock): + (wire, host) = sock.recvfrom(1024) + q = message.from_wire(wire) + logger.info(f'UDP Socket - Query ID: {q.id}, Question:{q.question}') + r = query.tls(q, UPSTREAM_IP, server_hostname=TLS_HOSTNAME) + logger.info(f'UDP Socket - Query ID: {q.id}, Answer: {r.answer}') + wire = r.to_wire() + sock.sendto(wire, host) + +def accept_wrapper(sock): + conn, addr = sock.accept() # Should be ready to read + logger.debug(f"TCP Socket - Accepted connection from {addr[0]}:{addr[1]}") + conn.setblocking(False) + data = types.SimpleNamespace(addr=addr, inb=b"", outb=b"") + events = selectors.EVENT_READ | selectors.EVENT_WRITE + sel.register(conn, events, data=data) + +def service_connection(key, mask): + sock = key.fileobj + data = key.data + if mask & selectors.EVENT_READ: + recv_data = sock.recv(1024) + if recv_data: + data.outb += recv_data + else: + logger.debug(f"TCP Socket - Closing connection to {data.addr[0]}:{data.addr[1]}") + sel.unregister(sock) + sock.close() + if mask & selectors.EVENT_WRITE: + if data.outb: + q = message.from_wire(data.outb[2:]) + logger.info(f'TCP Socket - Question from {data.addr[0]}:{data.addr[1]}, Query ID: {q.id}, Question:{q.question}') + r = query.tls(q, UPSTREAM_IP, server_hostname=TLS_HOSTNAME) + logger.info(f'TCP Socket - Response to {data.addr[0]}:{data.addr[1]}, Query ID: {q.id}, Answer: {r.answer}') + wire = r.to_wire() + length = binascii.unhexlify("%04x" % len(wire)) + if r: + sent = sock.send(length+wire) + data.outb = data.outb[sent:] + + +if __name__=="__main__": + + # Sockets Creations + + tsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + tsock.bind((HOST, PORT)) + tsock.listen() + logger.info(f"TCP Socket - Listening on {HOST}:{PORT}") + tsock.setblocking(False) + sel.register(tsock, selectors.EVENT_READ, data=None) + + usock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + usock.bind((HOST, PORT)) + logger.info(f"UDP Socket - Listening on {HOST}:{PORT}") + sel.register(usock, selectors.EVENT_READ, data=None) + + try: + while True: + events = sel.select(timeout=None) + for key, mask in events: + if key.data is None: + if key.fileobj.type == socket.SOCK_STREAM: + accept_wrapper(key.fileobj) + elif key.fileobj.type == socket.SOCK_DGRAM: + receive_message(key.fileobj) + else: + service_connection(key, mask) + except KeyboardInterrupt: + logger.info("Caught keyboard interrupt, exiting") + finally: + sel.close() diff --git a/src/dnproxy.yml b/src/dnproxy.yml new file mode 100644 index 0000000..99c9c59 --- /dev/null +++ b/src/dnproxy.yml @@ -0,0 +1,6 @@ +dnproxy: + host: "0.0.0.0" + port: 53 +upstream: + ip: "1.1.1.1" + hostname: "cloudflare-dns.com" \ No newline at end of file