diff --git a/README.md b/README.md index 521297b..12644cf 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ Dynamic DNS services currently supported include: domenehsop - See https://api.domeneshop.no/docs/#tag/ddns/paths/~1dyndns~1update/get Mythic Beasts - See https://www.mythic-beasts.com/support/api/dnsv2/dynamic-dns for details Enom - See https://www.enom.com for details + Infomaniak - See https://faq.infomaniak.com/2376 for details `ddclient` now supports many cable and DSL broadband routers. diff --git a/ddclient.conf.in b/ddclient.conf.in index 38b2b19..3c1811e 100644 --- a/ddclient.conf.in +++ b/ddclient.conf.in @@ -355,3 +355,11 @@ ssl=yes # use ssl-support. Works with #zone=example.com, \ #password=api-token \ #example.com,sub.example.com + +## +## Infomaniak (www.infomaniak.com) +## +# protocol=infomaniak, +# login=ddns_username, +# password=ddns_password +# example.com diff --git a/ddclient.in b/ddclient.in index 43eb3b1..69f9c18 100755 --- a/ddclient.in +++ b/ddclient.in @@ -978,6 +978,14 @@ my %services = ( 'min-interval' => setv(T_DELAY, 0, 0, 0, interval('5m')), }, }, + 'infomaniak' => { + 'updateable' => undef, + 'update' => \&nic_infomaniak_update, + 'examples' => \&nic_infomaniak_examples, + 'variables' => { + %{$variables{'service-common-defaults'}}, + }, + }, ); $variables{'merged'} = { map({ %{$services{$_}{'variables'}} } keys(%services)), @@ -7979,6 +7987,118 @@ sub nic_digitalocean_update { } } +###################################################################### +## nic_infomaniak_examples +###################################################################### +sub nic_infomaniak_examples { + return <<"EoEXAMPLE"; + +o 'infomaniak' + +The 'infomaniak' protocol is used by DNS services offered by www.infomaniak.com. + +Configuration variables applicable to the 'infomaniak' protocol are: + protocol=infomaniak + login=ddns_username ## the DDNS username set up in Infomaniak + password=ddns_password ## the DDNS username set up in Infomaniak + example.com ## domain name to update + +Example ${program}.conf file entries: + protocol=infomaniak, \\ + login=my-username, \\ + password=my-password \\ + my.address.com + +For more information about how to create a dynamic DNS, see https://faq.infomaniak.com/2357. + +EoEXAMPLE +} + +###################################################################### +## nic_infomaniak_update +## +## written by Timothée Andres +## +## based on https://faq.infomaniak.com/2376 +## +## needs one of the following urls to update: +## https://username:password@infomaniak.com/nic/update?hostname=subdomain.yourdomain.com&myip=1.2.3.4 +## https://infomaniak.com/nic/update?hostname=subdomain.yourdomain.com&myip=1.2.3.4&username=XXX&password=XXX +###################################################################### +sub nic_infomaniak_update { + debug("\nnic_infomaniak_update -------------------"); + + foreach my $h (@_) { + INFOMANIAK_IP_LOOP: + foreach my $v (4, 6) { + my $ip = delete $config{$h}{"wantipv$v"}; + + if (!defined $ip) { + debug("ipv%d not wanted, skipping", $v); + next; + } + + verbose("setting IP address to %s for %s", $ip, $h); + info("updating %s", $h); + + # No change in IP => nochg + # Bad auth => badauth + # Bad domain name => nohost + # Bad IP => nohost + # IP changed => good + # No domain name => Validation failed + my %statuses = ( + 'good' => (1, sprintf("IP set to %s for %s", $ip, $h)), + 'nochg' => (1, sprintf("IP already set to %s for %s", $ip, $h)), + 'nohost' => (0, sprintf("Bad domain name %s or bad IP %s", $h, $ip)), + 'badauth' => (0, sprintf("Bad authentication for %s", $h)), + ); + + my $url1 = "https://$config{$h}{'login'}:$config{$h}{'password'}"; + $url1 .= "\@infomaniak.com/nic/update"; + $url1 .= "?hostname=$h"; + $url1 .= "&myip=$ip"; + + my $url2 = "https://infomaniak.com/nic/update"; + $url2 .= "?hostname=$h"; + $url2 .= "&myip=$ip"; + $url2 .= "&username=$config{$h}{'login'}"; + $url2 .= "&password=$config{$h}{'password'}"; + + my $reply; + + foreach my $url ($url1, $url2) { + verbose("trying update with %s", $url); + $reply = geturl(proxy => opt('proxy'), url => $url); + if (!defined($reply) || !$reply) { + info("could not update %s using url %s, trying next one", $h, $url); + next; + } + + my ($status) = split / /, $reply, 1; + my ($updated, $msg) = + $statuses{$status} // (0, sprintf("Unknown reply from Infomaniak: %s", $reply)); + + if (defined $updated && $updated) { + info($msg); + $config{$h}{"ipv$v"} = $ip; + $config{$h}{'mtime'} = $config{$h}{'mtime'} // $now; + $config{$h}{'status'} = 'good'; + $config{$h}{"status-ipv$v"} = 'good'; + next INFOMANIAK_IP_LOOP; + } + else { + warning($msg); + } + } + + $config{$h}{'status'} = $config{$h}{'status'} // 'failed'; + $config{$h}{"status-ipv$v"} = 'failed'; + failed("updating %s: could not update IP on Infomaniak", $h); + } + } +} + # Execute main() if this file is run as a script or run via PAR (https://metacpan.org/pod/PAR), # otherwise do nothing. This "modulino" pattern makes it possible to import this file as a module # and test its functions directly; there's no need for test-only command-line arguments or stdout