File:
[ELWIX - Embedded LightWeight unIX -] /
embedaddon /
dnsmasq /
contrib /
dynamic-dnsmasq /
dynamic-dnsmasq.pl
Revision
1.1.1.1 (vendor branch):
download - view:
text,
annotated -
select for diffs -
revision graph
Mon Jul 29 19:37:40 2013 UTC (11 years, 1 month ago) by
misho
Branches:
elwix,
dnsmasq,
MAIN
CVS tags:
v8_2p1,
v2_84,
v2_76p1,
v2_71,
v2_66p0,
v2_66,
HEAD
dnsmasq
1: #!/usr/bin/perl
2: # dynamic-dnsmasq.pl - update dnsmasq's internal dns entries dynamically
3: # Copyright (C) 2004 Peter Willis
4: #
5: # This program is free software; you can redistribute it and/or modify
6: # it under the terms of the GNU General Public License as published by
7: # the Free Software Foundation; either version 2 of the License, or
8: # (at your option) any later version.
9: #
10: # This program is distributed in the hope that it will be useful,
11: # but WITHOUT ANY WARRANTY; without even the implied warranty of
12: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13: # GNU General Public License for more details.
14: #
15: # You should have received a copy of the GNU General Public License
16: # along with this program; if not, write to the Free Software
17: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18: #
19: # the purpose of this script is to be able to update dnsmasq's dns
20: # records from a remote dynamic dns client.
21: #
22: # basic use of this script:
23: # dynamic-dnsmasq.pl add testaccount 1234 testaccount.mydomain.com
24: # dynamic-dnsmasq.pl listen &
25: #
26: # this script tries to emulate DynDNS.org's dynamic dns service, so
27: # technically you should be able to use any DynDNS.org client to
28: # update the records here. tested and confirmed to work with ddnsu
29: # 1.3.1. just point the client's host to the IP of this machine,
30: # port 9020, and include the hostname, user and pass, and it should
31: # work.
32: #
33: # make sure "addn-hosts=/etc/dyndns-hosts" is in your /etc/dnsmasq.conf
34: # file and "nopoll" is commented out.
35:
36: use strict;
37: use IO::Socket;
38: use MIME::Base64;
39: use DB_File;
40: use Fcntl;
41:
42: my $accountdb = "accounts.db";
43: my $recordfile = "/etc/dyndns-hosts";
44: my $dnsmasqpidfile = "/var/run/dnsmasq.pid"; # if this doesn't exist, will look for process in /proc
45: my $listenaddress = "0.0.0.0";
46: my $listenport = 9020;
47:
48: # no editing past this point should be necessary
49:
50: if ( @ARGV < 1 ) {
51: die "Usage: $0 ADD|DEL|LISTUSERS|WRITEHOSTSFILE|LISTEN\n";
52: } elsif ( lc $ARGV[0] eq "add" ) {
53: die "Usage: $0 ADD USER PASS HOSTNAME\n" unless @ARGV == 4;
54: add_acct($ARGV[1], $ARGV[2], $ARGV[3]);
55: } elsif ( lc $ARGV[0] eq "del" ) {
56: die "Usage: $0 DEL USER\n" unless @ARGV == 2;
57: print "Are you sure you want to delete user \"$ARGV[1]\"? [N/y] ";
58: my $resp = <STDIN>;
59: chomp $resp;
60: if ( lc substr($resp,0,1) eq "y" ) {
61: del_acct($ARGV[1]);
62: }
63: } elsif ( lc $ARGV[0] eq "listusers" or lc $ARGV[0] eq "writehostsfile" ) {
64: my $X = tie my %h, "DB_File", $accountdb, O_RDWR|O_CREAT, 0600, $DB_HASH;
65: my $fh;
66: if ( lc $ARGV[0] eq "writehostsfile" ) {
67: open($fh, ">$recordfile") || die "Couldn't open recordfile \"$recordfile\": $!\n";
68: flock($fh, 2);
69: seek($fh, 0, 0);
70: truncate($fh, 0);
71: }
72: while ( my ($key, $val) = each %h ) {
73: my ($pass, $domain, $ip) = split("\t",$val);
74: if ( lc $ARGV[0] eq "listusers" ) {
75: print "user $key, hostname $domain, ip $ip\n";
76: } else {
77: if ( defined $ip ) {
78: print $fh "$ip\t$domain\n";
79: }
80: }
81: }
82: if ( lc $ARGV[0] eq "writehostsfile" ) {
83: flock($fh, 8);
84: close($fh);
85: dnsmasq_rescan_configs();
86: }
87: undef $X;
88: untie %h;
89: } elsif ( lc $ARGV[0] eq "listen" ) {
90: listen_for_updates();
91: }
92:
93: sub listen_for_updates {
94: my $sock = IO::Socket::INET->new(Listen => 5,
95: LocalAddr => $listenaddress, LocalPort => $listenport,
96: Proto => 'tcp', ReuseAddr => 1,
97: MultiHomed => 1) || die "Could not open listening socket: $!\n";
98: $SIG{'CHLD'} = 'IGNORE';
99: while ( my $client = $sock->accept() ) {
100: my $p = fork();
101: if ( $p != 0 ) {
102: next;
103: }
104: $SIG{'CHLD'} = 'DEFAULT';
105: my @headers;
106: my %cgi;
107: while ( <$client> ) {
108: s/(\r|\n)//g;
109: last if $_ eq "";
110: push @headers, $_;
111: }
112: foreach my $header (@headers) {
113: if ( $header =~ /^GET \/nic\/update\?([^\s].+) HTTP\/1\.[01]$/ ) {
114: foreach my $element (split('&', $1)) {
115: $cgi{(split '=', $element)[0]} = (split '=', $element)[1];
116: }
117: } elsif ( $header =~ /^Authorization: basic (.+)$/ ) {
118: unless ( defined $cgi{'hostname'} ) {
119: print_http_response($client, undef, "badsys");
120: exit(1);
121: }
122: if ( !exists $cgi{'myip'} ) {
123: $cgi{'myip'} = $client->peerhost();
124: }
125: my ($user,$pass) = split ":", MIME::Base64::decode($1);
126: if ( authorize($user, $pass, $cgi{'hostname'}, $cgi{'myip'}) == 0 ) {
127: print_http_response($client, $cgi{'myip'}, "good");
128: update_dns(\%cgi);
129: } else {
130: print_http_response($client, undef, "badauth");
131: exit(1);
132: }
133: last;
134: }
135: }
136: exit(0);
137: }
138: return(0);
139: }
140:
141: sub add_acct {
142: my ($user, $pass, $hostname) = @_;
143: my $X = tie my %h, "DB_File", $accountdb, O_RDWR|O_CREAT, 0600, $DB_HASH;
144: $X->put($user, join("\t", ($pass, $hostname)));
145: undef $X;
146: untie %h;
147: }
148:
149: sub del_acct {
150: my ($user, $pass, $hostname) = @_;
151: my $X = tie my %h, "DB_File", $accountdb, O_RDWR|O_CREAT, 0600, $DB_HASH;
152: $X->del($user);
153: undef $X;
154: untie %h;
155: }
156:
157:
158: sub authorize {
159: my $user = shift;
160: my $pass = shift;
161: my $hostname = shift;
162: my $ip = shift;;
163: my $X = tie my %h, "DB_File", $accountdb, O_RDWR|O_CREAT, 0600, $DB_HASH;
164: my ($spass, $shost) = split("\t", $h{$user});
165: if ( defined $h{$user} and ($spass eq $pass) and ($shost eq $hostname) ) {
166: $X->put($user, join("\t", $spass, $shost, $ip));
167: undef $X;
168: untie %h;
169: return(0);
170: }
171: undef $X;
172: untie %h;
173: return(1);
174: }
175:
176: sub print_http_response {
177: my $sock = shift;
178: my $ip = shift;
179: my $response = shift;
180: print $sock "HTTP/1.0 200 OK\n";
181: my @tmp = split /\s+/, scalar gmtime();
182: print $sock "Date: $tmp[0], $tmp[2] $tmp[1] $tmp[4] $tmp[3] GMT\n";
183: print $sock "Server: Peter's Fake DynDNS.org Server/1.0\n";
184: print $sock "Content-Type: text/plain; charset=ISO-8859-1\n";
185: print $sock "Connection: close\n";
186: print $sock "Transfer-Encoding: chunked\n";
187: print $sock "\n";
188: #print $sock "12\n"; # this was part of the dyndns response but i'm not sure what it is
189: print $sock "$response", defined($ip)? " $ip" : "" . "\n";
190: }
191:
192: sub update_dns {
193: my $hashref = shift;
194: my @records;
195: my $found = 0;
196: # update the addn-hosts file
197: open(FILE, "+<$recordfile") || die "Couldn't open recordfile \"$recordfile\": $!\n";
198: flock(FILE, 2);
199: while ( <FILE> ) {
200: if ( /^(\d+\.\d+\.\d+\.\d+)\s+$$hashref{'hostname'}\n$/si ) {
201: if ( $1 ne $$hashref{'myip'} ) {
202: push @records, "$$hashref{'myip'}\t$$hashref{'hostname'}\n";
203: $found = 1;
204: }
205: } else {
206: push @records, $_;
207: }
208: }
209: unless ( $found ) {
210: push @records, "$$hashref{'myip'}\t$$hashref{'hostname'}\n";
211: }
212: sysseek(FILE, 0, 0);
213: truncate(FILE, 0);
214: syswrite(FILE, join("", @records));
215: flock(FILE, 8);
216: close(FILE);
217: dnsmasq_rescan_configs();
218: return(0);
219: }
220:
221: sub dnsmasq_rescan_configs {
222: # send the HUP signal to dnsmasq
223: if ( -r $dnsmasqpidfile ) {
224: open(PID,"<$dnsmasqpidfile") || die "Could not open PID file \"$dnsmasqpidfile\": $!\n";
225: my $pid = <PID>;
226: close(PID);
227: chomp $pid;
228: if ( kill(0, $pid) ) {
229: kill(1, $pid);
230: } else {
231: goto LOOKFORDNSMASQ;
232: }
233: } else {
234: LOOKFORDNSMASQ:
235: opendir(DIR,"/proc") || die "Couldn't opendir /proc: $!\n";
236: my @dirs = grep(/^\d+$/, readdir(DIR));
237: closedir(DIR);
238: foreach my $process (@dirs) {
239: if ( open(FILE,"</proc/$process/cmdline") ) {
240: my $cmdline = <FILE>;
241: close(FILE);
242: if ( (split(/\0/,$cmdline))[0] =~ /dnsmasq/ ) {
243: kill(1, $process);
244: }
245: }
246: }
247: }
248: return(0);
249: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>