Skip to content

A dynamic DNS client using the INWX XML-RPC API, written in Rust.

License

Notifications You must be signed in to change notification settings

HimbeerserverDE/dyndns-rs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

57 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

dyndns

A dynamic DNS client using the INWX XML-RPC API, written in Rust.

This client uses the official XML-RPC API instead of the DynDNS API. As a result there is no limit on the number of DynDNS records.

It only works if you're using INWX's own nameservers for your zone.

Usage

dyndns [config]

where config is the config path (defaults to /data/dyndns.conf).

Configuration

The configuration file is simply a JSON file.

Since this file contains account credentials the client will print a warning if anyone other than its owner has any permissions. A reasonable default is 0600.

Unfortunately INWX does not support API tokens.

Two-factor authentication is currently not supported by this program. Feel free to contribute.

Custom servers for DNS resolution

If you want to run this program on a platform like rustkrazy that can't perform regular DNS lookups you may want to set your own nameserver in the config. This enables an in-process resolver instead of relying on libc.

All subsections accept an optional custom_dns setting that's expected to be a JSON string containing a socket address including the IP address (either version) and the port (mandatory).

This snippet enables custom DNS resolution using Quad9's Do53 resolver:

"custom_dns": "[2620:fe::fe]:53"

Getting your record IDs

Open the INWX web panel and create the records if you haven't already. Make sure to use the correct record type.

Then, inspect the grey row of the record using the developer tools. It should be a div with an ID formatted like this: record_div_X. The number X is the record ID.

Basic Config Example

{
	"ipv4": {
		"user": "INWX_ACCOUNT_NAME",
		"pass": "INWX_ACCOUNT_PASSWD",
		"records": [1, 2, 4],
		"link": "ROUTER_WAN_INTERFACE"
	},
	"ipv6": {
		"user": "INWX_ACCOUNT_NAME",
		"pass": "INWX_ACCOUNT_PASSWD",
		"records": [8, 16, 32],
		"link": "ROUTER_WAN_INTERFACE"
	},
	"interval": 300
}

The ipv4 and ipv6 subsections work in the same way. They push the global IP address of the network interface (link) to all records from the list. If multiple global addresses are present, a random one is selected.

The client checks for address updates every 300 seconds (5 minutes, can be changed). If the address update check or API call fails, the client will retry after the interval has passed. As a result the new values will be pushed within the interval as soon as the issue is solved.

You do not need to initialize the records to a specific value. Initializing to 0.0.0.0 or :: is sufficient.

Prefix Config Example

This client includes a special feature: It can update the prefix of your server machines without you having to run DDNS client software on each server. It does this by swapping the prefixes while leaving the interface identifiers of your machines untouched.

Ideally this program would directly interface with the router's DHCPv6 client. Right now it simply reads a LAN side IPv6 address of the router and truncates everything beyond the prefix length.

Getting your prefix length

Do some research on the internet to find out which prefix length you get from your ISP or look it up in your router web panel if it supports it. Common lengths include /64, /60, /59, /56 and /48. From my experience /56 is the most common prefix length for residential customers. If you aren't sure, /64 is a relatively safe option that should work in most single-subnet LANs.

{
	"net6": {
		"user": "INWX_ACCOUNT_NAME",
		"pass": "INWX_ACCOUNT_PASSWD",
		"records": [64, 128, 256],
		"len": 56,
		"link": "ROUTER_PRIMARY_LAN_INTERFACE"
	},
	"interval": 300
}

Unlike with the basic config the records have to be initialized with the correct interface identifiers. You can use any prefix, but I recommend to just set it to all-zero. Example: ::abcd.

Do note that you can merge this with the basic config if you want router connectivity via DynDNS, though only one interval key is used in this case.

OpenWrt

Cross compilation

Install cross:

cargo install cross

Then, install the correct target: -unknown-linux-musl

Example (aarch64):

rustup target add aarch64-unknown-linux-musl

You are now ready to cross-compile:

cross build --release --target aarch64-unknown-linux-musl

The resulting binary can be found at target/<TARGET>/release/dyndns and run on OpenWrt.

Init script

The script below can also be found in dyndns.rc. This is a simple OpenWrt init script to automatically start the client:

#!/bin/sh /etc/rc.common
#
# chkconfig: 35 99 15
# description: DynDNS (Rust)
#

START=99
STOP=15

start() {
	echo "Starting dyndns-rs" | logger -p daemon.info -t dyndns
	(/usr/bin/dyndns | logger -p daemon.info -t dyndns) &

	touch /var/lock/procd_dyndns.lock
	echo "dyndns-rs startup" | logger -p daemon.info -t dyndns
}

stop() {
	echo "Stopping dyndns-rs" | logger -p daemon.info -t dyndns
	killall dyndns

	rm -f /var/lock/procd_dyndns.lock
}

Put this in /etc/init.d/dyndns and add execution permissions. The dyndns binary must be located at /usr/bin/dyndns.

This will run the client as root, which undermines multi-user security advantages.

Config

Config is the same, but be sure to use the hardware interface names. The logical interfaces names (e.g. "WAN6") won't work.

About

A dynamic DNS client using the INWX XML-RPC API, written in Rust.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published