A patch for ddclient 3.8.1 based off of http://blog.peter-r.co.uk/cloudflare-ddclient-patch.html to add support for Cloudflare. Note: this patch sets SSL_verify_mode => SSL_VERIFY_NONE() in the interest of getting things working on a freenas box short of tinkering with the distribution's certificate bundle (The Right Way). See line 132. A sample…
--- ddclient-3.8.1/ddclient 2011-07-11 15:04:21.000000000 -0600 | |
+++ ddclient 2013-10-30 19:51:54.056629680 -0600 | |
@@ -1,4 +1,3 @@ | |
-#!/usr/bin/perl -w | |
#!/usr/local/bin/perl -w | |
###################################################################### | |
# $Id: ddclient 130 2011-07-11 21:02:07Z wimpunk $ | |
@@ -13,12 +12,18 @@ | |
# Support for multiple IP numbers added by | |
# Astaro AG, Ingo Schwarze <ischwarze-OOs/4mkCeqbQT0dZR+AlfA@public.gmane.org> September 16, 2008 | |
# | |
+# Modified to work with Cloudflare by Robert Ian Hawdon 2012-07-16: http://robertianhawdon.me.uk/ | |
+# | |
+# Further modified to work with Cloudflare by Peter Roberts 2013-09-26: blog.peter-r.co.uk | |
+# | |
###################################################################### | |
-require 5.004; | |
+require 5.014; | |
use strict; | |
use Getopt::Long; | |
use Sys::Hostname; | |
use IO::Socket; | |
+use POSIX 'setsid'; | |
+use JSON::Any; | |
my ($VERSION) = q$Revision: 130 $ =~ /(\d+)/; | |
@@ -29,9 +34,9 @@ | |
$program =~ s/d$//; | |
my $now = time; | |
my $hostname = hostname(); | |
-my $etc = ($program =~ /test/i) ? './' : '/etc/ddclient/'; | |
-my $cachedir = ($program =~ /test/i) ? './' : '/var/cache/ddclient/'; | |
-my $savedir = ($program =~ /test/i) ? 'URL/' : '/tmp/'; | |
+my $etc = ($program =~ /test/i) ? './' : '/usr/local/etc/'; | |
+my $cachedir = ($program =~ /test/i) ? './' : '/var/tmp/'; | |
+my $savedir = ($program =~ /test/i) ? 'URL/' : '/var/tmp/'; | |
my $msgs = ''; | |
my $last_msgs = ''; | |
@@ -39,7 +44,7 @@ | |
local $file = ''; | |
local $lineno = ''; | |
-$ENV{'PATH'} = (exists($ENV{PATH}) ? "$ENV{PATH}:" : "") . "/sbin:/usr/sbin:/bin:/usr/bin:/etc:/usr/lib:"; | |
+$ENV{'PATH'} = (exists($ENV{PATH}) ? "$ENV{PATH}:" : "") . "/sbin:/usr/local/sbin:/bin:/usr/local/bin:"; | |
sub T_ANY {'any'}; | |
sub T_STRING {'string'}; | |
@@ -429,6 +434,14 @@ | |
'login' => setv(T_LOGIN, 0, 0, 0, 'unused', undef), | |
'client' => setv(T_STRING, 0, 1, 1, $program, undef), | |
}, | |
+ 'cloudflare-common-defaults' => { | |
+ 'server' => setv(T_FQDNP, 1, 0, 1, 'www.cloudflare.com', undef), | |
+ 'zone' => setv(T_FQDN, 1, 0, 1, '', undef), | |
+ 'static' => setv(T_BOOL, 0, 1, 1, 0, undef), | |
+ 'wildcard' => setv(T_BOOL, 0, 1, 1, 0, undef), | |
+ 'mx' => setv(T_OFQDN, 0, 1, 1, '', undef), | |
+ 'backupmx' => setv(T_BOOL, 0, 1, 1, 0, undef), | |
+ }, | |
); | |
my %services = ( | |
'dyndns1' => { | |
@@ -563,6 +576,17 @@ | |
$variables{'service-common-defaults'}, | |
), | |
}, | |
+ 'cloudflare' => { | |
+ 'updateable' => undef, | |
+ 'update' => \&nic_cloudflare_update, | |
+ 'examples' => \&nic_cloudflare_examples, | |
+ 'variables' => merge( | |
+ { 'server' => setv(T_FQDNP, 1, 0, 1, 'www.cloudflare.com', undef) }, | |
+ { 'min-interval' => setv(T_DELAY, 0, 0, 1, interval('5m'), 0),}, | |
+ $variables{'cloudflare-common-defaults'}, | |
+ $variables{'service-common-defaults'}, | |
+ ), | |
+ }, | |
); | |
$variables{'merged'} = merge($variables{'global-defaults'}, | |
$variables{'service-common-defaults'}, | |
@@ -668,6 +692,9 @@ | |
; | |
} elsif (opt('daemon')) { | |
$SIG{'CHLD'} = 'IGNORE'; | |
+ chdir '/'; | |
+ open(STDIN, "</dev/null"); | |
+ open(STDOUT, ">/dev/null"); | |
my $pid = fork; | |
if ($pid < 0) { | |
print STDERR "${program}: can not fork ($!)\n"; | |
@@ -675,10 +702,9 @@ | |
} elsif ($pid) { | |
exit 0; | |
} | |
+ setsid; | |
$SIG{'CHLD'} = 'DEFAULT'; | |
- open(STDOUT, ">/dev/null"); | |
- open(STDERR, ">/dev/null"); | |
- open(STDIN, "</dev/null"); | |
+ open(STDERR, "&STDOUT"); | |
} | |
# write out the pid file if we're daemon'ized | |
@@ -1463,17 +1489,17 @@ | |
## execute the command. | |
local *FD; | |
if (! open(FD, $cmd)) { | |
- printf STDERR "$program: cannot execute command %s.\n", $cmd; | |
+ warning("$program: cannot execute command %s.\n", $cmd); | |
} elsif ($stdin && (! print FD "$stdin\n")) { | |
- printf STDERR "$program: failed writting to %s.\n", $cmd; | |
+ warning("$program: failed writing to %s.\n", $cmd); | |
close(FD); | |
} elsif (! close(FD)) { | |
- printf STDERR "$program: failed closing %s.($@)\n", $cmd; | |
+ warning("$program: failed closing %s.($@)\n", $cmd); | |
} elsif (opt('exec') && $?) { | |
- printf STDERR "$program: failed %s. ($@)\n", $cmd; | |
+ warning("$program: failed %s. ($@)\n", $cmd); | |
} else { | |
$ok = 1; | |
@@ -1861,6 +1887,7 @@ | |
Proto => 'tcp', | |
MultiHomed => 1, | |
Timeout => opt('timeout'), | |
+ SSL_verify_mode => SSL_VERIFY_NONE(), | |
); | |
defined $sd or warning("cannot connect to $peer:$port socket: $@ " . IO::Socket::SSL::errstr()); | |
} else { | |
@@ -3672,7 +3699,128 @@ | |
} | |
###################################################################### | |
-# vim: ai ts=4 sw=4 tw=78 : | |
+###################################################################### | |
+## nic_cloudflare_examples | |
+## | |
+## written by Ian Pye | |
+## | |
+###################################################################### | |
+sub nic_cloudflare_examples { | |
+ return <<EoEXAMPLE; | |
+o 'cloudflare' | |
+ | |
+The 'cloudflare' protocol is used by DNS service offered by www.cloudflare.com. | |
+ | |
+Configuration variables applicable to the 'cloudflare' protocol are: | |
+ protocol=cloudflare ## | |
+ server=fqdn.of.service ## defaults to www.cloudflare.com | |
+ zone=dns.zone ## your domain | |
+ login=service-login ## login name and password registered with the service | |
+ password=service-password ## | |
+ fully.qualified.host ## the host registered with the service. | |
+ | |
+Example ${program}.conf file entries: | |
+ ## single host update | |
+ protocol=cloudflare, | |
+ zone=dns-zone, \\ | |
+ login=my-cloudflare.com-login, \\ | |
+ password=my-cloudflare.com-api-key \\ | |
+ myhost.com | |
+ | |
+ ## multiple host update to the custom DNS service | |
+ protocol=cloudflare, | |
+ zone=dns.zone \\ | |
+ login=my-cloudflare.com-login, \\ | |
+ password=my-cloudflare.com-api-key \\ | |
+ my-toplevel-domain.com,my-other-domain.com | |
+EoEXAMPLE | |
+} | |
+###################################################################### | |
+## nic_cloudflare_update | |
+###################################################################### | |
+sub nic_cloudflare_update { | |
+ debug("\nnic_cloudflare_update -------------------"); | |
+ | |
+ ## group hosts with identical attributes together | |
+ my %groups = group_hosts_by([ @_ ], [ qw(ssh login password server wildcard mx backupmx) ]); | |
+ | |
+ ## update each set of hosts that had similar configurations | |
+ foreach my $sig (keys %groups) { | |
+ my @hosts = @{$groups{$sig}}; | |
+ my $hosts = join(',', @hosts); | |
+ my $key = $hosts[0]; | |
+ my $ip = $config{$key}{'wantip'}; | |
+ | |
+ # FQDNs | |
+ for my $domain (@hosts) { | |
+ my $hostname = $domain =~ s/\.$config{$key}{zone}$//r; | |
+ delete $config{$domain}{'wantip'}; | |
+ | |
+ info("setting IP address to %s for %s", $ip, $domain); | |
+ verbose("UPDATE:","updating %s", $domain); | |
+ | |
+ # Get domain ID | |
+ my $url = "https://$config{$key}{'server'}/api_json.html?a=rec_load_all"; | |
+ $url .= "&z=".$config{$key}{'zone'}; | |
+ $url .= "&email=".$config{$key}{'login'}; | |
+ $url .= "&tkn=".$config{$key}{'password'}; | |
+ | |
+ my $reply = geturl(opt('proxy'), $url); | |
+ unless ($reply) { | |
+ failed("updating %s: Could not connect to %s.", $domain, $config{$key}{'server'}); | |
+ last; | |
+ } | |
+ last if !header_ok($domain, $reply); | |
+ | |
+ # Strip header | |
+ $reply =~ s/^.*?\n\n//s; | |
+ my $response = JSON::Any->jsonToObj($reply); | |
+ if ($response->{result} eq 'error') { | |
+ failed ("%s", $response->{msg}); | |
+ next; | |
+ } | |
+ | |
+ # Pull the ID out of the json, messy | |
+ my ($id) = map { $_->{name} eq $domain ? $_->{rec_id} : () } @{ $response->{response}->{recs}->{objs} }; | |
+ unless($id) { | |
+ failed("updating %s: No domain ID found.", $domain); | |
+ next; | |
+ } | |
+ | |
+ # Set domain | |
+ $url = "https://$config{$key}{'server'}/api_json.html?a=rec_edit&type=A&ttl=1"; | |
+ $url .= "&name=$hostname"; | |
+ $url .= "&z=".$config{$key}{'zone'}; | |
+ $url .= "&id=".$id; | |
+ $url .= "&email=".$config{$key}{'login'}; | |
+ $url .= "&tkn=".$config{$key}{'password'}; | |
+ $url .= "&content="; | |
+ $url .= "$ip" if $ip; | |
+ | |
+ $reply = geturl(opt('proxy'), $url); | |
+ unless ($reply) { | |
+ failed("updating %s: Could not connect to %s.", $domain, $config{$domain}{'server'}); | |
+ last; | |
+ } | |
+ last if !header_ok($domain, $reply); | |
+ | |
+ # Strip header | |
+ $reply =~ s/^.*?\n\n//s; | |
+ $response = JSON::Any->jsonToObj($reply); | |
+ if ($response->{result} eq 'error') { | |
+ failed ("%s", $response->{msg}); | |
+ } else { | |
+ success ("%s -- Updated Successfully to %s", $domain, $ip); | |
+ | |
+ } | |
+ | |
+ # Cache | |
+ $config{$key}{'ip'} = $ip; | |
+ $config{$key}{'mtime'} = $now; | |
+ $config{$key}{'status'} = 'good'; | |
+ } | |
+ } | |
+} | |
__END__ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment