From 0cf217b0889d61e3e0c0255aa47adc9c1b3fd075 Mon Sep 17 00:00:00 2001 From: meemo Date: Sun, 6 Feb 2022 18:56:35 -0500 Subject: [PATCH 1/6] Cleanup, implement zero padding IP octets, switch to fstrings --- sudomemoDNS.py | 105 ++++++++++++++++++++++++++++++------------------- 1 file changed, 64 insertions(+), 41 deletions(-) diff --git a/sudomemoDNS.py b/sudomemoDNS.py index 96d7bec..4948569 100644 --- a/sudomemoDNS.py +++ b/sudomemoDNS.py @@ -12,20 +12,30 @@ import requests import json import sys + def get_platform(): platforms = { - 'linux1' : 'Linux', - 'linux2' : 'Linux', - 'darwin' : 'macOS', - 'win32' : 'Windows' + 'linux1': 'Linux', + 'linux2': 'Linux', + 'darwin': 'macOS', + 'win32': 'Windows' } if sys.platform not in platforms: return sys.platform return platforms[sys.platform] + SUDOMEMODNS_VERSION = "1.2" + +# Adds preceding zeros to IP addresses +# e.g `1.1.1.1` becomes `001.001.001.001` +def format_ip(address): + octets = str(address).split(".") + return f"{int(octets[0]):02d}.{int(octets[1]):02d}.{int(octets[2]):02d}.{int(octets[3]):02d}" + + def get_ip(): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: @@ -38,25 +48,27 @@ def get_ip(): s.close() return IP + EPOCH = datetime(1970, 1, 1) SERIAL = int((datetime.utcnow() - EPOCH).total_seconds()) MY_IP = get_ip() print("+===============================+") print("| Sudomemo DNS Server |") -print("| Version " + SUDOMEMODNS_VERSION + " |") +print(f"| Version {SUDOMEMODNS_VERSION} |") print("+===============================+\n") -print("== Welcome to sudomemoDNS! ==") -print("This server will allow you to connect to Sudomemo when your Internet Service Provider does not work with custom DNS.\n") +print("== Welcome to sudomemoDNS! ==") +print( + "This server will allow you to connect to Sudomemo when your Internet Service Provider does not work with custom DNS.\n") print("== How To Use ==") print("First, make sure that your console is connected to the same network as this computer.\n") print("Then, put these settings in for DNS on your console:") print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~") -print("Primary DNS: ",MY_IP) -print("Secondary DNS: 8.8.8.8") +print(f"Primary DNS: {MY_IP}") +print("Secondary DNS: 008.008.008.008") print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n") print("== Getting Help ==") @@ -74,24 +86,32 @@ TYPE_LOOKUP = { TXT: QTYPE.TXT, } + # Can't seem to turn off DNSLogger with a None type so let's just null it out with a dummy function class SudomemoDNSLogger(object): def log_recv(self, handler, data): pass + def log_send(self, handler, data): pass + def log_request(self, handler, request): print("[INFO] Received DNS request from console at " + handler.client_address[0]) + def log_reply(self, handler, reply): print("[INFO] Sent response to console at " + handler.client_address[0]) + def log_error(self, handler, e): print("[ERROR] Invalid DNS request from " + handler.client_address[0]) + def log_truncated(self, handler, reply): pass + def log_data(self, dnsobj): pass + class Record: def __init__(self, rdata_type, *args, rtype=None, rname=None, ttl=None, **kwargs): if isinstance(rdata_type, RD): @@ -103,12 +123,12 @@ class Record: if rdata_type == SOA and len(args) == 2: # add sensible times to SOA args += (( - SERIAL, # serial number - 60 * 60 * 1, # refresh - 60 * 60 * 3, # retry - 60 * 60 * 24, # expire - 60 * 60 * 1, # minimum - ),) + SERIAL, # serial number + 60 * 60 * 1, # refresh + 60 * 60 * 3, # retry + 60 * 60 * 24, # expire + 60 * 60 * 1, # minimum + ),) rdata = rdata_type(*args) if rtype: @@ -144,26 +164,28 @@ class Record: ZONES = {} try: - get_zones = requests.get("https://www.sudomemo.net/api/dns_zones.json", headers={'User-Agent': 'SudomemoDNS/' + SUDOMEMODNS_VERSION + ' (' + get_platform() + ')'}) + get_zones = requests.get("https://www.sudomemo.net/api/dns_zones.json", + headers={'User-Agent': 'SudomemoDNS/' + SUDOMEMODNS_VERSION + ' (' + get_platform() + ')'}) except requests.exceptions.Timeout: - print("[ERROR] Unable to load DNS data: Connection to Sudomemo timed out. Are you connected to the Internet?") + print("[ERROR] Unable to load DNS data: Connection to Sudomemo timed out. Are you connected to the Internet?") except requests.exceptions.RequestException as e: - print("[ERROR] Unable load DNS data.") - print("[ERROR] Exception: ",e) - sys.exit(1) + print("[ERROR] Unable load DNS data.") + print("[ERROR] Exception: ", e) + sys.exit(1) try: - zones = json.loads(get_zones.text) + zones = json.loads(get_zones.text) except ValueError as e: - print("[ERROR] Unable load DNS data: Invalid response from server. Check that you can visit sudomemo.net") + print("[ERROR] Unable load DNS data: Invalid response from server. Check that you can visit sudomemo.net") for zone in zones: - if zone["type"] == "a": - ZONES[zone["name"]] = [ Record(A, zone["value"]) ] - elif zone["type"] == "p": - ZONES[zone["name"]] = [ Record(A, socket.gethostbyname(zone["value"])) ] + if zone["type"] == "a": + ZONES[zone["name"]] = [Record(A, zone["value"])] + elif zone["type"] == "p": + ZONES[zone["name"]] = [Record(A, socket.gethostbyname(zone["value"]))] print("[INFO] DNS information loaded successfully.") + class Resolver: def __init__(self): self.zones = {DNSLabel(k): v for k, v in ZONES.items()} @@ -171,6 +193,7 @@ class Resolver: def resolve(self, request, handler): reply = request.reply() zone = self.zones.get(request.q.qname) + if zone is not None: for zone_records in zone: rr = zone_records.try_rr(request.q) @@ -196,26 +219,26 @@ dnsLogger = SudomemoDNSLogger() print("[INFO] Detected operating system:", get_platform()) if get_platform() == 'linux': - print("[INFO] Please note that you will have to run this as root or with permissions to bind to UDP port 53.") - print("[INFO] If you aren't seeing any requests, check that this is the case first with lsof -i:53 (requires lsof)") - print("[INFO] To run as root, prefix the command with 'sudo'") + print("[INFO] Please note that you will have to run this as root or with permissions to bind to UDP port 53.") + print("[INFO] If you aren't seeing any requests, check that this is the case first with lsof -i:53 (requires lsof)") + print("[INFO] To run as root, prefix the command with 'sudo'") elif get_platform() == 'macOS': - print("[INFO] Please note that you will have to run this as root or with permissions to bind to UDP port 53.") - print("[INFO] If you aren't seeing any requests, check that this is the case first with lsof -i:53 (requires lsof)") - print("[INFO] To run as root, prefix the command with 'sudo'") + print("[INFO] Please note that you will have to run this as root or with permissions to bind to UDP port 53.") + print("[INFO] If you aren't seeing any requests, check that this is the case first with lsof -i:53 (requires lsof)") + print("[INFO] To run as root, prefix the command with 'sudo'") elif get_platform() == 'Windows': - print("[INFO] Please note that you may have to allow this application through the firewall. If so, a popup will appear in a moment.") - print("[INFO] If you are not seeing any requests, make sure you have allowed this application through the firewall. If you have already done so, disregard this message.") + print("[INFO] Please note that you may have to allow this application through the firewall. If so, a popup will appear in a moment.") + print("[INFO] If you are not seeing any requests, make sure you have allowed this application through the firewall. If you have already done so, disregard this message.") try: - servers = [ - DNSServer(resolver=resolver, port=53, address=MY_IP, tcp=True, logger=dnsLogger), - DNSServer(resolver=resolver, port=53, address=MY_IP, tcp=False, logger=dnsLogger), - ] -except PermissionError: - print("[ERROR] Permission error: Check that you are running this as an administrator or root") - sys.exit(1) + servers = [ + DNSServer(resolver=resolver, port=53, address=MY_IP, tcp=True, logger=dnsLogger), + DNSServer(resolver=resolver, port=53, address=MY_IP, tcp=False, logger=dnsLogger) + ] +except PermissionError: + print("[ERROR] Permission error: Check that you are running this as an administrator or root") + sys.exit(1) print("[INFO] sudomemoDNS is ready. Now waiting for DNS requests from your console...") From 006a01b242f72f3daf89b046d77afd90c24da53d Mon Sep 17 00:00:00 2001 From: meemo Date: Sun, 6 Feb 2022 18:57:53 -0500 Subject: [PATCH 2/6] Add requirements.txt, .gitattributes, .gitignore --- .gitattributes | 2 ++ .gitignore | 1 + requirements.txt | 2 ++ 3 files changed, 5 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 requirements.txt diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c18dd8d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__/ diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..1b9e8ea --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +requests +dnslib From 9a6f9f69859fb92febe20acf3246ee53f367b37d Mon Sep 17 00:00:00 2001 From: meemo Date: Sun, 6 Feb 2022 19:04:20 -0500 Subject: [PATCH 3/6] 3 character padding, not 2 --- sudomemoDNS.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sudomemoDNS.py b/sudomemoDNS.py index 4948569..53ba036 100644 --- a/sudomemoDNS.py +++ b/sudomemoDNS.py @@ -33,7 +33,7 @@ SUDOMEMODNS_VERSION = "1.2" # e.g `1.1.1.1` becomes `001.001.001.001` def format_ip(address): octets = str(address).split(".") - return f"{int(octets[0]):02d}.{int(octets[1]):02d}.{int(octets[2]):02d}.{int(octets[3]):02d}" + return f"{int(octets[0]):03d}.{int(octets[1]):03d}.{int(octets[2]):03d}.{int(octets[3]):03d}" def get_ip(): @@ -67,7 +67,7 @@ print("First, make sure that your console is connected to the same network as th print("Then, put these settings in for DNS on your console:") print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~") -print(f"Primary DNS: {MY_IP}") +print(f"Primary DNS: {format_ip(MY_IP)}") print("Secondary DNS: 008.008.008.008") print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n") From fa9c7df4fec02bc45995895cbf94c8eb7741be73 Mon Sep 17 00:00:00 2001 From: meemo Date: Sun, 6 Feb 2022 19:16:50 -0500 Subject: [PATCH 4/6] Bump minor version, clean up readme, rename spec file to match new version number --- README.md | 34 +++++++++---------- sudomemoDNS.py | 4 +-- ...moDNS_v1.2.spec => sudomemoDNS_v1.2.1.spec | 2 +- 3 files changed, 19 insertions(+), 21 deletions(-) rename sudomemoDNS_v1.2.spec => sudomemoDNS_v1.2.1.spec (95%) diff --git a/README.md b/README.md index d69a240..3ec841d 100644 --- a/README.md +++ b/README.md @@ -15,19 +15,21 @@ Run the .exe provided in this release. You may have to click past a warning from ## Running on anything else: -Required packages (installable with pip): `requests`, `dnslib` +Required packages are installable with the following command: -Required Python version: 3+, tested with 3.6 + sudo python3 -m pip install -r requirements.txt - sudo sudomemoDNS.py +Required Python version: 3.6+, tested with 3.9.9 + + sudo python3 sudomemoDNS.py + +Note: `python3` is assumed to be the name of your python binary, if it is not then it will need to be substituted with the name and/or path of your binary. ## What you'll see when it starts up -The following will display when you start it up: - +===============================+ | Sudomemo DNS Server | - | Version 1.2 | + | Version 1.2.1 | +===============================+ == Welcome to sudomemoDNS! == @@ -40,24 +42,20 @@ The following will display when you start it up: Then, put these settings in for DNS on your console: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Primary DNS: XXX.XXX.XXX.XXX (NOTE: This value will be unique when you run the program) - Secondary DNS: 8.8.8.8 + Secondary DNS: 008.008.008.008 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ == Getting Help == Need help? Visit our Discord server or check out https://support.sudomemo.net -When entering your DNS settings, if the Primary DNS is displayed like this (this is an example): - -192.168.1.7 - -then you can enter it as follows: - -192.168.001.007 - ## Building on Windows -Install dnslib, requests, and pyinstaller via pip, then modify the paths in the spec file to where sudomemoDNS and the sudomemoDNS icon is stored. +- Install dnslib, requests, and pyinstaller via the following command as an administrator -Then, run the following as an administrator: + python -m pip install -r requirements.txt pyinstaller - pyinstaller.exe sudomemoDNS_v1.2.spec +- Modify the paths in all caps in `sudomemoDNS_v1.2.1.spec` to where sudomemoDNS and the sudomemoDNS icon is stored. + +- Run the following as an administrator: + + pyinstaller sudomemoDNS_v1.2.1.spec diff --git a/sudomemoDNS.py b/sudomemoDNS.py index 53ba036..b0d25c7 100644 --- a/sudomemoDNS.py +++ b/sudomemoDNS.py @@ -26,7 +26,7 @@ def get_platform(): return platforms[sys.platform] -SUDOMEMODNS_VERSION = "1.2" +SUDOMEMODNS_VERSION = "1.2.1" # Adds preceding zeros to IP addresses @@ -55,7 +55,7 @@ MY_IP = get_ip() print("+===============================+") print("| Sudomemo DNS Server |") -print(f"| Version {SUDOMEMODNS_VERSION} |") +print(f"| Version {SUDOMEMODNS_VERSION} |") print("+===============================+\n") print("== Welcome to sudomemoDNS! ==") diff --git a/sudomemoDNS_v1.2.spec b/sudomemoDNS_v1.2.1.spec similarity index 95% rename from sudomemoDNS_v1.2.spec rename to sudomemoDNS_v1.2.1.spec index a06ac5f..8a51ad4 100644 --- a/sudomemoDNS_v1.2.spec +++ b/sudomemoDNS_v1.2.1.spec @@ -23,7 +23,7 @@ exe = EXE(pyz, a.zipfiles, a.datas, [], - name='sudomemoDNS_v1.2', + name='sudomemoDNS_v1.2.1', debug=False, bootloader_ignore_signals=False, strip=False, From 77b4ab8fc77013691f4272c2de6cb1df12f6167a Mon Sep 17 00:00:00 2001 From: meemo Date: Sun, 6 Feb 2022 23:47:48 -0500 Subject: [PATCH 5/6] Optimize imports --- sudomemoDNS.py | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/sudomemoDNS.py b/sudomemoDNS.py index b0d25c7..7c8fc04 100644 --- a/sudomemoDNS.py +++ b/sudomemoDNS.py @@ -1,16 +1,17 @@ # sudomemoDNS from datetime import datetime +from json import loads +from socket import socket, AF_INET, SOCK_DGRAM +from sys import platform from time import sleep -from dnslib import DNSLabel, QTYPE, RD, RR from dnslib import A, AAAA, CNAME, MX, NS, SOA, TXT +from dnslib import DNSLabel, QTYPE, RD, RR from dnslib.server import DNSServer - -import socket -import requests -import json -import sys +from requests import get +from requests.exceptions import RequestException +from requests.exceptions import Timeout def get_platform(): @@ -20,10 +21,10 @@ def get_platform(): 'darwin': 'macOS', 'win32': 'Windows' } - if sys.platform not in platforms: - return sys.platform + if platform not in platforms: + return platform - return platforms[sys.platform] + return platforms[platform] SUDOMEMODNS_VERSION = "1.2.1" @@ -37,7 +38,7 @@ def format_ip(address): def get_ip(): - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s = socket(AF_INET, SOCK_DGRAM) try: # doesn't even have to be reachable s.connect(('10.255.255.255', 1)) @@ -164,16 +165,16 @@ class Record: ZONES = {} try: - get_zones = requests.get("https://www.sudomemo.net/api/dns_zones.json", - headers={'User-Agent': 'SudomemoDNS/' + SUDOMEMODNS_VERSION + ' (' + get_platform() + ')'}) -except requests.exceptions.Timeout: + get_zones = get("https://www.sudomemo.net/api/dns_zones.json", + headers={'User-Agent': 'SudomemoDNS/' + SUDOMEMODNS_VERSION + ' (' + get_platform() + ')'}) +except Timeout: print("[ERROR] Unable to load DNS data: Connection to Sudomemo timed out. Are you connected to the Internet?") -except requests.exceptions.RequestException as e: +except RequestException as e: print("[ERROR] Unable load DNS data.") print("[ERROR] Exception: ", e) - sys.exit(1) + exit(1) try: - zones = json.loads(get_zones.text) + zones = loads(get_zones.text) except ValueError as e: print("[ERROR] Unable load DNS data: Invalid response from server. Check that you can visit sudomemo.net") @@ -227,8 +228,10 @@ elif get_platform() == 'macOS': print("[INFO] If you aren't seeing any requests, check that this is the case first with lsof -i:53 (requires lsof)") print("[INFO] To run as root, prefix the command with 'sudo'") elif get_platform() == 'Windows': - print("[INFO] Please note that you may have to allow this application through the firewall. If so, a popup will appear in a moment.") - print("[INFO] If you are not seeing any requests, make sure you have allowed this application through the firewall. If you have already done so, disregard this message.") + print( + "[INFO] Please note that you may have to allow this application through the firewall. If so, a popup will appear in a moment.") + print( + "[INFO] If you are not seeing any requests, make sure you have allowed this application through the firewall. If you have already done so, disregard this message.") try: servers = [ @@ -238,7 +241,7 @@ try: except PermissionError: print("[ERROR] Permission error: Check that you are running this as an administrator or root") - sys.exit(1) + exit(1) print("[INFO] sudomemoDNS is ready. Now waiting for DNS requests from your console...") From 5607fb063824f03df647c0fb47691c10d1c3fb2a Mon Sep 17 00:00:00 2001 From: meemo Date: Mon, 7 Feb 2022 00:35:08 -0500 Subject: [PATCH 6/6] Switch windows build process to nuitka --- .gitignore | 7 +++++++ README.md | 16 +++++++++++----- sudomemoDNS.py | 4 ++-- sudomemoDNS_v1.2.1.spec | 32 -------------------------------- 4 files changed, 20 insertions(+), 39 deletions(-) delete mode 100644 sudomemoDNS_v1.2.1.spec diff --git a/.gitignore b/.gitignore index c18dd8d..df94962 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,8 @@ +.idea/ __pycache__/ +sudomemoDNS.build/ +sudomemoDNS.dist/ +sudomemoDNS.onefile-build/ + +sudomemoDNS.exe + diff --git a/README.md b/README.md index 3ec841d..8b6211c 100644 --- a/README.md +++ b/README.md @@ -50,12 +50,18 @@ Note: `python3` is assumed to be the name of your python binary, if it is not th ## Building on Windows -- Install dnslib, requests, and pyinstaller via the following command as an administrator +- Install Python 3.6+ (preferably 3.9). Make sure to install the x86 version for 32 bit targets. - python -m pip install -r requirements.txt pyinstaller +- Install all requirements via the following command as an administrator -- Modify the paths in all caps in `sudomemoDNS_v1.2.1.spec` to where sudomemoDNS and the sudomemoDNS icon is stored. +``` +python -m pip install -r requirements.txt nuitka zstandard +``` -- Run the following as an administrator: +- Run the following (answer yes to any prompts): - pyinstaller sudomemoDNS_v1.2.1.spec +```shell +python -m nuitka --standalone --onefile --windows-icon-from-ico=sudomemoDNS_icon.ico -o sudomemoDNS.exe sudomemoDNS.py +``` + +- Run `sudomemoDNS.exe` \ No newline at end of file diff --git a/sudomemoDNS.py b/sudomemoDNS.py index 7c8fc04..8522546 100644 --- a/sudomemoDNS.py +++ b/sudomemoDNS.py @@ -2,7 +2,7 @@ from datetime import datetime from json import loads -from socket import socket, AF_INET, SOCK_DGRAM +from socket import socket, AF_INET, SOCK_DGRAM, gethostbyname from sys import platform from time import sleep @@ -182,7 +182,7 @@ for zone in zones: if zone["type"] == "a": ZONES[zone["name"]] = [Record(A, zone["value"])] elif zone["type"] == "p": - ZONES[zone["name"]] = [Record(A, socket.gethostbyname(zone["value"]))] + ZONES[zone["name"]] = [Record(A, gethostbyname(zone["value"]))] print("[INFO] DNS information loaded successfully.") diff --git a/sudomemoDNS_v1.2.1.spec b/sudomemoDNS_v1.2.1.spec deleted file mode 100644 index 8a51ad4..0000000 --- a/sudomemoDNS_v1.2.1.spec +++ /dev/null @@ -1,32 +0,0 @@ -# -*- mode: python -*- - -block_cipher = None - - -a = Analysis(['sudomemoDNS.py'], - pathex=['C:\\PATH\\TO\\SUDOMEMODNS'], - binaries=[], - datas=[], - hiddenimports=[], - hookspath=[], - runtime_hooks=[], - excludes=[], - win_no_prefer_redirects=False, - win_private_assemblies=False, - cipher=block_cipher, - noarchive=False) -pyz = PYZ(a.pure, a.zipped_data, - cipher=block_cipher) -exe = EXE(pyz, - a.scripts, - a.binaries, - a.zipfiles, - a.datas, - [], - name='sudomemoDNS_v1.2.1', - debug=False, - bootloader_ignore_signals=False, - strip=False, - upx=True, - runtime_tmpdir=None, - console=True , icon='C:\\PATH\\TO\\SUDOMEMODNS\\ICON.ico')