diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e69de29
diff --git a/ac_logs/unknown_ac_20250920T051933901071Z.bin b/ac_logs/unknown_ac_20250920T051933901071Z.bin
new file mode 100644
index 0000000..e69de29
diff --git a/ac_logs/unknown_ac_20250920T051933901071Z.bin.meta b/ac_logs/unknown_ac_20250920T051933901071Z.bin.meta
new file mode 100644
index 0000000..c2f3c6c
--- /dev/null
+++ b/ac_logs/unknown_ac_20250920T051933901071Z.bin.meta
@@ -0,0 +1,8 @@
+Headers:
+Host: 192.168.0.30:8080
+Upgrade-Insecure-Requests: 1
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
+User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 18_7_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/140.0.7339.122 Mobile/15E148 Safari/604.1
+Accept-Language: en-US,en;q=0.9
+Accept-Encoding: gzip, deflate
+Connection: keep-alive
diff --git a/ac_logs/unknown_favicon.ico_20250920T051934015241Z.bin b/ac_logs/unknown_favicon.ico_20250920T051934015241Z.bin
new file mode 100644
index 0000000..e69de29
diff --git a/ac_logs/unknown_favicon.ico_20250920T051934015241Z.bin.meta b/ac_logs/unknown_favicon.ico_20250920T051934015241Z.bin.meta
new file mode 100644
index 0000000..f472511
--- /dev/null
+++ b/ac_logs/unknown_favicon.ico_20250920T051934015241Z.bin.meta
@@ -0,0 +1,6 @@
+Headers:
+Host: 192.168.0.30:8080
+Connection: keep-alive
+User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 18_7_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/140.0.7339.122 Mobile/15E148 Safari/604.1
+Accept-Encoding: gzip, deflate
+Accept-Language: en-US,en;q=0.9
diff --git a/ac_logs/unknown_favicon.ico_20250920T052017843768Z.bin b/ac_logs/unknown_favicon.ico_20250920T052017843768Z.bin
new file mode 100644
index 0000000..e69de29
diff --git a/ac_logs/unknown_favicon.ico_20250920T052017843768Z.bin.meta b/ac_logs/unknown_favicon.ico_20250920T052017843768Z.bin.meta
new file mode 100644
index 0000000..f472511
--- /dev/null
+++ b/ac_logs/unknown_favicon.ico_20250920T052017843768Z.bin.meta
@@ -0,0 +1,6 @@
+Headers:
+Host: 192.168.0.30:8080
+Connection: keep-alive
+User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 18_7_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/140.0.7339.122 Mobile/15E148 Safari/604.1
+Accept-Encoding: gzip, deflate
+Accept-Language: en-US,en;q=0.9
diff --git a/ac_logs/unknown_favicon.ico_20250920T052022406002Z.bin b/ac_logs/unknown_favicon.ico_20250920T052022406002Z.bin
new file mode 100644
index 0000000..e69de29
diff --git a/ac_logs/unknown_favicon.ico_20250920T052022406002Z.bin.meta b/ac_logs/unknown_favicon.ico_20250920T052022406002Z.bin.meta
new file mode 100644
index 0000000..f472511
--- /dev/null
+++ b/ac_logs/unknown_favicon.ico_20250920T052022406002Z.bin.meta
@@ -0,0 +1,6 @@
+Headers:
+Host: 192.168.0.30:8080
+Connection: keep-alive
+User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 18_7_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/140.0.7339.122 Mobile/15E148 Safari/604.1
+Accept-Encoding: gzip, deflate
+Accept-Language: en-US,en;q=0.9
diff --git a/ac_logs/unknown_favicon.ico_20250920T053055565208Z.bin b/ac_logs/unknown_favicon.ico_20250920T053055565208Z.bin
new file mode 100644
index 0000000..e69de29
diff --git a/ac_logs/unknown_favicon.ico_20250920T053055565208Z.bin.meta b/ac_logs/unknown_favicon.ico_20250920T053055565208Z.bin.meta
new file mode 100644
index 0000000..f472511
--- /dev/null
+++ b/ac_logs/unknown_favicon.ico_20250920T053055565208Z.bin.meta
@@ -0,0 +1,6 @@
+Headers:
+Host: 192.168.0.30:8080
+Connection: keep-alive
+User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 18_7_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/140.0.7339.122 Mobile/15E148 Safari/604.1
+Accept-Encoding: gzip, deflate
+Accept-Language: en-US,en;q=0.9
diff --git a/ac_logs/unknown_favicon.ico_20250920T054921452607Z.bin b/ac_logs/unknown_favicon.ico_20250920T054921452607Z.bin
new file mode 100644
index 0000000..e69de29
diff --git a/ac_logs/unknown_favicon.ico_20250920T054921452607Z.bin.meta b/ac_logs/unknown_favicon.ico_20250920T054921452607Z.bin.meta
new file mode 100644
index 0000000..f472511
--- /dev/null
+++ b/ac_logs/unknown_favicon.ico_20250920T054921452607Z.bin.meta
@@ -0,0 +1,6 @@
+Headers:
+Host: 192.168.0.30:8080
+Connection: keep-alive
+User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 18_7_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/140.0.7339.122 Mobile/15E148 Safari/604.1
+Accept-Encoding: gzip, deflate
+Accept-Language: en-US,en;q=0.9
diff --git a/ac_logs/unknown_list_20250920T052017795684Z.bin b/ac_logs/unknown_list_20250920T052017795684Z.bin
new file mode 100644
index 0000000..e69de29
diff --git a/ac_logs/unknown_list_20250920T052017795684Z.bin.meta b/ac_logs/unknown_list_20250920T052017795684Z.bin.meta
new file mode 100644
index 0000000..c2f3c6c
--- /dev/null
+++ b/ac_logs/unknown_list_20250920T052017795684Z.bin.meta
@@ -0,0 +1,8 @@
+Headers:
+Host: 192.168.0.30:8080
+Upgrade-Insecure-Requests: 1
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
+User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 18_7_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/140.0.7339.122 Mobile/15E148 Safari/604.1
+Accept-Language: en-US,en;q=0.9
+Accept-Encoding: gzip, deflate
+Connection: keep-alive
diff --git a/ac_logs/unknown_list_20250920T052022360296Z.bin b/ac_logs/unknown_list_20250920T052022360296Z.bin
new file mode 100644
index 0000000..e69de29
diff --git a/ac_logs/unknown_list_20250920T052022360296Z.bin.meta b/ac_logs/unknown_list_20250920T052022360296Z.bin.meta
new file mode 100644
index 0000000..c2f3c6c
--- /dev/null
+++ b/ac_logs/unknown_list_20250920T052022360296Z.bin.meta
@@ -0,0 +1,8 @@
+Headers:
+Host: 192.168.0.30:8080
+Upgrade-Insecure-Requests: 1
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
+User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 18_7_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/140.0.7339.122 Mobile/15E148 Safari/604.1
+Accept-Language: en-US,en;q=0.9
+Accept-Encoding: gzip, deflate
+Connection: keep-alive
diff --git a/dnsserver.py b/dnsserver.py
new file mode 100644
index 0000000..14eacc1
--- /dev/null
+++ b/dnsserver.py
@@ -0,0 +1,43 @@
+from dnslib import DNSRecord, QTYPE, RR, A
+from dnslib.server import DNSServer, BaseResolver
+import socket
+import datetime
+
+# Your server's LAN IP
+REDIRECT_IP = "192.168.0.30"
+LOG_FILE = "dns_queries.log"
+
+def log_query(qname, qtype, action, target):
+ """Log queries to a file and print them to console"""
+ ts = datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
+ line = f"[{ts}] {qname} ({qtype}) -> {action} {target}\n"
+ print(line.strip())
+ with open(LOG_FILE, "a", encoding="utf-8") as f:
+ f.write(line)
+
+class NintendoResolver(BaseResolver):
+ def resolve(self, request, handler):
+ reply = request.reply()
+ qname = str(request.q.qname)
+ qtype = QTYPE[request.q.qtype]
+
+ # Redirect Nintendo domains to our Flask server
+ if "nintendowifi.net" in qname.lower() or "nintendo.net" in qname.lower():
+ reply.add_answer(RR(request.q.qname, QTYPE.A, rdata=A(REDIRECT_IP), ttl=60))
+ log_query(qname, qtype, "REDIRECT", REDIRECT_IP)
+ else:
+ # Forward everything else
+ try:
+ ip = socket.gethostbyname(qname[:-1]) # strip trailing dot
+ reply.add_answer(RR(request.q.qname, QTYPE.A, rdata=A(ip), ttl=60))
+ log_query(qname, qtype, "FORWARD", ip)
+ except Exception as e:
+ log_query(qname, qtype, "FAILED", str(e))
+
+ return reply
+
+if __name__ == "__main__":
+ resolver = NintendoResolver()
+ server = DNSServer(resolver, port=53, address="0.0.0.0", tcp=False)
+ print(f"[*] DNS server started on 0.0.0.0:53 (redirecting Nintendo domains to {REDIRECT_IP})")
+ server.start()
diff --git a/server.log b/server.log
deleted file mode 100644
index 564ee64..0000000
--- a/server.log
+++ /dev/null
@@ -1 +0,0 @@
-[2025-09-15 19:05:32] Gen4 send Manaphy Egg [PPorg].pgt to 192.168.0.34
diff --git a/server.py b/server.py
index ea707d5..a736f34 100644
--- a/server.py
+++ b/server.py
@@ -1,51 +1,56 @@
-from flask import Flask, request, send_file, abort, jsonify
-import os, random, time
-
-BASE = os.path.dirname(os.path.abspath(__file__))
-GEN4_DIR = os.path.join(BASE, "wonder_gen4")
-GEN5_DIR = os.path.join(BASE, "wonder_gen5")
-LOGFILE = os.path.join(BASE, "server.log")
+from flask import Flask, request, Response, send_from_directory
+import os, random, datetime, xml.etree.ElementTree as ET
app = Flask(__name__)
-def log(msg):
- ts = time.strftime("%Y-%m-%d %H:%M:%S")
- line = f"[{ts}] {msg}\n"
- print(line, end="")
- with open(LOGFILE, "a") as f:
- f.write(line)
+BASE_DIR = os.path.dirname(__file__)
+LOG_DIR = os.path.join(BASE_DIR, "ac_logs")
+EVENTS_GEN4 = os.path.join(BASE_DIR, "wonder_gen4")
+EVENTS_GEN5 = os.path.join(BASE_DIR, "wonder_gen5")
-def list_files(folder, exts):
- return [f for f in os.listdir(folder) if any(f.lower().endswith(e) for e in exts)]
+os.makedirs(LOG_DIR, exist_ok=True)
-@app.route("/")
-def index():
- return "Pokémon Wonder Card Server running"
+# ===== Logging helper =====
+def save_raw_request(prefix: str, data: bytes, headers: dict):
+ ts = datetime.datetime.utcnow().strftime("%Y%m%dT%H%M%S%fZ")
+ filename = f"{prefix}_{ts}.bin"
+ with open(os.path.join(LOG_DIR, filename), "wb") as f:
+ f.write(data)
+ with open(os.path.join(LOG_DIR, filename + ".meta"), "w", encoding="utf-8") as f:
+ for k, v in headers.items():
+ f.write(f"{k}: {v}\n")
-@app.route("/gen4")
-def gen4():
- files = list_files(GEN4_DIR, [".pcd", ".pgt"])
- if not files: return abort(404, "No Gen4 files")
- chosen = request.args.get("file") or random.choice(files)
- if chosen not in files: return abort(404, "File not found")
- log(f"Gen4 send {chosen} to {request.remote_addr}")
- return send_file(os.path.join(GEN4_DIR, chosen), mimetype="application/octet-stream")
+# ===== Gen 4: random event =====
+@app.route("/gen4/event", methods=["GET"])
+def gen4_event():
+ files = [f for f in os.listdir(EVENTS_GEN4) if f.lower().endswith((".pgt", ".pcd"))]
+ if not files:
+ return Response("No Gen4 events found", mimetype="application/xml")
-@app.route("/gen5")
-def gen5():
- files = list_files(GEN5_DIR, [".pgf"])
- if not files: return abort(404, "No Gen5 files")
- chosen = request.args.get("file") or random.choice(files)
- if chosen not in files: return abort(404, "File not found")
- log(f"Gen5 send {chosen} to {request.remote_addr}")
- return send_file(os.path.join(GEN5_DIR, chosen), mimetype="application/octet-stream")
+ chosen = random.choice(files)
+ print(f"Serving Gen4 event: {chosen}")
+ return send_from_directory(EVENTS_GEN4, chosen, mimetype="application/octet-stream")
-@app.route("/list")
-def list_all():
- return jsonify({
- "gen4": list_files(GEN4_DIR, [".pcd", ".pgt"]),
- "gen5": list_files(GEN5_DIR, [".pgf"])
- })
+# ===== Gen 5: random event =====
+@app.route("/gen5/event", methods=["GET"])
+def gen5_event():
+ files = [f for f in os.listdir(EVENTS_GEN5) if f.lower().endswith(".pgf")]
+ if not files:
+ return Response("No Gen5 events found", mimetype="application/xml")
+
+ chosen = random.choice(files)
+ print(f"Serving Gen5 event: {chosen}")
+ return send_from_directory(EVENTS_GEN5, chosen, mimetype="application/octet-stream")
+
+# ===== Catch-all =====
+@app.route('/', defaults={'path': ''}, methods=['GET','POST'])
+@app.route('/', methods=['GET','POST'])
+def catch_all(path):
+ raw = request.get_data()
+ headers = dict(request.headers)
+ save_raw_request(f"unknown_{path}", raw, headers)
+ print(f"Unhandled request: /{path}")
+ return Response("Unhandled path", mimetype="application/xml")
if __name__ == "__main__":
- app.run(host="0.0.0.0", port=80)
+ app.run(host="0.0.0.0", port=8080)
diff --git a/start_services.bat b/start_services.bat
new file mode 100644
index 0000000..6712ef6
--- /dev/null
+++ b/start_services.bat
@@ -0,0 +1,19 @@
+@echo off
+setlocal
+
+REM Paths
+set BASE_DIR=C:\Users\hocke\Documents\GitHub\Pokemon-DNS-DS-Server
+set STUNNEL_EXE="C:\Program Files (x86)\stunnel\bin\stunnel.exe"
+set STUNNEL_CONF=%BASE_DIR%\wii-wfc-dev\stunnel\stunnel.conf
+
+echo === Starting DNS Server ===
+start "DNS Server" cmd /k "cd /d %BASE_DIR% && python dnsserver.py"
+
+echo === Starting Flask Server ===
+start "Flask Server" cmd /k "cd /d %BASE_DIR% && python server.py"
+
+echo === Starting stunnel ===
+start "stunnel" %STUNNEL_EXE% %STUNNEL_CONF%
+
+echo All services launched!
+pause
diff --git a/stop_services.bat b/stop_services.bat
new file mode 100644
index 0000000..d78a154
--- /dev/null
+++ b/stop_services.bat
@@ -0,0 +1,29 @@
+@echo off
+setlocal
+
+REM Paths
+set BASE_DIR=C:\Users\hocke\Documents\GitHub\Pokemon-DNS-DS-Server
+set STUNNEL_EXE="C:\Program Files (x86)\stunnel\bin\stunnel.exe"
+set STUNNEL_CONF=%BASE_DIR%\stunnel\stunnel.conf
+
+REM Start DNS Server
+start "DNS Server" cmd /c "cd /d %BASE_DIR% && python dnsserver.py"
+
+REM Start Flask Server
+start "Flask Server" cmd /c "cd /d %BASE_DIR% && python server.py"
+
+REM Start stunnel
+start "stunnel" %STUNNEL_EXE% %STUNNEL_CONF%
+
+echo.
+echo Servers are running. Close this window to stop them all.
+echo.
+
+REM Wait until user closes window
+pause >nul
+
+REM Kill services
+taskkill /IM python.exe /F >nul 2>&1
+taskkill /IM stunnel.exe /F >nul 2>&1
+
+echo All services stopped.
diff --git a/wii-wfc-dev/certs/nintendo-ca.pem b/wii-wfc-dev/certs/nintendo-ca.pem
new file mode 100644
index 0000000..b1c2f21
--- /dev/null
+++ b/wii-wfc-dev/certs/nintendo-ca.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDfTCCAuagAwIBAgIBADANBgkqhkiG9w0BAQUFADCBjDELMAkGA1UEBhMCVVMx
+EzARBgNVBAgTCldhc2hpbmd0b24xIDAeBgNVBAoTF05pbnRlbmRvIG9mIEFtZXJp
+Y2EgSW5jMQwwCgYDVQQLEwNOT0ExFDASBgNVBAMTC05pbnRlbmRvIENBMSIwIAYJ
+KoZIhvcNAQkBFhNjYUBub2EubmludGVuZG8uY29tMB4XDTA1MDgxMDA3MDIyMloX
+DTQ5MTIyODEyMDAwMFowgYwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n
+dG9uMSAwHgYDVQQKExdOaW50ZW5kbyBvZiBBbWVyaWNhIEluYzEMMAoGA1UECxMD
+Tk9BMRQwEgYDVQQDEwtOaW50ZW5kbyBDQTEiMCAGCSqGSIb3DQEJARYTY2FAbm9h
+Lm5pbnRlbmRvLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAs815l3dd
+iq+GqOjXcxx33xCQH4H4QZ4hVd+8/GP7GUPx9sRyQkm9rURoTvPaHeZN2PlZiNyu
+Pps4Ccp//9wkokR4eEmT1IRAELjsPtstk8gRyP14LWGtMa6GJrD9Wj+hPb/iS0ns
+zmaYWCYSwPv0d2Ub6vvLf+CMywKjTl6M6psCAwEAAaOB7DCB6TAdBgNVHQ4EFgQU
+e1dTPzGsd3Hx/UrmD0Ow1VVBn9IwgbkGA1UdIwSBsTCBroAUe1dTPzGsd3Hx/Urm
+D0Ow1VVBn9KhgZKkgY8wgYwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n
+dG9uMSAwHgYDVQQKExdOaW50ZW5kbyBvZiBBbWVyaWNhIEluYzEMMAoGA1UECxMD
+Tk9BMRQwEgYDVQQDEwtOaW50ZW5kbyBDQTEiMCAGCSqGSIb3DQEJARYTY2FAbm9h
+Lm5pbnRlbmRvLmNvbYIBADAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GB
+AF/sccq8/vxDNIho4Zal/mZZ+cbggUn8AH9ZeYD2kLvCd1w9j4O0IgkqEaBnYbho
+OR7R4ExsHSEGmFn1A7TNbkRgcRrcOIoO6HsRPmPiDX12NKEZyfN8cDwc0azKsJZd
+eYVJXqFQRJNZz3h5DkUDlXxJQMD3xALVAMehncWFC7jc
+-----END CERTIFICATE-----
diff --git a/wii-wfc-dev/certs/nwc.crt b/wii-wfc-dev/certs/nwc.crt
new file mode 100644
index 0000000..e69de29
diff --git a/wii-wfc-dev/certs/nwc.key b/wii-wfc-dev/certs/nwc.key
new file mode 100644
index 0000000..80698a4
--- /dev/null
+++ b/wii-wfc-dev/certs/nwc.key
@@ -0,0 +1,5 @@
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgAAAAAAAAAAAAAAAA
+R3PjA+68HXho4wtwNR2+qja1KjOhRANCAARNgTALVXey7qzm9xplJr10KCXkZcEA
+vUfFK/d8DDELSmj68KRellomg04PUPPzA2BBiSIpTu3HBVjeUCfR2zf5
+-----END PRIVATE KEY-----
diff --git a/wii-wfc-dev/certs/priv.hex b/wii-wfc-dev/certs/priv.hex
new file mode 100644
index 0000000..ff16d13
--- /dev/null
+++ b/wii-wfc-dev/certs/priv.hex
@@ -0,0 +1 @@
+004773E303EEBC1D7868E30B70351DBEAA36B52A33
\ No newline at end of file
diff --git a/wii-wfc-dev/certs/server.crt b/wii-wfc-dev/certs/server.crt
new file mode 100644
index 0000000..6cc30da
--- /dev/null
+++ b/wii-wfc-dev/certs/server.crt
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICqjCCAhMCFFlac2Q+iXBDUiNXP9NLm6gXIWo7MA0GCSqGSIb3DQEBBQUAMIGT
+MQswCQYDVQQGEwJVUzEVMBMGA1UECAwMUGVubnN5bHZhbmlhMRQwEgYDVQQHDAtN
+dXJyeXN2aWxsZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRkw
+FwYDVQQLDBBuaW50ZW5kb3dpZmkubmV0MRkwFwYDVQQDDBBuaW50ZW5kb3dpZmku
+bmV0MB4XDTI1MDkyMDAzNTMwMFoXDTI2MDkyMDAzNTMwMFowgZMxCzAJBgNVBAYT
+AlVTMRUwEwYDVQQIDAxQZW5uc3lsdmFuaWExFDASBgNVBAcMC011cnJ5c3ZpbGxl
+MSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxGTAXBgNVBAsMEG5p
+bnRlbmRvd2lmaS5uZXQxGTAXBgNVBAMMEG5pbnRlbmRvd2lmaS5uZXQwgZ8wDQYJ
+KoZIhvcNAQEBBQADgY0AMIGJAoGBANbz7GfMN/KBqp12LWUSxVflSqDKCuudfsTI
++32SnvOji6SZDDPpOLn64cPV3/bmEBxjBCHXsYIiz4q/2VK4YRyrStFDnOVsXw52
+awH3Kw7uTVpUQttJAm3aj0CJgJNoc1j+1Kb960EVk5jvGMSHK0ESDCCqOOPAEzqN
+wP0gClvfAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAgorZg1VXbmb+f2RSKKflW3hQ
+3TcHGsXX97Ai7F/U97ge89G+UT+0YHLILqsH/zlJc5sKKo4slMqzlwJXvGPTZ0iS
+eGkO87zo/ibv38H9qhRkKH8OjdKHJf9LjCewBdeth2VN5EToTt60OyNenmnj3p5m
+65bjEyE/+GDiTWbK1MQ=
+-----END CERTIFICATE-----
diff --git a/wii-wfc-dev/certs/server.key b/wii-wfc-dev/certs/server.key
new file mode 100644
index 0000000..e7542d4
--- /dev/null
+++ b/wii-wfc-dev/certs/server.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBANbz7GfMN/KBqp12
+LWUSxVflSqDKCuudfsTI+32SnvOji6SZDDPpOLn64cPV3/bmEBxjBCHXsYIiz4q/
+2VK4YRyrStFDnOVsXw52awH3Kw7uTVpUQttJAm3aj0CJgJNoc1j+1Kb960EVk5jv
+GMSHK0ESDCCqOOPAEzqNwP0gClvfAgMBAAECgYBk7MR7++vs5iroodswkL6txU/j
+7NlxxVIHLWK/k/8j4A8p6l5LZV6oG/6yx5HPyDeaTHddOHv04UlXGTr91uppAbf7
+BN8iXwZzAh7tT3QVluGXnO+hlrtRiPCpnSRKuG04wR1UehYJV3WrDKLVBHmqhxxF
+tdbOBRRcY+KrFnBWoQJBAPxqPyR/mtxzTH2KV8yHNaeku/U7OJTi0RXV/VbKviQs
+qzQYql+CEJxJLYz5r16ebnn+J1kakq+JLj/yCcZ50BECQQDaAXfmTrUK3CiHzrln
+nmyOia67N2biGzNoarz9O7IAqfAFVhnbw1pO/GDMXvq9yc4A4dlApDh02DTAXAIf
+21zvAkBdIQYy/TGx7IhoOUazm59cPxdqH3N820Kxf8Z840jMJ/7ytbcGjgJ65zT7
+MpRVoLa6I994rKk6Qq7hwPeCVydBAkAbEhebZ6afhJsb0hW/wjJkuMATy1thNFN7
+4dU59NGzt2ZwozJu0aFDMZ+rgRfNvWNxRJo/pqTEDZzvTf2NiyXNAkAo4bDqN37K
+0x86Gjn+L1a76jpgNtJ3o5L/GyJYlpQrK4Qn/ZX3TX/Lmv74BHjU8vy5lI66w5Yt
+l81+P9zvyEVy
+-----END PRIVATE KEY-----
diff --git a/wii-wfc-dev/requirements.txt b/wii-wfc-dev/requirements.txt
new file mode 100644
index 0000000..278ce2d
--- /dev/null
+++ b/wii-wfc-dev/requirements.txt
@@ -0,0 +1,2 @@
+cryptography>=3.3
+flask>=2.0
\ No newline at end of file
diff --git a/wii-wfc-dev/stunnel/stunnel.conf b/wii-wfc-dev/stunnel/stunnel.conf
new file mode 100644
index 0000000..8463108
--- /dev/null
+++ b/wii-wfc-dev/stunnel/stunnel.conf
@@ -0,0 +1,17 @@
+; stunnel.conf
+; Run in background (no console popup)
+foreground = quiet
+
+; Reduce verbosity (4 = info, 3 = warnings only)
+debug = 4
+
+; Write logs to file instead of popup
+output = stunnel.log
+
+[wfc]
+accept = 0.0.0.0:443
+connect = 127.0.0.1:8080
+cert = ../certs/server.crt
+key = ../certs/server.key
+sslVersion = SSLv23
+ciphers = ALL:!aNULL