Merge lp:~swt-techie/ubuntu/vivid/ddclient/bug-1068884 into lp:ubuntu/vivid/ddclient

Proposed by Scott Talbert
Status: Merged
Merge reported by: Sebastien Bacher
Merged at revision: not available
Proposed branch: lp:~swt-techie/ubuntu/vivid/ddclient/bug-1068884
Merge into: lp:ubuntu/vivid/ddclient
Diff against target: 3781 lines (+3728/-4)
6 files modified
.pc/applied-patches (+1/-0)
.pc/fix_digest_sha_freedns.diff/ddclient (+3683/-0)
ddclient (+9/-4)
debian/changelog (+7/-0)
debian/patches/fix_digest_sha_freedns.diff (+27/-0)
debian/patches/series (+1/-0)
To merge this branch: bzr merge lp:~swt-techie/ubuntu/vivid/ddclient/bug-1068884
Reviewer Review Type Date Requested Status
Daniel Holbach (community) Approve
Review via email: mp+243906@code.launchpad.net

Description of the change

Add patch from upstream which enables ddclient to find Digest::SHA to fix FreeDNS support (LP: #1068884)

To post a comment you must log in.
Revision history for this message
Daniel Holbach (dholbach) wrote :

Thanks. Uploaded.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.pc/applied-patches'
2--- .pc/applied-patches 2012-09-18 13:30:33 +0000
3+++ .pc/applied-patches 2014-12-07 04:23:06 +0000
4@@ -2,3 +2,4 @@
5 config_path.diff
6 maxinterval.diff
7 sample_ubuntu.diff
8+fix_digest_sha_freedns.diff
9
10=== added directory '.pc/fix_digest_sha_freedns.diff'
11=== added file '.pc/fix_digest_sha_freedns.diff/.timestamp'
12=== added file '.pc/fix_digest_sha_freedns.diff/ddclient'
13--- .pc/fix_digest_sha_freedns.diff/ddclient 1970-01-01 00:00:00 +0000
14+++ .pc/fix_digest_sha_freedns.diff/ddclient 2014-12-07 04:23:06 +0000
15@@ -0,0 +1,3683 @@
16+#!/usr/bin/perl -w
17+#!/usr/local/bin/perl -w
18+######################################################################
19+# $Id: ddclient 130 2011-07-11 21:02:07Z wimpunk $
20+#
21+# DDCLIENT - a Perl client for updating DynDNS information
22+#
23+# Author: Paul Burry (paul+ddclient@burry.ca)
24+# ddclient-developers: see https://sourceforge.net/project/memberlist.php?group_id=116817
25+#
26+# website: http://ddclient.sf.net
27+#
28+# Support for multiple IP numbers added by
29+# Astaro AG, Ingo Schwarze <ischwarze-OOs/4mkCeqbQT0dZR+AlfA@public.gmane.org> September 16, 2008
30+#
31+######################################################################
32+require 5.004;
33+use strict;
34+use Getopt::Long;
35+use Sys::Hostname;
36+use IO::Socket;
37+
38+my ($VERSION) = q$Revision: 130 $ =~ /(\d+)/;
39+
40+my $version = "3.8.1";
41+my $programd = $0;
42+$programd =~ s%^.*/%%;
43+my $program = $programd;
44+$program =~ s/d$//;
45+my $now = time;
46+my $hostname = hostname();
47+my $etc = ($program =~ /test/i) ? './' : '/etc/';
48+my $cachedir = ($program =~ /test/i) ? './' : '/var/cache/ddclient/';
49+my $savedir = ($program =~ /test/i) ? 'URL/' : '/tmp/';
50+my $msgs = '';
51+my $last_msgs = '';
52+
53+use vars qw($file $lineno);
54+local $file = '';
55+local $lineno = '';
56+
57+$ENV{'PATH'} = (exists($ENV{PATH}) ? "$ENV{PATH}:" : "") . "/sbin:/usr/sbin:/bin:/usr/bin:/etc:/usr/lib:";
58+
59+sub T_ANY {'any'};
60+sub T_STRING {'string'};
61+sub T_EMAIL {'e-mail address'};
62+sub T_NUMBER {'number'};
63+sub T_DELAY {'time delay (ie. 1d, 1hour, 1m)'};
64+sub T_LOGIN {'login'};
65+sub T_PASSWD {'password'};
66+sub T_BOOL {'boolean value'};
67+sub T_FQDN {'fully qualified host name'};
68+sub T_OFQDN {'optional fully qualified host name'};
69+sub T_FILE {'file name'};
70+sub T_FQDNP {'fully qualified host name and optional port number'};
71+sub T_PROTO {'protocol'}
72+sub T_USE {'ip strategy'}
73+sub T_IF {'interface'}
74+sub T_PROG {'program name'}
75+sub T_IP {'ip'}
76+sub T_POSTS {'postscript'};
77+
78+## strategies for obtaining an ip address.
79+my %builtinweb = (
80+ 'dyndns' => { 'url' => 'http://checkip.dyndns.org/', 'skip' =>
81+ 'Current IP Address:', },
82+ 'dnspark' => { 'url' => 'http://ipdetect.dnspark.com/', 'skip' => 'Current Address:', },
83+ 'loopia' => { 'url' => 'http://dns.loopia.se/checkip/checkip.php', 'skip' => 'Current Address:', },
84+);
85+my %builtinfw = (
86+ 'watchguard-soho' => {
87+ 'name' => 'Watchguard SOHO FW',
88+ 'url' => '/pubnet.htm',
89+ 'skip' => 'NAME=IPAddress VALUE=',
90+ },
91+ 'netopia-r910' => {
92+ 'name' => 'Netopia R910 FW',
93+ 'url' => '/WanEvtLog',
94+ 'skip' => 'local:',
95+ },
96+ 'smc-barricade' => {
97+ 'name' => 'SMC Barricade FW',
98+ 'url' => '/status.htm',
99+ 'skip' => 'IP Address',
100+ },
101+ 'smc-barricade-alt' => {
102+ 'name' => 'SMC Barricade FW (alternate config)',
103+ 'url' => '/status.HTM',
104+ 'skip' => 'WAN IP',
105+ },
106+ 'smc-barricade-alt' => {
107+ 'name' => 'SMC Barricade FW (alternate config)',
108+ 'url' => '/status.HTM',
109+ 'skip' => 'WAN IP',
110+ },
111+ 'smc-barricade-7401bra' => {
112+ 'name' => 'SMC Barricade 7401BRA FW',
113+ 'url' => '/admin/wan1.htm',
114+ 'skip' => 'IP Address',
115+ },
116+ 'netgear-rt3xx' => {
117+ 'name' => 'Netgear FW',
118+ 'url' => '/mtenSysStatus.html',
119+ 'skip' => 'IP Address',
120+ },
121+ 'elsa-lancom-dsl10' => {
122+ 'name' => 'ELSA LanCom DSL/10 DSL FW',
123+ 'url' => '/config/1/6/8/3/',
124+ 'skip' => 'IP.Address',
125+ },
126+ 'elsa-lancom-dsl10-ch01' => {
127+ 'name' => 'ELSA LanCom DSL/10 DSL FW (isdn ch01)',
128+ 'url' => '/config/1/6/8/3/',
129+ 'skip' => 'IP.Address.*?CH01',
130+ },
131+ 'elsa-lancom-dsl10-ch02' => {
132+ 'name' => 'ELSA LanCom DSL/10 DSL FW (isdn ch01)',
133+ 'url' => '/config/1/6/8/3/',
134+ 'skip' => 'IP.Address.*?CH02',
135+ },
136+ 'linksys' => {
137+ 'name' => 'Linksys FW',
138+ 'url' => '/Status.htm',
139+ 'skip' => 'WAN.*?Address',
140+ },
141+ 'linksys-ver2' => {
142+ 'name' => 'Linksys FW version 2',
143+ 'url' => '/RouterStatus.htm',
144+ 'skip' => 'WAN.*?Address',
145+ },
146+ 'linksys-ver3' => {
147+ 'name' => 'Linksys FW version 3',
148+ 'url' => '/Status_Router.htm',
149+ 'skip' => 'WAN.*?Address',
150+ },
151+ 'linksys-wrt854g' => {
152+ 'name' => 'Linksys WRT854G FW',
153+ 'url' => '/Status_Router.asp',
154+ 'skip' => 'IP Address:',
155+ },
156+ 'maxgate-ugate3x00' => {
157+ 'name' => 'MaxGate UGATE-3x00 FW',
158+ 'url' => '/Status.htm',
159+ 'skip' => 'WAN.*?IP Address',
160+ },
161+ 'netcomm-nb3' => {
162+ 'name' => 'NetComm NB3',
163+ 'url' => '/MainPage?id=6',
164+ 'skip' => 'ppp-0',
165+ },
166+ '3com-3c886a' => {
167+ 'name' => '3com 3c886a 56k Lan Modem',
168+ 'url' => '/stat3.htm',
169+ 'skip' => 'IP address in use',
170+ },
171+ 'sohoware-nbg800' => {
172+ 'name' => 'SOHOWare BroadGuard NBG800',
173+ 'url' => '/status.htm',
174+ 'skip' => 'Internet IP',
175+ },
176+ 'xsense-aero' => {
177+ 'name' => 'Xsense Aero',
178+ 'url' => '/A_SysInfo.htm',
179+ 'skip' => 'WAN.*?IP Address',
180+ },
181+ 'alcatel-stp' => {
182+ 'name' => 'Alcatel Speed Touch Pro',
183+ 'url' => '/cgi/router/',
184+ 'skip' => 'Brt',
185+ },
186+ 'alcatel-510' => {
187+ 'name' => 'Alcatel Speed Touch 510',
188+ 'url' => '/cgi/ip/',
189+ 'skip' => 'ppp',
190+ },
191+ 'allnet-1298' => {
192+ 'name' => 'Allnet 1298',
193+ 'url' => '/cgi/router/',
194+ 'skip' => 'WAN',
195+ },
196+ '3com-oc-remote812' => {
197+ 'name' => '3com OfficeConnect Remote 812',
198+ 'url' => '/callEvent',
199+ 'skip' => '.*LOCAL',
200+ },
201+ 'e-tech' => {
202+ 'name' => 'E-tech Router',
203+ 'url' => '/Status.htm',
204+ 'skip' => 'Public IP Address',
205+ },
206+ 'cayman-3220h' => {
207+ 'name' => 'Cayman 3220-H DSL',
208+ 'url' => '/shell/show+ip+interfaces',
209+ 'skip' => '.*inet',
210+ },
211+ 'vigor-2200usb' => {
212+ 'name' => 'Vigor 2200 USB',
213+ 'url' => '/doc/online.sht',
214+ 'skip' => 'PPPoA',
215+ },
216+ 'dlink-614' => {
217+ 'name' => 'D-Link DI-614+',
218+ 'url' => '/st_devic.html',
219+ 'skip' => 'WAN',
220+ },
221+ 'dlink-604' => {
222+ 'name' => 'D-Link DI-604',
223+ 'url' => '/st_devic.html',
224+ 'skip' => 'WAN.*?IP.*Address',
225+ },
226+ 'olitec-SX200' => {
227+ 'name' => 'olitec-SX200',
228+ 'url' => '/doc/wan.htm',
229+ 'skip' => 'st_wan_ip[0] = "',
230+ },
231+ 'westell-6100' => {
232+ 'name' => 'Westell C90-610015-06 DSL Router',
233+ 'url' => '/advstat.htm',
234+ 'skip' => 'IP.+?Address',
235+ },
236+ '2wire' => {
237+ 'name' => '2Wire 1701HG Gateway',
238+ 'url' => '/xslt?PAGE=B01',
239+ 'skip' => 'Internet Address:',
240+ },
241+ 'linksys-rv042-wan1' => {
242+ 'name' => 'Linksys RV042 Dual Homed Router WAN Port 2',
243+ 'url' => '/home.htm',
244+ 'skip' => 'WAN1 IP',
245+ },
246+ 'linksys-rv042-wan2' => {
247+ 'name' => 'Linksys RV042 Dual Homed Router WAN Port 2',
248+ 'url' => '/home.htm',
249+ 'skip' => 'WAN2 IP',
250+ },
251+ 'netgear-rp614' => {
252+ 'name' => 'Netgear RP614 FW',
253+ 'url' => '/sysstatus.html',
254+ 'skip' => 'IP Address',
255+ },
256+ 'watchguard-edge-x' => {
257+ 'name' => 'Watchguard Edge X FW',
258+ 'url' => '/netstat.htm',
259+ 'skip' => 'inet addr:',
260+ },
261+ 'dlink-524' => {
262+ 'name' => 'D-Link DI-524',
263+ 'url' => '/st_device.html',
264+ 'skip' => 'WAN.*?Addres',
265+ },
266+ 'rtp300' => {
267+ 'name' => 'Linksys RTP300',
268+ 'url' => '/cgi-bin/webcm?getpage=%2Fusr%2Fwww_safe%2Fhtml%2Fstatus%2FRouter.html',
269+ 'skip' => 'Internet.*?IP Address',
270+ },
271+ 'netgear-wpn824' => {
272+ 'name' => 'Netgear WPN824 FW',
273+ 'url' => '/RST_status.htm',
274+ 'skip' => 'IP Address',
275+ },
276+ 'linksys-wcg200' => {
277+ 'name' => 'Linksys WCG200 FW',
278+ 'url' => '/RgStatus.asp',
279+ 'skip' => 'WAN.IP.*?Address',
280+ },
281+ 'netgear-dg834g' => {
282+ 'name' => 'netgear-dg834g',
283+ 'url' => '/setup.cgi?next_file=s_status.htm&todo=cfg_init',
284+ 'skip' => '',
285+ },
286+ 'netgear-wgt624' => {
287+ 'name' => 'Netgear WGT624',
288+ 'url' => '/RST_st_dhcp.htm',
289+ 'skip' => 'IP Address</B></td><TD NOWRAP width="50%">',
290+ },
291+ 'sveasoft' => {
292+ 'name' => 'Sveasoft WRT54G/WRT54GS',
293+ 'url' => '/Status_Router.asp',
294+ 'skip' => 'var wan_ip',
295+ },
296+ 'smc-barricade-7004vbr' => {
297+ 'name' => 'SMC Barricade FW (7004VBR model config)',
298+ 'url' => '/status_main.stm',
299+ 'skip' => 'var wan_ip=',
300+ },
301+ 'sitecom-dc202' => {
302+ 'name' => 'Sitecom DC-202 FW',
303+ 'url' => '/status.htm',
304+ 'skip' => 'Internet IP Address',
305+ },
306+);
307+my %ip_strategies = (
308+ 'ip' => ": obtain IP from -ip {address}",
309+ 'web' => ": obtain IP from an IP discovery page on the web",
310+ 'fw' => ": obtain IP from the firewall specified by -fw {type|address}",
311+ 'if' => ": obtain IP from the -if {interface}",
312+ 'cmd' => ": obtain IP from the -cmd {external-command}",
313+ 'cisco' => ": obtain IP from Cisco FW at the -fw {address}",
314+ 'cisco-asa' => ": obtain IP from Cisco ASA at the -fw {address}",
315+ map { $_ => sprintf ": obtain IP from %s at the -fw {address}", $builtinfw{$_}->{'name'} } keys %builtinfw,
316+);
317+sub ip_strategies_usage {
318+ return map { sprintf(" -use=%-22s %s.", $_, $ip_strategies{$_}) } sort keys %ip_strategies;
319+}
320+
321+my %web_strategies = (
322+ 'dyndns'=> 1,
323+ 'dnspark'=> 1,
324+ 'loopia'=> 1,
325+);
326+
327+sub setv {
328+ return {
329+ 'type' => shift,
330+ 'required' => shift,
331+ 'cache' => shift,
332+ 'config' => shift,
333+ 'default' => shift,
334+ 'minimum' => shift,
335+ };
336+};
337+my %variables = (
338+ 'global-defaults' => {
339+ 'daemon' => setv(T_DELAY, 0, 0, 1, 0, interval('60s')),
340+ 'foreground' => setv(T_BOOL, 0, 0, 1, 0, undef),
341+ 'file' => setv(T_FILE, 0, 0, 1, "$etc$program.conf", undef),
342+ 'cache' => setv(T_FILE, 0, 0, 1, "$cachedir$program.cache", undef),
343+ 'pid' => setv(T_FILE, 0, 0, 1, "", undef),
344+ 'proxy' => setv(T_FQDNP, 0, 0, 1, '', undef),
345+ 'protocol' => setv(T_PROTO, 0, 0, 1, 'dyndns2', undef),
346+
347+ 'use' => setv(T_USE, 0, 0, 1, 'ip', undef),
348+ 'ip' => setv(T_IP, 0, 0, 1, undef, undef),
349+ 'if' => setv(T_IF, 0, 0, 1, 'ppp0', undef),
350+ 'if-skip' => setv(T_STRING,1, 0, 1, '', undef),
351+ 'web' => setv(T_STRING,0, 0, 1, 'dyndns', undef),
352+ 'web-skip' => setv(T_STRING,1, 0, 1, '', undef),
353+ 'fw' => setv(T_ANY, 0, 0, 1, '', undef),
354+ 'fw-skip' => setv(T_STRING,1, 0, 1, '', undef),
355+ 'fw-login' => setv(T_LOGIN, 1, 0, 1, '', undef),
356+ 'fw-password' => setv(T_PASSWD,1, 0, 1, '', undef),
357+ 'cmd' => setv(T_PROG, 0, 0, 1, '', undef),
358+ 'cmd-skip' => setv(T_STRING,1, 0, 1, '', undef),
359+
360+ 'timeout' => setv(T_DELAY, 0, 0, 1, interval('120s'), interval('120s')),
361+ 'retry' => setv(T_BOOL, 0, 0, 0, 0, undef),
362+ 'force' => setv(T_BOOL, 0, 0, 0, 0, undef),
363+ 'ssl' => setv(T_BOOL, 0, 0, 0, 0, undef),
364+
365+ 'syslog' => setv(T_BOOL, 0, 0, 1, 0, undef),
366+ 'facility' => setv(T_STRING,0, 0, 1, 'daemon', undef),
367+ 'priority' => setv(T_STRING,0, 0, 1, 'notice', undef),
368+ 'mail' => setv(T_EMAIL, 0, 0, 1, '', undef),
369+ 'mail-failure' => setv(T_EMAIL, 0, 0, 1, '', undef),
370+
371+ 'exec' => setv(T_BOOL, 0, 0, 1, 1, undef),
372+ 'debug' => setv(T_BOOL, 0, 0, 1, 0, undef),
373+ 'verbose' => setv(T_BOOL, 0, 0, 1, 0, undef),
374+ 'quiet' => setv(T_BOOL, 0, 0, 1, 0, undef),
375+ 'help' => setv(T_BOOL, 0, 0, 1, 0, undef),
376+ 'test' => setv(T_BOOL, 0, 0, 1, 0, undef),
377+ 'geturl' => setv(T_STRING,0, 0, 0, '', undef),
378+
379+ 'postscript' => setv(T_POSTS, 0, 0, 1, '', undef),
380+ },
381+ 'service-common-defaults' => {
382+ 'server' => setv(T_FQDNP, 1, 0, 1, 'members.dyndns.org', undef),
383+ 'login' => setv(T_LOGIN, 1, 0, 1, '', undef),
384+ 'password' => setv(T_PASSWD, 1, 0, 1, '', undef),
385+ 'host' => setv(T_STRING, 1, 1, 1, '', undef),
386+
387+ 'use' => setv(T_USE, 0, 0, 1, 'ip', undef),
388+ 'if' => setv(T_IF, 0, 0, 1, 'ppp0', undef),
389+ 'if-skip' => setv(T_STRING,0, 0, 1, '', undef),
390+ 'web' => setv(T_STRING,0, 0, 1, 'dyndns', undef),
391+ 'web-skip' => setv(T_STRING,0, 0, 1, '', undef),
392+ 'fw' => setv(T_ANY, 0, 0, 1, '', undef),
393+ 'fw-skip' => setv(T_STRING,0, 0, 1, '', undef),
394+ 'fw-login' => setv(T_LOGIN, 0, 0, 1, '', undef),
395+ 'fw-password' => setv(T_PASSWD,0, 0, 1, '', undef),
396+ 'cmd' => setv(T_PROG, 0, 0, 1, '', undef),
397+ 'cmd-skip' => setv(T_STRING,0, 0, 1, '', undef),
398+
399+ 'ip' => setv(T_IP, 0, 1, 0, undef, undef),
400+ 'wtime' => setv(T_DELAY, 0, 1, 1, 0, interval('30s')),
401+ 'mtime' => setv(T_NUMBER, 0, 1, 0, 0, undef),
402+ 'atime' => setv(T_NUMBER, 0, 1, 0, 0, undef),
403+ 'status' => setv(T_ANY, 0, 1, 0, '', undef),
404+ 'min-interval' => setv(T_DELAY, 0, 0, 1, interval('30s'), 0),
405+ 'max-interval' => setv(T_DELAY, 0, 0, 1, interval('30d'), 0),
406+ 'min-error-interval' => setv(T_DELAY, 0, 0, 1, interval('5m'), 0),
407+
408+ 'warned-min-interval' => setv(T_ANY, 0, 1, 0, 0, undef),
409+ 'warned-min-error-interval' => setv(T_ANY, 0, 1, 0, 0, undef),
410+ },
411+ 'dyndns-common-defaults' => {
412+ 'static' => setv(T_BOOL, 0, 1, 1, 0, undef),
413+ 'wildcard' => setv(T_BOOL, 0, 1, 1, 0, undef),
414+ 'mx' => setv(T_OFQDN, 0, 1, 1, '', undef),
415+ 'backupmx' => setv(T_BOOL, 0, 1, 1, 0, undef),
416+ },
417+ 'easydns-common-defaults' => {
418+ 'wildcard' => setv(T_BOOL, 0, 1, 1, 0, undef),
419+ 'mx' => setv(T_OFQDN, 0, 1, 1, '', undef),
420+ 'backupmx' => setv(T_BOOL, 0, 1, 1, 0, undef),
421+ },
422+ 'dnspark-common-defaults' => {
423+ 'mx' => setv(T_OFQDN, 0, 1, 1, '', undef),
424+ 'mxpri' => setv(T_NUMBER, 0, 0, 1, 5, undef),
425+ },
426+ 'noip-common-defaults' => {
427+ 'static' => setv(T_BOOL, 0, 1, 1, 0, undef),
428+ },
429+ 'noip-service-common-defaults' => {
430+ 'server' => setv(T_FQDNP, 1, 0, 1, 'dynupdate.no-ip.com', undef),
431+ 'login' => setv(T_LOGIN, 1, 0, 1, '', undef),
432+ 'password' => setv(T_PASSWD, 1, 0, 1, '', undef),
433+ 'host' => setv(T_STRING, 1, 1, 1, '', undef),
434+ 'ip' => setv(T_IP, 0, 1, 0, undef, undef),
435+ 'wtime' => setv(T_DELAY, 0, 1, 1, 0, interval('30s')),
436+ 'mtime' => setv(T_NUMBER, 0, 1, 0, 0, undef),
437+ 'atime' => setv(T_NUMBER, 0, 1, 0, 0, undef),
438+ 'status' => setv(T_ANY, 0, 1, 0, '', undef),
439+ 'min-interval' => setv(T_DELAY, 0, 0, 1, interval('30s'), 0),
440+ 'max-interval' => setv(T_DELAY, 0, 0, 1, interval('25d'), 0),
441+ 'min-error-interval' => setv(T_DELAY, 0, 0, 1, interval('5m'), 0),
442+ 'warned-min-interval' => setv(T_ANY, 0, 1, 0, 0, undef),
443+ 'warned-min-error-interval' => setv(T_ANY, 0, 1, 0, 0, undef),
444+ },
445+ 'zoneedit-service-common-defaults' => {
446+ 'zone' => setv(T_OFQDN, 0, 0, 1, undef, undef),
447+ },
448+ 'dtdns-common-defaults' => {
449+ 'login' => setv(T_LOGIN, 0, 0, 0, 'unused', undef),
450+ 'client' => setv(T_STRING, 0, 1, 1, $program, undef),
451+ },
452+);
453+my %services = (
454+ 'dyndns1' => {
455+ 'updateable' => \&nic_dyndns2_updateable,
456+ 'update' => \&nic_dyndns1_update,
457+ 'examples' => \&nic_dyndns1_examples,
458+ 'variables' => merge(
459+ $variables{'dyndns-common-defaults'},
460+ $variables{'service-common-defaults'},
461+ ),
462+ },
463+ 'dyndns2' => {
464+ 'updateable' => \&nic_dyndns2_updateable,
465+ 'update' => \&nic_dyndns2_update,
466+ 'examples' => \&nic_dyndns2_examples,
467+ 'variables' => merge(
468+ { 'custom' => setv(T_BOOL, 0, 1, 1, 0, undef), },
469+ { 'script' => setv(T_STRING, 1, 1, 1, '/nic/update', undef), },
470+# { 'offline' => setv(T_BOOL, 0, 1, 1, 0, undef), },
471+ $variables{'dyndns-common-defaults'},
472+ $variables{'service-common-defaults'},
473+ ),
474+ },
475+ 'noip' => {
476+ 'updateable' => undef,
477+ 'update' => \&nic_noip_update,
478+ 'examples' => \&nic_noip_examples,
479+ 'variables' => merge(
480+ { 'custom' => setv(T_BOOL, 0, 1, 1, 0, undef), },
481+ $variables{'noip-common-defaults'},
482+ $variables{'noip-service-common-defaults'},
483+ ),
484+ },
485+ 'concont' => {
486+ 'updateable' => undef,
487+ 'update' => \&nic_concont_update,
488+ 'examples' => \&nic_concont_examples,
489+ 'variables' => merge(
490+ $variables{'service-common-defaults'},
491+ { 'mx' => setv(T_OFQDN, 0, 1, 1, '', undef), },
492+ { 'wildcard' => setv(T_BOOL, 0, 1, 1, 0, undef), },
493+ ),
494+ },
495+ 'dslreports1' => {
496+ 'updateable' => undef,
497+ 'update' => \&nic_dslreports1_update,
498+ 'examples' => \&nic_dslreports1_examples,
499+ 'variables' => merge(
500+ { 'host' => setv(T_NUMBER, 1, 1, 1, 0, undef) },
501+ $variables{'service-common-defaults'},
502+ ),
503+ },
504+ 'hammernode1' => {
505+ 'updateable' => undef,
506+ 'update' => \&nic_hammernode1_update,
507+ 'examples' => \&nic_hammernode1_examples,
508+ 'variables' => merge(
509+ { 'server' => setv(T_FQDNP, 1, 0, 1, 'dup.hn.org', undef) },
510+ { 'min-interval' => setv(T_DELAY, 0, 0, 1, interval('5m'), 0),},
511+ $variables{'service-common-defaults'},
512+ ),
513+ },
514+ 'zoneedit1' => {
515+ 'updateable' => undef,
516+ 'update' => \&nic_zoneedit1_update,
517+ 'examples' => \&nic_zoneedit1_examples,
518+ 'variables' => merge(
519+ { 'server' => setv(T_FQDNP, 1, 0, 1, 'dynamic.zoneedit.com', undef) },
520+ { 'min-interval' => setv(T_DELAY, 0, 0, 1, interval('5m'), 0),},
521+ $variables{'service-common-defaults'},
522+ $variables{'zoneedit-service-common-defaults'},
523+ ),
524+ },
525+ 'easydns' => {
526+ 'updateable' => undef,
527+ 'update' => \&nic_easydns_update,
528+ 'examples' => \&nic_easydns_examples,
529+ 'variables' => merge(
530+ { 'server' => setv(T_FQDNP, 1, 0, 1, 'members.easydns.com', undef) },
531+ { 'min-interval' => setv(T_DELAY, 0, 0, 1, interval('5m'), 0),},
532+ $variables{'easydns-common-defaults'},
533+ $variables{'service-common-defaults'},
534+ ),
535+ },
536+ 'dnspark' => {
537+ 'updateable' => undef,
538+ 'update' => \&nic_dnspark_update,
539+ 'examples' => \&nic_dnspark_examples,
540+ 'variables' => merge(
541+ { 'server' => setv(T_FQDNP, 1, 0, 1, 'www.dnspark.com', undef) },
542+ { 'min-interval' => setv(T_DELAY, 0, 0, 1, interval('5m'), 0),},
543+ $variables{'dnspark-common-defaults'},
544+ $variables{'service-common-defaults'},
545+ ),
546+ },
547+ 'namecheap' => {
548+ 'updateable' => undef,
549+ 'update' => \&nic_namecheap_update,
550+ 'examples' => \&nic_namecheap_examples,
551+ 'variables' => merge(
552+ { 'server' => setv(T_FQDNP, 1, 0, 1, 'dynamicdns.park-your-domain.com', undef) },
553+ { 'min-interval' => setv(T_DELAY, 0, 0, 1, 0, interval('5m')),},
554+ $variables{'service-common-defaults'},
555+ ),
556+ },
557+ 'sitelutions' => {
558+ 'updateable' => undef,
559+ 'update' => \&nic_sitelutions_update,
560+ 'examples' => \&nic_sitelutions_examples,
561+ 'variables' => merge(
562+ { 'server' => setv(T_FQDNP, 1, 0, 1, 'www.sitelutions.com', undef) },
563+ { 'min-interval' => setv(T_DELAY, 0, 0, 1, 0, interval('5m')),},
564+ $variables{'service-common-defaults'},
565+ ),
566+ },
567+ 'freedns' => {
568+ 'updateable' => undef,
569+ 'update' => \&nic_freedns_update,
570+ 'examples' => \&nic_freedns_examples,
571+ 'variables' => merge(
572+ { 'server' => setv(T_FQDNP, 1, 0, 1, 'freedns.afraid.org', undef) },
573+ { 'min-interval' => setv(T_DELAY, 0, 0, 1, 0, interval('5m')),},
574+ $variables{'service-common-defaults'},
575+ ),
576+ },
577+ 'dtdns' => {
578+ 'updateable' => undef,
579+ 'update' => \&nic_dtdns_update,
580+ 'examples' => \&nic_dtdns_examples,
581+ 'variables' => merge(
582+ $variables{'dtdns-common-defaults'},
583+ $variables{'service-common-defaults'},
584+ ),
585+ },
586+);
587+$variables{'merged'} = merge($variables{'global-defaults'},
588+ $variables{'service-common-defaults'},
589+ $variables{'dyndns-common-defaults'},
590+ map { $services{$_}{'variables'} } keys %services,
591+);
592+
593+my @opt = (
594+ "usage: ${program} [options]",
595+ "options are:",
596+ [ "daemon", "=s", "-daemon delay : run as a daemon, specify delay as an interval." ],
597++ [ "foreground", "!", "-foreground : do not fork" ],
598+ [ "proxy", "=s", "-proxy host : use 'host' as the HTTP proxy" ],
599+ [ "server", "=s", "-server host : update DNS information on 'host'" ],
600+ [ "protocol", "=s", "-protocol type : update protocol used" ],
601+ [ "file", "=s", "-file path : load configuration information from 'path'" ],
602+ [ "cache", "=s", "-cache path : record address used in 'path'" ],
603+ [ "pid", "=s", "-pid path : record process id in 'path'" ],
604+ "",
605+ [ "use", "=s", "-use which : how the should IP address be obtained." ],
606+ &ip_strategies_usage(),
607+ "",
608+ [ "ip", "=s", "-ip address : set the IP address to 'address'" ],
609+ "",
610+ [ "if", "=s", "-if interface : obtain IP address from 'interface'" ],
611+ [ "if-skip", "=s", "-if-skip pattern : skip any IP addresses before 'pattern' in the output of ifconfig {if}" ],
612+ "",
613+ [ "web", "=s", "-web provider|url : obtain IP address from provider's IP checking page" ],
614+ [ "web-skip", "=s", "-web-skip pattern : skip any IP addresses before 'pattern' on the web provider|url" ],
615+ "",
616+ [ "fw", "=s", "-fw address|url : obtain IP address from firewall at 'address'" ],
617+ [ "fw-skip", "=s", "-fw-skip pattern : skip any IP addresses before 'pattern' on the firewall address|url" ],
618+ [ "fw-login", "=s", "-fw-login login : use 'login' when getting IP from fw" ],
619+ [ "fw-password", "=s", "-fw-password secret : use password 'secret' when getting IP from fw" ],
620+ "",
621+ [ "cmd", "=s", "-cmd program : obtain IP address from by calling {program}" ],
622+ [ "cmd-skip", "=s", "-cmd-skip pattern : skip any IP addresses before 'pattern' in the output of {cmd}" ],
623+ "",
624+ [ "login", "=s", "-login user : login as 'user'" ],
625+ [ "password", "=s", "-password secret : use password 'secret'" ],
626+ [ "host", "=s", "-host host : update DNS information for 'host'" ],
627+ "",
628+ [ "options", "=s", "-options opt,opt : optional per-service arguments (see below)" ],
629+ "",
630+ [ "ssl", "!", "-{no}ssl : do updates over encrypted SSL connection" ],
631+ [ "retry", "!", "-{no}retry : retry failed updates." ],
632+ [ "force", "!", "-{no}force : force an update even if the update may be unnecessary" ],
633+ [ "timeout", "=i", "-timeout max : wait at most 'max' seconds for the host to respond" ],
634+
635+ [ "syslog", "!", "-{no}syslog : log messages to syslog" ],
636+ [ "facility", "=s", "-facility {type} : log messages to syslog to facility {type}" ],
637+ [ "priority", "=s", "-priority {pri} : log messages to syslog with priority {pri}" ],
638+ [ "mail", "=s", "-mail address : e-mail messages to {address}" ],
639+ [ "mail-failure","=s", "-mail-failure address : e-mail messages for failed updates to {address}" ],
640+ [ "exec", "!", "-{no}exec : do {not} execute; just show what would be done" ],
641+ [ "debug", "!", "-{no}debug : print {no} debugging information" ],
642+ [ "verbose", "!", "-{no}verbose : print {no} verbose information" ],
643+ [ "quiet", "!", "-{no}quiet : print {no} messages for unnecessary updates" ],
644+ [ "help", "", "-help : this message" ],
645+ [ "postscript", "", "-postscript : script to run after updating ddclient, has new IP as param" ],
646+
647+ [ "query", "!", "-{no}query : print {no} ip addresses and exit" ],
648+ [ "test", "!", "" ], ## hidden
649+ [ "geturl", "=s", "" ], ## hidden
650+ "",
651+ nic_examples(),
652+ "$program version $version, ",
653+ " originally written by Paul Burry, paul+ddclient\@burry.ca",
654+ " project now maintained on http://ddclient.sourceforge.net"
655+);
656+
657+## process args
658+my ($opt_usage, %opt) = process_args(@opt);
659+my ($result, %config, %globals, %cache);
660+my $saved_cache = '';
661+my %saved_opt = %opt;
662+$result = 'OK';
663+
664+test_geturl(opt('geturl')) if opt('geturl');
665+
666+## process help option
667+if (opt('help')) {
668+ *STDERR = *STDOUT;
669+ usage(0);
670+}
671+
672+## read config file because 'daemon' mode may be defined there.
673+read_config(define($opt{'file'}, default('file')), \%config, \%globals);
674+init_config();
675+test_possible_ip() if opt('query');
676+
677+if (!opt('daemon') && $programd =~ /d$/) {
678+ $opt{'daemon'} = minimum('daemon');
679+}
680+my $caught_hup = 0;
681+my $caught_term = 0;
682+my $caught_kill = 0;
683+$SIG{'HUP'} = sub { $caught_hup = 1; };
684+$SIG{'TERM'} = sub { $caught_term = 1; };
685+$SIG{'KILL'} = sub { $caught_kill = 1; };
686+# don't fork() if foreground or force is on
687+if (opt('foreground') || opt('force')) {
688+ ;
689+} elsif (opt('daemon')) {
690+ $SIG{'CHLD'} = 'IGNORE';
691+ my $pid = fork;
692+ if ($pid < 0) {
693+ print STDERR "${program}: can not fork ($!)\n";
694+ exit -1;
695+ } elsif ($pid) {
696+ exit 0;
697+ }
698+ $SIG{'CHLD'} = 'DEFAULT';
699+ open(STDOUT, ">/dev/null");
700+ open(STDERR, ">/dev/null");
701+ open(STDIN, "</dev/null");
702+}
703+
704+# write out the pid file if we're daemon'ized
705+if(opt('daemon')) {
706+ write_pid();
707+ $opt{'syslog'} = 1;
708+}
709+
710+umask 077;
711+my $daemon;
712+do {
713+ $now = time;
714+ $result = 'OK';
715+ %opt = %saved_opt;
716+ if (opt('help')) {
717+ *STDERR = *STDOUT;
718+ printf("Help found");
719+ # usage();
720+ }
721+
722+ read_config(define($opt{'file'}, default('file')), \%config, \%globals);
723+ init_config();
724+ read_cache(opt('cache'), \%cache);
725+ print_info() if opt('debug') && opt('verbose');
726+
727+# usage("invalid argument '-use %s'; possible values are:\n\t%s", $opt{'use'}, join("\n\t,",sort keys %ip_strategies))
728+ usage("invalid argument '-use %s'; possible values are:\n%s", $opt{'use'}, join("\n",ip_strategies_usage()))
729+ unless exists $ip_strategies{lc opt('use')};
730+
731+ $daemon = $opt{'daemon'};
732+ $daemon = 0 if opt('force');
733+
734+ update_nics();
735+
736+ if ($daemon) {
737+ debug("sleep %s", $daemon);
738+ sendmail();
739+
740+ my $left = $daemon;
741+ while (($left > 0) && !$caught_hup && !$caught_term && !$caught_kill) {
742+ my $delay = $left > 10 ? 10 : $left;
743+
744+ $0 = sprintf("%s - sleeping for %s seconds", $program, $left);
745+ $left -= sleep $delay;
746+ }
747+ $caught_hup = 0;
748+ $result = 0;
749+
750+ } elsif (! scalar(%config)) {
751+ warning("no hosts to update.") unless !opt('quiet') || opt('verbose') || !$daemon;
752+ $result = 1;
753+
754+ } else {
755+ $result = $result eq 'OK' ? 0 : 1;
756+ }
757+} while ($daemon && !$result && !$caught_term && !$caught_kill);
758+
759+warning("caught SIGKILL; exiting") if $caught_kill;
760+unlink_pid();
761+sendmail();
762+
763+exit($result);
764+
765+######################################################################
766+## runpostscript
767+######################################################################
768+
769+sub runpostscript {
770+ my ($ip) = @_;
771+
772+ if ( defined $globals{postscript} ) {
773+ if ( -x $globals{postscript}) {
774+ system ("$globals{postscript} $ip &");
775+ } else {
776+ warning ("Can not execute post script: %s", $globals{postscript});
777+ }
778+ }
779+}
780+
781+######################################################################
782+## update_nics
783+######################################################################
784+sub update_nics {
785+ my %examined = ();
786+ my %iplist = ();
787+
788+ foreach my $s (sort keys %services) {
789+ my (@hosts, %ips) = ();
790+ my $updateable = $services{$s}{'updateable'};
791+ my $update = $services{$s}{'update'};
792+
793+ foreach my $h (sort keys %config) {
794+ next if $config{$h}{'protocol'} ne lc($s);
795+ $examined{$h} = 1;
796+ my $use = $config{$h}{'use'} || opt('use');
797+ local $opt{$use} = $config{$h}{$use} if $config{$h}{$use};
798+ # bug #13: we should only do this once
799+ # use isn't enough, we have to save the origin to.
800+ # this will break the multiple ip stuff if use has
801+ # been used twice for the same device.
802+ my $ip = "";
803+ if (defined $iplist{$use}) {
804+ $ip = $iplist{$use};
805+ } else {
806+ $ip = get_ip($use, $h);
807+ if (!defined $ip || !$ip) {
808+ warning("unable to determine IP address")
809+ if !$daemon || opt('verbose');
810+ next;
811+ }
812+ if ($ip !~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) {
813+ warning("malformed IP address (%s)", $ip);
814+ next;
815+ }
816+ $iplist{$use} = $ip;
817+ }
818+ $config{$h}{'wantip'} = $ip;
819+ next if !nic_updateable($h, $updateable);
820+ push @hosts, $h;
821+ $ips{$ip} = $h;
822+ }
823+ if (@hosts) {
824+ $0 = sprintf("%s - updating %s", $program, join(',', @hosts));
825+ &$update(@hosts);
826+ runpostscript(join ' ', keys %ips);
827+ }
828+ }
829+ foreach my $h (sort keys %config) {
830+ if (!exists $examined{$h}) {
831+ failed("%s was not updated because protocol %s is not supported.",
832+ $h, define($config{$h}{'protocol'}, '<undefined>')
833+ );
834+ }
835+ }
836+ write_cache(opt('cache'));
837+}
838+######################################################################
839+## unlink_pid()
840+######################################################################
841+sub unlink_pid {
842+ if (opt('pid') && opt('daemon')) {
843+ unlink opt('pid');
844+ }
845+}
846+
847+######################################################################
848+## write_pid()
849+######################################################################
850+sub write_pid {
851+ my $file = opt('pid');
852+
853+ if ($file && opt('daemon')) {
854+ local *FD;
855+ if (! open(FD, "> $file")) {
856+ warning("Cannot create file '%s'. ($!)", $file);
857+
858+ } else {
859+ printf FD "$$\n";
860+ close(FD);
861+ }
862+ }
863+}
864+
865+######################################################################
866+## write_cache($file)
867+######################################################################
868+sub write_cache {
869+ my ($file) = @_;
870+
871+ ## merge the updated host entries into the cache.
872+ foreach my $h (keys %config) {
873+ if (! exists $cache{$h} || $config{$h}{'update'}) {
874+ map {$cache{$h}{$_} = $config{$h}{$_} } @{$config{$h}{'cacheable'}};
875+
876+ } else {
877+ map {$cache{$h}{$_} = $config{$h}{$_} } qw(atime wtime status);
878+ }
879+ }
880+
881+ ## construct the cache file.
882+ my $cache = "";
883+ foreach my $h (sort keys %cache) {
884+ my $opt = join(',', map { "$_=".define($cache{$h}{$_},'') } sort keys %{$cache{$h}});
885+
886+ $cache .= sprintf "%s%s%s\n", $opt, ($opt ? ' ' : ''), $h;
887+ }
888+ $file = '' if defined($saved_cache) && $cache eq $saved_cache;
889+
890+ ## write the updates and other entries to the cache file.
891+ if ($file) {
892+ $saved_cache = undef;
893+ local *FD;
894+ if (! open(FD, "> $file")) {
895+ fatal("Cannot create file '%s'. ($!)", $file);
896+ }
897+ printf FD "## $program-$version\n";
898+ printf FD "## last updated at %s (%d)\n", prettytime($now), $now;
899+ printf FD $cache;
900+
901+ close(FD);
902+ }
903+}
904+######################################################################
905+## read_cache($file) - called before reading the .conf
906+######################################################################
907+sub read_cache {
908+ my $file = shift;
909+ my $config = shift;
910+ my $globals = {};
911+
912+ %{$config} = ();
913+ ## read the cache file ignoring anything on the command-line.
914+ if (-e $file) {
915+ my %saved = %opt;
916+ %opt = ();
917+ $saved_cache = _read_config($config, $globals, "##\\s*$program-$version\\s*", $file);
918+ %opt = %saved;
919+
920+ foreach my $h (keys %cache) {
921+ if (exists $config->{$h}) {
922+ foreach (qw(atime mtime wtime ip status)) {
923+ $config->{$h}{$_} = $cache{$h}{$_} if exists $cache{$h}{$_};
924+ }
925+ }
926+ }
927+ }
928+}
929+######################################################################
930+## parse_assignments(string) return (rest, %variables)
931+## parse_assignment(string) return (name, value, rest)
932+######################################################################
933+sub parse_assignments {
934+ my $rest = shift;
935+ my @args = @_;
936+ my %variables = ();
937+ my ($name, $value);
938+
939+ while (1) {
940+ $rest =~ s/^\s+//;
941+ ($name, $value, $rest) = parse_assignment($rest, @args);
942+ if (defined $name) {
943+ $variables{$name} = $value;
944+ } else {
945+ last;
946+ }
947+ }
948+ return ($rest, %variables);
949+}
950+sub parse_assignment {
951+ my $rest = shift;
952+ my $stop = @_ ? shift : '[\n\s,]';
953+ my ($c, $name, $value);
954+ my ($escape, $quote) = (0, '');
955+
956+ if ($rest =~ /^\s*([a-z][a-z_-]*)=(.*)/i) {
957+ ($name, $rest, $value) = ($1, $2, '');
958+
959+ while (length($c = substr($rest,0,1))) {
960+ $rest = substr($rest,1);
961+ if ($escape) {
962+ $value .= $c;
963+ $escape = 0;
964+ } elsif ($c eq "\\") {
965+ $escape = 1;
966+ } elsif ($quote && $c eq $quote) {
967+ $quote = ''
968+ } elsif (!$quote && $c =~ /[\'\"]/) {
969+ $quote = $c;
970+ } elsif (!$quote && $c =~ /^${stop}/) {
971+ last;
972+ } else {
973+ $value .= $c;
974+ }
975+ }
976+ }
977+ warning("assignment ended with an open quote") if $quote;
978+ return ($name, $value, $rest);
979+}
980+######################################################################
981+## read_config
982+######################################################################
983+sub read_config {
984+ my $file = shift;
985+ my $config = shift;
986+ my $globals = shift;
987+ my %globals = ();
988+
989+ _read_config($config, $globals, '', $file, %globals);
990+}
991+sub _read_config {
992+ my $config = shift;
993+ my $globals = shift;
994+ my $stamp = shift;
995+ local $file = shift;
996+ my %globals = @_;
997+ my %config = ();
998+ my $content = '';
999+
1000+ local *FD;
1001+ if (! open(FD, "< $file")) {
1002+ # fatal("Cannot open file '%s'. ($!)", $file);
1003+ warning("Cannot open file '%s'. ($!)", $file);
1004+ }
1005+ # Check for only owner has any access to config file
1006+ my ($dev, $ino, $mode, @statrest) = stat(FD);
1007+ if ($mode & 077) {
1008+ if (-f FD && (chmod 0600, $file)) {
1009+ warning("file $file must be accessible only by its owner (fixed).");
1010+ } else {
1011+ # fatal("file $file must be accessible only by its owner.");
1012+ warning("file $file must be accessible only by its owner.");
1013+ }
1014+ }
1015+
1016+ local $lineno = 0;
1017+ my $continuation = '';
1018+ my %passwords = ();
1019+ while (<FD>) {
1020+ s/[\r\n]//g;
1021+
1022+ $lineno++;
1023+
1024+ ## check for the program version stamp
1025+ if (($. == 1) && $stamp && ($_ !~ /^$stamp$/i)) {
1026+ warning("program version mismatch; ignoring %s", $file);
1027+ last;
1028+ }
1029+ if (/\\\s+$/) {
1030+ warning("whitespace follows the \\ at the end-of-line.\nIf you meant to have a line continuation, remove the trailing whitespace.");
1031+ }
1032+
1033+ $content .= "$_\n" unless /^#/;
1034+
1035+ ## parsing passwords is special
1036+ if (/^([^#]*\s)?([^#]*?password\S*?)\s*=\s*('.*'|[^']\S*)(.*)/) {
1037+ my ($head, $key, $value, $tail) = ($1 || '', $2, $3, $4);
1038+ $value = $1 if $value =~ /^'(.*)'$/;
1039+ $passwords{$key} = $value;
1040+ $_ = "${head}${key}=dummy${tail}";
1041+ }
1042+
1043+ ## remove comments
1044+ s/#.*//;
1045+
1046+ ## handle continuation lines
1047+ $_ = "$continuation$_";
1048+ if (/\\$/) {
1049+ chop;
1050+ $continuation = $_;
1051+ next;
1052+ }
1053+ $continuation = '';
1054+
1055+ s/^\s+//; # remove leading white space
1056+ s/\s+$//; # remove trailing white space
1057+ s/\s+/ /g; # canonify
1058+ next if /^$/;
1059+
1060+ ## expected configuration line is:
1061+ ## [opt=value,opt=..] [host [login [password]]]
1062+ my %locals;
1063+ ($_, %locals) = parse_assignments($_);
1064+ s/\s*,\s*/,/g;
1065+ my @args = split;
1066+
1067+ ## verify that keywords are valid...and check the value
1068+ foreach my $k (keys %locals) {
1069+ $locals{$k} = $passwords{$k} if defined $passwords{$k};
1070+ if (!exists $variables{'merged'}{$k}) {
1071+ warning("unrecognized keyword '%s' (ignored)", $k);
1072+ delete $locals{$k};
1073+ } else {
1074+ my $def = $variables{'merged'}{$k};
1075+ my $value = check_value($locals{$k}, $def);
1076+ if (!defined($value)) {
1077+ warning("Invalid Value for keyword '%s' = '%s'", $k, $locals{$k});
1078+ delete $locals{$k};
1079+ } else { $locals{$k} = $value; }
1080+ }
1081+ }
1082+ if (exists($locals{'host'})) {
1083+ $args[0] = @args ? "$args[0],$locals{host}" : "$locals{host}";
1084+ }
1085+ ## accumulate globals
1086+ if ($#args < 0) {
1087+ map { $globals{$_} = $locals{$_} } keys %locals;
1088+ }
1089+
1090+ ## process this host definition
1091+ if (@args) {
1092+ my ($host, $login, $password) = @args;
1093+
1094+ ## add in any globals..
1095+ %locals = %{ merge(\%locals, \%globals) };
1096+
1097+ ## override login and password if specified the old way.
1098+ $locals{'login'} = $login if defined $login;
1099+ $locals{'password'} = $password if defined $password;
1100+
1101+ ## allow {host} to be a comma separated list of hosts
1102+ foreach my $h (split_by_comma($host)) {
1103+ ## save a copy of the current globals
1104+ $config{$h} = { %locals };
1105+ $config{$h}{'host'} = $h;
1106+ }
1107+ }
1108+ %passwords = ();
1109+ }
1110+ close(FD);
1111+
1112+ warning("file ends while expecting a continuation line.")
1113+ if $continuation;
1114+
1115+ %$globals = %globals;
1116+ %$config = %config;
1117+
1118+ return $content;
1119+}
1120+######################################################################
1121+## init_config -
1122+######################################################################
1123+sub init_config {
1124+ %opt = %saved_opt;
1125+
1126+ ##
1127+ $opt{'quiet'} = 0 if opt('verbose');
1128+
1129+ ## infer the IP strategy if possible
1130+ $opt{'use'} = 'ip' if !define($opt{'use'}) && defined($opt{'ip'});
1131+ $opt{'use'} = 'if' if !define($opt{'use'}) && defined($opt{'if'});
1132+ $opt{'use'} = 'web' if !define($opt{'use'}) && defined($opt{'web'});
1133+
1134+ ## sanity check
1135+ $opt{'max-interval'} = min(interval(opt('max-interval')), interval(default('max-interval')));
1136+ $opt{'min-interval'} = max(interval(opt('min-interval')), interval(default('min-interval')));
1137+ $opt{'min-error-interval'} = max(interval(opt('min-error-interval')), interval(default('min-error-interval')));
1138+
1139+ $opt{'timeout'} = 0 if opt('timeout') < 0;
1140+
1141+ ## only set $opt{'daemon'} if it has been explicitly passed in
1142+ if (define($opt{'daemon'},$globals{'daemon'},0)) {
1143+ $opt{'daemon'} = interval(opt('daemon'));
1144+ $opt{'daemon'} = minimum('daemon')
1145+ if ($opt{'daemon'} < minimum('daemon'));
1146+ }
1147+
1148+ ## define or modify host options specified on the command-line
1149+ if (exists $opt{'options'} && defined $opt{'options'}) {
1150+ ## collect cmdline configuration options.
1151+ my %options = ();
1152+ foreach my $opt (split_by_comma($opt{'options'})) {
1153+ my ($name,$var) = split /\s*=\s*/, $opt;
1154+ $options{$name} = $var;
1155+ }
1156+ ## determine hosts specified with -host
1157+ my @hosts = ();
1158+ if (exists $opt{'host'}) {
1159+ foreach my $h (split_by_comma($opt{'host'})) {
1160+ push @hosts, $h;
1161+ }
1162+ }
1163+ ## and those in -options=...
1164+ if (exists $options{'host'}) {
1165+ foreach my $h (split_by_comma($options{'host'})) {
1166+ push @hosts, $h;
1167+ }
1168+ delete $options{'host'};
1169+ }
1170+ ## merge options into host definitions or globals
1171+ if (@hosts) {
1172+ foreach my $h (@hosts) {
1173+ $config{$h} = merge(\%options, $config{$h});
1174+ }
1175+ $opt{'host'} = join(',', @hosts);
1176+ } else {
1177+ %globals = %{ merge(\%options, \%globals) };
1178+ }
1179+ }
1180+
1181+ ## override global options with those on the command-line.
1182+ foreach my $o (keys %opt) {
1183+ if (defined $opt{$o} && exists $variables{'global-defaults'}{$o}) {
1184+ $globals{$o} = $opt{$o};
1185+ }
1186+ }
1187+
1188+ ## sanity check
1189+ if (defined $opt{'host'} && defined $opt{'retry'}) {
1190+ usage("options -retry and -host (or -option host=..) are mutually exclusive");
1191+ }
1192+
1193+ ## determine hosts to update (those on the cmd-line, config-file, or failed cached)
1194+ my @hosts = keys %config;
1195+ if (opt('host')) {
1196+ @hosts = split_by_comma($opt{'host'});
1197+ }
1198+ if (opt('retry')) {
1199+ @hosts = map { $_ if $cache{$_}{'status'} ne 'good' } keys %cache;
1200+ }
1201+
1202+ ## remove any other hosts
1203+ my %hosts;
1204+ map { $hosts{$_} = undef } @hosts;
1205+ map { delete $config{$_} unless exists $hosts{$_} } keys %config;
1206+
1207+ ## collect the cacheable variables.
1208+ foreach my $proto (keys %services) {
1209+ my @cacheable = ();
1210+ foreach my $k (keys %{$services{$proto}{'variables'}}) {
1211+ push @cacheable, $k if $services{$proto}{'variables'}{$k}{'cache'};
1212+ }
1213+ $services{$proto}{'cacheable'} = [ @cacheable ];
1214+ }
1215+
1216+ ## sanity check..
1217+ ## make sure config entries have all defaults and they meet minimums
1218+ ## first the globals...
1219+ foreach my $k (keys %globals) {
1220+ my $def = $variables{'merged'}{$k};
1221+ my $ovalue = define($globals{$k}, $def->{'default'});
1222+ my $value = check_value($ovalue, $def);
1223+ if ($def->{'required'} && !defined $value) {
1224+ $value = default($k);
1225+ warning("'%s=%s' is an invalid %s. (using default of %s)", $k, $ovalue, $def->{'type'}, $value);
1226+ }
1227+ $globals{$k} = $value;
1228+ }
1229+
1230+ ## now the host definitions...
1231+ HOST:
1232+ foreach my $h (keys %config) {
1233+ my $proto;
1234+ $proto = $config{$h}{'protocol'};
1235+ $proto = opt('protocol') if !defined($proto);
1236+
1237+ load_sha1_support() if ($proto eq "freedns");
1238+
1239+ if (!exists($services{$proto})) {
1240+ warning("skipping host: %s: unrecognized protocol '%s'", $h, $proto);
1241+ delete $config{$h};
1242+
1243+ } else {
1244+ my $svars = $services{$proto}{'variables'};
1245+ my $conf = { 'protocol' => $proto };
1246+
1247+ foreach my $k (keys %$svars) {
1248+ my $def = $svars->{$k};
1249+ my $ovalue = define($config{$h}{$k}, $def->{'default'});
1250+ my $value = check_value($ovalue, $def);
1251+ if ($def->{'required'} && !defined $value) {
1252+ warning("skipping host: %s: '%s=%s' is an invalid %s.", $h, $k, $ovalue, $def->{'type'});
1253+ delete $config{$h};
1254+ next HOST;
1255+ }
1256+ $conf->{$k} = $value;
1257+
1258+ }
1259+ $config{$h} = $conf;
1260+ $config{$h}{'cacheable'} = [ @{$services{$proto}{'cacheable'}} ];
1261+ }
1262+ }
1263+}
1264+
1265+######################################################################
1266+## usage
1267+######################################################################
1268+sub usage {
1269+ my $exitcode = 1;
1270+ $exitcode = shift if @_ != 0; # use first arg if given
1271+ my $msg = '';
1272+ if (@_) {
1273+ my $format = shift;
1274+ $msg .= sprintf $format, @_;
1275+ 1 while chomp($msg);
1276+ $msg .= "\n";
1277+ }
1278+ printf STDERR "%s%s\n", $msg, $opt_usage;
1279+ sendmail();
1280+ exit $exitcode;
1281+}
1282+
1283+######################################################################
1284+## process_args -
1285+######################################################################
1286+sub process_args {
1287+ my @spec = ();
1288+ my $usage = "";
1289+ my %opts = ();
1290+
1291+ foreach (@_) {
1292+ if (ref $_) {
1293+ my ($key, $specifier, $arg_usage) = @$_;
1294+ my $value = default($key);
1295+
1296+ ## add a option specifier
1297+ push @spec, $key . $specifier;
1298+
1299+ ## define the default value which can be overwritten later
1300+ $opt{$key} = undef;
1301+
1302+ next unless $arg_usage;
1303+
1304+ ## add a line to the usage;
1305+ $usage .= " $arg_usage";
1306+ if (defined($value) && $value ne '') {
1307+ $usage .= " (default: ";
1308+ if ($specifier eq '!') {
1309+ $usage .= "no" if ($specifier eq '!') && !$value;
1310+ $usage .= $key;
1311+ } else {
1312+ $usage .= $value;
1313+ }
1314+ $usage .= ")";
1315+ }
1316+ $usage .= ".";
1317+ } else {
1318+ $usage .= $_;
1319+ }
1320+ $usage .= "\n";
1321+ }
1322+ ## process the arguments
1323+ if (! GetOptions(\%opt, @spec)) {
1324+ $opt{"help"} = 1;
1325+ }
1326+ return ($usage, %opt);
1327+}
1328+######################################################################
1329+## test_possible_ip - print possible IPs
1330+######################################################################
1331+sub test_possible_ip {
1332+ local $opt{'debug'} = 0;
1333+
1334+ printf "use=ip, ip=%s address is %s\n", opt('ip'), define(get_ip('ip'), 'NOT FOUND')
1335+ if defined opt('ip');
1336+
1337+ {
1338+ local $opt{'use'} = 'if';
1339+ foreach my $if (grep {/^[a-zA-Z]/} `ifconfig -a`) {
1340+ $if =~ s/:?\s.*//is;
1341+ local $opt{'if'} = $if;
1342+ printf "use=if, if=%s address is %s\n", opt('if'), define(get_ip('if'), 'NOT FOUND');
1343+ }
1344+ }
1345+ if (opt('fw')) {
1346+ if (opt('fw') !~ m%/%) {
1347+ foreach my $fw (sort keys %builtinfw) {
1348+ local $opt{'use'} = $fw;
1349+ printf "use=$fw address is %s\n", define(get_ip($fw), 'NOT FOUND');
1350+ }
1351+ }
1352+ local $opt{'use'} = 'fw';
1353+ printf "use=fw, fw=%s address is %s\n", opt('fw'), define(get_ip(opt('fw')), 'NOT FOUND')
1354+ if ! exists $builtinfw{opt('fw')};
1355+
1356+ }
1357+ {
1358+ local $opt{'use'} = 'web';
1359+ foreach my $web (sort keys %builtinweb) {
1360+ local $opt{'web'} = $web;
1361+ printf "use=web, web=$web address is %s\n", define(get_ip('web'), 'NOT FOUND');
1362+ }
1363+ printf "use=web, web=%s address is %s\n", opt('web'), define(get_ip('web'), 'NOT FOUND')
1364+ if ! exists $builtinweb{opt('web')};
1365+ }
1366+ if (opt('cmd')) {
1367+ local $opt{'use'} = 'cmd';
1368+ printf "use=cmd, cmd=%s address is %s\n", opt('cmd'), define(get_ip('cmd'), 'NOT FOUND');
1369+ }
1370+ exit 0 unless opt('debug');
1371+}
1372+######################################################################
1373+## test_geturl - print (and save if -test) result of fetching a URL
1374+######################################################################
1375+sub test_geturl {
1376+ my $url = shift;
1377+
1378+ my $reply = geturl(opt('proxy'), $url, opt('login'), opt('password'));
1379+ print "URL $url\n";;
1380+ print defined($reply) ? $reply : "<undefined>\n";
1381+ exit;
1382+}
1383+######################################################################
1384+## load_file
1385+######################################################################
1386+sub load_file {
1387+ my $file = shift;
1388+ my $buffer = '';
1389+
1390+ if (exists($ENV{'TEST_CASE'})) {
1391+ my $try = "$file-$ENV{'TEST_CASE'}";
1392+ $file = $try if -f $try;
1393+ }
1394+
1395+ local *FD;
1396+ if (open(FD, "< $file")) {
1397+ read(FD, $buffer, -s FD);
1398+ close(FD);
1399+ debug("Loaded %d bytes from %s", length($buffer), $file);
1400+ } else {
1401+ debug("Load failed from %s ($!)", $file);
1402+ }
1403+ return $buffer
1404+}
1405+######################################################################
1406+## save_file
1407+######################################################################
1408+sub save_file {
1409+ my ($file, $buffer, $opt) = @_;
1410+
1411+ $file .= "-$ENV{'TEST_CASE'}" if exists $ENV{'TEST_CASE'};
1412+ if (defined $opt) {
1413+ my $i = 0;
1414+ while (-f "$file-$i") {
1415+ if ('unique' =~ /^$opt/i) {
1416+ my $a = join('\n', grep {!/^Date:/} split /\n/, $buffer);
1417+ my $b = join('\n', grep {!/^Date:/} split /\n/, load_file("$file-$i"));
1418+ last if $a eq $b;
1419+ }
1420+ $i++;
1421+ }
1422+ $file = "$file-$i";
1423+ }
1424+ debug("Saving to %s", $file);
1425+ local *FD;
1426+ open(FD, "> $file") or return;
1427+ print FD $buffer;
1428+ close(FD);
1429+ return $buffer;
1430+}
1431+######################################################################
1432+## print_opt
1433+## print_globals
1434+## print_config
1435+## print_cache
1436+## print_info
1437+######################################################################
1438+sub _print_hash {
1439+ my ($string, $ptr) = @_;
1440+ my $value = $ptr;
1441+
1442+ if (! defined($ptr)) {
1443+ $value = "<undefined>";
1444+ } elsif (ref $ptr eq 'HASH') {
1445+ foreach my $key (sort keys %$ptr) {
1446+ _print_hash("${string}\{$key\}", $ptr->{$key});
1447+ }
1448+ return;
1449+ }
1450+ printf "%-36s : %s\n", $string, $value;
1451+}
1452+sub print_hash {
1453+ my ($string, $hash) = @_;
1454+ printf "=== %s ====\n", $string;
1455+ _print_hash($string, $hash);
1456+}
1457+sub print_opt { print_hash("opt", \%opt); }
1458+sub print_globals { print_hash("globals", \%globals); }
1459+sub print_config { print_hash("config", \%config); }
1460+sub print_cache { print_hash("cache", \%cache); }
1461+sub print_info {
1462+ print_opt();
1463+ print_globals();
1464+ print_config();
1465+ print_cache();
1466+}
1467+######################################################################
1468+## pipecmd - run an external command
1469+## logger
1470+## sendmail
1471+######################################################################
1472+sub pipecmd {
1473+ my $cmd = shift;
1474+ my $stdin = join("\n", @_);
1475+ my $ok = 0;
1476+
1477+ ## remove trailing newlines
1478+ 1 while chomp($stdin);
1479+
1480+ ## override when debugging.
1481+ $cmd = opt('exec') ? "| $cmd" : "> /dev/null";
1482+
1483+ ## execute the command.
1484+ local *FD;
1485+ if (! open(FD, $cmd)) {
1486+ printf STDERR "$program: cannot execute command %s.\n", $cmd;
1487+
1488+ } elsif ($stdin && (! print FD "$stdin\n")) {
1489+ printf STDERR "$program: failed writting to %s.\n", $cmd;
1490+ close(FD);
1491+
1492+ } elsif (! close(FD)) {
1493+ printf STDERR "$program: failed closing %s.($@)\n", $cmd;
1494+
1495+ } elsif (opt('exec') && $?) {
1496+ printf STDERR "$program: failed %s. ($@)\n", $cmd;
1497+
1498+ } else {
1499+ $ok = 1;
1500+ }
1501+ return $ok;
1502+}
1503+sub logger {
1504+ if (opt('syslog') && opt('facility') && opt('priority')) {
1505+ my $facility = opt('facility');
1506+ my $priority = opt('priority');
1507+ return pipecmd("logger -p$facility.$priority -t${program}\[$$\]", @_);
1508+ }
1509+ return 1;
1510+}
1511+sub sendmail {
1512+ my $recipients = opt('mail');
1513+
1514+ if (opt('mail-failure') && ($result ne 'OK' && $result ne '0')) {
1515+ $recipients = opt('mail-failure');
1516+ }
1517+ if ($msgs && $recipients && $msgs ne $last_msgs) {
1518+ pipecmd("sendmail -oi $recipients",
1519+ "To: $recipients",
1520+ "Subject: status report from $program\@$hostname",
1521+ "\r\n",
1522+ $msgs,
1523+ "",
1524+ "regards,",
1525+ " $program\@$hostname (version $version)"
1526+ );
1527+ }
1528+ $last_msgs = $msgs;
1529+ $msgs = '';
1530+}
1531+######################################################################
1532+## split_by_comma
1533+## merge
1534+## default
1535+## minimum
1536+## opt
1537+######################################################################
1538+sub split_by_comma {
1539+ my $string = shift;
1540+
1541+ return split /\s*[, ]\s*/, $string if defined $string;
1542+ return ();
1543+}
1544+sub merge {
1545+ my %merged = ();
1546+ foreach my $h (@_) {
1547+ foreach my $k (keys %$h) {
1548+ $merged{$k} = $h->{$k} unless exists $merged{$k};
1549+ }
1550+ }
1551+ return \%merged;
1552+}
1553+sub default {
1554+ my $v = shift;
1555+ return $variables{'merged'}{$v}{'default'};
1556+}
1557+sub minimum {
1558+ my $v = shift;
1559+ return $variables{'merged'}{$v}{'minimum'};
1560+}
1561+sub opt {
1562+ my $v = shift;
1563+ my $h = shift;
1564+ return $config{$h}{$v} if defined($h && $config{$h}{$v});
1565+ return $opt{$v} if defined $opt{$v};
1566+ return $globals{$v} if defined $globals{$v};
1567+ return default($v) if defined default($v);
1568+ return undef;
1569+}
1570+sub min {
1571+ my $min = shift;
1572+ foreach my $arg (@_) {
1573+ $min = $arg if $arg < $min;
1574+ }
1575+ return $min;
1576+}
1577+sub max {
1578+ my $max = shift;
1579+ foreach my $arg (@_) {
1580+ $max = $arg if $arg > $max;
1581+ }
1582+ return $max;
1583+}
1584+######################################################################
1585+## define
1586+######################################################################
1587+sub define {
1588+ foreach (@_) {
1589+ return $_ if defined $_;
1590+ }
1591+ return undef;
1592+}
1593+######################################################################
1594+## ynu
1595+######################################################################
1596+sub ynu {
1597+ my ($value, $yes, $no, $undef) = @_;
1598+
1599+ return $no if !defined($value) || !$value;
1600+ return $yes if $value eq '1';
1601+ foreach (qw(yes true)) {
1602+ return $yes if $_ =~ /^$value/i;
1603+ }
1604+ foreach (qw(no false)) {
1605+ return $no if $_ =~ /^$value/i;
1606+ }
1607+ return $undef;
1608+}
1609+######################################################################
1610+## msg
1611+## debug
1612+## warning
1613+## fatal
1614+######################################################################
1615+sub _msg {
1616+ my $log = shift;
1617+ my $prefix = shift;
1618+ my $format = shift;
1619+ my $buffer = sprintf $format, @_;
1620+ chomp($buffer);
1621+
1622+ $prefix = sprintf "%-9s ", $prefix if $prefix;
1623+ if ($file) {
1624+ $prefix .= "file $file";
1625+ $prefix .= ", line $lineno" if $lineno;
1626+ $prefix .= ": ";
1627+ }
1628+ if ($prefix) {
1629+ $buffer = "$prefix$buffer";
1630+ $buffer =~ s/\n/\n$prefix /g;
1631+ }
1632+ $buffer .= "\n";
1633+ print $buffer;
1634+
1635+ $msgs .= $buffer if $log;
1636+ logger($buffer) if $log;
1637+
1638+}
1639+sub msg { _msg(0, '', @_); }
1640+sub verbose { _msg(1, @_) if opt('verbose'); }
1641+sub info { _msg(1, 'INFO:', @_) if opt('verbose'); }
1642+sub debug { _msg(0, 'DEBUG:', @_) if opt('debug'); }
1643+sub debug2 { _msg(0, 'DEBUG:', @_) if opt('debug') && opt('verbose');}
1644+sub warning { _msg(1, 'WARNING:', @_); }
1645+sub fatal { _msg(1, 'FATAL:', @_); sendmail(); exit(1); }
1646+sub success { _msg(1, 'SUCCESS:', @_); }
1647+sub failed { _msg(1, 'FAILED:', @_); $result = 'FAILED'; }
1648+sub prettytime { return scalar(localtime(shift)); }
1649+
1650+sub prettyinterval {
1651+ my $interval = shift;
1652+ use integer;
1653+ my $s = $interval % 60; $interval /= 60;
1654+ my $m = $interval % 60; $interval /= 60;
1655+ my $h = $interval % 24; $interval /= 24;
1656+ my $d = $interval;
1657+
1658+ my $string = "";
1659+ $string .= "$d day" if $d;
1660+ $string .= "s" if $d > 1;
1661+ $string .= ", " if $string && $h;
1662+ $string .= "$h hour" if $h;
1663+ $string .= "s" if $h > 1;
1664+ $string .= ", " if $string && $m;
1665+ $string .= "$m minute" if $m;
1666+ $string .= "s" if $m > 1;
1667+ $string .= ", " if $string && $s;
1668+ $string .= "$s second" if $s;
1669+ $string .= "s" if $s > 1;
1670+ return $string;
1671+}
1672+sub interval {
1673+ my $value = shift;
1674+ if ($value =~ /^(\d+)(seconds|s)/i) {
1675+ $value = $1;
1676+ } elsif ($value =~ /^(\d+)(minutes|m)/i) {
1677+ $value = $1 * 60;
1678+ } elsif ($value =~ /^(\d+)(hours|h)/i) {
1679+ $value = $1 * 60*60;
1680+ } elsif ($value =~ /^(\d+)(days|d)/i) {
1681+ $value = $1 * 60*60*24;
1682+ } elsif ($value !~ /^\d+$/) {
1683+ $value = undef;
1684+ }
1685+ return $value;
1686+}
1687+sub interval_expired {
1688+ my ($host, $time, $interval) = @_;
1689+
1690+ return 1 if !exists $cache{$host};
1691+ return 1 if !exists $cache{$host}{$time} || !$cache{$host}{$time};
1692+ return 1 if !exists $config{$host}{$interval} || !$config{$host}{$interval};
1693+
1694+ return $now > ($cache{$host}{$time} + $config{$host}{$interval});
1695+}
1696+
1697+
1698+
1699+######################################################################
1700+## check_value
1701+######################################################################
1702+sub check_value {
1703+ my ($value, $def) = @_;
1704+ my $type = $def->{'type'};
1705+ my $min = $def->{'minimum'};
1706+ my $required = $def->{'required'};
1707+
1708+ if (!defined $value && !$required) {
1709+ ;
1710+
1711+ } elsif ($type eq T_DELAY) {
1712+ $value = interval($value);
1713+ $value = $min if defined($value) && defined($min) && $value < $min;
1714+
1715+ } elsif ($type eq T_NUMBER) {
1716+ return undef if $value !~ /^\d+$/;
1717+ $value = $min if defined($min) && $value < $min;
1718+
1719+ } elsif ($type eq T_BOOL) {
1720+ if ($value =~ /^y(es)?$|^t(true)?$|^1$/i) {
1721+ $value = 1;
1722+ } elsif ($value =~ /^n(o)?$|^f(alse)?$|^0$/i) {
1723+ $value = 0;
1724+ } else {
1725+ return undef;
1726+ }
1727+ } elsif ($type eq T_FQDN || $type eq T_OFQDN && $value ne '') {
1728+ $value = lc $value;
1729+ return undef if $value !~ /[^.]\.[^.]/;
1730+
1731+ } elsif ($type eq T_FQDNP) {
1732+ $value = lc $value;
1733+ return undef if $value !~ /[^.]\.[^.].*(:\d+)?$/;
1734+
1735+ } elsif ($type eq T_PROTO) {
1736+ $value = lc $value;
1737+ return undef if ! exists $services{$value};
1738+
1739+ } elsif ($type eq T_USE) {
1740+ $value = lc $value;
1741+ return undef if ! exists $ip_strategies{$value};
1742+
1743+ } elsif ($type eq T_FILE) {
1744+ return undef if $value eq "";
1745+
1746+ } elsif ($type eq T_IF) {
1747+ return undef if $value !~ /^[a-z0-9:._-]+$/;
1748+
1749+ } elsif ($type eq T_PROG) {
1750+ return undef if $value eq "";
1751+
1752+ } elsif ($type eq T_LOGIN) {
1753+ return undef if $value eq "";
1754+
1755+# } elsif ($type eq T_PASSWD) {
1756+# return undef if $value =~ /:/;
1757+
1758+ } elsif ($type eq T_IP) {
1759+ return undef if $value !~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
1760+ }
1761+ return $value;
1762+}
1763+######################################################################
1764+## encode_base64 - from MIME::Base64
1765+######################################################################
1766+sub encode_base64 ($;$) {
1767+ my $res = '';
1768+ my $eol = $_[1];
1769+ $eol = "\n" unless defined $eol;
1770+ pos($_[0]) = 0; # ensure start at the beginning
1771+ while ($_[0] =~ /(.{1,45})/gs) {
1772+ $res .= substr(pack('u', $1), 1);
1773+ chop($res);
1774+ }
1775+ $res =~ tr|` -_|AA-Za-z0-9+/|; # `# help emacs
1776+
1777+ # fix padding at the end
1778+ my $padding = (3 - length($_[0]) % 3) % 3;
1779+ $res =~ s/.{$padding}$/'=' x $padding/e if $padding;
1780+ $res;
1781+}
1782+######################################################################
1783+## load_ssl_support
1784+######################################################################
1785+sub load_ssl_support {
1786+ my $ssl_loaded = eval {require IO::Socket::SSL};
1787+ unless ($ssl_loaded) {
1788+ fatal(<<"EOM");
1789+Error loading the Perl module IO::Socket::SSL needed for SSL connect.
1790+On Debian, the package libio-socket-ssl-perl must be installed.
1791+On Red Hat, the package perl-IO-Socket-SSL must be installed.
1792+EOM
1793+ }
1794+ import IO::Socket::SSL;
1795+ { no warnings; $IO::Socket::SSL::DEBUG = 0; }
1796+}
1797+######################################################################
1798+## load_sha1_support
1799+######################################################################
1800+sub load_sha1_support {
1801+ my $sha1_loaded = eval {require Digest::SHA1};
1802+ unless ($sha1_loaded) {
1803+ fatal(<<"EOM");
1804+Error loading the Perl module Digest::SHA1 needed for freedns update.
1805+On Debian, the package libdigest-sha1-perl must be installed.
1806+EOM
1807+ }
1808+ import Digest::SHA1 (qw/sha1_hex/);
1809+}
1810+######################################################################
1811+## geturl
1812+######################################################################
1813+sub geturl {
1814+ my $proxy = shift || '';
1815+ my $url = shift || '';
1816+ my $login = shift || '';
1817+ my $password = shift || '';
1818+ my ($peer, $server, $port, $default_port, $use_ssl);
1819+ my ($sd, $rq, $request, $reply);
1820+
1821+ debug("proxy = $proxy");
1822+ debug("url = %s", $url);
1823+ ## canonify proxy and url
1824+ my $force_ssl;
1825+ $force_ssl = 1 if ($url =~ /^https:/);
1826+ $proxy =~ s%^https?://%%i;
1827+ $url =~ s%^https?://%%i;
1828+ $server = $url;
1829+ $server =~ s%/.*%%;
1830+ $url = "/" unless $url =~ m%/%;
1831+ $url =~ s%^[^/]*/%%;
1832+
1833+ debug("server = $server");
1834+ opt('fw') && debug("opt(fw = ",opt('fw'),")");
1835+ $globals{'fw'} && debug("glo fw = $globals{'fw'}");
1836+ #if ( $globals{'ssl'} and $server ne $globals{'fw'} ) {
1837+ ## always omit SSL for connections to local router
1838+ if ( $force_ssl || ($globals{'ssl'} and (caller(1))[3] ne 'main::get_ip') ) {
1839+ $use_ssl = 1;
1840+ $default_port = 443;
1841+ load_ssl_support;
1842+ } else {
1843+ $use_ssl = 0;
1844+ $default_port = 80;
1845+ }
1846+
1847+ ## determine peer and port to use.
1848+ $peer = $proxy || $server;
1849+ $peer =~ s%/.*%%;
1850+ $port = $peer;
1851+ $port =~ s%^.*:%%;
1852+ $port = $default_port unless $port =~ /^\d+$/;
1853+ $peer =~ s%:.*$%%;
1854+
1855+ my $to = sprintf "%s%s", $server, $proxy ? " via proxy $peer:$port" : "";
1856+ verbose("CONNECT:", "%s", $to);
1857+
1858+ $request = "GET ";
1859+ $request .= "http://$server" if $proxy;
1860+ $request .= "/$url HTTP/1.0\n";
1861+ $request .= "Host: $server\n";
1862+
1863+ my $auth = encode_base64("${login}:${password}");
1864+ $request .= "Authorization: Basic $auth\n" if $login || $password;
1865+ $request .= "User-Agent: ${program}/${version}\n";
1866+ $request .= "Connection: close\n";
1867+ $request .= "\n";
1868+
1869+ ## make sure newlines are <cr><lf> for some pedantic proxy servers
1870+ ($rq = $request) =~ s/\n/\r\n/g;
1871+
1872+ # local $^W = 0;
1873+ $0 = sprintf("%s - connecting to %s port %s", $program, $peer, $port);
1874+ if (! opt('exec')) {
1875+ debug("skipped network connection");
1876+ verbose("SENDING:", "%s", $request);
1877+ } elsif ($use_ssl) {
1878+ $sd = IO::Socket::SSL->new(
1879+ PeerAddr => $peer,
1880+ PeerPort => $port,
1881+ Proto => 'tcp',
1882+ MultiHomed => 1,
1883+ Timeout => opt('timeout'),
1884+ );
1885+ defined $sd or warning("cannot connect to $peer:$port socket: $@ " . IO::Socket::SSL::errstr());
1886+ } else {
1887+ $sd = IO::Socket::INET->new(
1888+ PeerAddr => $peer,
1889+ PeerPort => $port,
1890+ Proto => 'tcp',
1891+ MultiHomed => 1,
1892+ Timeout => opt('timeout'),
1893+ );
1894+ defined $sd or warning("cannot connect to $peer:$port socket: $@");
1895+ }
1896+
1897+ if (defined $sd) {
1898+ ## send the request to the http server
1899+ verbose("CONNECTED: ", $use_ssl ? 'using SSL' : 'using HTTP');
1900+ verbose("SENDING:", "%s", $request);
1901+
1902+ $0 = sprintf("%s - sending to %s port %s", $program, $peer, $port);
1903+ my $result = syswrite $sd, $rq;
1904+ if ($result != length($rq)) {
1905+ warning("cannot send to $peer:$port ($!).");
1906+ } else {
1907+ $0 = sprintf("%s - reading from %s port %s", $program, $peer, $port);
1908+ eval {
1909+ local $SIG{'ALRM'} = sub { die "timeout";};
1910+ alarm(opt('timeout')) if opt('timeout') > 0;
1911+ while ($_ = <$sd>) {
1912+ $0 = sprintf("%s - read from %s port %s", $program, $peer, $port);
1913+ verbose("RECEIVE:", "%s", define($_, "<undefined>"));
1914+ $reply .= $_ if defined $_;
1915+ }
1916+ if (opt('timeout') > 0) {
1917+ alarm(0);
1918+ }
1919+ };
1920+ close($sd);
1921+
1922+ if ($@ and $@ =~ /timeout/) {
1923+ warning("TIMEOUT: %s after %s seconds", $to, opt('timeout'));
1924+ $reply = '';
1925+ }
1926+ $reply = '' if !defined $reply;
1927+ }
1928+ }
1929+ $0 = sprintf("%s - closed %s port %s", $program, $peer, $port);
1930+
1931+ ## during testing simulate reading the URL
1932+ if (opt('test')) {
1933+ my $filename = "$server/$url";
1934+ $filename =~ s|/|%2F|g;
1935+ if (opt('exec')) {
1936+ $reply = save_file("${savedir}$filename", $reply, 'unique');
1937+ } else {
1938+ $reply = load_file("${savedir}$filename");
1939+ }
1940+ }
1941+
1942+ $reply =~ s/\r//g if defined $reply;
1943+ return $reply;
1944+}
1945+######################################################################
1946+## get_ip
1947+######################################################################
1948+sub get_ip {
1949+ my $use = lc shift;
1950+ my $h = shift;
1951+ my ($ip, $arg, $reply, $url, $skip) = (undef, opt($use), '');
1952+ $arg = '' unless $arg;
1953+
1954+ if ($use eq 'ip') {
1955+ $ip = opt('ip', $h);
1956+ $arg = 'ip';
1957+
1958+ } elsif ($use eq 'if') {
1959+ $skip = opt('if-skip', $h) || '';
1960+ $reply = `ifconfig $arg 2> /dev/null`;
1961+ $reply = '' if $?;
1962+
1963+ } elsif ($use eq 'cmd') {
1964+ if ($arg) {
1965+ $skip = opt('cmd-skip', $h) || '';
1966+ $reply = `$arg`;
1967+ $reply = '' if $?;
1968+ }
1969+
1970+ } elsif ($use eq 'web') {
1971+ $url = opt('web', $h) || '';
1972+ $skip = opt('web-skip', $h) || '';
1973+
1974+ if (exists $builtinweb{$url}) {
1975+ $skip = $builtinweb{$url}->{'skip'} unless $skip;
1976+ $url = $builtinweb{$url}->{'url'};
1977+ }
1978+ $arg = $url;
1979+
1980+ if ($url) {
1981+ $reply = geturl(opt('proxy', $h), $url) || '';
1982+ }
1983+
1984+ } elsif (($use eq 'cisco')) {
1985+ # Stuff added to support Cisco router ip http daemon
1986+ # User fw-login should only have level 1 access to prevent
1987+ # password theft. This is pretty harmless.
1988+ my $queryif = opt('if', $h);
1989+ $skip = opt('fw-skip', $h) || '';
1990+
1991+ # Convert slashes to protected value "\/"
1992+ $queryif =~ s%\/%\\\/%g;
1993+
1994+ # Protect special HTML characters (like '?')
1995+ $queryif =~ s/([\?&= ])/sprintf("%%%02x",ord($1))/ge;
1996+
1997+ $url = "http://".opt('fw', $h)."/level/1/exec/show/ip/interface/brief/${queryif}/CR";
1998+ $reply = geturl('', $url, opt('fw-login', $h), opt('fw-password', $h)) || '';
1999+ $arg = $url;
2000+
2001+ } elsif (($use eq 'cisco-asa')) {
2002+ # Stuff added to support Cisco ASA ip https daemon
2003+ # User fw-login should only have level 1 access to prevent
2004+ # password theft. This is pretty harmless.
2005+ my $queryif = opt('if', $h);
2006+ $skip = opt('fw-skip', $h) || '';
2007+
2008+ # Convert slashes to protected value "\/"
2009+ $queryif =~ s%\/%\\\/%g;
2010+
2011+ # Protect special HTML characters (like '?')
2012+ $queryif =~ s/([\?&= ])/sprintf("%%%02x",ord($1))/ge;
2013+
2014+ $url = "https://".opt('fw', $h)."/exec/show%20interface%20${queryif}";
2015+ $reply = geturl('', $url, opt('fw-login', $h), opt('fw-password', $h)) || '';
2016+ $arg = $url;
2017+
2018+ } else {
2019+ $url = opt('fw', $h) || '';
2020+ $skip = opt('fw-skip', $h) || '';
2021+
2022+ if (exists $builtinfw{$use}) {
2023+ $skip = $builtinfw{$use}->{'skip'} unless $skip;
2024+ $url = "http://${url}" . $builtinfw{$use}->{'url'} unless $url =~ /\//;
2025+ }
2026+ $arg = $url;
2027+
2028+ if ($url) {
2029+ $reply = geturl('', $url, opt('fw-login', $h), opt('fw-password', $h)) || '';
2030+ }
2031+ }
2032+ if (!defined $reply) {
2033+ $reply = '';
2034+ }
2035+ if ($skip) {
2036+ $skip =~ s/ /\\s/is;
2037+ $reply =~ s/^.*?${skip}//is;
2038+ }
2039+ if ($reply =~ /^.*?\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\b.*/is) {
2040+ $ip = $1;
2041+ }
2042+ if (($use ne 'ip') && (define($ip,'') eq '0.0.0.0')) {
2043+ $ip = undef;
2044+ }
2045+
2046+ debug("get_ip: using %s, %s reports %s", $use, $arg, define($ip, "<undefined>"));
2047+ return $ip;
2048+}
2049+
2050+######################################################################
2051+## group_hosts_by
2052+######################################################################
2053+sub group_hosts_by {
2054+ my ($hosts, $attributes) = @_;
2055+
2056+ my %groups = ();
2057+ foreach my $h (@$hosts) {
2058+ my @keys = (@$attributes, 'wantip');
2059+ map { $config{$h}{$_} = '' unless exists $config{$h}{$_} } @keys;
2060+ my $sig = join(',', map { "$_=$config{$h}{$_}" } @keys);
2061+
2062+ push @{$groups{$sig}}, $h;
2063+ }
2064+ return %groups;
2065+}
2066+######################################################################
2067+## nic_examples
2068+######################################################################
2069+sub nic_examples {
2070+ my $examples = "";
2071+ my $separator = "";
2072+ foreach my $s (sort keys %services) {
2073+ my $subr = $services{$s}{'examples'};
2074+ my $example;
2075+
2076+ if (defined($subr) && ($example = &$subr())) {
2077+ chomp($example);
2078+ $examples .= $example;
2079+ $examples .= "\n\n$separator";
2080+ $separator = "\n";
2081+ }
2082+ }
2083+ my $intro = <<EoEXAMPLE;
2084+== CONFIGURING ${program}
2085+
2086+The configuration file, ${program}.conf, can be used to define the
2087+default behaviour and operation of ${program}. The file consists of
2088+sequences of global variable definitions and host definitions.
2089+
2090+Global definitions look like:
2091+ name=value [,name=value]*
2092+
2093+For example:
2094+ daemon=5m
2095+ use=if, if=eth0
2096+ proxy=proxy.myisp.com
2097+ protocol=dyndns2
2098+
2099+specifies that ${program} should operate as a daemon, checking the
2100+eth0 interface for an IP address change every 5 minutes and use the
2101+'dyndns2' protocol by default. The daemon interval can be specified
2102+as seconds (600s), minutes (5m), hours (1h) or days (1d).
2103+
2104+Host definitions look like:
2105+ [name=value [,name=value]*]* a.host.domain [,b.host.domain] [login] [password]
2106+
2107+For example:
2108+ protocol=hammernode1, \\
2109+ login=my-hn-login, password=my-hn-password myhost.hn.org
2110+ login=my-login, password=my-password myhost.dyndns.org,my2nd.dyndns.org
2111+
2112+specifies two host definitions.
2113+
2114+The first definition will use the hammernode1 protocol,
2115+my-hn-login and my-hn-password to update the ip-address of
2116+myhost.hn.org and my2ndhost.hn.org.
2117+
2118+The second host definition will use the current default protocol
2119+('dyndns2'), my-login and my-password to update the ip-address of
2120+myhost.dyndns.org and my2ndhost.dyndns.org.
2121+
2122+The order of this sequence is significant because the values of any
2123+global variable definitions are bound to a host definition when the
2124+host definition is encountered.
2125+
2126+See the sample-${program}.conf file for further examples.
2127+EoEXAMPLE
2128+ $intro .= "\n== NIC specific variables and examples:\n$examples" if $examples;
2129+ return $intro;
2130+}
2131+######################################################################
2132+## nic_updateable
2133+######################################################################
2134+sub nic_updateable {
2135+ my $host = shift;
2136+ my $sub = shift;
2137+ my $update = 0;
2138+ my $ip = $config{$host}{'wantip'};
2139+
2140+ if ($config{$host}{'login'} eq '') {
2141+ warning("null login name specified for host %s.", $host);
2142+
2143+ } elsif ($config{$host}{'password'} eq '') {
2144+ warning("null password specified for host %s.", $host);
2145+
2146+ } elsif ($opt{'force'}) {
2147+ info("forcing update of %s.", $host);
2148+ $update = 1;
2149+
2150+ } elsif (!exists($cache{$host})) {
2151+ info("forcing updating %s because no cached entry exists.", $host);
2152+ $update = 1;
2153+
2154+ } elsif ($cache{$host}{'wtime'} && $cache{$host}{'wtime'} > $now) {
2155+ warning("cannot update %s from %s to %s until after %s.",
2156+ $host,
2157+ ($cache{$host}{'ip'} ? $cache{$host}{'ip'} : '<nothing>'), $ip,
2158+ prettytime($cache{$host}{'wtime'})
2159+ );
2160+
2161+ } elsif ($cache{$host}{'mtime'} && interval_expired($host, 'mtime', 'max-interval')) {
2162+ warning("forcing update of %s from %s to %s; %s since last update on %s.",
2163+ $host,
2164+ ($cache{$host}{'ip'} ? $cache{$host}{'ip'} : '<nothing>'), $ip,
2165+ prettyinterval($config{$host}{'max-interval'}),
2166+ prettytime($cache{$host}{'mtime'})
2167+ );
2168+ $update = 1;
2169+
2170+ } elsif ((!exists($cache{$host}{'ip'})) ||
2171+ ("$cache{$host}{'ip'}" ne "$ip")) {
2172+ if (($cache{$host}{'status'} eq 'good') &&
2173+ !interval_expired($host, 'mtime', 'min-interval')) {
2174+
2175+ warning("skipping update of %s from %s to %s.\nlast updated %s.\nWait at least %s between update attempts.",
2176+ $host,
2177+ ($cache{$host}{'ip'} ? $cache{$host}{'ip'} : '<nothing>'),
2178+ $ip,
2179+ ($cache{$host}{'mtime'} ? prettytime($cache{$host}{'mtime'}) : '<never>'),
2180+ prettyinterval($config{$host}{'min-interval'})
2181+ )
2182+ if opt('verbose') || !define($cache{$host}{'warned-min-interval'}, 0);
2183+
2184+ $cache{$host}{'warned-min-interval'} = $now;
2185+
2186+ } elsif (($cache{$host}{'status'} ne 'good') && !interval_expired($host, 'atime', 'min-error-interval')) {
2187+
2188+ warning("skipping update of %s from %s to %s.\nlast updated %s but last attempt on %s failed.\nWait at least %s between update attempts.",
2189+ $host,
2190+ ($cache{$host}{'ip'} ? $cache{$host}{'ip'} : '<nothing>'),
2191+ $ip,
2192+ ($cache{$host}{'mtime'} ? prettytime($cache{$host}{'mtime'}) : '<never>'),
2193+ ($cache{$host}{'atime'} ? prettytime($cache{$host}{'atime'}) : '<never>'),
2194+ prettyinterval($config{$host}{'min-error-interval'})
2195+ )
2196+ if opt('verbose') || !define($cache{$host}{'warned-min-error-interval'}, 0);
2197+
2198+ $cache{$host}{'warned-min-error-interval'} = $now;
2199+
2200+ } else {
2201+ $update = 1;
2202+ }
2203+
2204+ } elsif (defined($sub) && &$sub($host)) {
2205+ $update = 1;
2206+ } elsif ((defined($cache{$host}{'static'}) && defined($config{$host}{'static'}) &&
2207+ ($cache{$host}{'static'} ne $config{$host}{'static'})) ||
2208+ (defined($cache{$host}{'wildcard'}) && defined($config{$host}{'wildcard'}) &&
2209+ ($cache{$host}{'wildcard'} ne $config{$host}{'wildcard'})) ||
2210+ (defined($cache{$host}{'mx'}) && defined($config{$host}{'mx'}) &&
2211+ ($cache{$host}{'mx'} ne $config{$host}{'mx'})) ||
2212+ (defined($cache{$host}{'backupmx'}) && defined($config{$host}{'backupmx'}) &&
2213+ ($cache{$host}{'backupmx'} ne $config{$host}{'backupmx'})) ) {
2214+ info("updating %s because host settings have been changed.", $host);
2215+ $update = 1;
2216+
2217+ } else {
2218+ success("%s: skipped: IP address was already set to %s.", $host, $ip)
2219+ if opt('verbose');
2220+ }
2221+ $config{$host}{'status'} = define($cache{$host}{'status'},'');
2222+ $config{$host}{'update'} = $update;
2223+ if ($update) {
2224+ $config{$host}{'status'} = 'noconnect';
2225+ $config{$host}{'atime'} = $now;
2226+ $config{$host}{'wtime'} = 0;
2227+ $config{$host}{'warned-min-interval'} = 0;
2228+ $config{$host}{'warned-min-error-interval'} = 0;
2229+
2230+ delete $cache{$host}{'warned-min-interval'};
2231+ delete $cache{$host}{'warned-min-error-interval'};
2232+ }
2233+
2234+ return $update;
2235+}
2236+######################################################################
2237+## header_ok
2238+######################################################################
2239+sub header_ok {
2240+ my ($host, $line) = @_;
2241+ my $ok = 0;
2242+
2243+ if ($line =~ m%^s*HTTP/1.*\s+(\d+)%i) {
2244+ my $result = $1;
2245+
2246+ if ($result eq '200') {
2247+ $ok = 1;
2248+
2249+ } elsif ($result eq '401') {
2250+ failed("updating %s: authorization failed (%s)", $host, $line);
2251+ }
2252+
2253+ } else {
2254+ failed("updating %s: unexpected line (%s)", $host, $line);
2255+ }
2256+ return $ok;
2257+}
2258+######################################################################
2259+## nic_dyndns1_examples
2260+######################################################################
2261+sub nic_dyndns1_examples {
2262+ return <<EoEXAMPLE;
2263+o 'dyndns1'
2264+
2265+The 'dyndns1' protocol is a deprecated protocol used by the free dynamic
2266+DNS service offered by www.dyndns.org. The 'dyndns2' should be used to
2267+update the www.dyndns.org service. However, other services are also
2268+using this protocol so support is still provided by ${program}.
2269+
2270+Configuration variables applicable to the 'dyndns1' protocol are:
2271+ protocol=dyndns1 ##
2272+ server=fqdn.of.service ## defaults to members.dyndns.org
2273+ backupmx=no|yes ## indicates that this host is the primary MX for the domain.
2274+ mx=any.host.domain ## a host MX'ing for this host definition.
2275+ wildcard=no|yes ## add a DNS wildcard CNAME record that points to {host}
2276+ login=service-login ## login name and password registered with the service
2277+ password=service-password ##
2278+ fully.qualified.host ## the host registered with the service.
2279+
2280+Example ${program}.conf file entries:
2281+ ## single host update
2282+ protocol=dyndns1, \\
2283+ login=my-dyndns.org-login, \\
2284+ password=my-dyndns.org-password \\
2285+ myhost.dyndns.org
2286+
2287+ ## multiple host update with wildcard'ing mx, and backupmx
2288+ protocol=dyndns1, \\
2289+ login=my-dyndns.org-login, \\
2290+ password=my-dyndns.org-password, \\
2291+ mx=a.host.willing.to.mx.for.me,backupmx=yes,wildcard=yes \\
2292+ myhost.dyndns.org,my2ndhost.dyndns.org
2293+EoEXAMPLE
2294+}
2295+######################################################################
2296+## nic_dyndns1_update
2297+######################################################################
2298+sub nic_dyndns1_update {
2299+ debug("\nnic_dyndns1_update -------------------");
2300+ ## update each configured host
2301+ foreach my $h (@_) {
2302+ my $ip = delete $config{$h}{'wantip'};
2303+ info("setting IP address to %s for %s", $ip, $h);
2304+ verbose("UPDATE:","updating %s", $h);
2305+
2306+ my $url;
2307+ $url = "http://$config{$h}{'server'}/nic/";
2308+ $url .= ynu($config{$h}{'static'}, 'statdns', 'dyndns', 'dyndns');
2309+ $url .= "?action=edit&started=1&hostname=YES&host_id=$h";
2310+ $url .= "&myip=";
2311+ $url .= $ip if $ip;
2312+ $url .= "&wildcard=ON" if ynu($config{$h}{'wildcard'}, 1, 0, 0);
2313+ if ($config{$h}{'mx'}) {
2314+ $url .= "&mx=$config{$h}{'mx'}";
2315+ $url .= "&backmx=" . ynu($config{$h}{'backupmx'}, 'YES', 'NO');
2316+ }
2317+
2318+ my $reply = geturl(opt('proxy'), $url, $config{$h}{'login'}, $config{$h}{'password'});
2319+ if (!defined($reply) || !$reply) {
2320+ failed("updating %s: Could not connect to %s.", $h, $config{$h}{'server'});
2321+ next;
2322+ }
2323+ last if !header_ok($h, $reply);
2324+
2325+ my @reply = split /\n/, $reply;
2326+ my ($title, $return_code, $error_code) = ('','','');
2327+ foreach my $line (@reply) {
2328+ $title = $1 if $line =~ m%<TITLE>\s*(.*)\s*</TITLE>%i;
2329+ $return_code = $1 if $line =~ m%^return\s+code\s*:\s*(.*)\s*$%i;
2330+ $error_code = $1 if $line =~ m%^error\s+code\s*:\s*(.*)\s*$%i;
2331+ }
2332+
2333+ if ($return_code ne 'NOERROR' || $error_code ne 'NOERROR' || !$title) {
2334+ $config{$h}{'status'} = 'failed';
2335+ $title = "incomplete response from $config{$h}{server}" unless $title;
2336+ warning("SENT: %s", $url) unless opt('verbose');
2337+ warning("REPLIED: %s", $reply);
2338+ failed("updating %s: %s", $h, $title);
2339+
2340+ } else {
2341+ $config{$h}{'ip'} = $ip;
2342+ $config{$h}{'mtime'} = $now;
2343+ $config{$h}{'status'} = 'good';
2344+ success("updating %s: %s: IP address set to %s (%s)", $h, $return_code, $ip, $title);
2345+ }
2346+ }
2347+}
2348+######################################################################
2349+## nic_dyndns2_updateable
2350+######################################################################
2351+sub nic_dyndns2_updateable {
2352+ my $host = shift;
2353+ my $update = 0;
2354+
2355+ if ($config{$host}{'mx'} ne $cache{$host}{'mx'}) {
2356+ info("forcing updating %s because 'mx' has changed to %s.", $host, $config{$host}{'mx'});
2357+ $update = 1;
2358+
2359+ } elsif ($config{$host}{'mx'} && (ynu($config{$host}{'backupmx'},1,2,3) ne ynu($config{$host}{'backupmx'},1,2,3))) {
2360+ info("forcing updating %s because 'backupmx' has changed to %s.", $host, ynu($config{$host}{'backupmx'},"YES","NO","NO"));
2361+ $update = 1;
2362+
2363+ } elsif ($config{$host}{'static'} ne $cache{$host}{'static'}) {
2364+
2365+ info("forcing updating %s because 'static' has changed to %s.", $host, ynu($config{$host}{'static'},"YES","NO","NO"));
2366+ $update = 1;
2367+
2368+ }
2369+ return $update;
2370+}
2371+######################################################################
2372+## nic_dyndns2_examples
2373+######################################################################
2374+sub nic_dyndns2_examples {
2375+ return <<EoEXAMPLE;
2376+o 'dyndns2'
2377+
2378+The 'dyndns2' protocol is a newer low-bandwidth protocol used by a
2379+free dynamic DNS service offered by www.dyndns.org. It supports
2380+features of the older 'dyndns1' in addition to others. [These will be
2381+supported in a future version of ${program}.]
2382+
2383+Configuration variables applicable to the 'dyndns2' protocol are:
2384+ protocol=dyndns2 ##
2385+ server=fqdn.of.service ## defaults to members.dyndns.org
2386+ script=/path/to/script ## defaults to /nic/update
2387+ backupmx=no|yes ## indicates that this host is the primary MX for the domain.
2388+ static=no|yes ## indicates that this host has a static IP address.
2389+ custom=no|yes ## indicates that this host is a 'custom' top-level domain name.
2390+ mx=any.host.domain ## a host MX'ing for this host definition.
2391+ wildcard=no|yes ## add a DNS wildcard CNAME record that points to {host}
2392+ login=service-login ## login name and password registered with the service
2393+ password=service-password ##
2394+ fully.qualified.host ## the host registered with the service.
2395+
2396+Example ${program}.conf file entries:
2397+ ## single host update
2398+ protocol=dyndns2, \\
2399+ login=my-dyndns.org-login, \\
2400+ password=my-dyndns.org-password \\
2401+ myhost.dyndns.org
2402+
2403+ ## multiple host update with wildcard'ing mx, and backupmx
2404+ protocol=dyndns2, \\
2405+ login=my-dyndns.org-login, \\
2406+ password=my-dyndns.org-password, \\
2407+ mx=a.host.willing.to.mx.for.me,backupmx=yes,wildcard=yes \\
2408+ myhost.dyndns.org,my2ndhost.dyndns.org
2409+
2410+ ## multiple host update to the custom DNS service
2411+ protocol=dyndns2, \\
2412+ login=my-dyndns.org-login, \\
2413+ password=my-dyndns.org-password \\
2414+ my-toplevel-domain.com,my-other-domain.com
2415+EoEXAMPLE
2416+}
2417+######################################################################
2418+## nic_dyndns2_update
2419+######################################################################
2420+sub nic_dyndns2_update {
2421+ debug("\nnic_dyndns2_update -------------------");
2422+
2423+ ## group hosts with identical attributes together
2424+ my %groups = group_hosts_by([ @_ ], [ qw(login password server static custom wildcard mx backupmx) ]);
2425+
2426+ my %errors = (
2427+ 'badauth' => 'Bad authorization (username or password)',
2428+ 'badsys' => 'The system parameter given was not valid',
2429+
2430+ 'notfqdn' => 'A Fully-Qualified Domain Name was not provided',
2431+ 'nohost' => 'The hostname specified does not exist in the database',
2432+ '!yours' => 'The hostname specified exists, but not under the username currently being used',
2433+ '!donator' => 'The offline setting was set, when the user is not a donator',
2434+ '!active' => 'The hostname specified is in a Custom DNS domain which has not yet been activated.',
2435+ 'abuse', => 'The hostname specified is blocked for abuse; you should receive an email notification ' .
2436+ 'which provides an unblock request link. More info can be found on ' .
2437+ 'https://www.dyndns.com/support/abuse.html',
2438+
2439+ 'numhost' => 'System error: Too many or too few hosts found. Contact support@dyndns.org',
2440+ 'dnserr' => 'System error: DNS error encountered. Contact support@dyndns.org',
2441+
2442+ 'nochg' => 'No update required; unnecessary attempts to change to the current address are considered abusive',
2443+ );
2444+
2445+ ## update each set of hosts that had similar configurations
2446+ foreach my $sig (keys %groups) {
2447+ my @hosts = @{$groups{$sig}};
2448+ my $hosts = join(',', @hosts);
2449+ my $h = $hosts[0];
2450+ my $ip = $config{$h}{'wantip'};
2451+ delete $config{$_}{'wantip'} foreach @hosts;
2452+
2453+ info("setting IP address to %s for %s", $ip, $hosts);
2454+ verbose("UPDATE:","updating %s", $hosts);
2455+
2456+ ## Select the DynDNS system to update
2457+ my $url = "http://$config{$h}{'server'}$config{$h}{'script'}?system=";
2458+ if ($config{$h}{'custom'}) {
2459+ warning("updating %s: 'custom' and 'static' may not be used together. ('static' ignored)", $hosts)
2460+ if $config{$h}{'static'};
2461+# warning("updating %s: 'custom' and 'offline' may not be used together. ('offline' ignored)", $hosts)
2462+# if $config{$h}{'offline'};
2463+ $url .= 'custom';
2464+
2465+ } elsif ($config{$h}{'static'}) {
2466+# warning("updating %s: 'static' and 'offline' may not be used together. ('offline' ignored)", $hosts)
2467+# if $config{$h}{'offline'};
2468+ $url .= 'statdns';
2469+
2470+ } else {
2471+ $url .= 'dyndns';
2472+ }
2473+
2474+ $url .= "&hostname=$hosts";
2475+ $url .= "&myip=";
2476+ $url .= $ip if $ip;
2477+
2478+ ## some args are not valid for a custom domain.
2479+ $url .= "&wildcard=ON" if ynu($config{$h}{'wildcard'}, 1, 0, 0);
2480+ if ($config{$h}{'mx'}) {
2481+ $url .= "&mx=$config{$h}{'mx'}";
2482+ $url .= "&backmx=" . ynu($config{$h}{'backupmx'}, 'YES', 'NO');
2483+ }
2484+
2485+ my $reply = geturl(opt('proxy'), $url, $config{$h}{'login'}, $config{$h}{'password'});
2486+ if (!defined($reply) || !$reply) {
2487+ failed("updating %s: Could not connect to %s.", $hosts, $config{$h}{'server'});
2488+ last;
2489+ }
2490+ last if !header_ok($hosts, $reply);
2491+
2492+ my @reply = split /\n/, $reply;
2493+ my $state = 'header';
2494+ my $returnedip = $ip;
2495+
2496+ foreach my $line (@reply) {
2497+ if ($state eq 'header') {
2498+ $state = 'body';
2499+
2500+ } elsif ($state eq 'body') {
2501+ $state = 'results' if $line eq '';
2502+
2503+ } elsif ($state =~ /^results/) {
2504+ $state = 'results2';
2505+
2506+ # bug #10: some dyndns providers does not return the IP so
2507+ # we can't use the returned IP
2508+ my ($status, $returnedip) = split / /, lc $line;
2509+ $ip = $returnedip if (not $ip);
2510+ my $h = shift @hosts;
2511+
2512+ $config{$h}{'status'} = $status;
2513+ if ($status eq 'good') {
2514+ $config{$h}{'ip'} = $ip;
2515+ $config{$h}{'mtime'} = $now;
2516+ success("updating %s: %s: IP address set to %s", $h, $status, $ip);
2517+
2518+ } elsif (exists $errors{$status}) {
2519+ if ($status eq 'nochg') {
2520+ warning("updating %s: %s: %s", $h, $status, $errors{$status});
2521+ $config{$h}{'ip'} = $ip;
2522+ $config{$h}{'mtime'} = $now;
2523+ $config{$h}{'status'} = 'good';
2524+
2525+ } else {
2526+ failed("updating %s: %s: %s", $h, $status, $errors{$status});
2527+ }
2528+
2529+ } elsif ($status =~ /w(\d+)(.)/) {
2530+ my ($wait, $units) = ($1, lc $2);
2531+ my ($sec, $scale) = ($wait, 1);
2532+
2533+ ($scale, $units) = (1, 'seconds') if $units eq 's';
2534+ ($scale, $units) = (60, 'minutes') if $units eq 'm';
2535+ ($scale, $units) = (60*60, 'hours') if $units eq 'h';
2536+
2537+ $sec = $wait * $scale;
2538+ $config{$h}{'wtime'} = $now + $sec;
2539+ warning("updating %s: %s: wait $wait $units before further updates", $h, $status, $ip);
2540+
2541+ } else {
2542+ failed("updating %s: %s: unexpected status (%s)", $h, $line);
2543+ }
2544+ }
2545+ }
2546+ failed("updating %s: Could not connect to %s.", $hosts, $config{$h}{'server'})
2547+ if $state ne 'results2';
2548+ }
2549+}
2550+
2551+
2552+######################################################################
2553+## nic_noip_update
2554+## Note: uses same features as nic_dyndns2_update, less return codes
2555+######################################################################
2556+sub nic_noip_update {
2557+ debug("\nnic_noip_update -------------------");
2558+
2559+ ## group hosts with identical attributes together
2560+ my %groups = group_hosts_by([ @_ ], [ qw(login password server static custom wildcard mx backupmx) ]);
2561+
2562+ my %errors = (
2563+ 'badauth' => 'Invalid username or password',
2564+ 'badagent' => 'Invalid user agent',
2565+ 'nohost' => 'The hostname specified does not exist in the database',
2566+ '!donator' => 'The offline setting was set, when the user is not a donator',
2567+ 'abuse', => 'The hostname specified is blocked for abuse; open a trouble ticket at http://www.no-ip.com',
2568+ 'numhost' => 'System error: Too many or too few hosts found. open a trouble ticket at http://www.no-ip.com',
2569+ 'dnserr' => 'System error: DNS error encountered. Contact support@dyndns.org',
2570+ 'nochg' => 'No update required; unnecessary attempts to change to the current address are considered abusive',
2571+ );
2572+
2573+ ## update each set of hosts that had similar configurations
2574+ foreach my $sig (keys %groups) {
2575+ my @hosts = @{$groups{$sig}};
2576+ my $hosts = join(',', @hosts);
2577+ my $h = $hosts[0];
2578+ my $ip = $config{$h}{'wantip'};
2579+ delete $config{$_}{'wantip'} foreach @hosts;
2580+
2581+ info("setting IP address to %s for %s", $ip, $hosts);
2582+ verbose("UPDATE:","updating %s", $hosts);
2583+
2584+ my $url = "http://$config{$h}{'server'}/nic/update?system=";
2585+ $url .= 'noip';
2586+ $url .= "&hostname=$hosts";
2587+ $url .= "&myip=";
2588+ $url .= $ip if $ip;
2589+
2590+
2591+ print "here..." . $config{$h}{'login'} . " --> " . $config{$h}{'password'} . "\n";
2592+
2593+
2594+ my $reply = geturl(opt('proxy'), $url, $config{$h}{'login'}, $config{$h}{'password'});
2595+ if (!defined($reply) || !$reply) {
2596+ failed("updating %s: Could not connect to %s.", $hosts, $config{$h}{'server'});
2597+ last;
2598+ }
2599+ last if !header_ok($hosts, $reply);
2600+
2601+ my @reply = split /\n/, $reply;
2602+ my $state = 'header';
2603+ foreach my $line (@reply) {
2604+ if ($state eq 'header') {
2605+ $state = 'body';
2606+
2607+ } elsif ($state eq 'body') {
2608+ $state = 'results' if $line eq '';
2609+
2610+ } elsif ($state =~ /^results/) {
2611+ $state = 'results2';
2612+
2613+ my ($status, $ip) = split / /, lc $line;
2614+ my $h = shift @hosts;
2615+
2616+ $config{$h}{'status'} = $status;
2617+ if ($status eq 'good') {
2618+ $config{$h}{'ip'} = $ip;
2619+ $config{$h}{'mtime'} = $now;
2620+ success("updating %s: %s: IP address set to %s", $h, $status, $ip);
2621+
2622+ } elsif (exists $errors{$status}) {
2623+ if ($status eq 'nochg') {
2624+ warning("updating %s: %s: %s", $h, $status, $errors{$status});
2625+ $config{$h}{'ip'} = $ip;
2626+ $config{$h}{'mtime'} = $now;
2627+ $config{$h}{'status'} = 'good';
2628+
2629+ } else {
2630+ failed("updating %s: %s: %s", $h, $status, $errors{$status});
2631+ }
2632+
2633+ } elsif ($status =~ /w(\d+)(.)/) {
2634+ my ($wait, $units) = ($1, lc $2);
2635+ my ($sec, $scale) = ($wait, 1);
2636+
2637+ ($scale, $units) = (1, 'seconds') if $units eq 's';
2638+ ($scale, $units) = (60, 'minutes') if $units eq 'm';
2639+ ($scale, $units) = (60*60, 'hours') if $units eq 'h';
2640+
2641+ $sec = $wait * $scale;
2642+ $config{$h}{'wtime'} = $now + $sec;
2643+ warning("updating %s: %s: wait $wait $units before further updates", $h, $status, $ip);
2644+
2645+ } else {
2646+ failed("updating %s: %s: unexpected status (%s)", $h, $line);
2647+ }
2648+ }
2649+ }
2650+ failed("updating %s: Could not connect to %s.", $hosts, $config{$h}{'server'})
2651+ if $state ne 'results2';
2652+ }
2653+}
2654+######################################################################
2655+## nic_noip_examples
2656+######################################################################
2657+sub nic_noip_examples {
2658+ return <<EoEXAMPLE;
2659+o 'noip'
2660+
2661+The 'No-IP Compatible' protocol is used to make dynamic dns updates
2662+over an http request. Details of the protocol are outlined at:
2663+http://www.no-ip.com/integrate/
2664+
2665+Configuration variables applicable to the 'noip' protocol are:
2666+ protocol=noip ##
2667+ server=fqdn.of.service ## defaults to dynupdate.no-ip.com
2668+ login=service-login ## login name and password registered with the service
2669+ password=service-password ##
2670+ fully.qualified.host ## the host registered with the service.
2671+
2672+Example ${program}.conf file entries:
2673+ ## single host update
2674+ protocol=noip, \\
2675+ login=userlogin\@domain.com, \\
2676+ password=noip-password \\
2677+ myhost.no-ip.biz
2678+
2679+
2680+EoEXAMPLE
2681+}
2682+
2683+######################################################################
2684+## nic_concont_examples
2685+######################################################################
2686+sub nic_concont_examples {
2687+ return <<EoEXAMPLE;
2688+o 'concont'
2689+
2690+The 'concont' protocol is the protocol used by the content management
2691+system ConCont's dydns module. This is currently used by the free
2692+dynamic DNS service offered by Tyrmida at www.dydns.za.net
2693+
2694+Configuration variables applicable to the 'concont' protocol are:
2695+ protocol=concont ##
2696+ server=www.fqdn.of.service ## for example www.dydns.za.net (for most add a www)
2697+ login=service-login ## login registered with the service
2698+ password=service-password ## password registered with the service
2699+ mx=mail.server.fqdn ## fqdn of the server handling domain\'s mail (leave out for none)
2700+ wildcard=yes|no ## set yes for wild (*.host.domain) support
2701+ fully.qualified.host ## the host registered with the service.
2702+
2703+Example ${program}.conf file entries:
2704+ ## single host update
2705+ protocol=concont, \\
2706+ login=dydns.za.net, \\
2707+ password=my-dydns.za.net-password, \\
2708+ mx=mailserver.fqdn, \\
2709+ wildcard=yes \\
2710+ myhost.hn.org
2711+
2712+EoEXAMPLE
2713+}
2714+######################################################################
2715+## nic_concont_update
2716+######################################################################
2717+sub nic_concont_update {
2718+ debug("\nnic_concont_update -------------------");
2719+
2720+ ## update each configured host
2721+ foreach my $h (@_) {
2722+ my $ip = delete $config{$h}{'wantip'};
2723+ info("setting IP address to %s for %s", $ip, $h);
2724+ verbose("UPDATE:","updating %s", $h);
2725+
2726+ # Set the URL that we're going to to update
2727+ my $url;
2728+ $url = "http://$config{$h}{'server'}/modules/dydns/update.php";
2729+ $url .= "?username=";
2730+ $url .= $config{$h}{'login'};
2731+ $url .= "&password=";
2732+ $url .= $config{$h}{'password'};
2733+ $url .= "&wildcard=";
2734+ $url .= $config{$h}{'wildcard'};
2735+ $url .= "&mx=";
2736+ $url .= $config{$h}{'mx'};
2737+ $url .= "&host=";
2738+ $url .= $h;
2739+ $url .= "&ip=";
2740+ $url .= $ip;
2741+
2742+ # Try to get URL
2743+ my $reply = geturl(opt('proxy'), $url);
2744+
2745+ # No response, declare as failed
2746+ if (!defined($reply) || !$reply) {
2747+ failed("updating %s: Could not connect to %s.", $h, $config{$h}{'server'});
2748+ last;
2749+ }
2750+ last if !header_ok($h, $reply);
2751+
2752+ # Response found, just declare as success (this is ugly, we need more error checking)
2753+ if ($reply =~ /SUCCESS/)
2754+ {
2755+ $config{$h}{'ip'} = $ip;
2756+ $config{$h}{'mtime'} = $now;
2757+ $config{$h}{'status'} = 'good';
2758+ success("updating %s: good: IP address set to %s", $h, $ip);
2759+ }
2760+ else
2761+ {
2762+ my @reply = split /\n/, $reply;
2763+ my $returned = pop(@reply);
2764+ $config{$h}{'status'} = 'failed';
2765+ failed("updating %s: Server said: '$returned'", $h);
2766+ }
2767+ }
2768+}
2769+######################################################################
2770+## nic_dslreports1_examples
2771+######################################################################
2772+sub nic_dslreports1_examples {
2773+ return <<EoEXAMPLE;
2774+o 'dslreports1'
2775+
2776+The 'dslreports1' protocol is used by a free DSL monitoring service
2777+offered by www.dslreports.com.
2778+
2779+Configuration variables applicable to the 'dslreports1' protocol are:
2780+ protocol=dslreports1 ##
2781+ server=fqdn.of.service ## defaults to www.dslreports.com
2782+ login=service-login ## login name and password registered with the service
2783+ password=service-password ##
2784+ unique-number ## the host registered with the service.
2785+
2786+Example ${program}.conf file entries:
2787+ ## single host update
2788+ protocol=dslreports1, \\
2789+ server=www.dslreports.com, \\
2790+ login=my-dslreports-login, \\
2791+ password=my-dslreports-password \\
2792+ 123456
2793+
2794+Note: DSL Reports uses a unique number as the host name. This number
2795+can be found on the Monitor Control web page.
2796+EoEXAMPLE
2797+}
2798+######################################################################
2799+## nic_dslreports1_update
2800+######################################################################
2801+sub nic_dslreports1_update {
2802+ debug("\nnic_dslreports1_update -------------------");
2803+ ## update each configured host
2804+ foreach my $h (@_) {
2805+ my $ip = delete $config{$h}{'wantip'};
2806+ info("setting IP address to %s for %s", $ip, $h);
2807+ verbose("UPDATE:","updating %s", $h);
2808+
2809+ my $url;
2810+ $url = "http://$config{$h}{'server'}/nic/";
2811+ $url .= ynu($config{$h}{'static'}, 'statdns', 'dyndns', 'dyndns');
2812+ $url .= "?action=edit&started=1&hostname=YES&host_id=$h";
2813+ $url .= "&myip=";
2814+ $url .= $ip if $ip;
2815+
2816+ my $reply = geturl(opt('proxy'), $url, $config{$h}{'login'}, $config{$h}{'password'});
2817+ if (!defined($reply) || !$reply) {
2818+ failed("updating %s: Could not connect to %s.", $h, $config{$h}{'server'});
2819+ next;
2820+ }
2821+
2822+ my @reply = split /\n/, $reply;
2823+ my $return_code = '';
2824+ foreach my $line (@reply) {
2825+ $return_code = $1 if $line =~ m%^return\s+code\s*:\s*(.*)\s*$%i;
2826+ }
2827+
2828+ if ($return_code !~ /NOERROR/) {
2829+ $config{$h}{'status'} = 'failed';
2830+ warning("SENT: %s", $url) unless opt('verbose');
2831+ warning("REPLIED: %s", $reply);
2832+ failed("updating %s", $h);
2833+
2834+ } else {
2835+ $config{$h}{'ip'} = $ip;
2836+ $config{$h}{'mtime'} = $now;
2837+ $config{$h}{'status'} = 'good';
2838+ success("updating %s: %s: IP address set to %s", $h, $return_code, $ip);
2839+ }
2840+ }
2841+}
2842+######################################################################
2843+## nic_hammernode1_examples
2844+######################################################################
2845+sub nic_hammernode1_examples {
2846+ return <<EoEXAMPLE;
2847+o 'hammernode1'
2848+
2849+The 'hammernode1' protocol is the protocol used by the free dynamic
2850+DNS service offered by Hammernode at www.hn.org
2851+
2852+Configuration variables applicable to the 'hammernode1' protocol are:
2853+ protocol=hammernode1 ##
2854+ server=fqdn.of.service ## defaults to members.dyndns.org
2855+ login=service-login ## login name and password registered with the service
2856+ password=service-password ##
2857+ fully.qualified.host ## the host registered with the service.
2858+
2859+Example ${program}.conf file entries:
2860+ ## single host update
2861+ protocol=hammernode1, \\
2862+ login=my-hn.org-login, \\
2863+ password=my-hn.org-password \\
2864+ myhost.hn.org
2865+
2866+ ## multiple host update
2867+ protocol=hammernode1, \\
2868+ login=my-hn.org-login, \\
2869+ password=my-hn.org-password, \\
2870+ myhost.hn.org,my2ndhost.hn.org
2871+EoEXAMPLE
2872+}
2873+######################################################################
2874+## nic_hammernode1_update
2875+######################################################################
2876+sub nic_hammernode1_update {
2877+ debug("\nnic_hammernode1_update -------------------");
2878+
2879+ ## update each configured host
2880+ foreach my $h (@_) {
2881+ my $ip = delete $config{$h}{'wantip'};
2882+ info("setting IP address to %s for %s", $ip, $h);
2883+ verbose("UPDATE:","updating %s", $h);
2884+
2885+ my $url;
2886+ $url = "http://$config{$h}{'server'}/vanity/update";
2887+ $url .= "?ver=1";
2888+ $url .= "&ip=";
2889+ $url .= $ip if $ip;
2890+
2891+ my $reply = geturl(opt('proxy'), $url, $config{$h}{'login'}, $config{$h}{'password'});
2892+ if (!defined($reply) || !$reply) {
2893+ failed("updating %s: Could not connect to %s.", $h, $config{$h}{'server'});
2894+ last;
2895+ }
2896+ last if !header_ok($h, $reply);
2897+
2898+ my @reply = split /\n/, $reply;
2899+ if (grep /<!--\s+DDNS_Response_Code=101\s+-->/i, @reply) {
2900+ $config{$h}{'ip'} = $ip;
2901+ $config{$h}{'mtime'} = $now;
2902+ $config{$h}{'status'} = 'good';
2903+ success("updating %s: good: IP address set to %s", $h, $ip);
2904+ } else {
2905+ $config{$h}{'status'} = 'failed';
2906+ warning("SENT: %s", $url) unless opt('verbose');
2907+ warning("REPLIED: %s", $reply);
2908+ failed("updating %s: Invalid reply.", $h);
2909+ }
2910+ }
2911+}
2912+######################################################################
2913+## nic_zoneedit1_examples
2914+######################################################################
2915+sub nic_zoneedit1_examples {
2916+ return <<EoEXAMPLE;
2917+o 'zoneedit1'
2918+
2919+The 'zoneedit1' protocol is used by a DNS service offered by
2920+www.zoneedit.com.
2921+
2922+Configuration variables applicable to the 'zoneedit1' protocol are:
2923+ protocol=zoneedit1 ##
2924+ server=fqdn.of.service ## defaults to www.zoneedit.com
2925+ zone=zone-where-domains-are ## only needed if 1 or more subdomains are deeper
2926+ ## than 1 level in relation to the zone where it
2927+ ## is defined. For example, b.foo.com in a zone
2928+ ## foo.com doesn't need this, but a.b.foo.com in
2929+ ## the same zone needs zone=foo.com
2930+ login=service-login ## login name and password registered with the service
2931+ password=service-password ##
2932+ your.domain.name ## the host registered with the service.
2933+
2934+Example ${program}.conf file entries:
2935+ ## single host update
2936+ protocol=zoneedit1, \\
2937+ server=dynamic.zoneedit.com, \\
2938+ zone=zone-where-domains-are, \\
2939+ login=my-zoneedit-login, \\
2940+ password=my-zoneedit-password \\
2941+ my.domain.name
2942+EoEXAMPLE
2943+}
2944+
2945+######################################################################
2946+## nic_zoneedit1_updateable
2947+######################################################################
2948+sub nic_zoneedit1_updateable {
2949+ return 0;
2950+}
2951+
2952+######################################################################
2953+## nic_zoneedit1_update
2954+# <SUCCESS CODE="200" TEXT="Update succeeded." ZONE="trialdomain.com" IP="127.0.0.12">
2955+# <SUCCESS CODE="201" TEXT="No records need updating." ZONE="bannedware.com">
2956+# <ERROR CODE="701" TEXT="Zone is not set up in this account." ZONE="bad.com">
2957+######################################################################
2958+sub nic_zoneedit1_update {
2959+ debug("\nnic_zoneedit1_update -------------------");
2960+
2961+ ## group hosts with identical attributes together
2962+ my %groups = group_hosts_by([ @_ ], [ qw(login password server zone) ]);
2963+
2964+ ## update each set of hosts that had similar configurations
2965+ foreach my $sig (keys %groups) {
2966+ my @hosts = @{$groups{$sig}};
2967+ my $hosts = join(',', @hosts);
2968+ my $h = $hosts[0];
2969+ my $ip = $config{$h}{'wantip'};
2970+ delete $config{$_}{'wantip'} foreach @hosts;
2971+
2972+ info("setting IP address to %s for %s", $ip, $hosts);
2973+ verbose("UPDATE:","updating %s", $hosts);
2974+
2975+ my $url = '';
2976+ $url .= "http://$config{$h}{'server'}/auth/dynamic.html";
2977+ $url .= "?host=$hosts";
2978+ $url .= "&dnsto=$ip" if $ip;
2979+ $url .= "&zone=$config{$h}{'zone'}" if defined $config{$h}{'zone'};
2980+
2981+ my $reply = geturl(opt('proxy'), $url, $config{$h}{'login'}, $config{$h}{'password'});
2982+ if (!defined($reply) || !$reply) {
2983+ failed("updating %s: Could not connect to %s.", $hosts, $config{$h}{'server'});
2984+ last;
2985+ }
2986+ last if !header_ok($hosts, $reply);
2987+
2988+ my @reply = split /\n/, $reply;
2989+ foreach my $line (@reply) {
2990+ if ($line =~ /^[^<]*<(SUCCESS|ERROR)\s+([^>]+)>(.*)/) {
2991+ my ($status, $assignments, $rest) = ($1, $2, $3);
2992+ my ($left, %var) = parse_assignments($assignments);
2993+
2994+ if (keys %var) {
2995+ my ($status_code, $status_text, $status_ip) = ('999', '', $ip);
2996+ $status_code = $var{'CODE'} if exists $var{'CODE'};
2997+ $status_text = $var{'TEXT'} if exists $var{'TEXT'};
2998+ $status_ip = $var{'IP'} if exists $var{'IP'};
2999+
3000+ if ($status eq 'SUCCESS' || ($status eq 'ERROR' && $var{'CODE'} eq '707')) {
3001+ $config{$h}{'ip'} = $status_ip;
3002+ $config{$h}{'mtime'} = $now;
3003+ $config{$h}{'status'} = 'good';
3004+
3005+ success("updating %s: IP address set to %s (%s: %s)", $h, $ip, $status_code, $status_text);
3006+
3007+ } else {
3008+ $config{$h}{'status'} = 'failed';
3009+ failed("updating %s: %s: %s", $h, $status_code, $status_text);
3010+ }
3011+ shift @hosts;
3012+ $h = $hosts[0];
3013+ $hosts = join(',', @hosts);
3014+ }
3015+ $line = $rest;
3016+ redo if $line;
3017+ }
3018+ }
3019+ failed("updating %s: no response from %s", $hosts, $config{$h}{'server'})
3020+ if @hosts;
3021+ }
3022+}
3023+######################################################################
3024+## nic_easydns_updateable
3025+######################################################################
3026+sub nic_easydns_updateable {
3027+ my $host = shift;
3028+ my $update = 0;
3029+
3030+ if ($config{$host}{'mx'} ne $cache{$host}{'mx'}) {
3031+ info("forcing updating %s because 'mx' has changed to %s.", $host, $config{$host}{'mx'});
3032+ $update = 1;
3033+
3034+ } elsif ($config{$host}{'mx'} && (ynu($config{$host}{'backupmx'},1,2,3) ne ynu($config{$host}{'backupmx'},1,2,3))) {
3035+ info("forcing updating %s because 'backupmx' has changed to %s.", $host, ynu($config{$host}{'backupmx'},"YES","NO","NO"));
3036+ $update = 1;
3037+
3038+ } elsif ($config{$host}{'static'} ne $cache{$host}{'static'}) {
3039+
3040+ info("forcing updating %s because 'static' has changed to %s.", $host, ynu($config{$host}{'static'},"YES","NO","NO"));
3041+ $update = 1;
3042+
3043+ }
3044+ return $update;
3045+}
3046+######################################################################
3047+## nic_easydns_examples
3048+######################################################################
3049+sub nic_easydns_examples {
3050+ return <<EoEXAMPLE;
3051+o 'easydns'
3052+
3053+The 'easydns' protocol is used by the for fee DNS service offered
3054+by www.easydns.com.
3055+
3056+Configuration variables applicable to the 'easydns' protocol are:
3057+ protocol=easydns ##
3058+ server=fqdn.of.service ## defaults to members.easydns.com
3059+ backupmx=no|yes ## indicates that EasyDNS should be the secondary MX
3060+ ## for this domain or host.
3061+ mx=any.host.domain ## a host MX'ing for this host or domain.
3062+ wildcard=no|yes ## add a DNS wildcard CNAME record that points to {host}
3063+ login=service-login ## login name and password registered with the service
3064+ password=service-password ##
3065+ fully.qualified.host ## the host registered with the service.
3066+
3067+Example ${program}.conf file entries:
3068+ ## single host update
3069+ protocol=easydns, \\
3070+ login=my-easydns.com-login, \\
3071+ password=my-easydns.com-password \\
3072+ myhost.easydns.com
3073+
3074+ ## multiple host update with wildcard'ing mx, and backupmx
3075+ protocol=easydns, \\
3076+ login=my-easydns.com-login, \\
3077+ password=my-easydns.com-password, \\
3078+ mx=a.host.willing.to.mx.for.me, \\
3079+ backupmx=yes, \\
3080+ wildcard=yes \\
3081+ my-toplevel-domain.com,my-other-domain.com
3082+
3083+ ## multiple host update to the custom DNS service
3084+ protocol=easydns, \\
3085+ login=my-easydns.com-login, \\
3086+ password=my-easydns.com-password \\
3087+ my-toplevel-domain.com,my-other-domain.com
3088+EoEXAMPLE
3089+}
3090+######################################################################
3091+## nic_easydns_update
3092+######################################################################
3093+sub nic_easydns_update {
3094+ debug("\nnic_easydns_update -------------------");
3095+
3096+ ## group hosts with identical attributes together
3097+ ## my %groups = group_hosts_by([ @_ ], [ qw(login password server wildcard mx backupmx) ]);
3098+
3099+ ## each host is in a group by itself
3100+ my %groups = map { $_ => [ $_ ] } @_;
3101+
3102+ my %errors = (
3103+ 'NOACCESS' => 'Authentication failed. This happens if the username/password OR host or domain are wrong.',
3104+ 'NOSERVICE'=> 'Dynamic DNS is not turned on for this domain.',
3105+ 'ILLEGAL' => 'Client sent data that is not allowed in a dynamic DNS update.',
3106+ 'TOOSOON' => 'Update frequency is too short.',
3107+ );
3108+
3109+ ## update each set of hosts that had similar configurations
3110+ foreach my $sig (keys %groups) {
3111+ my @hosts = @{$groups{$sig}};
3112+ my $hosts = join(',', @hosts);
3113+ my $h = $hosts[0];
3114+ my $ip = $config{$h}{'wantip'};
3115+ delete $config{$_}{'wantip'} foreach @hosts;
3116+
3117+ info("setting IP address to %s for %s", $ip, $hosts);
3118+ verbose("UPDATE:","updating %s", $hosts);
3119+
3120+ #'http://members.easydns.com/dyn/dyndns.php?hostname=test.burry.ca&myip=10.20.30.40&wildcard=ON'
3121+
3122+ my $url;
3123+ $url = "http://$config{$h}{'server'}/dyn/dyndns.php?";
3124+ $url .= "hostname=$hosts";
3125+ $url .= "&myip=";
3126+ $url .= $ip if $ip;
3127+ $url .= "&wildcard=" . ynu($config{$h}{'wildcard'}, 'ON', 'OFF', 'OFF') if defined $config{$h}{'wildcard'};
3128+
3129+ if ($config{$h}{'mx'}) {
3130+ $url .= "&mx=$config{$h}{'mx'}";
3131+ $url .= "&backmx=" . ynu($config{$h}{'backupmx'}, 'YES', 'NO');
3132+ }
3133+
3134+ my $reply = geturl(opt('proxy'), $url, $config{$h}{'login'}, $config{$h}{'password'});
3135+ if (!defined($reply) || !$reply) {
3136+ failed("updating %s: Could not connect to %s.", $hosts, $config{$h}{'server'});
3137+ last;
3138+ }
3139+ last if !header_ok($hosts, $reply);
3140+
3141+ my @reply = split /\n/, $reply;
3142+ my $state = 'header';
3143+ foreach my $line (@reply) {
3144+ if ($state eq 'header') {
3145+ $state = 'body';
3146+
3147+ } elsif ($state eq 'body') {
3148+ $state = 'results' if $line eq '';
3149+
3150+ } elsif ($state =~ /^results/) {
3151+ $state = 'results2';
3152+
3153+ my ($status) = $line =~ /^(\S*)\b.*/;
3154+ my $h = shift @hosts;
3155+
3156+ $config{$h}{'status'} = $status;
3157+ if ($status eq 'NOERROR') {
3158+ $config{$h}{'ip'} = $ip;
3159+ $config{$h}{'mtime'} = $now;
3160+ success("updating %s: %s: IP address set to %s", $h, $status, $ip);
3161+
3162+ } elsif ($status =~ /TOOSOON/) {
3163+ ## make sure we wait at least a little
3164+ my ($wait, $units) = (5, 'm');
3165+ my ($sec, $scale) = ($wait, 1);
3166+
3167+ ($scale, $units) = (1, 'seconds') if $units eq 's';
3168+ ($scale, $units) = (60, 'minutes') if $units eq 'm';
3169+ ($scale, $units) = (60*60, 'hours') if $units eq 'h';
3170+ $config{$h}{'wtime'} = $now + $sec;
3171+ warning("updating %s: %s: wait $wait $units before further updates", $h, $status, $ip);
3172+
3173+ } elsif (exists $errors{$status}) {
3174+ failed("updating %s: %s: %s", $h, $line, $errors{$status});
3175+
3176+ } else {
3177+ failed("updating %s: %s: unexpected status (%s)", $h, $line);
3178+ }
3179+ last;
3180+ }
3181+ }
3182+ failed("updating %s: Could not connect to %s.", $hosts, $config{$h}{'server'})
3183+ if $state ne 'results2';
3184+ }
3185+}
3186+######################################################################
3187+
3188+######################################################################
3189+## nic_dnspark_updateable
3190+######################################################################
3191+sub nic_dnspark_updateable {
3192+ my $host = shift;
3193+ my $update = 0;
3194+
3195+ if ($config{$host}{'mx'} ne $cache{$host}{'mx'}) {
3196+ info("forcing updating %s because 'mx' has changed to %s.", $host, $config{$host}{'mx'});
3197+ $update = 1;
3198+
3199+ } elsif ($config{$host}{'mx'} && ($config{$host}{'mxpri'} ne $cache{$host}{'mxpri'})) {
3200+ info("forcing updating %s because 'mxpri' has changed to %s.", $host, $config{$host}{'mxpri'});
3201+ $update = 1;
3202+ }
3203+ return $update;
3204+}
3205+######################################################################
3206+## nic_dnspark_examples
3207+######################################################################
3208+sub nic_dnspark_examples {
3209+ return <<EoEXAMPLE;
3210+o 'dnspark'
3211+
3212+The 'dnspark' protocol is used by DNS service offered by www.dnspark.com.
3213+
3214+Configuration variables applicable to the 'dnspark' protocol are:
3215+ protocol=dnspark ##
3216+ server=fqdn.of.service ## defaults to www.dnspark.com
3217+ backupmx=no|yes ## indicates that DNSPark should be the secondary MX
3218+ ## for this domain or host.
3219+ mx=any.host.domain ## a host MX'ing for this host or domain.
3220+ mxpri=priority ## MX priority.
3221+ login=service-login ## login name and password registered with the service
3222+ password=service-password ##
3223+ fully.qualified.host ## the host registered with the service.
3224+
3225+Example ${program}.conf file entries:
3226+ ## single host update
3227+ protocol=dnspark, \\
3228+ login=my-dnspark.com-login, \\
3229+ password=my-dnspark.com-password \\
3230+ myhost.dnspark.com
3231+
3232+ ## multiple host update with wildcard'ing mx, and backupmx
3233+ protocol=dnspark, \\
3234+ login=my-dnspark.com-login, \\
3235+ password=my-dnspark.com-password, \\
3236+ mx=a.host.willing.to.mx.for.me, \\
3237+ mxpri=10, \\
3238+ my-toplevel-domain.com,my-other-domain.com
3239+
3240+ ## multiple host update to the custom DNS service
3241+ protocol=dnspark, \\
3242+ login=my-dnspark.com-login, \\
3243+ password=my-dnspark.com-password \\
3244+ my-toplevel-domain.com,my-other-domain.com
3245+EoEXAMPLE
3246+}
3247+######################################################################
3248+## nic_dnspark_update
3249+######################################################################
3250+sub nic_dnspark_update {
3251+ debug("\nnic_dnspark_update -------------------");
3252+
3253+ ## group hosts with identical attributes together
3254+ ## my %groups = group_hosts_by([ @_ ], [ qw(login password server wildcard mx backupmx) ]);
3255+
3256+ ## each host is in a group by itself
3257+ my %groups = map { $_ => [ $_ ] } @_;
3258+
3259+ my %errors = (
3260+ 'nochange' => 'No changes made to the hostname(s). Continual updates with no changes lead to blocked clients.',
3261+ 'nofqdn' => 'No valid FQDN (fully qualified domain name) was specified',
3262+ 'nohost'=> 'An invalid hostname was specified. This due to the fact the hostname has not been created in the system. Creating new host names via clients is not supported.',
3263+ 'abuse' => 'The hostname specified has been blocked for abuse.',
3264+ 'unauth' => 'The username specified is not authorized to update this hostname and domain.',
3265+ 'blocked' => 'The dynamic update client (specified by the user-agent) has been blocked from the system.',
3266+ 'notdyn' => 'The hostname specified has not been marked as a dynamic host. Hosts must be marked as dynamic in the system in order to be updated via clients. This prevents unwanted or accidental updates.',
3267+ );
3268+
3269+ ## update each set of hosts that had similar configurations
3270+ foreach my $sig (keys %groups) {
3271+ my @hosts = @{$groups{$sig}};
3272+ my $hosts = join(',', @hosts);
3273+ my $h = $hosts[0];
3274+ my $ip = $config{$h}{'wantip'};
3275+ delete $config{$_}{'wantip'} foreach @hosts;
3276+
3277+ info("setting IP address to %s for %s", $ip, $hosts);
3278+ verbose("UPDATE:","updating %s", $hosts);
3279+
3280+ #'http://www.dnspark.com:80/visitors/update.html?myip=10.20.30.40&hostname=test.burry.ca'
3281+
3282+ my $url;
3283+ $url = "http://$config{$h}{'server'}/visitors/update.html";
3284+ $url .= "?hostname=$hosts";
3285+ $url .= "&myip=";
3286+ $url .= $ip if $ip;
3287+
3288+ if ($config{$h}{'mx'}) {
3289+ $url .= "&mx=$config{$h}{'mx'}";
3290+ $url .= "&mxpri=" . $config{$h}{'mxpri'};
3291+ }
3292+
3293+ my $reply = geturl(opt('proxy'), $url, $config{$h}{'login'}, $config{$h}{'password'});
3294+ if (!defined($reply) || !$reply) {
3295+ failed("updating %s: Could not connect to %s.", $hosts, $config{$h}{'server'});
3296+ last;
3297+ }
3298+ last if !header_ok($hosts, $reply);
3299+
3300+ my @reply = split /\n/, $reply;
3301+ my $state = 'header';
3302+ foreach my $line (@reply) {
3303+ if ($state eq 'header') {
3304+ $state = 'body';
3305+
3306+ } elsif ($state eq 'body') {
3307+ $state = 'results' if $line eq '';
3308+
3309+ } elsif ($state =~ /^results/) {
3310+ $state = 'results2';
3311+
3312+ my ($status) = $line =~ /^(\S*)\b.*/;
3313+ my $h = pop @hosts;
3314+
3315+ $config{$h}{'status'} = $status;
3316+ if ($status eq 'ok') {
3317+ $config{$h}{'ip'} = $ip;
3318+ $config{$h}{'mtime'} = $now;
3319+ success("updating %s: %s: IP address set to %s", $h, $status, $ip);
3320+
3321+ } elsif ($status =~ /TOOSOON/) {
3322+ ## make sure we wait at least a little
3323+ my ($wait, $units) = (5, 'm');
3324+ my ($sec, $scale) = ($wait, 1);
3325+
3326+ ($scale, $units) = (1, 'seconds') if $units eq 's';
3327+ ($scale, $units) = (60, 'minutes') if $units eq 'm';
3328+ ($scale, $units) = (60*60, 'hours') if $units eq 'h';
3329+ $config{$h}{'wtime'} = $now + $sec;
3330+ warning("updating %s: %s: wait $wait $units before further updates", $h, $status, $ip);
3331+
3332+ } elsif (exists $errors{$status}) {
3333+ failed("updating %s: %s: %s", $h, $line, $errors{$status});
3334+
3335+ } else {
3336+ failed("updating %s: %s: unexpected status (%s)", $h, $line);
3337+ }
3338+ last;
3339+ }
3340+ }
3341+ failed("updating %s: Could not connect to %s.", $hosts, $config{$h}{'server'})
3342+ if $state ne 'results2';
3343+ }
3344+}
3345+
3346+######################################################################
3347+
3348+######################################################################
3349+## nic_namecheap_examples
3350+######################################################################
3351+sub nic_namecheap_examples {
3352+ return <<EoEXAMPLE;
3353+
3354+o 'namecheap'
3355+
3356+The 'namecheap' protocol is used by DNS service offered by www.namecheap.com.
3357+
3358+Configuration variables applicable to the 'namecheap' protocol are:
3359+ protocol=namecheap ##
3360+ server=fqdn.of.service ## defaults to dynamicdns.park-your-domain.com
3361+ login=service-login ## login name and password registered with the service
3362+ password=service-password ##
3363+ fully.qualified.host ## the host registered with the service.
3364+
3365+Example ${program}.conf file entries:
3366+ ## single host update
3367+ protocol=namecheap, \\
3368+ login=my-namecheap.com-login, \\
3369+ password=my-namecheap.com-password \\
3370+ myhost.namecheap.com
3371+
3372+EoEXAMPLE
3373+}
3374+######################################################################
3375+## nic_namecheap_update
3376+##
3377+## written by Dan Boardman
3378+##
3379+## based on http://www.namecheap.com/resources/help/index.asp?t=dynamicdns
3380+## needs this url to update:
3381+## http://dynamicdns.park-your-domain.com/update?host=host_name&
3382+## domain=domain.com&password=domain_password[&ip=your_ip]
3383+##
3384+######################################################################
3385+sub nic_namecheap_update {
3386+
3387+
3388+ debug("\nnic_namecheap1_update -------------------");
3389+
3390+ ## update each configured host
3391+ foreach my $h (@_) {
3392+ my $ip = delete $config{$h}{'wantip'};
3393+ info("setting IP address to %s for %s", $ip, $h);
3394+ verbose("UPDATE:","updating %s", $h);
3395+
3396+ my $url;
3397+ $url = "http://$config{$h}{'server'}/update";
3398+ $url .= "?host=$h";
3399+ $url .= "&domain=$config{$h}{'login'}";
3400+ $url .= "&password=$config{$h}{'password'}";
3401+ $url .= "&ip=";
3402+ $url .= $ip if $ip;
3403+
3404+ my $reply = geturl(opt('proxy'), $url);
3405+ if (!defined($reply) || !$reply) {
3406+ failed("updating %s: Could not connect to %s.", $h, $config{$h}{'server'});
3407+ last;
3408+ }
3409+ last if !header_ok($h, $reply);
3410+
3411+ my @reply = split /\n/, $reply;
3412+ if (grep /<ErrCount>0/i, @reply) {
3413+ $config{$h}{'ip'} = $ip;
3414+ $config{$h}{'mtime'} = $now;
3415+ $config{$h}{'status'} = 'good';
3416+ success("updating %s: good: IP address set to %s", $h, $ip);
3417+ } else {
3418+ $config{$h}{'status'} = 'failed';
3419+ warning("SENT: %s", $url) unless opt('verbose');
3420+ warning("REPLIED: %s", $reply);
3421+ failed("updating %s: Invalid reply.", $h);
3422+ }
3423+ }
3424+}
3425+
3426+######################################################################
3427+
3428+
3429+######################################################################
3430+
3431+######################################################################
3432+## nic_sitelutions_examples
3433+######################################################################
3434+sub nic_sitelutions_examples {
3435+ return <<EoEXAMPLE;
3436+
3437+o 'sitelutions'
3438+
3439+The 'sitelutions' protocol is used by DNS services offered by www.sitelutions.com.
3440+
3441+Configuration variables applicable to the 'sitelutions' protocol are:
3442+ protocol=sitelutions ##
3443+ server=fqdn.of.service ## defaults to sitelutions.com
3444+ login=service-login ## login name and password registered with the service
3445+ password=service-password ##
3446+ A_record_id ## Id of the A record for the host registered with the service.
3447+
3448+Example ${program}.conf file entries:
3449+ ## single host update
3450+ protocol=sitelutions, \\
3451+ login=my-sitelutions.com-login, \\
3452+ password=my-sitelutions.com-password \\
3453+ my-sitelutions.com-id_of_A_record
3454+
3455+EoEXAMPLE
3456+}
3457+######################################################################
3458+## nic_sitelutions_update
3459+##
3460+## written by Mike W. Smith
3461+##
3462+## based on http://www.sitelutions.com/help/dynamic_dns_clients#updatespec
3463+## needs this url to update:
3464+## https://www.sitelutions.com/dnsup?id=990331&user=myemail@mydomain.com&pass=SecretPass&ip=192.168.10.4
3465+## domain=domain.com&password=domain_password&ip=your_ip
3466+##
3467+######################################################################
3468+sub nic_sitelutions_update {
3469+
3470+
3471+ debug("\nnic_sitelutions_update -------------------");
3472+
3473+ ## update each configured host
3474+ foreach my $h (@_) {
3475+ my $ip = delete $config{$h}{'wantip'};
3476+ info("setting IP address to %s for %s", $ip, $h);
3477+ verbose("UPDATE:","updating %s", $h);
3478+
3479+ my $url;
3480+ $url = "http://$config{$h}{'server'}/dnsup";
3481+ $url .= "?id=$h";
3482+ $url .= "&user=$config{$h}{'login'}";
3483+ $url .= "&pass=$config{$h}{'password'}";
3484+ $url .= "&ip=";
3485+ $url .= $ip if $ip;
3486+
3487+ my $reply = geturl(opt('proxy'), $url);
3488+ if (!defined($reply) || !$reply) {
3489+ failed("updating %s: Could not connect to %s.", $h, $config{$h}{'server'});
3490+ last;
3491+ }
3492+ last if !header_ok($h, $reply);
3493+
3494+ my @reply = split /\n/, $reply;
3495+ if (grep /success/i, @reply) {
3496+ $config{$h}{'ip'} = $ip;
3497+ $config{$h}{'mtime'} = $now;
3498+ $config{$h}{'status'} = 'good';
3499+ success("updating %s: good: IP address set to %s", $h, $ip);
3500+ } else {
3501+ $config{$h}{'status'} = 'failed';
3502+ warning("SENT: %s", $url) unless opt('verbose');
3503+ warning("REPLIED: %s", $reply);
3504+ failed("updating %s: Invalid reply.", $h);
3505+ }
3506+ }
3507+}
3508+
3509+######################################################################
3510+
3511+######################################################################
3512+## nic_freedns_examples
3513+######################################################################
3514+sub nic_freedns_examples {
3515+return <<EoEXAMPLE;
3516+
3517+o 'freedns'
3518+
3519+The 'freedns' protocol is used by DNS services offered by freedns.afraid.org.
3520+
3521+Configuration variables applicable to the 'freedns' protocol are:
3522+ protocol=freedns ##
3523+ server=fqdn.of.service ## defaults to freedns.afraid.org
3524+ login=service-login ## login name and password registered with the service
3525+ password=service-password ##
3526+ fully.qualified.host ## the host registered with the service.
3527+
3528+Example ${program}.conf file entries:
3529+ ## single host update
3530+ protocol=freedns, \\
3531+ login=my-freedns.afraid.org-login, \\
3532+ password=my-freedns.afraid.org-password \\
3533+ myhost.afraid.com
3534+
3535+EoEXAMPLE
3536+}
3537+######################################################################
3538+## nic_freedns_update
3539+##
3540+## written by John Haney
3541+##
3542+## based on http://freedns.afraid.org/api/
3543+## needs this url to update:
3544+## http://freedns.afraid.org/api/?action=getdyndns&sha=<sha1sum of login|password>
3545+## This returns a list of host|currentIP|updateURL lines.
3546+## Pick the line that matches myhost, and fetch the URL.
3547+## word 'Updated' for success, 'fail' for failure.
3548+##
3549+######################################################################
3550+sub nic_freedns_update {
3551+
3552+
3553+ debug("\nnic_freedns_update -------------------");
3554+
3555+ ## First get the list of updatable hosts
3556+ my $url;
3557+ $url = "http://$config{$_[0]}{'server'}/api/?action=getdyndns&sha=".&sha1_hex("$config{$_[0]}{'login'}|$config{$_[0]}{'password'}");
3558+ my $reply = geturl(opt('proxy'), $url);
3559+ if (!defined($reply) || !$reply || !header_ok($_[0], $reply)) {
3560+ failed("updating %s: Could not connect to %s for site list.", $_[0], $url);
3561+ return;
3562+ }
3563+ my @lines = split("\n", $reply);
3564+ my %freedns_hosts;
3565+ grep {
3566+ my @rec = split(/\|/, $_);
3567+ $freedns_hosts{$rec[0]} = \@rec if ($#rec > 0);
3568+ } @lines;
3569+ if (!keys %freedns_hosts) {
3570+ failed("Could not get freedns update URLs from %s", $config{$_[0]}{'server'});
3571+ return;
3572+ }
3573+ ## update each configured host
3574+ foreach my $h (@_) {
3575+ if(!$h){ next };
3576+ my $ip = delete $config{$h}{'wantip'};
3577+ info("setting IP address to %s for %s", $ip, $h);
3578+ verbose("UPDATE:","updating %s", $h);
3579+
3580+ if($ip eq $freedns_hosts{$h}->[1]) {
3581+ $config{$h}{'ip'} = $ip;
3582+ $config{$h}{'mtime'} = $now;
3583+ $config{$h}{'status'} = 'good';
3584+ success("update not necessary %s: good: IP address already set to %s", $h, $ip);
3585+ } else {
3586+ my $reply = geturl(opt('proxy'), $freedns_hosts{$h}->[2]);
3587+ if (!defined($reply) || !$reply) {
3588+ failed("updating %s: Could not connect to %s.", $h, $freedns_hosts{$h}->[2]);
3589+ last;
3590+ }
3591+ if(!header_ok($h, $reply)) {
3592+ $config{$h}{'status'} = 'failed';
3593+ last;
3594+ }
3595+
3596+ if($reply =~ /Updated.*$h.*to.*$ip/) {
3597+ $config{$h}{'ip'} = $ip;
3598+ $config{$h}{'mtime'} = $now;
3599+ $config{$h}{'status'} = 'good';
3600+ success("updating %s: good: IP address set to %s", $h, $ip);
3601+ } else {
3602+ $config{$h}{'status'} = 'failed';
3603+ warning("SENT: %s", $freedns_hosts{$h}->[2]) unless opt('verbose');
3604+ warning("REPLIED: %s", $reply);
3605+ failed("updating %s: Invalid reply.", $h);
3606+ }
3607+ }
3608+ }
3609+}
3610+
3611+######################################################################
3612+
3613+######################################################################
3614+## nic_dtdns_examples
3615+######################################################################
3616+sub nic_dtdns_examples {
3617+ return <<EoEXAMPLE;
3618+o 'dtdns'
3619+
3620+The 'dtdns' protocol is the protocol used by the dynamic hostname services
3621+of the 'DtDNS' dns services. This is currently used by the free
3622+dynamic DNS service offered by www.dtdns.com.
3623+
3624+Configuration variables applicable to the 'dtdns' protocol are:
3625+ protocol=dtdns ##
3626+ server=www.fqdn.of.service ## defaults to www.dtdns.com
3627+ password=service-password ## password registered with the service
3628+ client=name_of_updater ## defaults to $program (10 chars max, no spaces)
3629+ fully.qualified.host ## the host registered with the service.
3630+
3631+Example ${program}.conf file entries:
3632+ ## single host update
3633+ protocol=dtdns, \\
3634+ password=my-dydns.za.net-password, \\
3635+ client=ddclient \\
3636+ myhost.dtdns.net
3637+
3638+EoEXAMPLE
3639+}
3640+
3641+######################################################################
3642+## nic_dtdns_update
3643+## by Achim Franke
3644+######################################################################
3645+sub nic_dtdns_update {
3646+ debug("\nnic_dtdns_update -------------------");
3647+
3648+ ## update each configured host
3649+ foreach my $h (@_) {
3650+ my $ip = delete $config{$h}{'wantip'};
3651+ info("setting IP address to %s for %s", $ip, $h);
3652+ verbose("UPDATE:","updating %s", $h);
3653+
3654+ # Set the URL that we're going to to update
3655+ my $url;
3656+ $url = "http://$config{$h}{'server'}/api/autodns.cfm";
3657+ $url .= "?id=";
3658+ $url .= $h;
3659+ $url .= "&pw=";
3660+ $url .= $config{$h}{'password'};
3661+ $url .= "&ip=";
3662+ $url .= $ip;
3663+ $url .= "&client=";
3664+ $url .= $config{$h}{'client'};
3665+
3666+ # Try to get URL
3667+ my $reply = geturl(opt('proxy'), $url);
3668+
3669+ # No response, declare as failed
3670+ if (!defined($reply) || !$reply) {
3671+ failed("updating %s: Could not connect to %s.", $h, $config{$h}{'server'});
3672+ last;
3673+ }
3674+ last if !header_ok($h, $reply);
3675+
3676+ # Response found, just declare as success (this is ugly, we need more error checking)
3677+ if ($reply =~ /now\spoints\sto/)
3678+ {
3679+ $config{$h}{'ip'} = $ip;
3680+ $config{$h}{'mtime'} = $now;
3681+ $config{$h}{'status'} = 'good';
3682+ success("updating %s: good: IP address set to %s", $h, $ip);
3683+ }
3684+ else
3685+ {
3686+ my @reply = split /\n/, $reply;
3687+ my $returned = pop(@reply);
3688+ $config{$h}{'status'} = 'failed';
3689+ failed("updating %s: Server said: '$returned'", $h);
3690+ }
3691+ }
3692+}
3693+
3694+######################################################################
3695+# vim: ai ts=4 sw=4 tw=78 :
3696+
3697+
3698+__END__
3699
3700=== modified file 'ddclient'
3701--- ddclient 2012-09-18 13:30:33 +0000
3702+++ ddclient 2014-12-07 04:23:06 +0000
3703@@ -1784,13 +1784,18 @@
3704 ######################################################################
3705 sub load_sha1_support {
3706 my $sha1_loaded = eval {require Digest::SHA1};
3707- unless ($sha1_loaded) {
3708+ my $sha_loaded = eval {require Digest::SHA};
3709+ unless ($sha1_loaded || $sha_loaded) {
3710 fatal(<<"EOM");
3711-Error loading the Perl module Digest::SHA1 needed for freedns update.
3712-On Debian, the package libdigest-sha1-perl must be installed.
3713+Error loading the Perl module Digest::SHA1 or Digest::SHA needed for freedns update.
3714+On Debian, the package libdigest-sha1-perl or libdigest-sha-perl must be installed.
3715 EOM
3716 }
3717- import Digest::SHA1 (qw/sha1_hex/);
3718+ if($sha1_loaded) {
3719+ import Digest::SHA1 (qw/sha1_hex/);
3720+ } elsif($sha_loaded) {
3721+ import Digest::SHA (qw/sha1_hex/);
3722+ }
3723 }
3724 ######################################################################
3725 ## geturl
3726
3727=== modified file 'debian/changelog'
3728--- debian/changelog 2013-07-29 14:49:34 +0000
3729+++ debian/changelog 2014-12-07 04:23:06 +0000
3730@@ -1,3 +1,10 @@
3731+ddclient (3.8.1-1ubuntu3) vivid; urgency=low
3732+
3733+ * Add patch from upstream which enables ddclient to find Digest::SHA to
3734+ fix FreeDNS support (LP: #1068884)
3735+
3736+ -- Scott Talbert <swt@techie.net> Sat, 06 Dec 2014 22:49:13 -0500
3737+
3738 ddclient (3.8.1-1ubuntu2) saucy; urgency=low
3739
3740 * debian/ddclient.NetworkManager
3741
3742=== added file 'debian/patches/fix_digest_sha_freedns.diff'
3743--- debian/patches/fix_digest_sha_freedns.diff 1970-01-01 00:00:00 +0000
3744+++ debian/patches/fix_digest_sha_freedns.diff 2014-12-07 04:23:06 +0000
3745@@ -0,0 +1,27 @@
3746+Index: ddclient/ddclient
3747+===================================================================
3748+--- ddclient.orig/ddclient 2014-12-06 21:59:46.763693000 -0500
3749++++ ddclient/ddclient 2014-12-06 22:32:58.422405251 -0500
3750+@@ -1784,13 +1784,18 @@
3751+ ######################################################################
3752+ sub load_sha1_support {
3753+ my $sha1_loaded = eval {require Digest::SHA1};
3754+- unless ($sha1_loaded) {
3755++ my $sha_loaded = eval {require Digest::SHA};
3756++ unless ($sha1_loaded || $sha_loaded) {
3757+ fatal(<<"EOM");
3758+-Error loading the Perl module Digest::SHA1 needed for freedns update.
3759+-On Debian, the package libdigest-sha1-perl must be installed.
3760++Error loading the Perl module Digest::SHA1 or Digest::SHA needed for freedns update.
3761++On Debian, the package libdigest-sha1-perl or libdigest-sha-perl must be installed.
3762+ EOM
3763+ }
3764+- import Digest::SHA1 (qw/sha1_hex/);
3765++ if($sha1_loaded) {
3766++ import Digest::SHA1 (qw/sha1_hex/);
3767++ } elsif($sha_loaded) {
3768++ import Digest::SHA (qw/sha1_hex/);
3769++ }
3770+ }
3771+ ######################################################################
3772+ ## geturl
3773
3774=== modified file 'debian/patches/series'
3775--- debian/patches/series 2012-09-18 13:30:33 +0000
3776+++ debian/patches/series 2014-12-07 04:23:06 +0000
3777@@ -2,3 +2,4 @@
3778 config_path.diff
3779 maxinterval.diff
3780 sample_ubuntu.diff
3781+fix_digest_sha_freedns.diff

Subscribers

People subscribed via source and target branches

to all changes: