ddns-a

Crates.ioddns-a
lib.rsddns-a
version0.1.2
created_at2026-01-07 13:39:20.70711+00
updated_at2026-01-22 15:03:11.615751+00
descriptionA lightweight Dynamic DNS client for Windows that monitors IP address changes and notifies external services via webhooks
homepagehttps://github.com/doraemonkeys/ddns-a
repositoryhttps://github.com/doraemonkeys/ddns-a
max_upload_size
id2028265
size532,365
doraemon (doraemonkeys)

documentation

README

ddns-a

Crates.io Downloads License

Test Coverage Rust PRs Welcome

A lightweight Dynamic DNS client for Windows that monitors IP address changes and notifies external services via webhooks.

Features

  • Real-time monitoring – Uses Windows API events with polling fallback
  • State persistence – Detects IP changes that occurred during program downtime
  • Flexible filtering – Include/exclude adapters by name regex or kind (ethernet, wireless, virtual, loopback)
  • Customizable webhooks – Any HTTP method, headers, bearer auth, Handlebars templates
  • Robust retry – Exponential backoff with configurable limits
  • Graceful shutdown – Handles Ctrl+C cleanly

Installation

cargo install --git https://github.com/doraemonkeys/ddns-a.git
cargo install ddns-a

Quick Start

# Monitor IPv6 changes and send to webhook
ddns-a --url https://example.com/webhook --ip-version ipv6

# Monitor both IPv4 and IPv6, exclude virtual adapters
ddns-a --url https://api.example.com/ddns --ip-version both --exclude-kind virtual

# With bearer token and custom template
ddns-a --url https://api.example.com/ddns \
       --ip-version ipv4 \
       --bearer YOUR_TOKEN \
       --body-template '{"ip": "{{address}}", "adapter": "{{adapter}}"}'

# Test mode - log changes without sending webhooks
ddns-a --url https://example.com/webhook --ip-version ipv6 --dry-run --verbose

# With state persistence (detect changes across restarts)
ddns-a --url https://example.com/webhook --ip-version ipv6 --state-file ~/.ddns-a/state.json

# Generate config file
ddns-a init

CLI Options

ddns-a [OPTIONS] --url <URL> --ip-version <VERSION>
ddns-a init [--output <FILE>]

Required:
    --url <URL>                  Webhook URL
    --ip-version <VERSION>       ipv4 | ipv6 | both

Request:
    --method <METHOD>            HTTP method (default: POST)
    --header <K=V>               HTTP header (repeatable)
    --bearer <TOKEN>             Bearer token
    --body-template <TEMPLATE>   Handlebars template

Filter:
    --include-adapter <PATTERN>  Include adapters matching regex
    --exclude-adapter <PATTERN>  Exclude adapters matching regex
    --include-kind <KIND>        Include adapters by kind (ethernet, wireless, virtual, loopback)
    --exclude-kind <KIND>        Exclude adapters by kind
    --change-kind <KIND>         Filter by change type: added | removed | both (default: both)

Monitor:
    --poll-interval <SEC>        Polling interval (default: 60)
    --poll-only                  Disable API events, polling only
    --state-file <PATH>          State file for detecting changes across restarts

Retry:
    --retry-max <N>              Max attempts (default: 3)
    --retry-delay <SEC>          Initial delay (default: 5)

Other:
    --config <FILE>              Config file path
    --dry-run                    Log changes without sending webhooks
    --verbose                    Enable debug logging

Default Filtering Behavior

By default (without any filter options):

Adapter Type Monitored
Ethernet ✅ Yes
Wi-Fi ✅ Yes
Virtual (VMware, VirtualBox, Hyper-V, etc.) ✅ Yes
Loopback (127.0.0.1 / ::1) ❌ No (excluded by default)

Note: Loopback is excluded by default. Use --include-kind loopback to monitor it.
Recommendation: Use --include-kind ethernet,wireless to monitor only physical adapters. Virtual adapters (Hyper-V, VMware, Mobile Hotspot) are automatically detected via Windows HardwareInterface flag.

Filter Examples

# Exclude virtual adapters
ddns-a --url ... --ip-version ipv6 --exclude-kind virtual

# Monitor only physical Ethernet and Wireless adapters (auto-excludes Hyper-V, VMware, etc.)
ddns-a --url ... --ip-version ipv6 --include-kind ethernet,wireless

# Monitor only adapters matching regex
ddns-a --url ... --ip-version ipv6 --include-adapter "^Ethernet$"

# Combine kind and name filters
ddns-a --url ... --ip-version both \
       --exclude-kind virtual \
       --exclude-adapter "^Docker"

# Only report newly added IPs (ignore removals)
ddns-a --url ... --ip-version ipv6 --change-kind added

Configuration File

Generate a template:

ddns-a init --output ddns-a.toml

Example ddns-a.toml:

[webhook]
url = "https://api.example.com/ddns"
ip_version = "ipv6"
method = "POST"
body_template = '{"ip": "{{address}}", "adapter": "{{adapter}}", "event": "{{kind}}"}'

# Optional: bearer token or custom headers
# bearer = "your-token"
# [webhook.headers]
# X-Custom-Header = "value"

[filter]
# include_kinds = ["ethernet", "wireless"]
exclude_kinds = ["virtual"]
# include = ["^Ethernet", "^Wi-Fi"]
# exclude = ["^Docker"]

[monitor]
poll_interval = 60
poll_only = false
# state_file = "ddns-a-state.json"
# change_kind = "both"  # added | removed | both

[retry]
max_attempts = 3
initial_delay = 5
max_delay = 60
multiplier = 2.0

Priority: CLI arguments > Config file > Built-in defaults

Body Template Variables

Use Handlebars syntax:

Variable Description
{{adapter}} Adapter name
{{address}} IP address
{{kind}} added or removed
{{timestamp}} Unix timestamp

Example:

{"ip": "{{address}}", "adapter": "{{adapter}}", "event": "{{kind}}"}

How It Works

  1. On startup, fetches current IP addresses from all (filtered) adapters
  2. If --state-file is set, compares with saved state and triggers webhooks for changes during downtime
  3. Listens for Windows network change events via NotifyIpInterfaceChange API
  4. Falls back to pure polling if API events fail
  5. On IP change, sends webhook with retry on failure
  6. Uses debouncing to merge rapid changes (2s window)

Platform Support

Currently Windows-only. The architecture supports adding Linux/macOS via platform-specific AddressFetcher and ApiListener implementations.

License

Apache License 2.0

Commit count: 37

cargo fmt