Package: ddclient
Version: 3.8.0-11.3
Severity: wishlist
Tags: ipv6 patch

A patch for 3.8.1 to support IPv6 on the two services I know that can handle
it.  Both dyndns and freedns automatically recognize the type of address being
passed to them and use it to update the A or the AAAA record.

I added the "usev6" variable, that works exactly as the "use" one.  So,  if you
are using dynds and use "usev6" instead of "use" on your existing definition
*and* you have a global IPv6 address, then you're all set.

For "usev6" only the "ip" and "if" strategies are implemented and the "if" one
uses "ip -6 addr list" instead of "ifconfig" because I found it easier to
parse. But upstream was concerned about portability so "ifconfig" should be
used in the future.

The patch has been in use for a year and a half in many installations with no
problems reported.



-- System Information:
Debian Release: 6.0.6
  APT prefers stable
  APT policy: (500, 'stable')
Architecture: i386 (i686)

Kernel: Linux 3.4.1 (SMP w/1 CPU core)
Locale: LANG=es_UY.utf8, LC_CTYPE=es_UY.utf8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash

Versions of packages ddclient depends on:
ii  debconf [debconf-2 1.5.36.1              Debian configuration management sy
ii  initscripts        2.88dsf-13.1+squeeze1 scripts for initializing and shutt
ii  lsb-base           3.2-23.2squeeze1      Linux Standard Base 3.2 init scrip
ii  perl [perl5]       5.10.1-17squeeze3     Larry Wall's Practical Extraction 

Versions of packages ddclient recommends:
ii  libio-socket-ssl-perl    1.33-1+squeeze1 Perl module implementing object or

ddclient suggests no packages.

-- debconf information excluded
--- ddclient	2011-07-11 18:04:21.000000000 -0300
+++ /usr/sbin/ddclient	2011-09-10 01:47:43.721029588 -0300
@@ -55,6 +55,7 @@
 sub T_FQDNP	{'fully qualified host name and optional port number'};
 sub T_PROTO	{'protocol'}
 sub T_USE	{'ip strategy'}
+sub T_USEV6	{'ipv6 strategy'}
 sub T_IF        {'interface'}
 sub T_PROG      {'program name'}
 sub T_IP        {'ip'}
@@ -325,6 +326,7 @@
 	'protocol'            => setv(T_PROTO, 0, 0, 1, 'dyndns2',            undef),
 
 	'use'                 => setv(T_USE,   0, 0, 1, 'ip',                 undef),
+	'usev6'               => setv(T_USEV6, 0, 0, 1, undef,                undef),
 	'ip'                  => setv(T_IP,    0, 0, 1, undef,                undef),
 	'if'                  => setv(T_IF,    0, 0, 1, 'ppp0',               undef),
 	'if-skip'             => setv(T_STRING,1, 0, 1, '',                   undef),
@@ -365,6 +367,7 @@
 	'host'                => setv(T_STRING, 1, 1, 1, '',                  undef),
 
 	'use'                 => setv(T_USE,   0, 0, 1, 'ip',                 undef),
+	'usev6'               => setv(T_USE,   0, 0, 1, undef,                undef),
 	'if'                  => setv(T_IF,    0, 0, 1, 'ppp0',               undef),
 	'if-skip'             => setv(T_STRING,0, 0, 1, '',                   undef),
 	'web'                 => setv(T_STRING,0, 0, 1, 'dyndns',             undef),
@@ -583,6 +586,7 @@
     [ "pid",         "=s", "-pid path             : record process id in 'path'" ],
     "",			     
     [ "use",         "=s", "-use which            : how the should IP address be obtained." ],
+    [ "usev6",       "=s", "-usev6 which          : how the should IPv6 address be obtained." ],
                                                   &ip_strategies_usage(),
     "",			     
     [ "ip",          "=s", "-ip address           : set the IP address to 'address'" ],
@@ -769,11 +773,19 @@
 		my (@hosts, %ips) = ();
 		my $updateable = $services{$s}{'updateable'};
 		my $update     = $services{$s}{'update'};
+		my $ipv6 = 0;
 
 		foreach my $h (sort keys %config) {
 			next if $config{$h}{'protocol'} ne lc($s);
 			$examined{$h} = 1;
-			my $use = $config{$h}{'use'} || opt('use');
+			my $use = '';
+		        $ipv6 = 1 if (defined $config{$h}{'usev6'});
+			if ($ipv6) {
+			    $use = $config{$h}{'usev6'} || opt('usev6');
+			}
+			else {
+			    $use = $config{$h}{'use'} || opt('use');
+			}
 			local $opt{$use} = $config{$h}{$use} if $config{$h}{$use};
 			# bug #13: we should only do this once
 			# use isn't enough, we have to save the origin to.
@@ -783,16 +795,21 @@
 			if (defined $iplist{$use}) {
 				$ip = $iplist{$use};
 			} else {
-				$ip = get_ip($use, $h);
+				$ip = get_ip($use, $h) if !$ipv6;
+				$ip = get_ipv6($use, $h) if $ipv6;
 				if (!defined $ip || !$ip) {
 					warning("unable to determine IP address")
 						if !$daemon || opt('verbose');
 					next;
 				}
-				if ($ip !~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) {
+				if (!$ipv6 and $ip !~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) {
 					warning("malformed IP address (%s)", $ip);
 					next;
 				}
+				if ($ipv6 and $ip !~ /^[0-9a-f:]+$/) {
+					warning("malformed IPv6 address (%s)", $ip);
+					next;
+				}
 				$iplist{$use} = $ip;
 			}
 			$config{$h}{'wantip'} = $ip;
@@ -933,7 +950,7 @@
     my ($c, $name, $value);
     my ($escape, $quote) = (0, '');
 
-    if ($rest =~ /^\s*([a-z][a-z_-]*)=(.*)/i) {
+    if ($rest =~ /^\s*([a-z][0-9a-z_-]*)=(.*)/i) {
 	($name, $rest, $value) = ($1, $2, '');
 
 	while (length($c = substr($rest,0,1))) {
@@ -1111,6 +1128,11 @@
     $opt{'use'} = 'if'  if !define($opt{'use'}) && defined($opt{'if'});
     $opt{'use'} = 'web' if !define($opt{'use'}) && defined($opt{'web'});
 
+    ## infer the IPv6 strategy if possible
+    $opt{'usev6'} = 'ip'  if !define($opt{'usev6'}) && defined($opt{'ip'});
+    $opt{'usev6'} = 'if'  if !define($opt{'usev6'}) && defined($opt{'if'});
+    $opt{'usev6'} = 'web' if !define($opt{'usev6'}) && defined($opt{'web'});
+
     ## sanity check
     $opt{'max-interval'}       = min(interval(opt('max-interval')), interval(default('max-interval')));
     $opt{'min-interval'}       = max(interval(opt('min-interval')), interval(default('min-interval')));
@@ -1720,6 +1742,10 @@
 	$value = lc $value;
 	return undef if ! exists $ip_strategies{$value};
 
+    } elsif ($type eq T_USEV6) {
+	$value = lc $value;
+	return undef if ! exists $ip_strategies{$value};
+
     } elsif ($type eq T_FILE) {
 	return undef if $value eq "";
 
@@ -1736,7 +1762,7 @@
 #	return undef if $value =~ /:/;
 
     } elsif ($type eq T_IP) {
-	return undef if $value !~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
+	return undef if $value !~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/ and $value !~ /^[0-9a-f:]+$/;
     }
     return $value;
 }
@@ -2020,6 +2046,42 @@
 	$ip = $1;
     }
     if (($use ne 'ip') && (define($ip,'') eq '0.0.0.0')) {
+	$ip = undef;
+    }
+
+    debug("get_ip: using %s, %s reports %s", $use, $arg, define($ip, "<undefined>"));
+    return $ip;
+}
+
+######################################################################
+## get_ipv6
+######################################################################
+sub get_ipv6 {
+    my $use = lc shift;
+    my $h = shift;
+    my ($ip, $arg, $reply, $url, $skip) = (undef, opt($use), '');
+    $arg = '' unless $arg;
+
+    if ($use eq 'ip') {
+	$ip  = opt('ip', $h);
+	$arg = 'ip';
+
+    } elsif ($use eq 'if') {
+	$skip  = opt('if-skip', $h)  || '';
+	$reply = `ip -6 addr list dev $arg | grep "scope.global" 2> /dev/null`;
+	$reply = '' if $?;
+    }
+    if (!defined $reply) {
+	$reply = '';
+    }
+    if ($skip) {
+	$skip  =~ s/ /\\s/is;
+    	$reply =~ s/^.*?${skip}//is;
+    }
+    if ($reply =~ /.*? ([0-9:][^\/]*)/i) {
+	$ip = $1;
+    }
+    if (($use ne 'ip') && (define($ip,'') eq '0.0.0.0')) {
 	$ip = undef;
     }
 

Reply via email to