mirror of
https://github.com/openwrt/packages.git
synced 2026-04-14 18:53:12 +04:00
ddns-scripts: add netcup.com support
Add a new netcup DDNS provider using the netcup DNS api
(ccp.netcup.net) with API key authentication.
Configuration mapping:
* username = netcup customer number
* password = netcup API password
* param_enc = netcup API key (generated in the CCP)
* domain = fully qualified subdomain to update (e.g. home.example.de)
* param_opt = (optional) root/zone domain override (e.g. example.de)
When omitted the root domain is derived by stripping the
leftmost label from 'domain'. This only works correctly for
a single subdomain level (e.g. "home.example.de").
param_opt MUST be set explicitly in two cases:
1. Deep subdomains: domain=test.internal.example.org
2. ccSLD apex domains: domain=example.co.nz
Signed-off-by: Tim Flubshi <flubshi@gmail.com>
This commit is contained in:
committed by
Florian Eckert
parent
80d95db425
commit
10b8f04e3e
@@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=ddns-scripts
|
||||
PKG_VERSION:=2.8.3
|
||||
PKG_RELEASE:=1
|
||||
PKG_RELEASE:=2
|
||||
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
|
||||
@@ -320,6 +320,17 @@ define Package/ddns-scripts-scaleway/description
|
||||
'option param_opt' (Optional) The TTL of the RR
|
||||
endef
|
||||
|
||||
define Package/ddns-scripts-netcup-com
|
||||
$(call Package/ddns-scripts/Default)
|
||||
TITLE:=Extension for netcup.com API
|
||||
DEPENDS:=ddns-scripts +curl
|
||||
PROVIDES:=ddns-scripts_netcup-com
|
||||
endef
|
||||
|
||||
define Package/ddns-scripts-netcup-com/description
|
||||
Dynamic DNS Client scripts extension for 'netcup.com API'.
|
||||
endef
|
||||
|
||||
define Package/ddns-scripts-transip
|
||||
$(call Package/ddns-scripts/Default)
|
||||
TITLE:=Extension for TransIP API
|
||||
@@ -451,6 +462,7 @@ define Package/ddns-scripts-services/install
|
||||
rm $(1)/usr/share/ddns/default/godaddy.com-v1.json
|
||||
rm $(1)/usr/share/ddns/default/hetzner.cloud.json
|
||||
rm $(1)/usr/share/ddns/default/namesilo.com-v1.json
|
||||
rm $(1)/usr/share/ddns/default/netcup.com.json
|
||||
rm $(1)/usr/share/ddns/default/digitalocean.com-v2.json
|
||||
rm $(1)/usr/share/ddns/default/dnspod.cn.json
|
||||
rm $(1)/usr/share/ddns/default/dnspod.cn-v3.json
|
||||
@@ -611,6 +623,24 @@ fi
|
||||
exit 0
|
||||
endef
|
||||
|
||||
define Package/ddns-scripts-netcup-com/install
|
||||
$(INSTALL_DIR) $(1)/usr/lib/ddns
|
||||
$(INSTALL_BIN) ./files/usr/lib/ddns/update_netcup_com.sh \
|
||||
$(1)/usr/lib/ddns
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/share/ddns/default
|
||||
$(INSTALL_DATA) ./files/usr/share/ddns/default/netcup.com.json \
|
||||
$(1)/usr/share/ddns/default
|
||||
endef
|
||||
|
||||
define Package/ddns-scripts-netcup-com/prerm
|
||||
#!/bin/sh
|
||||
if [ -z "$${IPKG_INSTROOT}" ]; then
|
||||
/etc/init.d/ddns stop
|
||||
fi
|
||||
exit 0
|
||||
endef
|
||||
|
||||
define Package/ddns-scripts-digitalocean/install
|
||||
$(INSTALL_DIR) $(1)/usr/lib/ddns
|
||||
$(INSTALL_BIN) ./files/usr/lib/ddns/update_digitalocean_com_v2.sh \
|
||||
@@ -959,6 +989,7 @@ $(eval $(call BuildPackage,ddns-scripts-freedns))
|
||||
$(eval $(call BuildPackage,ddns-scripts-godaddy))
|
||||
$(eval $(call BuildPackage,ddns-scripts-hetzner-cloud))
|
||||
$(eval $(call BuildPackage,ddns-scripts-namesilo))
|
||||
$(eval $(call BuildPackage,ddns-scripts-netcup-com))
|
||||
$(eval $(call BuildPackage,ddns-scripts-digitalocean))
|
||||
$(eval $(call BuildPackage,ddns-scripts-dnspod))
|
||||
$(eval $(call BuildPackage,ddns-scripts-dnspod-v3))
|
||||
|
||||
251
net/ddns-scripts/files/usr/lib/ddns/update_netcup_com.sh
Executable file
251
net/ddns-scripts/files/usr/lib/ddns/update_netcup_com.sh
Executable file
@@ -0,0 +1,251 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# DDNS update script for the netcup DNS API
|
||||
# https://ccp.netcup.net/run/webservice/servers/endpoint.php?JSON
|
||||
#
|
||||
# For use with the OpenWrt ddns-scripts package.
|
||||
# Sourced by dynamic_dns_updater.sh — do NOT call directly.
|
||||
#
|
||||
# Configuration mapping (set in /etc/config/ddns):
|
||||
# username = netcup customer number
|
||||
# password = netcup API password
|
||||
# param_enc = netcup API key (generated in the CCP)
|
||||
# domain = fully qualified subdomain to update (e.g. home.example.de)
|
||||
# param_opt = (optional) root/zone domain override (e.g. example.de)
|
||||
# When omitted the root domain is derived by stripping the
|
||||
# leftmost label from 'domain'. This only works correctly for
|
||||
# a single subdomain level (e.g. "home.example.de").
|
||||
# param_opt MUST be set explicitly in two cases:
|
||||
# 1. Deep subdomains: domain=test.internal.example.org
|
||||
# → param_opt=example.org (hostname becomes "test.internal")
|
||||
# 2. ccSLD apex domains: domain=example.co.nz
|
||||
# → param_opt=example.co.nz (hostname becomes "@")
|
||||
# Note: a subdomain of a ccSLD works without param_opt:
|
||||
# domain=home.example.co.nz → zone "example.co.nz" is
|
||||
# derived correctly by stripping the leftmost label.
|
||||
|
||||
. /usr/share/libubox/jshn.sh
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Constants
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
readonly __NETCUP_ENDPOINT="https://ccp.netcup.net/run/webservice/servers/endpoint.php?JSON"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Validate required configuration variables
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
[ -z "$username" ] && write_log 14 "netcup DDNS: 'username' (customer number) not set"
|
||||
[ -z "$password" ] && write_log 14 "netcup DDNS: 'password' (API password) not set"
|
||||
[ -z "$param_enc" ] && write_log 14 "netcup DDNS: 'param_enc' (API key) not set"
|
||||
[ -z "$domain" ] && write_log 14 "netcup DDNS: 'domain' (subdomain to update) not set"
|
||||
[ -z "$__IP" ] && write_log 14 "netcup DDNS: __IP (current IP) not set by the framework"
|
||||
[ -z "$REGISTERED_IP" ] && write_log 14 "netcup DDNS: REGISTERED_IP not set by the framework"
|
||||
|
||||
# Require an HTTPS-capable client — the netcup endpoint is HTTPS only.
|
||||
[ -z "$CURL_SSL" ] && [ -z "$WGET_SSL" ] && \
|
||||
write_log 14 "netcup DDNS: neither curl nor wget with HTTPS support is available"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Derive DNS zone and record hostname from configuration
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Use param_opt as an explicit zone override; otherwise strip the leftmost
|
||||
# DNS label to obtain the root domain (e.g. "home.example.de" → "example.de").
|
||||
# This automatic derivation only works for a single subdomain level — set
|
||||
# param_opt explicitly for deep subdomains or ccSLD apex domains (see header).
|
||||
if [ -n "$param_opt" ]; then
|
||||
__ZONE="$param_opt"
|
||||
else
|
||||
__ZONE="${domain#*.}"
|
||||
# If the result contains no dot the input was already a root domain.
|
||||
case "$__ZONE" in
|
||||
*.*) : ;;
|
||||
*) __ZONE="$domain" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# The record hostname is everything left of the zone name.
|
||||
# For the zone apex itself use "@".
|
||||
[ "$domain" = "$__ZONE" ] \
|
||||
&& __REC_HOSTNAME="@" \
|
||||
|| __REC_HOSTNAME="${domain%.${__ZONE}}"
|
||||
|
||||
# DNS record type derived from the ip-version setting.
|
||||
[ "${use_ipv6:-0}" -ne 0 ] && __RRTYPE="AAAA" || __RRTYPE="A"
|
||||
|
||||
write_log 7 "netcup DDNS: zone='$__ZONE' hostname='$__REC_HOSTNAME' type=$__RRTYPE target=$__IP"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# netcup_post()
|
||||
#
|
||||
# POST the JSON object currently held in jshn state to the netcup endpoint.
|
||||
# The response body is written to the framework's $DATFILE.
|
||||
# Stderr of the HTTP client goes to $ERRFILE.
|
||||
#
|
||||
# Returns the exit code of the HTTP client (0 = transport OK).
|
||||
# Response status is not validated here; use netcup_check_response().
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
netcup_post() {
|
||||
local __payload
|
||||
__payload="$(json_dump)"
|
||||
write_log 7 "netcup DDNS: POST payload: $__payload"
|
||||
|
||||
if [ -n "$CURL_SSL" ]; then
|
||||
$CURL -Ss \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$__payload" \
|
||||
-o "$DATFILE" 2>"$ERRFILE" \
|
||||
"$__NETCUP_ENDPOINT"
|
||||
else
|
||||
# WGET_SSL is always GNU Wget, which supports --header and --post-data.
|
||||
$WGET_SSL -q \
|
||||
--header="Content-Type: application/json" \
|
||||
--post-data="$__payload" \
|
||||
-O "$DATFILE" \
|
||||
"$__NETCUP_ENDPOINT" 2>"$ERRFILE"
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# netcup_check_response()
|
||||
#
|
||||
# Load $DATFILE as JSON and assert the API returned status "success".
|
||||
# On failure the jshn state is cleared and the script terminates.
|
||||
#
|
||||
# $1 — human-readable context string for the error log (e.g. "login")
|
||||
#
|
||||
# On success the jshn JSON state remains loaded so the caller can continue
|
||||
# reading fields. The caller is responsible for calling json_cleanup().
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
netcup_check_response() {
|
||||
local __context="$1"
|
||||
local __status __statuscode __shortmsg
|
||||
|
||||
json_load "$(cat "$DATFILE")"
|
||||
json_get_var __status "status"
|
||||
json_get_var __statuscode "statuscode"
|
||||
json_get_var __shortmsg "shortmessage"
|
||||
|
||||
if [ "$__status" != "success" ]; then
|
||||
json_cleanup
|
||||
write_log 14 "netcup DDNS: $__context failed (status='$__status' code=$__statuscode): $__shortmsg"
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Main update procedure
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
write_log 6 "netcup DDNS: starting update — '$domain' → $__IP"
|
||||
|
||||
# --- Step 1: Authenticate and obtain a session ID --------------------------
|
||||
|
||||
json_init
|
||||
json_add_string "action" "login"
|
||||
json_add_object "param"
|
||||
json_add_string "customernumber" "$username"
|
||||
json_add_string "apikey" "$param_enc"
|
||||
json_add_string "apipassword" "$password"
|
||||
json_close_object
|
||||
|
||||
netcup_post || write_log 14 "netcup DDNS: HTTP request failed during login"
|
||||
netcup_check_response "login"
|
||||
|
||||
json_select "responsedata"
|
||||
json_get_var __SESSION_ID "apisessionid"
|
||||
json_select ".."
|
||||
json_cleanup
|
||||
|
||||
[ -z "$__SESSION_ID" ] && \
|
||||
write_log 14 "netcup DDNS: login succeeded but no session ID was returned"
|
||||
|
||||
write_log 6 "netcup DDNS: login successful"
|
||||
|
||||
# --- Step 2: Fetch all DNS records for the zone ----------------------------
|
||||
|
||||
json_init
|
||||
json_add_string "action" "infoDnsRecords"
|
||||
json_add_object "param"
|
||||
json_add_string "domainname" "$__ZONE"
|
||||
json_add_string "customernumber" "$username"
|
||||
json_add_string "apikey" "$param_enc"
|
||||
json_add_string "apisessionid" "$__SESSION_ID"
|
||||
json_close_object
|
||||
|
||||
netcup_post || write_log 14 "netcup DDNS: HTTP request failed during infoDnsRecords"
|
||||
netcup_check_response "infoDnsRecords"
|
||||
|
||||
# --- Step 3: Find the record matching our hostname and type ----------------
|
||||
#
|
||||
# The API returns ALL records of the zone (A, AAAA, MX, TXT, …).
|
||||
# We iterate and look for the record where both hostname and type match
|
||||
# the values derived from the 'domain' configuration option.
|
||||
#
|
||||
# The record ID is required by updateDnsRecords to address the exact record.
|
||||
|
||||
__MATCH_ID=""
|
||||
|
||||
json_select "responsedata"
|
||||
json_select "dnsrecords"
|
||||
json_get_keys __RECORD_KEYS
|
||||
|
||||
for __key in $__RECORD_KEYS; do
|
||||
json_select "$__key"
|
||||
json_get_var __rec_id "id"
|
||||
json_get_var __rec_name "hostname"
|
||||
json_get_var __rec_type "type"
|
||||
json_get_var __rec_destination "destination"
|
||||
json_select ".."
|
||||
|
||||
write_log 7 "netcup DDNS: examining record id=$__rec_id '$__rec_name' [$__rec_type] = '$__rec_destination'"
|
||||
|
||||
if [ "$__rec_type" = "$__RRTYPE" ] \
|
||||
&& [ "$__rec_name" = "$__REC_HOSTNAME" ] \
|
||||
&& [ "$__rec_destination" = "$REGISTERED_IP" ]; then
|
||||
__MATCH_ID="$__rec_id"
|
||||
write_log 7 "netcup DDNS: matched record id=$__MATCH_ID"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
json_cleanup
|
||||
|
||||
[ -z "$__MATCH_ID" ] && \
|
||||
write_log 14 "netcup DDNS: no [$__RRTYPE] record found for hostname '$__REC_HOSTNAME' in zone '$__ZONE'"
|
||||
|
||||
# --- Step 4: Update the matched record with the new IP ---------------------
|
||||
|
||||
json_init
|
||||
json_add_string "action" "updateDnsRecords"
|
||||
json_add_object "param"
|
||||
json_add_string "domainname" "$__ZONE"
|
||||
json_add_string "customernumber" "$username"
|
||||
json_add_string "apikey" "$param_enc"
|
||||
json_add_string "apisessionid" "$__SESSION_ID"
|
||||
json_add_object "dnsrecordset"
|
||||
json_add_array "dnsrecords"
|
||||
json_add_object
|
||||
json_add_string "id" "$__MATCH_ID"
|
||||
json_add_string "hostname" "$__REC_HOSTNAME"
|
||||
json_add_string "type" "$__RRTYPE"
|
||||
json_add_string "priority" ""
|
||||
json_add_string "destination" "$__IP"
|
||||
json_add_string "deleterecord" "false"
|
||||
json_close_object
|
||||
json_close_array
|
||||
json_close_object
|
||||
json_close_object
|
||||
|
||||
netcup_post || write_log 14 "netcup DDNS: HTTP request failed during updateDnsRecords"
|
||||
netcup_check_response "updateDnsRecords"
|
||||
json_cleanup
|
||||
|
||||
write_log 6 "netcup DDNS: '$__REC_HOSTNAME.$__ZONE' [$__RRTYPE] updated to $__IP"
|
||||
|
||||
return 0
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "netcup.com",
|
||||
"ipv4": {
|
||||
"url": "update_netcup_com.sh"
|
||||
},
|
||||
"ipv6": {
|
||||
"url": "update_netcup_com.sh"
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,7 @@ myonlineportal.net
|
||||
mythic-beasts.com
|
||||
mythic-beasts.com-v2
|
||||
namecheap.com
|
||||
netcup.com
|
||||
njal.la
|
||||
no-ip.pl
|
||||
now-dns.com
|
||||
|
||||
Reference in New Issue
Block a user