3
# janus - Dynamic DNS watcher for FreeS/WAN & forks
4
# (c) 2004 Tiago Freitas Leal <tfl@netcbo.pt>
6
# This is a fork of ipsec_monitor
7
# Copyright (C) 2003 by Tim Niemueller <tim@niemueller.de>
8
# Website: http://www.niemueller.de/software/perl/ipsecmonitor
10
# This program is free software; you can redistribute it and/or modify
11
# it under the terms of the GNU General Public License as published by
12
# the Free Software Foundation; either version 2 of the License, or
13
# (at your option) any later version.
15
# This program is distributed in the hope that it will be useful,
16
# but WITHOUT ANY WARRANTY; without even the implied warranty of
17
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
# GNU General Public License for more details.
21
# Released: 17.08.2004
31
#### Constants, just to make code readable
33
my $ipsec="/usr/local/sbin/ipsec";
34
my $janus="/usr/local/bin/janus";
38
GetOptions("help" => \$params{'help'},
39
"start" => \$params{'start'},
40
"stop" => \$params{'stop'},
41
"restart" => \$params{'restart'},
42
"script:s" => \$params{'script'},
43
"t:i" => \$params{'t'},
45
"nolog" => \$params{'nolog'},
46
"ver" => \$params{'ver'},
49
if($params{'help'} ne "") {
53
if( ($params{'script'} ne "") && (! -x $params{'script'}) ) {
54
failure_exit("The script file does not exist or is not executable.");
58
print "janus watcher $VERSION\n";
62
if($params{'t'} eq "") {
67
$SIG{'TERM'} = \&terminate_daemon;
68
$SIG{'HUP'} = \&check_status;
70
#### Globals to make it quick and dirty
72
my $lockfile = "/var/lock/janus.lock";
73
my $pidfile = "/var/run/janus.pid";
74
my $ctlfile = "/var/run/janus.ctl";
75
my $cfgfile = "/etc/ipsec.conf";
77
#### Main Program, main() like
78
if($params{'stop'} || $params{'restart'}) { kill_daemon(); } # --stop never returns from this call
79
startlog("janus_run");
80
logmsg("info", "Starting janus watcher...");
81
daemonize(); # only daemon returns from this call
84
startlog("janus watcher[$$]");
85
logmsg("info", "Starting Janus - Dynamic DNS watcher (Version $VERSION)");
95
#### Subs related to reading and writting configuration and control files
99
my $action; # 0 = none; 1 = replace; 2 = replace&pullup; 3 = add; 4 = restart
100
open (CTL, "$ctlfile") or failure_exit("unable to open control file");
103
open(CTL,">$ctlfile") or failure_exit("unable to open control file");
107
foreach $line (@temp) {
109
my @temp = split(/\,/,$line);
112
if($newadd = gethostbyname($temp[1])) {
113
$newadd = join(".", unpack('C4', $newadd));
114
if($temp[2] ne $newadd) {
115
# Left address has changed, must update connection
116
if($temp[0] eq '%local') {
117
logmsg("info", "local dynamic: host \"$temp[1]\" changed IP $temp[2] to $newadd");
118
$action = 4; # restart only if local dynamic changed
120
logmsg("info", "\"$temp[0]\": host \"$temp[1]\" changed IP $temp[2] to $newadd");
122
if($temp[5] eq 'start') {
125
if(!$temp[2]) {$action = 3;}
132
if($newadd = gethostbyname($temp[3])) {
133
$newadd = join(".", unpack('C4', $newadd));
134
if($temp[4] ne $newadd) {
135
# Right address has changed, must update connection
136
logmsg("info", "\"$temp[0]\": host \"$temp[3]\" changed IP $temp[4] to $newadd");
138
$action = 1; # replace only if no action for left side
139
if($temp[5] eq 'start') {
140
$action = 2; # replace&pullup only if no action for left side
143
if(!$temp[4]) {$action = 3;}
150
logmsg("info", "\"$temp[0]\": replacing connection");
151
system("$ipsec auto --rereadall");
152
system("$ipsec auto --replace $temp[0]");
153
} elsif($action eq 2) {
154
logmsg("info", "\"$temp[0]\": replacing and pulling up connection");
155
system("$ipsec auto --rereadall");
156
system("$ipsec auto --replace $temp[0]");
157
system("$ipsec auto --up $temp[0]");
158
} elsif($action eq 3) {
159
logmsg("info", "\"$temp[0]\": adding connection");
160
system("$ipsec auto --rereadall");
161
system("$ipsec auto --add $temp[0]");
162
} elsif($action eq 4) {
163
logmsg("info", "Restarting IPSec and janus...");
164
system("$ipsec setup --restart");
165
system("$janus --restart");
167
if($params{'script'}) {
168
# We have a script file, wait 60 seconds and then execute it
170
system($params{'script'});
173
print CTL "$temp[0],$temp[1],$temp[2],$temp[3],$temp[4],$temp[5]\n";
190
open(CTL,">$ctlfile") or failure_exit("unable to open control file");
192
open (CONFIG, "$cfgfile") or failure_exit("unable to open config file");
196
foreach $line(@temp) {
198
my @temp = split(/[\t= ]+/,$line);
200
if(($section ne '') && ($temp[0] eq 'conn')) {
204
if($temp[0] eq 'config' && $temp[1] eq 'setup') {
210
if($section ne 'end') {
211
if($conn eq '%default') {
212
$default{'left'} = $left;
213
$default{'x_leftdynamic'} = $x_leftdynamic;
214
$default{'right'} = $right;
215
$default{'x_rightdynamic'} = $x_rightdynamic;
216
$default{'auto'} = $auto;
220
$x_rightdynamic = '';
223
if(!$left){ $left = $default{'left'}; }
224
if(!$x_leftdynamic){ $x_leftdynamic = $default{'x_leftdynamic'}; }
225
if(!$right){ $right = $default{'right'}; }
226
if(!$x_rightdynamic){ $x_rightdynamic = $default{'x_rightdynamic'}; }
227
if(!$auto){ $auto = $default{'auto'}; }
229
if($x_leftdynamic eq 'yes') {
230
if($conn eq '%local') {
231
$leftconn = "local dynamic";
235
if(!&validip($left) && $left && $left ne '%any') {
236
if($addleft = gethostbyname($left)) {
237
$addleft = join(".", unpack('C4', $addleft));
238
logmsg("info", "\"$leftconn\": watching host \"$left\" on $addleft");
240
logmsg("info", "\"$leftconn\": name lookup failed for host \"$left\"");
243
logmsg("info", "\"$leftconn\": ignoring host \"$left\"");
249
if($x_rightdynamic eq 'yes') {
250
if(!&validip($right) && $right && $right ne '%any') {
251
if($addright = gethostbyname($right)) {
252
$addright = join(".", unpack('C4', $addright));
253
logmsg("info", "\"$conn\": watching host \"$right\" on $addright");
255
logmsg("info", "\"$conn\": name lookup failed for host \"$right\"");
258
logmsg("info", "\"$conn\": ignoring host \"$right\"");
264
if($left ne '' || $right ne '') {
265
print CTL "$conn,$left,$addleft,$right,$addright,$auto\n";
272
$x_rightdynamic = '';
277
if($conn eq '%local') {
278
if($temp[1] eq 'x_localdynamic') {
280
$x_leftdynamic = 'yes';
283
if($temp[1] eq 'left') {
285
} elsif($temp[1] eq 'right') {
287
} elsif($temp[1] eq 'x_leftdynamic') {
288
$x_leftdynamic = $temp[2];
289
} elsif($temp[1] eq 'x_rightdynamic') {
290
$x_rightdynamic = $temp[2];
291
} elsif($temp[1] eq 'auto') {
303
if(!($ip =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/)) {
306
my @octets = ($1, $2, $3, $4);
307
foreach $_ (@octets) {
310
if($_ < 0 || $_ > 255) {
318
#### Subs related to basic program stuff (daemon, fifo, lock etc.)
320
# Could be modified to syslog for example
322
unless ($params{'nolog'}) {
323
use Sys::Syslog qw(:DEFAULT setlogsock);
325
openlog($_[0], "", "authpriv");
330
my $priority = shift;
335
print "$now ($priority): $msg, @_\n";
337
unless ($params{'nolog'}) {
338
syslog($priority, $msg, @_);
343
# logs the errors and exits the program
345
logmsg("info",$_[0]);
350
# Disconnects from console
353
chdir '/' or failure_exit("Can't chdir to /: $!");
354
open STDIN, '/dev/null' or failure_exit("daemonize: Can't read /dev/null: $!");
355
open STDOUT, '>/dev/null' or failure_exit("daemonize: Can't write to /dev/null: $!");
356
defined($pid = fork) or failure_exit("Can't fork: $!");
358
setsid() or failure_exit("Can't start a new session: $!");
362
# creates the lockfile
364
open(LOCK, ">$lockfile");
365
my $ok = flock(LOCK, LOCK_EX | LOCK_NB);
367
failure_exit("LOCK janus is already running") if(! $ok);
370
# removes the lockfile
372
flock(LOCK, LOCK_UN);
377
# creates the pidfile
379
open(PID, ">$pidfile");
380
my $ok = flock(PID, LOCK_EX | LOCK_NB);
382
failure_exit("PID janus is already running") if(! $ok);
384
flock(PID, LOCK_EX | LOCK_NB);
387
# removes the pidfile
396
open(FILE, "$pidfile");
397
$pid = <FILE>; chop $pid;
400
system ("/bin/kill $pid");
401
if($params{'stop'}) { exit; }
403
if($params{'stop'}) { failure_exit("janus isn't running"); }
406
# Terminates daemon closing ISDN connection,
407
# used as signal handler
408
sub terminate_daemon {
409
alarm 0; # Stop timer
413
logmsg("info", "Closing down");
414
unless ($params{'nolog'}) {
420
# prints some basic usage message
422
print "janus v.$VERSION (c) 2004 by Tiago Freitas Leal\n",
423
"janus is a fork of ipsec_monitor v.0.1 (c) 2003 by Tim Niemueller\n",
424
"Watches dynamic DNS hosts and replaces the connection when the IP\n",
425
"address changes.\n",
427
"janus [--script=SCRIPT -t t -d -nolog]\n",
429
"janus --restart [--script=SCRIPT -t t -d -nolog]\n",
430
" Kills the present janus task and starts a new one.\n",
434
" --script=SCRIPT: Path to an additional script that is executed\n",
435
" 1 minute after the connection has been replaced.\n",
436
" For example routing stuff that needs to be done.\n",
437
" Script must be executable!\n",
438
" -t t: Checks every t seconds if connection parameters have changed.\n",
439
" Default is 180 seconds\n",
440
" -d: Debug mode. Do not fork to background, log output to STDOUT.\n",
441
" --nolog: Don't log.\n\n";
443
" Outputs version information.\n",