From 9ec3d5c0fb52729c79bce8344e2741866cc38b32 Mon Sep 17 00:00:00 2001 From: StasonJatham Date: Thu, 21 May 2026 18:57:45 +0200 Subject: [PATCH] Avoid fake IPv6 examples --- karldns/web/components.py | 29 +++++++++++++++++++++-------- karldns/web/templates_management.py | 4 +--- tests/test_ui.py | 20 +++++++++++++++++++- 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/karldns/web/components.py b/karldns/web/components.py index dd1dfde..ed20c80 100644 --- a/karldns/web/components.py +++ b/karldns/web/components.py @@ -143,14 +143,27 @@ def _example_update_ips(request_ip: str | None = None) -> tuple[str, str]: def _manual_update_commands(update_url: str, request_ip: str | None = None) -> str: update_base = update_url.split("?", 1)[0] - ipv4, ipv6 = _example_update_ips(request_ip) - return "\n".join( - [ - f"curl -sS '{update_base}?myip={ipv4}'", - f"curl -sS '{update_base}?myipv6={ipv6}'", - f"curl -sS '{update_base}' # use request IP", - ] - ) + request_version, request_address = _known_request_ip(request_ip) + lines = [] + if request_version in {None, 4}: + ipv4, _ = _example_update_ips(request_ip) + lines.append(f"curl -sS '{update_base}?myip={ipv4}'") + if request_version == 6: + lines.append(f"curl -sS '{update_base}?myipv6={request_address}'") + lines.append(f"curl -sS '{update_base}' # use request IP") + return "\n".join(lines) + + +def _known_request_ip(request_ip: str | None = None) -> tuple[int | None, str]: + if not request_ip: + return None, "" + try: + parsed = ip_address(request_ip.strip()) + except ValueError: + return None, "" + if not parsed.is_global: + return None, "" + return parsed.version, str(parsed) def _manual_update_panel(commands: str) -> SafeHtml: diff --git a/karldns/web/templates_management.py b/karldns/web/templates_management.py index 5d2f6dc..25357c6 100644 --- a/karldns/web/templates_management.py +++ b/karldns/web/templates_management.py @@ -7,7 +7,6 @@ from .components import ( _copy_menu, _copy_row, _event_label, - _example_update_ips, _fritzbox_fields, _manual_update_commands, _manual_update_panel, @@ -168,7 +167,6 @@ def _render_pending_management_page( cleanup_hours = settings.cleanup_pending_account_hours cleanup_text = f"{cleanup_hours} hours" if cleanup_hours != 24 else "24 hours" manual_commands = _manual_update_commands(update_url, request_ip) - example_ipv4, example_ipv6 = _example_update_ips(request_ip) return _page( "KarlDNS setup", trusted(f""" @@ -211,7 +209,7 @@ def _render_pending_management_page(

Manual updates.

-

Scripts can send the IP explicitly, or omit it and let KarlDNS use the request IP. Your current public examples use {escape(example_ipv4)} and {escape(example_ipv6)} where available.

+

Scripts can send the IP explicitly, or omit it and let KarlDNS use the request IP. Visible examples use your public IP when KarlDNS or your browser can detect it.

{_code_window(manual_commands, copy_label="manual update commands", class_name="mt-3")}
diff --git a/tests/test_ui.py b/tests/test_ui.py index 2514018..60f616a 100644 --- a/tests/test_ui.py +++ b/tests/test_ui.py @@ -36,7 +36,7 @@ def test_self_service_managed_hostname_generates_random_account(tmp_path: Path) assert "Manual updates" in response.text assert "curl -sS" in response.text assert "203.0.113.10" in response.text - assert "2001:db8::10" in response.text + assert "2001:db8::10" not in response.text assert "Public:" in response.text assert "Secret:" in response.text assert ".ddns.example.net" in response.text @@ -139,6 +139,24 @@ def test_public_examples_use_cloudflare_ip_from_private_reverse_proxy(tmp_path: assert "myip=8.8.4.4" in created.text +def test_pending_examples_use_real_ipv6_when_request_is_ipv6(tmp_path: Path) -> None: + app = make_app( + DdnsSettings( + database_path=tmp_path / "ddns.sqlite3", + admin_password="admin", + hostname_suffix="ddns.example.net", + public_base_url="https://karldns.de", + ) + ) + client = TestClient(app, client=("2001:4860:4860::8888", 12345)) + + response = client.post("/") + + assert response.status_code == 200 + assert "myipv6=2001:4860:4860::8888" in response.text + assert "myip=203.0.113.10" not in response.text + + def test_generated_hostname_must_be_activated_before_updates_work(tmp_path: Path) -> None: database_path = tmp_path / "ddns.sqlite3" app = make_app(