Add files via upload

This commit is contained in:
Rekushi authored and GitHub committed 2026-09-11 05:40:31 +01:00
1 parent b55ab60cd3
commit b3fcacd2a1
4 files changed
+344 -57

No files matched your search

+8
View File
@@ -0,0 +1,8 @@
# Attribution
reximemoDNS is based on the open-source sudomemoDNS project by Austin Burk / Team Sudomemo.
The original project is distributed under the MIT License; see `LICENSE`.
RexiMemo-specific changes include the project branding, default DNS targets, RexiMemo host interception, upstream DNS forwarding, and configuration options.
RexiMemo is not affiliated with or endorsed by Team Sudomemo, Hatena Co., Ltd., or Nintendo.
+91 -55
View File
@@ -1,67 +1,103 @@
![sudomemoDNS Logo](/sudomemoDNS_banner.png) # reximemoDNS
### This server will allow you to connect to Sudomemo when your Internet Service Provider does not work with custom DNS.
A small local DNS helper for connecting Flipnote Studio to **RexiMemo** when a network or ISP does not reliably allow the console to use RexiMemo's public DNS directly.
It runs on a computer on the same local network as the Nintendo DSi. Flipnote Studio's Nintendo/Hatena hostnames are redirected to RexiMemo, while unrelated DNS lookups are forwarded to a normal upstream resolver.
## Default RexiMemo routing
reximemoDNS redirects these hostnames to RexiMemo's legacy service address:
- `nas.nintendowifi.net`
- `flipnote.hatena.com`
- `ugomemo.hatena.ne.jp`
The default RexiMemo server IP is **141.147.76.115**. All other DNS queries are forwarded to **8.8.8.8** by default.
`conntest.nintendowifi.net` is deliberately not intercepted, matching RexiMemo's server-side DNS configuration.
## Setup ## Setup
The setup process does not differ from what is shown at https://flipnot.es/setup except for the values to enter in your custom DNS settings. 1. Put the computer running reximemoDNS and the DSi on the same local network.
2. Install the requirements.
3. Run reximemoDNS as Administrator/root because DNS uses port 53.
4. Enter the **Primary DNS** address printed by the program into the DSi connection settings.
5. Use `008.008.008.008` as Secondary DNS.
6. Save the connection and open Flipnote Studio → Flipnote Hatena.
First, make sure that your console is connected to the same network as this computer. ### Install requirements
Please submit any issues to Sudomemo Support.
## Running on Windows:
Run the .exe provided in this release. You may have to click past a warning from Windows SmartScreen (as it sometimes turns its nose up at applications bundled by PyInstaller) or your firewall (as it needs to listen for DNS requests).
## Running on anything else:
Required packages are installable with the following command:
sudo python3 -m pip install -r requirements.txt
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
+===============================+
| Sudomemo DNS Server |
| Version 1.2.1 |
+===============================+
== Welcome to sudomemoDNS! ==
This server will allow you to connect to Sudomemo when your Internet Service Provider does
not work with custom DNS.
== How To Use ==
First, make sure that your console is connected to the same network as this computer.
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: 008.008.008.008
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
== Getting Help ==
Need help? Visit our Discord server or check out https://support.sudomemo.net
## Building on Windows
- Install Python 3.6+ (preferably 3.9). Make sure to install the x86 version for 32 bit targets.
- Install all requirements via the following command as an administrator
```bash
python -m pip install -r requirements.txt
``` ```
### Run
Windows, from an Administrator terminal:
```powershell
python reximemoDNS.py
```
Linux/macOS:
```bash
sudo python3 reximemoDNS.py
```
When it starts, it prints the LAN address to enter on the DSi.
## Configuration
The defaults are already set for the current RexiMemo server, but they can be changed with environment variables:
- `REXIMEMODNS_SERVER_IP` — RexiMemo legacy-service IPv4 address. Default: `141.147.76.115`
- `REXIMEMODNS_UPSTREAM_DNS` — resolver used for all non-RexiMemo names. Default: `8.8.8.8`
- `REXIMEMODNS_BIND_IP` — local interface/address on which to listen. By default, reximemoDNS detects the computer's LAN IPv4 address.
- `REXIMEMODNS_PORT` — DNS listening port. Default: `53`
Example:
```bash
REXIMEMODNS_SERVER_IP=141.147.76.115 REXIMEMODNS_UPSTREAM_DNS=1.1.1.1 sudo python3 reximemoDNS.py
```
On Windows PowerShell:
```powershell
$env:REXIMEMODNS_SERVER_IP="141.147.76.115"
$env:REXIMEMODNS_UPSTREAM_DNS="1.1.1.1"
python reximemoDNS.py
```
## Troubleshooting
If the program cannot bind to port 53, run it as Administrator/root and make sure another DNS server is not already using that port.
If no console requests appear in the log, check the computer firewall and confirm the DSi's Primary DNS exactly matches the address printed by reximemoDNS.
If ordinary websites or the DSi connection test stop resolving while reximemoDNS is running, verify that `REXIMEMODNS_UPSTREAM_DNS` points to a reachable DNS resolver.
For RexiMemo support, join the Discord server: **https://discord.gg/K5mKWvaruF**
## Building a Windows executable
Install Nuitka and its build dependencies:
```powershell
python -m pip install -r requirements.txt nuitka zstandard python -m pip install -r requirements.txt nuitka zstandard
``` ```
- Run the following (answer yes to any prompts): Then build:
```shell ```powershell
python -m nuitka --standalone --onefile --windows-icon-from-ico=sudomemoDNS_icon.ico -o sudomemoDNS.exe sudomemoDNS.py python -m nuitka --standalone --onefile -o reximemoDNS.exe reximemoDNS.py
``` ```
- Run `sudomemoDNS.exe` No RexiMemo-specific icon is bundled in this source tree, so the executable uses the default application icon unless you supply one when building.
## License and attribution
reximemoDNS is derived from the open-source `sudomemoDNS` project by Austin Burk / Team Sudomemo. The original MIT copyright and license are preserved in `LICENSE`.
See `NOTICE.md` for attribution and RexiMemo-specific changes.
+1 -2
View File
@@ -1,2 +1 @@
requests dnslib>=0.9.24
dnslib
+244
View File
@@ -0,0 +1,244 @@
# reximemoDNS
#
# RexiMemo adaptation of the MIT-licensed sudomemoDNS project.
# See LICENSE and NOTICE.md for attribution.
from datetime import datetime
from ipaddress import IPv4Address, ip_address
import os
from socket import AF_INET, SOCK_DGRAM, socket
from sys import platform
from time import sleep
from dnslib import A, DNSLabel, DNSRecord, QTYPE, RCODE, RR
from dnslib.server import DNSServer
REXIMEMODNS_VERSION = "1.0.0"
DEFAULT_REXIMEMO_SERVER_IP = "141.147.76.115"
DEFAULT_UPSTREAM_DNS = "8.8.8.8"
# Keep this list aligned with RexiMemo's LOCAL_DNS_HOSTS in network_services.py.
# conntest.nintendowifi.net is intentionally not intercepted.
REXIMEMO_HOSTS = (
"nas.nintendowifi.net",
"flipnote.hatena.com",
"ugomemo.hatena.ne.jp",
)
def get_platform():
platforms = {
"linux": "Linux",
"linux1": "Linux",
"linux2": "Linux",
"darwin": "macOS",
"win32": "Windows",
}
return platforms.get(platform, platform)
def format_ip(address):
"""Format an IPv4 address the way Nintendo connection screens display it."""
octets = str(address).split(".")
return ".".join(f"{int(octet):03d}" for octet in octets)
def _ipv4_env(name, default):
value = (os.environ.get(name) or default).strip()
try:
parsed = ip_address(value)
except ValueError as exc:
raise SystemExit(f"[ERROR] {name} must be a valid IPv4 address; got {value!r}") from exc
if not isinstance(parsed, IPv4Address):
raise SystemExit(f"[ERROR] {name} must be an IPv4 address; got {value!r}")
return str(parsed)
def get_ip():
configured = (os.environ.get("REXIMEMODNS_BIND_IP") or "").strip()
if configured:
return _ipv4_env("REXIMEMODNS_BIND_IP", configured)
probe = socket(AF_INET, SOCK_DGRAM)
try:
# The destination does not need to be reachable. Connecting a UDP socket
# is only used to discover which local IPv4 address the OS would route.
probe.connect(("10.255.255.255", 1))
return probe.getsockname()[0]
except OSError:
return "127.0.0.1"
finally:
probe.close()
def _port_env():
raw = (os.environ.get("REXIMEMODNS_PORT") or "53").strip()
try:
value = int(raw)
except ValueError as exc:
raise SystemExit(f"[ERROR] REXIMEMODNS_PORT must be a number; got {raw!r}") from exc
if not 1 <= value <= 65535:
raise SystemExit("[ERROR] REXIMEMODNS_PORT must be between 1 and 65535")
return value
MY_IP = get_ip()
REXIMEMO_SERVER_IP = _ipv4_env("REXIMEMODNS_SERVER_IP", DEFAULT_REXIMEMO_SERVER_IP)
UPSTREAM_DNS = _ipv4_env("REXIMEMODNS_UPSTREAM_DNS", DEFAULT_UPSTREAM_DNS)
DNS_PORT = _port_env()
SERIAL = int((datetime.utcnow() - datetime(1970, 1, 1)).total_seconds())
def _print_banner():
print("+===============================+")
print("| RexiMemo DNS Server |")
print(f"| Version {REXIMEMODNS_VERSION:<13}|")
print("+===============================+\n")
print("== Welcome to reximemoDNS! ==")
print(
"This local DNS helper routes Flipnote Studio's Nintendo/Hatena service names "
"to RexiMemo while forwarding unrelated DNS normally.\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(f"Primary DNS: {format_ip(MY_IP)}")
print(f"Secondary DNS: {format_ip(DEFAULT_UPSTREAM_DNS)}")
print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n")
print("== RexiMemo Routing ==")
print(f"RexiMemo server: {REXIMEMO_SERVER_IP}")
for hostname in REXIMEMO_HOSTS:
print(f" {hostname} -> {REXIMEMO_SERVER_IP}")
print(f"Other DNS queries -> {UPSTREAM_DNS}\n")
print("== Getting Help ==")
print("Join the RexiMemo Discord server: https://discord.gg/K5mKWvaruF\n")
class RexiMemoDNSLogger(object):
def log_recv(self, handler, data):
pass
def log_send(self, handler, data):
pass
def log_request(self, handler, request):
qname = str(request.q.qname).rstrip(".")
qtype = QTYPE.get(request.q.qtype, str(request.q.qtype))
print(
"[INFO] DNS request from %s: %s %s"
% (handler.client_address[0], qtype, qname)
)
def log_reply(self, handler, reply):
answer_count = len(reply.rr)
print(
"[INFO] DNS response to %s: %d answer%s"
% (handler.client_address[0], answer_count, "" if answer_count == 1 else "s")
)
def log_error(self, handler, error):
print("[ERROR] Invalid DNS request from %s: %s" % (handler.client_address[0], error))
def log_truncated(self, handler, reply):
pass
def log_data(self, dnsobj):
pass
class Resolver:
def __init__(self):
self.reximemo_names = {DNSLabel(name) for name in REXIMEMO_HOSTS}
def _reximemo_reply(self, request):
reply = request.reply()
# Mirror RexiMemo's own DNS behavior: A/ANY gets the legacy-service IPv4
# address. Other query types get an authoritative empty NOERROR response.
if request.q.qtype in (QTYPE.A, QTYPE.ANY):
reply.add_answer(
RR(
rname=request.q.qname,
rtype=QTYPE.A,
rclass=1,
ttl=0,
rdata=A(REXIMEMO_SERVER_IP),
)
)
return reply
def _forward(self, request, handler):
try:
raw = request.send(
UPSTREAM_DNS,
53,
tcp=(getattr(handler, "protocol", "udp") == "tcp"),
timeout=3,
)
reply = DNSRecord.parse(raw)
# Never relay a mismatched DNS transaction as if it answered this query.
if reply.header.id != request.header.id:
raise ValueError("upstream DNS transaction ID mismatch")
return reply
except Exception as exc:
print(f"[ERROR] Upstream DNS lookup via {UPSTREAM_DNS} failed: {exc}")
reply = request.reply()
reply.header.rcode = RCODE.SERVFAIL
return reply
def resolve(self, request, handler):
if request.q.qname in self.reximemo_names:
return self._reximemo_reply(request)
return self._forward(request, handler)
def main():
_print_banner()
print("[INFO] Starting reximemoDNS...")
print("[INFO] Detected operating system:", get_platform())
current_platform = get_platform()
if current_platform in {"Linux", "macOS"}:
print("[INFO] DNS uses UDP/TCP port 53, so this normally must run as root.")
print("[INFO] If no requests appear, also check whether another service is already using port 53.")
elif current_platform == "Windows":
print("[INFO] Windows may ask you to allow reximemoDNS through the firewall.")
resolver = Resolver()
logger = RexiMemoDNSLogger()
try:
servers = [
DNSServer(resolver=resolver, port=DNS_PORT, address=MY_IP, tcp=True, logger=logger),
DNSServer(resolver=resolver, port=DNS_PORT, address=MY_IP, tcp=False, logger=logger),
]
except PermissionError:
print("[ERROR] Permission denied while opening DNS port. Run as Administrator/root.")
raise SystemExit(1)
except OSError as exc:
print(f"[ERROR] Could not bind DNS server to {MY_IP}:{DNS_PORT}: {exc}")
raise SystemExit(1)
print(f"[INFO] reximemoDNS is ready on {MY_IP}:{DNS_PORT} (UDP/TCP).")
print("[INFO] Waiting for DNS requests from your console...")
for server in servers:
server.start_thread()
try:
while True:
sleep(0.1)
except KeyboardInterrupt:
print("\n[INFO] Stopping reximemoDNS...")
finally:
for server in servers:
server.stop()
if __name__ == "__main__":
main()