|
From: <abe...@us...> - 2013-09-20 22:57:23
|
Revision: 6199
http://sourceforge.net/p/astlinux/code/6199
Author: abelbeck
Date: 2013-09-20 22:57:20 +0000 (Fri, 20 Sep 2013)
Log Message:
-----------
dynamicdns, add "ddclient" support along with the old "inadyn" client. DDCLIENT="ddclient" enables it.
Modified Paths:
--------------
branches/1.0/package/inadyn/inadyn.mk
branches/1.0/project/astlinux/target_skeleton/stat/etc/rc.conf
Added Paths:
-----------
branches/1.0/package/inadyn/ddclient/
branches/1.0/package/inadyn/ddclient/ddclient.conf
branches/1.0/package/inadyn/ddclient/ddclient.pl
branches/1.0/package/inadyn/dynamicdns.init
Removed Paths:
-------------
branches/1.0/package/inadyn/inadyn.init
Added: branches/1.0/package/inadyn/ddclient/ddclient.conf
===================================================================
--- branches/1.0/package/inadyn/ddclient/ddclient.conf (rev 0)
+++ branches/1.0/package/inadyn/ddclient/ddclient.conf 2013-09-20 22:57:20 UTC (rev 6199)
@@ -0,0 +1,60 @@
+daemon=@DELAY@
+@USE_WEB@use=web, web=@WEB_STR@
+@USE_IF@use=if, if=@EXTIF@
+#@zoneedit@>
+#@zoneedit@>## ZoneEdit
+#@zoneedit@>server=dynamic.zoneedit.com
+#@zoneedit@>protocol=zoneedit1
+#@zoneedit@>login=@DDUSER@
+#@zoneedit@>password=@DDPASS@
+#@zoneedit@>@DDHOST@
+#@dyndns@>
+#@dyndns@>## dyndns.org dynamic addresses
+#@dyndns@>server=members.dyndns.org
+#@dyndns@>protocol=dyndns2
+#@dyndns@>login=@DDUSER@
+#@dyndns@>password=@DDPASS@
+#@dyndns@>@DDHOST@
+#@dyndns-static@>
+#@dyndns-static@>## dyndns.org static addresses
+#@dyndns-static@>server=members.dyndns.org
+#@dyndns-static@>protocol=dyndns2
+#@dyndns-static@>login=@DDUSER@
+#@dyndns-static@>password=@DDPASS@
+#@dyndns-static@>static=yes, @DDHOST@
+#@dyndns-custom@>
+#@dyndns-custom@>## dyndns.org custom addresses
+#@dyndns-custom@>server=members.dyndns.org
+#@dyndns-custom@>protocol=dyndns2
+#@dyndns-custom@>login=@DDUSER@
+#@dyndns-custom@>password=@DDPASS@
+#@dyndns-custom@>custom=yes, @DDHOST@
+#@no-ip@>
+#@no-ip@>## No-IP
+#@no-ip@>server=dynupdate.no-ip.com
+#@no-ip@>protocol=noip
+#@no-ip@>login=@DDUSER@
+#@no-ip@>password=@DDPASS@
+#@no-ip@>@DDHOST@
+#@freedns@>
+#@freedns@>## FreeDNS
+#@freedns@>server=freedns.afraid.org
+#@freedns@>protocol=freedns
+#@freedns@>login=@DDUSER@
+#@freedns@>password=@DDPASS@
+#@freedns@>@DDHOST@
+#@dnsomatic@>
+#@dnsomatic@>## DNS-O-MATIC
+#@dnsomatic@>server=updates.dnsomatic.com
+#@dnsomatic@>protocol=dyndns2
+#@dnsomatic@>login=@DDUSER@
+#@dnsomatic@>password=@DDPASS@
+#@dnsomatic@>@DDHOST@
+#@pairnic@>
+#@pairnic@>## pairNIC
+#@pairnic@>server=dynamic.pairnic.com
+#@pairnic@>protocol=dyndns2
+#@pairnic@>login=@DDUSER@
+#@pairnic@>password=@DDPASS@
+#@pairnic@>@DDHOST@
+
Added: branches/1.0/package/inadyn/ddclient/ddclient.pl
===================================================================
--- branches/1.0/package/inadyn/ddclient/ddclient.pl (rev 0)
+++ branches/1.0/package/inadyn/ddclient/ddclient.pl 2013-09-20 22:57:20 UTC (rev 6199)
@@ -0,0 +1,3765 @@
+#!/usr/bin/perl -w
+######################################################################
+# $Id: ddclient 153 2013-07-08 13:20:35Z wimpunk $
+#
+# DDCLIENT - a Perl client for updating DynDNS information
+#
+# Author: Paul Burry (pau...@bu...)
+# ddclient-developers: see https://sourceforge.net/project/memberlist.php?group_id=116817
+#
+# website: http://ddclient.sf.net
+#
+# Support for multiple IP numbers added by
+# Astaro AG, Ingo Schwarze <ischwarze-OOs/4mk...@pu...> September 16, 2008
+#
+######################################################################
+require 5.004;
+use strict;
+use Getopt::Long;
+use Sys::Hostname;
+use IO::Socket;
+
+my ($VERSION) = q$Revision: 153 $ =~ /(\d+)/;
+
+my $version = "3.8.0-r". $VERSION;
+my $programd = $0;
+$programd =~ s%^.*/%%;
+my $program = $programd;
+$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 $msgs = '';
+my $last_msgs = '';
+
+use vars qw($file $lineno);
+local $file = '';
+local $lineno = '';
+
+$ENV{'PATH'} = (exists($ENV{PATH}) ? "$ENV{PATH}:" : "") . "/sbin:/usr/sbin:/bin:/usr/bin:/etc:/usr/lib:";
+
+sub T_ANY {'any'};
+sub T_STRING {'string'};
+sub T_EMAIL {'e-mail address'};
+sub T_NUMBER {'number'};
+sub T_DELAY {'time delay (ie. 1d, 1hour, 1m)'};
+sub T_LOGIN {'login'};
+sub T_PASSWD {'password'};
+sub T_BOOL {'boolean value'};
+sub T_FQDN {'fully qualified host name'};
+sub T_OFQDN {'optional fully qualified host name'};
+sub T_FILE {'file name'};
+sub T_FQDNP {'fully qualified host name and optional port number'};
+sub T_PROTO {'protocol'}
+sub T_USE {'ip strategy'}
+sub T_IF {'interface'}
+sub T_PROG {'program name'}
+sub T_IP {'ip'}
+sub T_POSTS {'postscript'};
+
+## strategies for obtaining an ip address.
+my %builtinweb = (
+ 'dyndns' => { 'url' => 'http://checkip.dyndns.org/', 'skip' =>
+ 'Current IP Address:', },
+ 'dnspark' => { 'url' => 'http://ipdetect.dnspark.com/', 'skip' => 'Current Address:', },
+ 'loopia' => { 'url' => 'http://dns.loopia.se/checkip/checkip.php', 'skip' => 'Current Address:', },
+);
+my %builtinfw = (
+ 'watchguard-soho' => {
+ 'name' => 'Watchguard SOHO FW',
+ 'url' => '/pubnet.htm',
+ 'skip' => 'NAME=IPAddress VALUE=',
+ },
+ 'netopia-r910' => {
+ 'name' => 'Netopia R910 FW',
+ 'url' => '/WanEvtLog',
+ 'skip' => 'local:',
+ },
+ 'smc-barricade' => {
+ 'name' => 'SMC Barricade FW',
+ 'url' => '/status.htm',
+ 'skip' => 'IP Address',
+ },
+ 'smc-barricade-alt' => {
+ 'name' => 'SMC Barricade FW (alternate config)',
+ 'url' => '/status.HTM',
+ 'skip' => 'WAN IP',
+ },
+ 'smc-barricade-7401bra' => {
+ 'name' => 'SMC Barricade 7401BRA FW',
+ 'url' => '/admin/wan1.htm',
+ 'skip' => 'IP Address',
+ },
+ 'netgear-rt3xx' => {
+ 'name' => 'Netgear FW',
+ 'url' => '/mtenSysStatus.html',
+ 'skip' => 'IP Address',
+ },
+ 'elsa-lancom-dsl10' => {
+ 'name' => 'ELSA LanCom DSL/10 DSL FW',
+ 'url' => '/config/1/6/8/3/',
+ 'skip' => 'IP.Address',
+ },
+ 'elsa-lancom-dsl10-ch01' => {
+ 'name' => 'ELSA LanCom DSL/10 DSL FW (isdn ch01)',
+ 'url' => '/config/1/6/8/3/',
+ 'skip' => 'IP.Address.*?CH01',
+ },
+ 'elsa-lancom-dsl10-ch02' => {
+ 'name' => 'ELSA LanCom DSL/10 DSL FW (isdn ch01)',
+ 'url' => '/config/1/6/8/3/',
+ 'skip' => 'IP.Address.*?CH02',
+ },
+ 'linksys' => {
+ 'name' => 'Linksys FW',
+ 'url' => '/Status.htm',
+ 'skip' => 'WAN.*?Address',
+ },
+ 'linksys-ver2' => {
+ 'name' => 'Linksys FW version 2',
+ 'url' => '/RouterStatus.htm',
+ 'skip' => 'WAN.*?Address',
+ },
+ 'linksys-ver3' => {
+ 'name' => 'Linksys FW version 3',
+ 'url' => '/Status_Router.htm',
+ 'skip' => 'WAN.*?Address',
+ },
+ 'linksys-wrt854g' => {
+ 'name' => 'Linksys WRT854G FW',
+ 'url' => '/Status_Router.asp',
+ 'skip' => 'IP Address:',
+ },
+ 'maxgate-ugate3x00' => {
+ 'name' => 'MaxGate UGATE-3x00 FW',
+ 'url' => '/Status.htm',
+ 'skip' => 'WAN.*?IP Address',
+ },
+ 'netcomm-nb3' => {
+ 'name' => 'NetComm NB3',
+ 'url' => '/MainPage?id=6',
+ 'skip' => 'ppp-0',
+ },
+ '3com-3c886a' => {
+ 'name' => '3com 3c886a 56k Lan Modem',
+ 'url' => '/stat3.htm',
+ 'skip' => 'IP address in use',
+ },
+ 'sohoware-nbg800' => {
+ 'name' => 'SOHOWare BroadGuard NBG800',
+ 'url' => '/status.htm',
+ 'skip' => 'Internet IP',
+ },
+ 'xsense-aero' => {
+ 'name' => 'Xsense Aero',
+ 'url' => '/A_SysInfo.htm',
+ 'skip' => 'WAN.*?IP Address',
+ },
+ 'alcatel-stp' => {
+ 'name' => 'Alcatel Speed Touch Pro',
+ 'url' => '/cgi/router/',
+ 'skip' => 'Brt',
+ },
+ 'alcatel-510' => {
+ 'name' => 'Alcatel Speed Touch 510',
+ 'url' => '/cgi/ip/',
+ 'skip' => 'ppp',
+ },
+ 'allnet-1298' => {
+ 'name' => 'Allnet 1298',
+ 'url' => '/cgi/router/',
+ 'skip' => 'WAN',
+ },
+ '3com-oc-remote812' => {
+ 'name' => '3com OfficeConnect Remote 812',
+ 'url' => '/callEvent',
+ 'skip' => '.*LOCAL',
+ },
+ 'e-tech' => {
+ 'name' => 'E-tech Router',
+ 'url' => '/Status.htm',
+ 'skip' => 'Public IP Address',
+ },
+ 'cayman-3220h' => {
+ 'name' => 'Cayman 3220-H DSL',
+ 'url' => '/shell/show+ip+interfaces',
+ 'skip' => '.*inet',
+ },
+ 'vigor-2200usb' => {
+ 'name' => 'Vigor 2200 USB',
+ 'url' => '/doc/online.sht',
+ 'skip' => 'PPPoA',
+ },
+ 'dlink-614' => {
+ 'name' => 'D-Link DI-614+',
+ 'url' => '/st_devic.html',
+ 'skip' => 'WAN',
+ },
+ 'dlink-604' => {
+ 'name' => 'D-Link DI-604',
+ 'url' => '/st_devic.html',
+ 'skip' => 'WAN.*?IP.*Address',
+ },
+ 'olitec-SX200' => {
+ 'name' => 'olitec-SX200',
+ 'url' => '/doc/wan.htm',
+ 'skip' => 'st_wan_ip[0] = "',
+ },
+ 'westell-6100' => {
+ 'name' => 'Westell C90-610015-06 DSL Router',
+ 'url' => '/advstat.htm',
+ 'skip' => 'IP.+?Address',
+ },
+ '2wire' => {
+ 'name' => '2Wire 1701HG Gateway',
+ 'url' => '/xslt?PAGE=B01',
+ 'skip' => 'Internet Address:',
+ },
+ 'linksys-rv042-wan1' => {
+ 'name' => 'Linksys RV042 Dual Homed Router WAN Port 2',
+ 'url' => '/home.htm',
+ 'skip' => 'WAN1 IP',
+ },
+ 'linksys-rv042-wan2' => {
+ 'name' => 'Linksys RV042 Dual Homed Router WAN Port 2',
+ 'url' => '/home.htm',
+ 'skip' => 'WAN2 IP',
+ },
+ 'netgear-rp614' => {
+ 'name' => 'Netgear RP614 FW',
+ 'url' => '/sysstatus.html',
+ 'skip' => 'IP Address',
+ },
+ 'watchguard-edge-x' => {
+ 'name' => 'Watchguard Edge X FW',
+ 'url' => '/netstat.htm',
+ 'skip' => 'inet addr:',
+ },
+ 'dlink-524' => {
+ 'name' => 'D-Link DI-524',
+ 'url' => '/st_device.html',
+ 'skip' => 'WAN.*?Addres',
+ },
+ 'rtp300' => {
+ 'name' => 'Linksys RTP300',
+ 'url' => '/cgi-bin/webcm?getpage=%2Fusr%2Fwww_safe%2Fhtml%2Fstatus%2FRouter.html',
+ 'skip' => 'Internet.*?IP Address',
+ },
+ 'netgear-wpn824' => {
+ 'name' => 'Netgear WPN824 FW',
+ 'url' => '/RST_status.htm',
+ 'skip' => 'IP Address',
+ },
+ 'linksys-wcg200' => {
+ 'name' => 'Linksys WCG200 FW',
+ 'url' => '/RgStatus.asp',
+ 'skip' => 'WAN.IP.*?Address',
+ },
+ 'netgear-dg834g' => {
+ 'name' => 'netgear-dg834g',
+ 'url' => '/setup.cgi?next_file=s_status.htm&todo=cfg_init',
+ 'skip' => '',
+ },
+ 'netgear-wgt624' => {
+ 'name' => 'Netgear WGT624',
+ 'url' => '/RST_st_dhcp.htm',
+ 'skip' => 'IP Address</B></td><TD NOWRAP width="50%">',
+ },
+ 'sveasoft' => {
+ 'name' => 'Sveasoft WRT54G/WRT54GS',
+ 'url' => '/Status_Router.asp',
+ 'skip' => 'var wan_ip',
+ },
+ 'smc-barricade-7004vbr' => {
+ 'name' => 'SMC Barricade FW (7004VBR model config)',
+ 'url' => '/status_main.stm',
+ 'skip' => 'var wan_ip=',
+ },
+ 'sitecom-dc202' => {
+ 'name' => 'Sitecom DC-202 FW',
+ 'url' => '/status.htm',
+ 'skip' => 'Internet IP Address',
+ },
+);
+my %ip_strategies = (
+ 'ip' => ": obtain IP from -ip {address}",
+ 'web' => ": obtain IP from an IP discovery page on the web",
+ 'fw' => ": obtain IP from the firewall specified by -fw {type|address}",
+ 'if' => ": obtain IP from the -if {interface}",
+ 'cmd' => ": obtain IP from the -cmd {external-command}",
+ 'cisco' => ": obtain IP from Cisco FW at the -fw {address}",
+ 'cisco-asa' => ": obtain IP from Cisco ASA at the -fw {address}",
+ map { $_ => sprintf ": obtain IP from %s at the -fw {address}", $builtinfw{$_}->{'name'} } keys %builtinfw,
+);
+sub ip_strategies_usage {
+ return map { sprintf(" -use=%-22s %s.", $_, $ip_strategies{$_}) } sort keys %ip_strategies;
+}
+
+my %web_strategies = (
+ 'dyndns'=> 1,
+ 'dnspark'=> 1,
+ 'loopia'=> 1,
+);
+
+sub setv {
+ return {
+ 'type' => shift,
+ 'required' => shift,
+ 'cache' => shift,
+ 'config' => shift,
+ 'default' => shift,
+ 'minimum' => shift,
+ };
+};
+my %variables = (
+ 'global-defaults' => {
+ 'daemon' => setv(T_DELAY, 0, 0, 1, 0, interval('60s')),
+ 'foreground' => setv(T_BOOL, 0, 0, 1, 0, undef),
+ 'file' => setv(T_FILE, 0, 0, 1, "$etc$program.conf", undef),
+ 'cache' => setv(T_FILE, 0, 0, 1, "$cachedir$program.cache", undef),
+ 'pid' => setv(T_FILE, 0, 0, 1, "", undef),
+ 'proxy' => setv(T_FQDNP, 0, 0, 1, '', undef),
+ 'protocol' => setv(T_PROTO, 0, 0, 1, 'dyndns2', undef),
+
+ 'use' => setv(T_USE, 0, 0, 1, 'ip', 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),
+ 'web' => setv(T_STRING,0, 0, 1, 'dyndns', undef),
+ 'web-skip' => setv(T_STRING,1, 0, 1, '', undef),
+ 'fw' => setv(T_ANY, 0, 0, 1, '', undef),
+ 'fw-skip' => setv(T_STRING,1, 0, 1, '', undef),
+ 'fw-login' => setv(T_LOGIN, 1, 0, 1, '', undef),
+ 'fw-password' => setv(T_PASSWD,1, 0, 1, '', undef),
+ 'cmd' => setv(T_PROG, 0, 0, 1, '', undef),
+ 'cmd-skip' => setv(T_STRING,1, 0, 1, '', undef),
+
+ 'timeout' => setv(T_DELAY, 0, 0, 1, interval('120s'), interval('120s')),
+ 'retry' => setv(T_BOOL, 0, 0, 0, 0, undef),
+ 'force' => setv(T_BOOL, 0, 0, 0, 0, undef),
+ 'ssl' => setv(T_BOOL, 0, 0, 0, 0, undef),
+
+ 'syslog' => setv(T_BOOL, 0, 0, 1, 0, undef),
+ 'facility' => setv(T_STRING,0, 0, 1, 'daemon', undef),
+ 'priority' => setv(T_STRING,0, 0, 1, 'notice', undef),
+ 'mail' => setv(T_EMAIL, 0, 0, 1, '', undef),
+ 'mail-failure' => setv(T_EMAIL, 0, 0, 1, '', undef),
+
+ 'exec' => setv(T_BOOL, 0, 0, 1, 1, undef),
+ 'debug' => setv(T_BOOL, 0, 0, 1, 0, undef),
+ 'verbose' => setv(T_BOOL, 0, 0, 1, 0, undef),
+ 'quiet' => setv(T_BOOL, 0, 0, 1, 0, undef),
+ 'help' => setv(T_BOOL, 0, 0, 1, 0, undef),
+ 'test' => setv(T_BOOL, 0, 0, 1, 0, undef),
+ 'geturl' => setv(T_STRING,0, 0, 0, '', undef),
+
+ 'postscript' => setv(T_POSTS, 0, 0, 1, '', undef),
+ },
+ 'service-common-defaults' => {
+ 'server' => setv(T_FQDNP, 1, 0, 1, 'members.dyndns.org', undef),
+ 'login' => setv(T_LOGIN, 1, 0, 1, '', undef),
+ 'password' => setv(T_PASSWD, 1, 0, 1, '', undef),
+ 'host' => setv(T_STRING, 1, 1, 1, '', undef),
+
+ 'use' => setv(T_USE, 0, 0, 1, 'ip', 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),
+ 'web-skip' => setv(T_STRING,0, 0, 1, '', undef),
+ 'fw' => setv(T_ANY, 0, 0, 1, '', undef),
+ 'fw-skip' => setv(T_STRING,0, 0, 1, '', undef),
+ 'fw-login' => setv(T_LOGIN, 0, 0, 1, '', undef),
+ 'fw-password' => setv(T_PASSWD,0, 0, 1, '', undef),
+ 'cmd' => setv(T_PROG, 0, 0, 1, '', undef),
+ 'cmd-skip' => setv(T_STRING,0, 0, 1, '', undef),
+
+ 'ip' => setv(T_IP, 0, 1, 0, undef, undef),
+ 'wtime' => setv(T_DELAY, 0, 1, 1, 0, interval('30s')),
+ 'mtime' => setv(T_NUMBER, 0, 1, 0, 0, undef),
+ 'atime' => setv(T_NUMBER, 0, 1, 0, 0, undef),
+ 'status' => setv(T_ANY, 0, 1, 0, '', undef),
+ 'min-interval' => setv(T_DELAY, 0, 0, 1, interval('30s'), 0),
+ 'max-interval' => setv(T_DELAY, 0, 0, 1, interval('25d'), 0),
+ 'min-error-interval' => setv(T_DELAY, 0, 0, 1, interval('5m'), 0),
+
+ 'warned-min-interval' => setv(T_ANY, 0, 1, 0, 0, undef),
+ 'warned-min-error-interval' => setv(T_ANY, 0, 1, 0, 0, undef),
+ },
+ 'dyndns-common-defaults' => {
+ '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),
+ },
+ 'easydns-common-defaults' => {
+ '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),
+ },
+ 'dnspark-common-defaults' => {
+ 'mx' => setv(T_OFQDN, 0, 1, 1, '', undef),
+ 'mxpri' => setv(T_NUMBER, 0, 0, 1, 5, undef),
+ },
+ 'noip-common-defaults' => {
+ 'static' => setv(T_BOOL, 0, 1, 1, 0, undef),
+ },
+ 'noip-service-common-defaults' => {
+ 'server' => setv(T_FQDNP, 1, 0, 1, 'dynupdate.no-ip.com', undef),
+ 'login' => setv(T_LOGIN, 1, 0, 1, '', undef),
+ 'password' => setv(T_PASSWD, 1, 0, 1, '', undef),
+ 'host' => setv(T_STRING, 1, 1, 1, '', undef),
+ 'ip' => setv(T_IP, 0, 1, 0, undef, undef),
+ 'wtime' => setv(T_DELAY, 0, 1, 1, 0, interval('30s')),
+ 'mtime' => setv(T_NUMBER, 0, 1, 0, 0, undef),
+ 'atime' => setv(T_NUMBER, 0, 1, 0, 0, undef),
+ 'status' => setv(T_ANY, 0, 1, 0, '', undef),
+ 'min-interval' => setv(T_DELAY, 0, 0, 1, interval('30s'), 0),
+ 'max-interval' => setv(T_DELAY, 0, 0, 1, interval('25d'), 0),
+ 'min-error-interval' => setv(T_DELAY, 0, 0, 1, interval('5m'), 0),
+ 'warned-min-interval' => setv(T_ANY, 0, 1, 0, 0, undef),
+ 'warned-min-error-interval' => setv(T_ANY, 0, 1, 0, 0, undef),
+ },
+ 'zoneedit-service-common-defaults' => {
+ 'zone' => setv(T_OFQDN, 0, 0, 1, undef, undef),
+ },
+ 'dtdns-common-defaults' => {
+ 'login' => setv(T_LOGIN, 0, 0, 0, 'unused', undef),
+ 'client' => setv(T_STRING, 0, 1, 1, $program, undef),
+ },
+);
+my %services = (
+ 'dyndns1' => {
+ 'updateable' => \&nic_dyndns2_updateable,
+ 'update' => \&nic_dyndns1_update,
+ 'examples' => \&nic_dyndns1_examples,
+ 'variables' => merge(
+ $variables{'dyndns-common-defaults'},
+ $variables{'service-common-defaults'},
+ ),
+ },
+ 'dyndns2' => {
+ 'updateable' => \&nic_dyndns2_updateable,
+ 'update' => \&nic_dyndns2_update,
+ 'examples' => \&nic_dyndns2_examples,
+ 'variables' => merge(
+ { 'custom' => setv(T_BOOL, 0, 1, 1, 0, undef), },
+ { 'script' => setv(T_STRING, 1, 1, 1, '/nic/update', undef), },
+# { 'offline' => setv(T_BOOL, 0, 1, 1, 0, undef), },
+ $variables{'dyndns-common-defaults'},
+ $variables{'service-common-defaults'},
+ ),
+ },
+ 'noip' => {
+ 'updateable' => undef,
+ 'update' => \&nic_noip_update,
+ 'examples' => \&nic_noip_examples,
+ 'variables' => merge(
+ { 'custom' => setv(T_BOOL, 0, 1, 1, 0, undef), },
+ $variables{'noip-common-defaults'},
+ $variables{'noip-service-common-defaults'},
+ ),
+ },
+ 'concont' => {
+ 'updateable' => undef,
+ 'update' => \&nic_concont_update,
+ 'examples' => \&nic_concont_examples,
+ 'variables' => merge(
+ $variables{'service-common-defaults'},
+ { 'mx' => setv(T_OFQDN, 0, 1, 1, '', undef), },
+ { 'wildcard' => setv(T_BOOL, 0, 1, 1, 0, undef), },
+ ),
+ },
+ 'dslreports1' => {
+ 'updateable' => undef,
+ 'update' => \&nic_dslreports1_update,
+ 'examples' => \&nic_dslreports1_examples,
+ 'variables' => merge(
+ { 'host' => setv(T_NUMBER, 1, 1, 1, 0, undef) },
+ $variables{'service-common-defaults'},
+ ),
+ },
+ 'hammernode1' => {
+ 'updateable' => undef,
+ 'update' => \&nic_hammernode1_update,
+ 'examples' => \&nic_hammernode1_examples,
+ 'variables' => merge(
+ { 'server' => setv(T_FQDNP, 1, 0, 1, 'dup.hn.org', undef) },
+ { 'min-interval' => setv(T_DELAY, 0, 0, 1, interval('5m'), 0),},
+ $variables{'service-common-defaults'},
+ ),
+ },
+ 'zoneedit1' => {
+ 'updateable' => undef,
+ 'update' => \&nic_zoneedit1_update,
+ 'examples' => \&nic_zoneedit1_examples,
+ 'variables' => merge(
+ { 'server' => setv(T_FQDNP, 1, 0, 1, 'dynamic.zoneedit.com', undef) },
+ { 'min-interval' => setv(T_DELAY, 0, 0, 1, interval('5m'), 0),},
+ $variables{'service-common-defaults'},
+ $variables{'zoneedit-service-common-defaults'},
+ ),
+ },
+ 'easydns' => {
+ 'updateable' => undef,
+ 'update' => \&nic_easydns_update,
+ 'examples' => \&nic_easydns_examples,
+ 'variables' => merge(
+ { 'server' => setv(T_FQDNP, 1, 0, 1, 'members.easydns.com', undef) },
+ { 'min-interval' => setv(T_DELAY, 0, 0, 1, interval('5m'), 0),},
+ $variables{'easydns-common-defaults'},
+ $variables{'service-common-defaults'},
+ ),
+ },
+ 'dnspark' => {
+ 'updateable' => undef,
+ 'update' => \&nic_dnspark_update,
+ 'examples' => \&nic_dnspark_examples,
+ 'variables' => merge(
+ { 'server' => setv(T_FQDNP, 1, 0, 1, 'www.dnspark.com', undef) },
+ { 'min-interval' => setv(T_DELAY, 0, 0, 1, interval('5m'), 0),},
+ $variables{'dnspark-common-defaults'},
+ $variables{'service-common-defaults'},
+ ),
+ },
+ 'namecheap' => {
+ 'updateable' => undef,
+ 'update' => \&nic_namecheap_update,
+ 'examples' => \&nic_namecheap_examples,
+ 'variables' => merge(
+ { 'server' => setv(T_FQDNP, 1, 0, 1, 'dynamicdns.park-your-domain.com', undef) },
+ { 'min-interval' => setv(T_DELAY, 0, 0, 1, 0, interval('5m')),},
+ $variables{'service-common-defaults'},
+ ),
+ },
+ 'sitelutions' => {
+ 'updateable' => undef,
+ 'update' => \&nic_sitelutions_update,
+ 'examples' => \&nic_sitelutions_examples,
+ 'variables' => merge(
+ { 'server' => setv(T_FQDNP, 1, 0, 1, 'www.sitelutions.com', undef) },
+ { 'min-interval' => setv(T_DELAY, 0, 0, 1, 0, interval('5m')),},
+ $variables{'service-common-defaults'},
+ ),
+ },
+ 'freedns' => {
+ 'updateable' => undef,
+ 'update' => \&nic_freedns_update,
+ 'examples' => \&nic_freedns_examples,
+ 'variables' => merge(
+ { 'server' => setv(T_FQDNP, 1, 0, 1, 'freedns.afraid.org', undef) },
+ { 'min-interval' => setv(T_DELAY, 0, 0, 1, 0, interval('5m')),},
+ $variables{'service-common-defaults'},
+ ),
+ },
+ 'changeip' => {
+ 'updateable' => undef,
+ 'update' => \&nic_changeip_update,
+ 'examples' => \&nic_changeip_examples,
+ 'variables' => merge(
+ { 'server' => setv(T_FQDNP, 1, 0, 1, 'nic.changeip.com', undef) },
+ { 'min-interval' => setv(T_DELAY, 0, 0, 1, 0, interval('5m')),},
+ $variables{'service-common-defaults'},
+ ),
+ },
+ 'dtdns' => {
+ 'updateable' => undef,
+ 'update' => \&nic_dtdns_update,
+ 'examples' => \&nic_dtdns_examples,
+ 'variables' => merge(
+ $variables{'dtdns-common-defaults'},
+ $variables{'service-common-defaults'},
+ ),
+ },
+);
+$variables{'merged'} = merge($variables{'global-defaults'},
+ $variables{'service-common-defaults'},
+ $variables{'dyndns-common-defaults'},
+ map { $services{$_}{'variables'} } keys %services,
+);
+
+my @opt = (
+ "usage: ${program} [options]",
+ "options are:",
+ [ "daemon", "=s", "-daemon delay : run as a daemon, specify delay as an interval." ],
++ [ "foreground", "!", "-foreground : do not fork" ],
+ [ "proxy", "=s", "-proxy host : use 'host' as the HTTP proxy" ],
+ [ "server", "=s", "-server host : update DNS information on 'host'" ],
+ [ "protocol", "=s", "-protocol type : update protocol used" ],
+ [ "file", "=s", "-file path : load configuration information from 'path'" ],
+ [ "cache", "=s", "-cache path : record address used in 'path'" ],
+ [ "pid", "=s", "-pid path : record process id in 'path'" ],
+ "",
+ [ "use", "=s", "-use which : how the should IP address be obtained." ],
+ &ip_strategies_usage(),
+ "",
+ [ "ip", "=s", "-ip address : set the IP address to 'address'" ],
+ "",
+ [ "if", "=s", "-if interface : obtain IP address from 'interface'" ],
+ [ "if-skip", "=s", "-if-skip pattern : skip any IP addresses before 'pattern' in the output of ifconfig {if}" ],
+ "",
+ [ "web", "=s", "-web provider|url : obtain IP address from provider's IP checking page" ],
+ [ "web-skip", "=s", "-web-skip pattern : skip any IP addresses before 'pattern' on the web provider|url" ],
+ "",
+ [ "fw", "=s", "-fw address|url : obtain IP address from firewall at 'address'" ],
+ [ "fw-skip", "=s", "-fw-skip pattern : skip any IP addresses before 'pattern' on the firewall address|url" ],
+ [ "fw-login", "=s", "-fw-login login : use 'login' when getting IP from fw" ],
+ [ "fw-password", "=s", "-fw-password secret : use password 'secret' when getting IP from fw" ],
+ "",
+ [ "cmd", "=s", "-cmd program : obtain IP address from by calling {program}" ],
+ [ "cmd-skip", "=s", "-cmd-skip pattern : skip any IP addresses before 'pattern' in the output of {cmd}" ],
+ "",
+ [ "login", "=s", "-login user : login as 'user'" ],
+ [ "password", "=s", "-password secret : use password 'secret'" ],
+ [ "host", "=s", "-host host : update DNS information for 'host'" ],
+ "",
+ [ "options", "=s", "-options opt,opt : optional per-service arguments (see below)" ],
+ "",
+ [ "ssl", "!", "-{no}ssl : do updates over encrypted SSL connection" ],
+ [ "retry", "!", "-{no}retry : retry failed updates." ],
+ [ "force", "!", "-{no}force : force an update even if the update may be unnecessary" ],
+ [ "timeout", "=i", "-timeout max : wait at most 'max' seconds for the host to respond" ],
+
+ [ "syslog", "!", "-{no}syslog : log messages to syslog" ],
+ [ "facility", "=s", "-facility {type} : log messages to syslog to facility {type}" ],
+ [ "priority", "=s", "-priority {pri} : log messages to syslog with priority {pri}" ],
+ [ "mail", "=s", "-mail address : e-mail messages to {address}" ],
+ [ "mail-failure","=s", "-mail-failure address : e-mail messages for failed updates to {address}" ],
+ [ "exec", "!", "-{no}exec : do {not} execute; just show what would be done" ],
+ [ "debug", "!", "-{no}debug : print {no} debugging information" ],
+ [ "verbose", "!", "-{no}verbose : print {no} verbose information" ],
+ [ "quiet", "!", "-{no}quiet : print {no} messages for unnecessary updates" ],
+ [ "help", "", "-help : this message" ],
+ [ "postscript", "", "-postscript : script to run after updating ddclient, has new IP as param" ],
+
+ [ "query", "!", "-{no}query : print {no} ip addresses and exit" ],
+ [ "test", "!", "" ], ## hidden
+ [ "geturl", "=s", "" ], ## hidden
+ "",
+ nic_examples(),
+ "$program version $version, ",
+ " originally written by Paul Burry, paul+ddclient\@burry.ca",
+ " project now maintained on http://ddclient.sourceforge.net"
+);
+
+## process args
+my ($opt_usage, %opt) = process_args(@opt);
+my ($result, %config, %globals, %cache);
+my $saved_cache = '';
+my %saved_opt = %opt;
+$result = 'OK';
+
+test_geturl(opt('geturl')) if opt('geturl');
+
+## process help option
+if (opt('help')) {
+ *STDERR = *STDOUT;
+ usage(0);
+}
+
+## read config file because 'daemon' mode may be defined there.
+read_config(define($opt{'file'}, default('file')), \%config, \%globals);
+init_config();
+test_possible_ip() if opt('query');
+
+if (!opt('daemon') && $programd =~ /d$/) {
+ $opt{'daemon'} = minimum('daemon');
+}
+my $caught_hup = 0;
+my $caught_term = 0;
+my $caught_kill = 0;
+$SIG{'HUP'} = sub { $caught_hup = 1; };
+$SIG{'TERM'} = sub { $caught_term = 1; };
+$SIG{'KILL'} = sub { $caught_kill = 1; };
+# don't fork() if foreground or force is on
+if (opt('foreground') || opt('force')) {
+ ;
+} elsif (opt('daemon')) {
+ $SIG{'CHLD'} = 'IGNORE';
+ my $pid = fork;
+ if ($pid < 0) {
+ print STDERR "${program}: can not fork ($!)\n";
+ exit -1;
+ } elsif ($pid) {
+ exit 0;
+ }
+ $SIG{'CHLD'} = 'DEFAULT';
+ open(STDOUT, ">/dev/null");
+ open(STDERR, ">/dev/null");
+ open(STDIN, "</dev/null");
+}
+
+# write out the pid file if we're daemon'ized
+if(opt('daemon')) {
+ write_pid();
+ $opt{'syslog'} = 1;
+}
+
+umask 077;
+my $daemon;
+do {
+ $now = time;
+ $result = 'OK';
+ %opt = %saved_opt;
+ if (opt('help')) {
+ *STDERR = *STDOUT;
+ printf("Help found");
+ # usage();
+ }
+
+ read_config(define($opt{'file'}, default('file')), \%config, \%globals);
+ init_config();
+ read_cache(opt('cache'), \%cache);
+ print_info() if opt('debug') && opt('verbose');
+
+# usage("invalid argument '-use %s'; possible values are:\n\t%s", $opt{'use'}, join("\n\t,",sort keys %ip_strategies))
+ usage("invalid argument '-use %s'; possible values are:\n%s", $opt{'use'}, join("\n",ip_strategies_usage()))
+ unless exists $ip_strategies{lc opt('use')};
+
+ $daemon = $opt{'daemon'};
+ $daemon = 0 if opt('force');
+
+ update_nics();
+
+ if ($daemon) {
+ debug("sleep %s", $daemon);
+ sendmail();
+
+ my $left = $daemon;
+ while (($left > 0) && !$caught_hup && !$caught_term && !$caught_kill) {
+ my $delay = $left > 10 ? 10 : $left;
+
+ $0 = sprintf("%s - sleeping for %s seconds", $program, $left);
+ $left -= sleep $delay;
+ }
+ $caught_hup = 0;
+ $result = 0;
+
+ } elsif (! scalar(%config)) {
+ warning("no hosts to update.") unless !opt('quiet') || opt('verbose') || !$daemon;
+ $result = 1;
+
+ } else {
+ $result = $result eq 'OK' ? 0 : 1;
+ }
+} while ($daemon && !$result && !$caught_term && !$caught_kill);
+
+warning("caught SIGKILL; exiting") if $caught_kill;
+unlink_pid();
+sendmail();
+
+exit($result);
+
+######################################################################
+## runpostscript
+######################################################################
+
+sub runpostscript {
+ my ($ip) = @_;
+
+ if ( defined $globals{postscript} ) {
+ if ( -x $globals{postscript}) {
+ system ("$globals{postscript} $ip &");
+ } else {
+ warning ("Can not execute post script: %s", $globals{postscript});
+ }
+ }
+}
+
+######################################################################
+## update_nics
+######################################################################
+sub update_nics {
+ my %examined = ();
+ my %iplist = ();
+
+ foreach my $s (sort keys %services) {
+ my (@hosts, %ips) = ();
+ my $updateable = $services{$s}{'updateable'};
+ my $update = $services{$s}{'update'};
+
+ foreach my $h (sort keys %config) {
+ next if $config{$h}{'protocol'} ne lc($s);
+ $examined{$h} = 1;
+ # we only do this once per 'use' and argument combination
+ my $use = opt('use', $h);
+ my $arg_ip = opt('ip', $h) || '';
+ my $arg_fw = opt('fw', $h) || '';
+ my $arg_if = opt('if', $h) || '';
+ my $arg_web = opt('web', $h) || '';
+ my $arg_cmd = opt('cmd', $h) || '';
+ my $ip = "";
+ if (exists $iplist{$use}{$arg_ip}{$arg_fw}{$arg_if}{$arg_web}{$arg_cmd}) {
+ $ip = $iplist{$use}{$arg_ip}{$arg_fw}{$arg_if}{$arg_web}{$arg_cmd};
+ } else {
+ $ip = get_ip($use, $h);
+ 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}$/) {
+ warning("malformed IP address (%s)", $ip);
+ next;
+ }
+ $iplist{$use}{$arg_ip}{$arg_fw}{$arg_if}{$arg_web}{$arg_cmd} = $ip;
+ }
+ $config{$h}{'wantip'} = $ip;
+ next if !nic_updateable($h, $updateable);
+ push @hosts, $h;
+ $ips{$ip} = $h;
+ }
+ if (@hosts) {
+ $0 = sprintf("%s - updating %s", $program, join(',', @hosts));
+ &$update(@hosts);
+ runpostscript(join ' ', keys %ips);
+ }
+ }
+ foreach my $h (sort keys %config) {
+ if (!exists $examined{$h}) {
+ failed("%s was not updated because protocol %s is not supported.",
+ $h, define($config{$h}{'protocol'}, '<undefined>')
+ );
+ }
+ }
+ write_cache(opt('cache'));
+}
+######################################################################
+## unlink_pid()
+######################################################################
+sub unlink_pid {
+ if (opt('pid') && opt('daemon')) {
+ unlink opt('pid');
+ }
+}
+
+######################################################################
+## write_pid()
+######################################################################
+sub write_pid {
+ my $file = opt('pid');
+
+ if ($file && opt('daemon')) {
+ local *FD;
+ if (! open(FD, "> $file")) {
+ warning("Cannot create file '%s'. ($!)", $file);
+
+ } else {
+ printf FD "$$\n";
+ close(FD);
+ }
+ }
+}
+
+######################################################################
+## write_cache($file)
+######################################################################
+sub write_cache {
+ my ($file) = @_;
+
+ ## merge the updated host entries into the cache.
+ foreach my $h (keys %config) {
+ if (! exists $cache{$h} || $config{$h}{'update'}) {
+ map {$cache{$h}{$_} = $config{$h}{$_} } @{$config{$h}{'cacheable'}};
+
+ } else {
+ map {$cache{$h}{$_} = $config{$h}{$_} } qw(atime wtime status);
+ }
+ }
+
+ ## construct the cache file.
+ my $cache = "";
+ foreach my $h (sort keys %cache) {
+ my $opt = join(',', map { "$_=".define($cache{$h}{$_},'') } sort keys %{$cache{$h}});
+
+ $cache .= sprintf "%s%s%s\n", $opt, ($opt ? ' ' : ''), $h;
+ }
+ $file = '' if defined($saved_cache) && $cache eq $saved_cache;
+
+ ## write the updates and other entries to the cache file.
+ if ($file) {
+ $saved_cache = undef;
+ local *FD;
+ if (! open(FD, "> $file")) {
+ fatal("Cannot create file '%s'. ($!)", $file);
+ }
+ printf FD "## $program-$version\n";
+ printf FD "## last updated at %s (%d)\n", prettytime($now), $now;
+ printf FD $cache;
+
+ close(FD);
+ }
+}
+######################################################################
+## read_cache($file) - called before reading the .conf
+######################################################################
+sub read_cache {
+ my $file = shift;
+ my $config = shift;
+ my $globals = {};
+
+ %{$config} = ();
+ ## read the cache file ignoring anything on the command-line.
+ if (-e $file) {
+ my %saved = %opt;
+ %opt = ();
+ $saved_cache = _read_config($config, $globals, "##\\s*$program-$version\\s*", $file);
+ %opt = %saved;
+
+ foreach my $h (keys %cache) {
+ if (exists $config->{$h}) {
+ foreach (qw(atime mtime wtime ip status)) {
+ $config->{$h}{$_} = $cache{$h}{$_} if exists $cache{$h}{$_};
+ }
+ }
+ }
+ }
+}
+######################################################################
+## parse_assignments(string) return (rest, %variables)
+## parse_assignment(string) return (name, value, rest)
+######################################################################
+sub parse_assignments {
+ my $rest = shift;
+ my @args = @_;
+ my %variables = ();
+ my ($name, $value);
+
+ while (1) {
+ $rest =~ s/^\s+//;
+ ($name, $value, $rest) = parse_assignment($rest, @args);
+ if (defined $name) {
+ $variables{$name} = $value;
+ } else {
+ last;
+ }
+ }
+ return ($rest, %variables);
+}
+sub parse_assignment {
+ my $rest = shift;
+ my $stop = @_ ? shift : '[\n\s,]';
+ my ($c, $name, $value);
+ my ($escape, $quote) = (0, '');
+
+ if ($rest =~ /^\s*([a-z][a-z_-]*)=(.*)/i) {
+ ($name, $rest, $value) = ($1, $2, '');
+
+ while (length($c = substr($rest,0,1))) {
+ $rest = substr($rest,1);
+ if ($escape) {
+ $value .= $c;
+ $escape = 0;
+ } elsif ($c eq "\\") {
+ $escape = 1;
+ } elsif ($quote && $c eq $quote) {
+ $quote = ''
+ } elsif (!$quote && $c =~ /[\'\"]/) {
+ $quote = $c;
+ } elsif (!$quote && $c =~ /^${stop}/) {
+ last;
+ } else {
+ $value .= $c;
+ }
+ }
+ }
+ warning("assignment ended with an open quote") if $quote;
+ return ($name, $value, $rest);
+}
+######################################################################
+## read_config
+######################################################################
+sub read_config {
+ my $file = shift;
+ my $config = shift;
+ my $globals = shift;
+ my %globals = ();
+
+ _read_config($config, $globals, '', $file, %globals);
+}
+sub _read_config {
+ my $config = shift;
+ my $globals = shift;
+ my $stamp = shift;
+ local $file = shift;
+ my %globals = @_;
+ my %config = ();
+ my $content = '';
+
+ local *FD;
+ if (! open(FD, "< $file")) {
+ # fatal("Cannot open file '%s'. ($!)", $file);
+ warning("Cannot open file '%s'. ($!)", $file);
+ }
+ # Check for only owner has any access to config file
+ my ($dev, $ino, $mode, @statrest) = stat(FD);
+ if ($mode & 077) {
+ if (-f FD && (chmod 0600, $file)) {
+ warning("file $file must be accessible only by its owner (fixed).");
+ } else {
+ # fatal("file $file must be accessible only by its owner.");
+ warning("file $file must be accessible only by its owner.");
+ }
+ }
+
+ local $lineno = 0;
+ my $continuation = '';
+ my %passwords = ();
+ while (<FD>) {
+ s/[\r\n]//g;
+
+ $lineno++;
+
+ ## check for the program version stamp
+ if (($. == 1) && $stamp && ($_ !~ /^$stamp$/i)) {
+ warning("program version mismatch; ignoring %s", $file);
+ last;
+ }
+ if (/\\\s+$/) {
+ warning("whitespace follows the \\ at the end-of-line.\nIf you meant to have a line continuation, remove the trailing whitespace.");
+ }
+
+ $content .= "$_\n" unless /^#/;
+
+ ## parsing passwords is special
+ if (/^([^#]*\s)?([^#]*?password\S*?)\s*=\s*('.*'|[^']\S*)(.*)/) {
+ my ($head, $key, $value, $tail) = ($1 || '', $2, $3, $4);
+ $value = $1 if $value =~ /^'(.*)'$/;
+ $passwords{$key} = $value;
+ $_ = "${head}${key}=dummy${tail}";
+ }
+
+ ## remove comments
+ s/#.*//;
+
+ ## handle continuation lines
+ $_ = "$continuation$_";
+ if (/\\$/) {
+ chop;
+ $continuation = $_;
+ next;
+ }
+ $continuation = '';
+
+ s/^\s+//; # remove leading white space
+ s/\s+$//; # remove trailing white space
+ s/\s+/ /g; # canonify
+ next if /^$/;
+
+ ## expected configuration line is:
+ ## [opt=value,opt=..] [host [login [password]]]
+ my %locals;
+ ($_, %locals) = parse_assignments($_);
+ s/\s*,\s*/,/g;
+ my @args = split;
+
+ ## verify that keywords are valid...and check the value
+ foreach my $k (keys %locals) {
+ $locals{$k} = $passwords{$k} if defined $passwords{$k};
+ if (!exists $variables{'merged'}{$k}) {
+ warning("unrecognized keyword '%s' (ignored)", $k);
+ delete $locals{$k};
+ } else {
+ my $def = $variables{'merged'}{$k};
+ my $value = check_value($locals{$k}, $def);
+ if (!defined($value)) {
+ warning("Invalid Value for keyword '%s' = '%s'", $k, $locals{$k});
+ delete $locals{$k};
+ } else { $locals{$k} = $value; }
+ }
+ }
+ if (exists($locals{'host'})) {
+ $args[0] = @args ? "$args[0],$locals{host}" : "$locals{host}";
+ }
+ ## accumulate globals
+ if ($#args < 0) {
+ map { $globals{$_} = $locals{$_} } keys %locals;
+ }
+
+ ## process this host definition
+ if (@args) {
+ my ($host, $login, $password) = @args;
+
+ ## add in any globals..
+ %locals = %{ merge(\%locals, \%globals) };
+
+ ## override login and password if specified the old way.
+ $locals{'login'} = $login if defined $login;
+ $locals{'password'} = $password if defined $password;
+
+ ## allow {host} to be a comma separated list of hosts
+ foreach my $h (split_by_comma($host)) {
+ ## save a copy of the current globals
+ $config{$h} = { %locals };
+ $config{$h}{'host'} = $h;
+ }
+ }
+ %passwords = ();
+ }
+ close(FD);
+
+ warning("file ends while expecting a continuation line.")
+ if $continuation;
+
+ %$globals = %globals;
+ %$config = %config;
+
+ return $content;
+}
+######################################################################
+## init_config -
+######################################################################
+sub init_config {
+ %opt = %saved_opt;
+
+ ##
+ $opt{'quiet'} = 0 if opt('verbose');
+
+ ## infer the IP strategy if possible
+ $opt{'use'} = 'ip' if !define($opt{'use'}) && defined($opt{'ip'});
+ $opt{'use'} = 'if' if !define($opt{'use'}) && defined($opt{'if'});
+ $opt{'use'} = 'web' if !define($opt{'use'}) && 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')));
+ $opt{'min-error-interval'} = max(interval(opt('min-error-interval')), interval(default('min-error-interval')));
+
+ $opt{'timeout'} = 0 if opt('timeout') < 0;
+
+ ## only set $opt{'daemon'} if it has been explicitly passed in
+ if (define($opt{'daemon'},$globals{'daemon'},0)) {
+ $opt{'daemon'} = interval(opt('daemon'));
+ $opt{'daemon'} = minimum('daemon')
+ if ($opt{'daemon'} < minimum('daemon'));
+ }
+
+ ## define or modify host options specified on the command-line
+ if (exists $opt{'options'} && defined $opt{'options'}) {
+ ## collect cmdline configuration options.
+ my %options = ();
+ foreach my $opt (split_by_comma($opt{'options'})) {
+ my ($name,$var) = split /\s*=\s*/, $opt;
+ $options{$name} = $var;
+ }
+ ## determine hosts specified with -host
+ my @hosts = ();
+ if (exists $opt{'host'}) {
+ foreach my $h (split_by_comma($opt{'host'})) {
+ push @hosts, $h;
+ }
+ }
+ ## and those in -options=...
+ if (exists $options{'host'}) {
+ foreach my $h (split_by_comma($options{'host'})) {
+ push @hosts, $h;
+ }
+ delete $options{'host'};
+ }
+ ## merge options into host definitions or globals
+ if (@hosts) {
+ foreach my $h (@hosts) {
+ $config{$h} = merge(\%options, $config{$h});
+ }
+ $opt{'host'} = join(',', @hosts);
+ } else {
+ %globals = %{ merge(\%options, \%globals) };
+ }
+ }
+
+ ## override global options with those on the command-line.
+ foreach my $o (keys %opt) {
+ if (defined $opt{$o} && exists $variables{'global-defaults'}{$o}) {
+ $globals{$o} = $opt{$o};
+ }
+ }
+
+ ## sanity check
+ if (defined $opt{'host'} && defined $opt{'retry'}) {
+ usage("options -retry and -host (or -option host=..) are mutually exclusive");
+ }
+
+ ## determine hosts to update (those on the cmd-line, config-file, or failed cached)
+ my @hosts = keys %config;
+ if (opt('host')) {
+ @hosts = split_by_comma($opt{'host'});
+ }
+ if (opt('retry')) {
+ @hosts = map { $_ if $cache{$_}{'status'} ne 'good' } keys %cache;
+ }
+
+ ## remove any other hosts
+ my %hosts;
+ map { $hosts{$_} = undef } @hosts;
+ map { delete $config{$_} unless exists $hosts{$_} } keys %config;
+
+ ## collect the cacheable variables.
+ foreach my $proto (keys %services) {
+ my @cacheable = ();
+ foreach my $k (keys %{$services{$proto}{'variables'}}) {
+ push @cacheable, $k if $services{$proto}{'variables'}{$k}{'cache'};
+ }
+ $services{$proto}{'cacheable'} = [ @cacheable ];
+ }
+
+ ## sanity check..
+ ## make sure config entries have all defaults and they meet minimums
+ ## first the globals...
+ foreach my $k (keys %globals) {
+ my $def = $variables{'merged'}{$k};
+ my $ovalue = define($globals{$k}, $def->{'default'});
+ my $value = check_value($ovalue, $def);
+ if ($def->{'required'} && !defined $value) {
+ $value = default($k);
+ warning("'%s=%s' is an invalid %s. (using default of %s)", $k, $ovalue, $def->{'type'}, $value);
+ }
+ $globals{$k} = $value;
+ }
+
+ ## now the host definitions...
+ HOST:
+ foreach my $h (keys %config) {
+ my $proto;
+ $proto = $config{$h}{'protocol'};
+ $proto = opt('protocol') if !defined($proto);
+
+ load_sha1_support() if ($proto eq "freedns");
+
+ if (!exists($services{$proto})) {
+ warning("skipping host: %s: unrecognized protocol '%s'", $h, $proto);
+ delete $config{$h};
+
+ } else {
+ my $svars = $services{$proto}{'variables'};
+ my $conf = { 'protocol' => $proto };
+
+ foreach my $k (keys %$svars) {
+ my $def = $svars->{$k};
+ my $ovalue = define($config{$h}{$k}, $def->{'default'});
+ my $value = check_value($ovalue, $def);
+ if ($def->{'required'} && !defined $value) {
+ warning("skipping host: %s: '%s=%s' is an invalid %s.", $h, $k, $ovalue, $def->{'type'});
+ delete $config{$h};
+ next HOST;
+ }
+ $conf->{$k} = $value;
+
+ }
+ $config{$h} = $conf;
+ $config{$h}{'cacheable'} = [ @{$services{$proto}{'cacheable'}} ];
+ }
+ }
+}
+
+######################################################################
+## usage
+######################################################################
+sub usage {
+ my $exitcode = 1;
+ $exitcode = shift if @_ != 0; # use first arg if given
+ my $msg = '';
+ if (@_) {
+ my $format = shift;
+ $msg .= sprintf $format, @_;
+ 1 while chomp($msg);
+ $msg .= "\n";
+ }
+ printf STDERR "%s%s\n", $msg, $opt_usage;
+ sendmail();
+ exit $exitcode;
+}
+
+######################################################################
+## process_args -
+######################################################################
+sub process_args {
+ my @spec = ();
+ my $usage = "";
+ my %opts = ();
+
+ foreach (@_) {
+ if (ref $_) {
+ my ($key, $specifier, $arg_usage) = @$_;
+ my $value = default($key);
+
+ ## add a option specifier
+ push @spec, $key . $specifier;
+
+ ## define the default value which can be overwritten later
+ $opt{$key} = undef;
+
+ next unless $arg_usage;
+
+ ## add a line to the usage;
+ $usage .= " $arg_usage";
+ if (defined($value) && $value ne '') {
+ $usage .= " (default: ";
+ if ($specifier eq '!') {
+ $usage .= "no" if ($specifier eq '!') && !$value;
+ $usage .= $key;
+ } else {
+ $usage .= $value;
+ }
+ $usage .= ")";
+ }
+ $usage .= ".";
+ } else {
+ $usage .= $_;
+ }
+ $usage .= "\n";
+ }
+ ## process the arguments
+ if (! GetOptions(\%opt, @spec)) {
+ $opt{"help"} = 1;
+ }
+ return ($usage, %opt);
+}
+######################################################################
+## test_possible_ip - print possible IPs
+######################################################################
+sub test_possible_ip {
+ local $opt{'debug'} = 0;
+
+ printf "use=ip, ip=%s address is %s\n", opt('ip'), define(get_ip('ip'), 'NOT FOUND')
+ if defined opt('ip');
+
+ {
+ local $opt{'use'} = 'if';
+ foreach my $if (grep {/^[a-zA-Z]/} `ifconfig -a`) {
+ $if =~ s/:?\s.*//is;
+ local $opt{'if'} = $if;
+ printf "use=if, if=%s address is %s\n", opt('if'), define(get_ip('if'), 'NOT FOUND');
+ }
+ }
+ if (opt('fw')) {
+ if (opt('fw') !~ m%/%) {
+ foreach my $fw (sort keys %builtinfw) {
+ local $opt{'use'} = $fw;
+ printf "use=$fw address is %s\n", define(get_ip($fw), 'NOT FOUND');
+ }
+ }
+ local $opt{'use'} = 'fw';
+ printf "use=fw, fw=%s address is %s\n", opt('fw'), define(get_ip(opt('fw')), 'NOT FOUND')
+ if ! exists $builtinfw{opt('fw')};
+
+ }
+ {
+ local $opt{'use'} = 'web';
+ foreach my $web (sort keys %builtinweb) {
+ local $opt{'web'} = $web;
+ printf "use=web, web=$web address is %s\n", define(get_ip('web'), 'NOT FOUND');
+ }
+ printf "use=web, web=%s address is %s\n", opt('web'), define(get_ip('web'), 'NOT FOUND')
+ if ! exists $builtinweb{opt('web')};
+ }
+ if (opt('cmd')) {
+ local $opt{'use'} = 'cmd';
+ printf "use=cmd, cmd=%s address is %s\n", opt('cmd'), define(get_ip('cmd'), 'NOT FOUND');
+ }
+ exit 0 unless opt('debug');
+}
+######################################################################
+## test_geturl - print (and save if -test) result of fetching a URL
+######################################################################
+sub test_geturl {
+ my $url = shift;
+
+ my $reply = geturl(opt('proxy'), $url, opt('login'), opt('password'));
+ print "URL $url\n";;
+ print defined($reply) ? $reply : "<undefined>\n";
+ exit;
+}
+######################################################################
+## load_file
+######################################################################
+sub load_file {
+ my $file = shift;
+ my $buffer = '';
+
+ if (exists($ENV{'TEST_CASE'})) {
+ my $try = "$file-$ENV{'TEST_CASE'}";
+ $file = $try if -f $try;
+ }
+
+ local *FD;
+ if (open(FD, "< $file")) {
+ read(FD, $buffer, -s FD);
+ close(FD);
+ debug("Loaded %d bytes from %s", length($buffer), $file);
+ } else {
+ debug("Load failed from %s ($!)", $file);
+ }
+ return $buffer
+}
+######################################################################
+## save_file
+######################################################################
+sub save_file {
+ my ($file, $buffer, $opt) = @_;
+
+ $file .= "-$ENV{'TEST_CASE'}" if exists $ENV{'TEST_CASE'};
+ if (defined $opt) {
+ my $i = 0;
+ while (-f "$file-$i") {
+ if ('unique' =~ /^$opt/i) {
+ my $a = join('\n', grep {!/^Date:/} split /\n/, $buffer);
+ my $b = join('\n', grep {!/^Date:/} split /\n/, load_file("$file-$i"));
+ last if $a eq $b;
+ }
+ $i++;
+ }
+ $file = "$file-$i";
+ }
+ debug("Saving to %s", $file);
+ local *FD;
+ open(FD, "> $file") or return;
+ print FD $buffer;
+ close(FD);
+ return $buffer;
+}
+######################################################################
+## print_opt
+## print_globals
+## print_config
+## print_cache
+## print_info
+######################################################################
+sub _print_hash {
+ my ($string, $ptr) = @_;
+ my $value = $ptr;
+
+ if (! defined($ptr)) {
+ $value = "<undefined>";
+ } elsif (ref $ptr eq 'HASH') {
+ foreach my $key (sort keys %$ptr) {
+ _print_hash("${string}\{$key\}", $ptr->{$key});
+ }
+ return;
+ }
+ printf "%-36s : %s\n", $string, $value;
+}
+sub print_hash {
+ my ($string, $hash) = @_;
+ printf "=== %s ====\n", $string;
+ _print_hash($string, $hash);
+}
+sub print_opt { print_hash("opt", \%opt); }
+sub print_globals { print_hash("globals", \%globals); }
+sub print_config { print_hash("config", \%config); }
+sub print_cache { print_hash("cache", \%cache); }
+sub print_info {
+ print_opt();
+ print_globals();
+ print_config();
+ print_cache();
+}
+######################################################################
+## pipecmd - run an external command
+## logger
+## sendmail
+######################################################################
+sub pipecmd {
+ my $cmd = shift;
+ my $stdin = join("\n", @_);
+ my $ok = 0;
+
+ ## remove trailing newlines
+ 1 while chomp($stdin);
+
+ ## override when debugging.
+ $cmd = opt('exec') ? "| $cmd" : "> /dev/null";
+
+ ## execute the command.
+ local *FD;
+ if (! open(FD, $cmd)) {
+ printf STDERR "$program: cannot execute command %s.\n", $cmd;
+
+ } elsif ($stdin && (! print FD "$stdin\n")) {
+ printf STDERR "$program: failed writting to %s.\n", $cmd;
+ close(FD);
+
+ } elsif (! close(FD)) {
+ printf STDERR "$program: failed closing %s.($@)\n", $cmd;
+
+ } elsif (opt('exec') && $?) {
+ printf STDERR "$program: failed %s. ($@)\n", $cmd;
+
+ } else {
+ $ok = 1;
+ }
+ return $ok;
+}
+sub logger {
+ if (opt('syslog') && opt('facility') && opt('priority')) {
+ my $facility = opt('facility');
+ my $priority = opt('priority');
+ return pipecmd("logger -p$facility.$priority -t${program}\[$$\]", @_);
+ }
+ return 1;
+}
+sub sendmail {
+ my $recipients = opt('mail');
+
+ if (opt('mail-failure') && ($result ne 'OK' && $result ne '0')) {
+ $recipients = opt('mail-failure');
+ }
+ if ($msgs && $recipients && $msgs ne $last_msgs) {
+ pipecmd("sendmail -oi $recipients",
+ "To: $recipients",
+ "Subject: status report from $program\@$hostname",
+ "\r\n",
+ $msgs,
+ "",
+ "regards,",
+ " $program\@$hostname (version $version)"
+ );
+ }
+ $last_msgs = $msgs;
+ $msgs = '';
+}
+######################################################################
+## split_by_comma
+## merge
+## default
+## minimum
+## opt
+######################################################################
+sub split_by_comma {
+ my $string = shift;
+
+ return split /\s*[, ]\s*/, $string if defined $string;
+ return ();
+}
+sub merge {
+ my %merged = ();
+ foreach my $h (@_) {
+ foreach my $k (keys %$h) {
+ $merged{$k} = $h->{$k} unless exists $merged{$k};
+ }
+ }
+ return \%merged;
+}
+sub default {
+ my $v = shift;
+ return $variables{'merged'}{$v}{'default'};
+}
+sub minimum {
+ my $v = shift;
+ return $variables{'merged'}{$v}{'minimum'};
+}
+sub opt {
+ my $v = shift;
+ my $h = shift;
+ return $config{$h}{$v} if defined($h && $config{$h}{$v});
+ return $opt{$v} if defined $opt{$v};
+ return $globals{$v} if defined $globals{$v};
+ return default($v) if defined default($v);
+ return undef;
+}
+sub min {
+ my $min = shift;
+ foreach my $arg (@_) {
+ $min = $arg if $arg < $min;
+ }
+ return $min;
+}
+sub max {
+ my $max = shift;
+ foreach my $arg (@_) {
+ $max = $arg if $arg > $max;
+ }
+ return $max;
+}
+######################################################################
+## define
+######################################################################
+sub define {
+ foreach (@_) {
+ return $_ if defined $_;
+ }
+ return undef;
+}
+######################################################################
+## ynu
+######################################################################
+sub ynu {
+ my ($value, $yes, $no, $undef) = @_;
+
+ return $no if !defined($value) || !$value;
+ return $yes if $value eq '1';
+ foreach (qw(yes true)) {
+ return $yes if $_ =~ /^$value/i;
+ }
+ foreach (qw(no false)) {
+ return $no if $_ =~ /^$value/i;
+ }
+ return $undef;
+}
+######################################################################
+## msg
+## debug
+## warning
+## fatal
+######################################################################
+sub _msg {
+ my $log = shift;
+ my $prefix = shift;
+ my $format = shift;
+ my $buffer = sprintf $format, @_;
+ chomp($buffer);
+
+ $prefix = sprintf "%-9s ", $prefix if $prefix;
+ if ($file) {
+ $prefix .= "file $file";
+ $prefix .= ", line $lineno" if $lineno;
+ $prefix .= ": ";
+ }
+ if ($prefix) {
+ $buffer = "$prefix$buffer";
+ $buffer =~ s/\n/\n$prefix /g;
+ }
+ $buffer .= "\n";
+ print $buffer;
+
+ $msgs .= $buffer if $log;
+ logger($buffer) if $log;
+
+}
+sub msg { _msg(0, '', @_); }
+sub verbose { _msg(1, @_) if opt('verbose'); }
+sub info { _msg(1, 'INFO:', @_) if opt('verbose'); }
+sub debug { _msg(0, 'DEBUG:', @_) if opt('debug'); }
+sub debug2 { _msg(0, 'DEBUG:', @_) if opt('debug') && opt('verbose');}
+sub warning { _msg(1, 'WARNING:', @_); ...
[truncated message content] |