From c9754a125ce6996682e7400b36afb51ec5523901 Mon Sep 17 00:00:00 2001 From: satrapes Date: Fri, 30 Sep 2022 15:49:57 +0300 Subject: [PATCH] Add support for njal.la (#459) Co-authored-by: Dimitris Paraskevopoulos --- README.md | 1 + ddclient.conf.in | 19 +++++--- ddclient.in | 116 +++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 126 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index f61608c..c1f7b74 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ Dynamic DNS services currently supported include: Gandi - See https://gandi.net dnsexit - See https://dnsexit.com/ for details 1984.is - See https://www.1984.is/product/freedns/ for details + Njal.la - See https://njal.la/docs/ddns/ `ddclient` now supports many cable and DSL broadband routers. diff --git a/ddclient.conf.in b/ddclient.conf.in index 26c7abc..d6bf96c 100644 --- a/ddclient.conf.in +++ b/ddclient.conf.in @@ -19,12 +19,12 @@ daemon=300 # check every 300 seconds syslog=yes # log update msgs to syslog mail=root # mail all msgs to root -mail-failure=root # mail failed update msgs to root +mail-failure=root # mail failed update msgs to root pid=@runstatedir@/ddclient.pid # record PID in file. ssl=yes # use ssl-support. Works with - # ssl-library -# postscript=script # run script after updating. The - # new IP is added as argument. + # ssl-library +# postscript=script # run script after updating. The + # new IP is added as argument. # #use=watchguard-soho, fw=192.168.111.1:80 # via Watchguard's SOHO FW #use=netopia-r910, fw=192.168.111.1:80 # via Netopia R910 FW @@ -88,7 +88,6 @@ ssl=yes # use ssl-support. Works with # protocol=dyndns2 \ # your-static-host.dyndns.org -## ## ## dyndns.org custom addresses ## @@ -154,7 +153,6 @@ ssl=yes # use ssl-support. Works with # zone=example.com \ # example.com,subdomain.example.com -## ## ## Loopia (loopia.se) ## @@ -302,9 +300,18 @@ ssl=yes # use ssl-support. Works with # password=mypassword \ # myhost.mydomain.com +## ## dnsexit (www.dnsexit.com) ## #protocol=dnsexit, \ #login=myusername, \ #password=mypassword, \ #subdomain-1.domain.com,subdomain-2.domain.com + +## +## Njal.la (http://njal.la/) +## +# protocol=njalla, +# password=mypassword +# quietreply=no|yes +# my-domain.com diff --git a/ddclient.in b/ddclient.in index 94f49a3..99197d8 100755 --- a/ddclient.in +++ b/ddclient.in @@ -762,6 +762,17 @@ my %services = ( 'zone' => setv(T_FQDN, 1, 0, undef, undef), }, }, + 'njalla' => { + 'updateable' => undef, + 'update' => \&nic_njalla_update, + 'examples' => \&nic_njalla_examples, + 'variables' => { + %{$variables{'service-common-defaults'}}, + 'login' => setv(T_STRING, 0, 0, 'unused', undef), + 'server' => setv(T_FQDNP, 1, 0, 'njal.la', undef), + 'quietreply' => setv(T_BOOL, 0, 1, 0, undef) + }, + }, 'noip' => { 'updateable' => undef, 'update' => \&nic_noip_update, @@ -1706,7 +1717,7 @@ sub init_config { $proto = opt('protocol') if !defined($proto); load_sha1_support($proto) if (grep (/^$proto$/, ("freedns", "nfsn"))); - load_json_support($proto) if (grep (/^$proto$/, ("1984", "cloudflare", "gandi", "godaddy", "hetzner", "yandex", "nfsn"))); + load_json_support($proto) if (grep (/^$proto$/, ("1984", "cloudflare", "gandi", "godaddy", "hetzner", "yandex", "nfsn", "njalla"))); if (!exists($services{$proto})) { warning("skipping host: %s: unrecognized protocol '%s'", $h, $proto); @@ -2693,7 +2704,7 @@ sub fetch_via_curl { $curl->setopt(WWW::Curl::Easy->CURLOPT_CAINFO, opt('ssl_ca_file')) if defined(opt('ssl_ca_file')); $curl->setopt(WWW::Curl::Easy->CURLOPT_CAPATH, opt('ssl_ca_dir')) if defined(opt('ssl_ca_dir')); $curl->setopt(WWW::Curl::Easy->CURLOPT_IPRESOLVE, - ($ipversion == 4) ? WWW::Curl::Easy->CURL_IPRESOLVE_V4 : + ($ipversion == 4) ? WWW::Curl::Easy->CURL_IPRESOLVE_V4 : ($ipversion == 6) ? WWW::Curl::Easy->CURL_IPRESOLVE_V6 : WWW::Curl::Easy->CURL_IPRESOLVE_WHATEVER); $curl->setopt(WWW::Curl::Easy->CURLOPT_USERAGENT, "${program}/${version}"); @@ -3172,7 +3183,7 @@ sub get_ip_from_interface { debug("Reply from '%s' :\n------\n%s------", $cmd, $reply); ## IPv6 is more complex than IPv4. Start by filtering on only "inet6" addresses - ## Then remove deprecated or temporary addresses and finally seleect on global or local addresses + ## Then remove deprecated or temporary addresses and finally seleect on global or local addresses my @reply = split(/\n/, $reply); @reply = grep(/\binet6\b/, @reply); # Select only IPv6 entries @reply = grep(!/\bdeprecated\b|\btemporary\b/, @reply); # Remove deprecated and temporary @@ -5041,6 +5052,103 @@ sub nic_nfsn_update { ###################################################################### +###################################################################### +## nic_njalla_examples +###################################################################### +sub nic_njalla_examples { + return <<"EoEXAMPLE"; + +o 'njalla' + +The 'njalla' protocol is used by DNS service offered by njal.la. + +Configuration variables applicable to the 'njalla' protocol are: + protocol=njalla ## + password=service-password ## Generated password for your dynamic DNS record + quietreply=no|yes ## If yes return empty response on success with status 200 but print errors + domain ## subdomain to update, use @ for base domain name, * for catch all + +Example ${program}.conf file entries: + ## single host update + protocol=njalla \\ + password=njal.la-key + quietreply=no + domain.com + +EoEXAMPLE +} +###################################################################### +## nic_njalla_update +## +## written by satrapes +## +## based on https://njal.la/docs/ddns/ +## needs this url to update: +## https://njal.la/update?h=host_name&k=domain_password&a=your_ip +## response contains "code 200" on succesful completion +###################################################################### +sub nic_njalla_update { + debug("\nnic_njalla_update -------------------"); + + foreach my $h (@_) { + # Read input params + my $ipv4 = delete $config{$h}{'wantipv4'}; + my $ipv6 = delete $config{$h}{'wantipv6'}; + my $quietreply = delete $config{$h}{'quietreply'}; + my $ip_output = ''; + + # Build url + my $url = "https://$config{$h}{'server'}/update/?h=$h&k=$config{$h}{'password'}"; + my $auto = 1; + foreach my $ip ($ipv4, $ipv6) { + next if (!$ip); + $auto = 0; + my $ipv = ($ip eq ($ipv6 // '')) ? '6' : '4'; + my $type = ($ip eq ($ipv6 // '')) ? 'aaaa' : 'a'; + $ip_output .= " IP v$ipv: $ip,"; + $url .= "&$type=$ip"; + } + $url .= (($auto eq 1)) ? '&auto' : ''; + $url .= (($quietreply eq 1)) ? '&quiet' : ''; + + info("setting address to%s for %s", ($ip_output eq '') ? ' auto' : $ip_output, $h); + verbose("UPDATE:", "updating %s", $h); + debug("url: %s", $url); + + # Try to get URL + my $reply = geturl(proxy => opt('proxy'), url => $url); + my $response = ''; + if ($quietreply) { + $reply =~ qr/invalid host or key/mp; + $response = ${^MATCH}; + if (!$response) { + success("updating %s: good: IP address set to %s", $h, $ip_output); + } + elsif ($response =~ /invalid host or key/) { + failed("Invalid host or key"); + } else { + failed("Unknown response"); + } + } else { + $reply =~ qr/{(?:[^{}]*|(?R))*}/mp; + $response = eval {decode_json(${^MATCH})}; + # No response, declare as failed + if (!defined($reply) || !$reply) { + failed("updating %s: Could not connect to %s.", $h, $config{$h}{'server'}); + } else { + # Strip header + if ($response->{status} == 401 && $response->{message} =~ /invalid host or key/) { + failed("Invalid host or key"); + } elsif ($response->{status} == 200 && $response->{message} =~ /record updated/) { + success("updating %s: good: IP address set to %s", $h, $response->{value}->{A}); + } else { + failed("Unknown response"); + } + } + } + } +} + ###################################################################### ## nic_sitelutions_examples ###################################################################### @@ -5323,7 +5431,7 @@ sub nic_1984_update { next; } next if !header_ok($host, $reply); - + # Strip header $reply =~ qr/{(?:[^{}]*|(?R))*}/mp; my $response = eval { decode_json(${^MATCH}) };