Skip to content

ddns-scripts: add ionos.com dyndns provider - #30353

Open
databloat wants to merge 1 commit into
openwrt:masterfrom
databloat:add-ionos-ddns
Open

ddns-scripts: add ionos.com dyndns provider#30353
databloat wants to merge 1 commit into
openwrt:masterfrom
databloat:add-ionos-ddns

Conversation

@databloat

Copy link
Copy Markdown

📦 Package Details

Maintainer: @databloat
(You can find this by checking the history of the package Makefile.)

Description:
Adds ionos.com as predefined DDNS service. IONOS' dynamic DNS API only supports domains hosted at IONOS and uses a single fixed update URL with a generated token as password; no username or domain parameter is required in the request itself.


🧪 Run Testing Details

  • OpenWrt Version: 25.12.5
  • OpenWrt Target/Subtarget: qoriq/generic
  • OpenWrt Device: WatchGuard M300

✅ Formalities

  • [ X] I have reviewed the CONTRIBUTING.md file for detailed contributing guidelines.

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 1 new commit. Nothing here blocks a merge.

Packaging side checks out:

  • PKG_RELEASE 2 → 3 is the right move — a service definition is added without a PKG_VERSION change.
  • ionos.com belongs in files/usr/share/ddns/list: it is a plain URL service, not one of the special ones that Package/ddns-scripts-services/install deletes and ships from a dedicated subpackage, so no Makefile subpackage is needed. Placement between infomaniak.com and ipnodns.ru matches the (loosely alphabetical) surroundings.
  • [PASSWORD] is URL-encoded before substitution, so a token with reserved characters in the q= parameter is safe.
  • Omitting "answer" is fine — send_update() skips the response check when it is empty, and the update is still verified through the lookup_host DNS lookup. Omitting [IP] (provider derives the address from the request source) also has in-tree precedent (dy.fi, now-dns.com, dhis.org, …).
  • Commit message accurately describes the diff.

Two inline remarks: one question about the missing ipv6 section, and one optional indentation nit.


Generated by Claude Code

Comment on lines +2 to +5
"name": "ionos.com",
"ipv4": {
"url": "https://ipv4.api.hosting.ionos.com/dns/v1/dyndns?q=[PASSWORD]"
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: indented with spaces; 98 of the 100 existing files in files/usr/share/ddns/default/ are tab-indented (e.g. infomaniak.com.json,`` the neighbour in the list). Purely cosmetic — JSON parses either way — but worth matching the family:

Suggested change
"name": "ionos.com",
"ipv4": {
"url": "https://ipv4.api.hosting.ionos.com/dns/v1/dyndns?q=[PASSWORD]"
}
"name": "ionos.com",
"ipv4": {
"url": "https://ipv4.api.hosting.ionos.com/dns/v1/dyndns?q=[PASSWORD]"
}

Generated by Claude Code

{
"name": "ionos.com",
"ipv4": {
"url": "https://ipv4.api.hosting.ionos.com/dns/v1/dyndns?q=[PASSWORD]"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the missing "ipv6" block intentional? The host name here is explicitly ipv4.api.hosting.ionos.com, which suggests IONOS also hands out an ipv6.api.hosting.ionos.com/dns/v1/dyndns?q=[PASSWORD] counterpart for the same token — in which case an "ipv6" section with that URL would come for free.

Consequence of leaving it out: LuCI treats the provider as unusable for IPv6 — overview.js:131 sets `service_supported = false` when "Use IPv6" is on and the JSON has no `ipv6` key. On the CLI side [`get_service_data()`](https://github.com/openwrt/packages/blob/ff8d23b07a7144f7d7c37ad738c473b78c9423c7/net/ddns-scripts/files/usr/lib/ddns/dynamic_dns_functions.sh#L284-L292``) does json_select "ipv6", and with the key absent data ends up empty, so update_script is silently set to the bare directory /usr/lib/ddns/ and the section aborts with a confusing "Custom update_script not found!".

IPv4-only definitions do exist in-tree (30 of them), so this isn't wrong as such — just checking whether you verified the v6 endpoint doesn't work rather than not needing it.


Generated by Claude Code

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thx for the info! Added a separate ipv6 block in a new commit, verified working separately for both A and AAAA record updates against a live domain, using the ipv4=/ipv6= query parameters as documented by IONOS.

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 1 new commit (662f562, ipv6 support). One inline finding that does block a merge as written: [IP6] is not a placeholder ddns-scripts substitutes, so the AAAA update URL ships a literal [IP6].

The rest of the new commit is fine — the ipv4=[IP] addition uses a supported placeholder, PKG_RELEASE was already bumped to 3 by the first commit so no further bump is needed, and the commit message matches the diff.

The tab-vs-space indentation nit from the earlier review still stands; note its suggestion block predates the ipv6 block, so apply it by hand rather than with the one-click button.


Generated by Claude Code

"url": "https://ipv4.api.hosting.ionos.com/dns/v1/dyndns?q=[PASSWORD]&ipv4=[IP]"
},
"ipv6": {
"url": "https://ipv4.api.hosting.ionos.com/dns/v1/dyndns?q=[PASSWORD]&ipv6=[IP6]"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[IP6] is not a placeholder ddns-scripts knows about, so it is sent to IONOS verbatim. send_update() picks the address family first (use_ipv6 decides which address ends up in __IP) and then does a fixed set of replacements — dynamic_dns_functions.sh:841-842 substitutes [USERNAME], [PASSWORD], [PARAMENC], [PARAMOPT], [DOMAIN] and [IP] and nothing else. [IP6] appears nowhere in the tree; every one of the 40 in-tree ipv6 sections uses [IP] too, including ones with a v6-specific host such as afraid.org-v2-basic.json:7.`` The request as written therefore goes out as &ipv6=%5BIP6%5D (or literal brackets), never the detected AAAA address.

Suggested change
"url": "https://ipv4.api.hosting.ionos.com/dns/v1/dyndns?q=[PASSWORD]&ipv6=[IP6]"
"url": "https://ipv4.api.hosting.ionos.com/dns/v1/dyndns?q=[PASSWORD]&ipv6=[IP]"

Worth re-testing after the fix: since the previous run sent a bogus ipv6= value, whatever made the AAAA update succeed was probably IONOS falling back to the request source address rather than the parameter — which would also mean the parameter is not what distinguishes the two families here, and the ipv6.api.hosting.ionos.com host may still be needed for v6.


Generated by Claude Code

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 1 new commit (5873ad7). The fix is correct: [IP] is the placeholder send_update() substitutes for both address families, and the commit message matches the diff exactly. Nothing here blocks a merge.

Two inline items, neither of them a blocker:

  • A question about the host in the ipv6 section — it is still ipv4.api.hosting.ionos.com, which only matters if a user enables "Force IP Version" or binds to a network.
  • A nit: on squashing the three commits, since the last two are review fallout on a 9-line file, plus a stale sentence in the first commit's message.

The space-vs-tab indentation nit from the first review is still open; its suggestion block predates the ipv6 section, so apply it by hand rather than with the one-click button. FormalityCheck is green on this head.


Generated by Claude Code

"url": "https://ipv4.api.hosting.ionos.com/dns/v1/dyndns?q=[PASSWORD]&ipv4=[IP]"
},
"ipv6": {
"url": "https://ipv4.api.hosting.ionos.com/dns/v1/dyndns?q=[PASSWORD]&ipv6=[IP]"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The placeholder is right now — send_update() substitutes [IP] for both families,`` so this sends the detected AAAA address. What is left on this line is the host: the ipv6 section points at `ipv4.api.hosting.ionos.com`.

That is fine for a default run, where the address travels in ipv6= and the transport family does not matter. It is not fine for the two settings that pin the transport to v6, both of which LuCI exposes:

If ipv4.api.hosting.ionos.com has no AAAA record, both configurations fail to connect at all. I could not resolve AAAA from this environment, so I cannot tell either way — you can, against the live domain. Did the AAAA test run with "Force IP Version" enabled, or only in the default configuration?

For context: no other in-tree ipv6 section uses a v4-named host. The closest analogue, dynv6.com.json:7-9,`` does reuse one endpoint for both families with ipv4=/`ipv6=` exactly as you do here — but its host name is family-neutral. If IONOS reaches the same API over v6 under another name, using it here would make the section work in every configuration. (This supersedes my earlier speculation that a separate v6 endpoint was required — the shared endpoint plus `ipv6=` clearly matches how IONOS documents it.)


Generated by Claude Code

{
"name": "ionos.com",
"ipv4": {
"url": "https://ipv4.api.hosting.ionos.com/dns/v1/dyndns?q=[PASSWORD]&ipv4=[IP]"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: series hygiene — the three commits add one 9-line file, and the last two are review fallout: 662f562 introduces &ipv6=[IP6], 5873ad7 only corrects that string. Squashing all three into a single "ddns-scripts: add ionos.com dyndns provider" commit would leave a cleaner history, and nothing in the series depends on the intermediate states.

While squashing, the message body is worth a pass too: it currently says the API "uses a single fixed update URL … no username or domain parameter is required in the request itself", which was written before this &ipv4=[IP] and its ipv6= counterpart existed. The final file passes the address explicitly per family, so the description and the file now disagree.


Generated by Claude Code

Adds ionos.com as predefined DDNS service. IONOS' dynamic DNS API
only supports domains hosted at IONOS and uses a single fixed
update URL with a generated token as password. Both ipv4 and ipv6
updates use the same endpoint, distinguished by explicitly passing
the detected address via the ipv4= or ipv6= query parameter as
documented by IONOS. Verified working separately for both A and
AAAA record updates against a live domain.

Signed-off-by: Lasse Hanschen <[email protected]>

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 1 new commit (245a786); no new issues found.

The three commits are now squashed into one, as suggested — git diff 5873ad72..245a7867 is empty, so the tree is byte-identical to the head I reviewed yesterday and only the history changed. The rewritten message body now describes the final file accurately (both families on one endpoint, distinguished by ipv4=/ipv6=), so the stale sentence flagged last time is gone. All three FormalityCheck jobs are green on this head.

Two earlier inline threads are still open and still apply to the current file — not repeated inline to avoid duplicates:

  • The ipv6 section pointing at ipv4.api.hosting.ionos.com (thread) — a question about whether the AAAA test ran with "Force IP Version" / bind_network set, not a blocker for a default run.
  • Space-vs-tab indentation in the JSON (thread) — cosmetic; its suggestion block predates the ipv6 section, so apply it by hand rather than with the one-click button.

Neither blocks a merge.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants