#! /usr/bin/perl ################################################ # # miniDNS.net Record Updater Release 0.4 # Written by: Michael Fung # # Copyright 2002-2003 Michael Fung. All rights reserved. # # This program is free software; you can redistribute # it and/or modify it under the GPL. # # Last Updated: 24-Sep-2003 # ################################################ ################################################ # # User Configurable parameters $interface = "ppp0"; $login_id = "mylogin"; $password = "my3058"; $hostname = "myhostname.minidns.net"; # # Advanced users: please set the file paths to the appropiate place for your OS # $log_file = "/var/log/miniUpdate.log"; $lock_file = "/var/run/miniUpdate.pid"; $ifconfig = "/sbin/ifconfig"; # # If your public IP is eaten by the router(e.g. your machine only get 192.168.x.x), # please set $no_public_ip to 1 and $report_url to the URL of an IP address # reporting site. However, you should get consent of the reporting site before # using it. # $no_public_ip = 0; $report_url = "http://a-reporting-site/a-script"; # ################################################ use Proc::Daemon; use POSIX; use IO::Socket; use Digest::MD5; use LWP::Simple; # define some constants $server_addr = "update.minidns.net"; $server_port = "9120"; $version = "0.4"; $support_version = "0.8.1"; $ctx = Digest::MD5->new; $last_ip = ""; $max_interval = 604800; # max interval (7days) between updates to prevent inactivity if ($no_public_ip) { $retry_period = 300; # prevent net abuse } else { $retry_period = 30; } # define debug logging subroutine sub logwrite { my $msg = shift; my $date_text = strftime "%Y-%m-%d %H:%M:%S", localtime; my $logtext = "[" . $date_text . "] $msg\n"; open(LOGFP, ">> $log_file"); print LOGFP "$logtext"; close(LOGFP); } # define ip reader sub get_ipaddr { my $new_ip = ''; if ($no_public_ip) { my $page = get($report_url); if ($page =~ /(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/) { $new_ip = "$1.$2.$3.$4"; } else { $new_ip = $last_ip; } } else { $new_ip = `$ifconfig $interface | grep inet | cut -d : -f 2 | cut -d \\ -f 1`; chomp($new_ip); } return $new_ip; } # define prg exit sub prg_exit { logwrite("*** Shutdown ***"); close($server); unlink "$lock_file"; exit; } sub read_server_cooked { my $line; eval { local $SIG{ALRM} = sub { die "Timeout\n" }; alarm 10; $line = <$server>; alarm 0; }; if ($@) { return; } else { chomp($line); $line =~ s/\r//g; # get rid of CR $line =~ s/\n//g; # get rid of LF my $space_offset = index($line, " "); my $status_code = substr($line, 0, $space_offset); my $status_text = substr($line, $space_offset + 1); if ($status_code eq "ERR") { logwrite("### FATAL ERROR #### $status_text"); print "Fatal Error: $status_text\n"; &prg_exit; } else { return $status_text; } } } sub mini_connect { # connect $server = IO::Socket::INET->new( Proto => 'tcp', PeerAddr => $server_addr, PeerPort => $server_port); return unless $server; # read welcome message return unless $status = read_server_cooked(); # print "$status\n"; # send agent identity print $server "agent miniUpdate/$version\n"; # read ok return unless $status = read_server_cooked(); # send version command print $server "version\n"; # read version return unless $status = read_server_cooked(); # print "$status\n"; if ($status ne $support_version) { logwrite("### WARNING ### Version mismatch, please check for upgrade"); } # send login cmd print $server "login $login_id digest-md5\n"; #print $server "login $login_id plain\n"; # test hang # read challenge: return unless $challenge = read_server_cooked(); # print "Challenge: $challenge\n"; # compute response: $challenge_bin = pack("H32", $challenge); $ctx->reset; $ctx->add($password); #$md5password = $ctx->hexdigest; #$md5password_bin = pack("H32", $md5password); $md5password_bin = $ctx->digest; $ctx->reset; $ctx->add($md5password_bin . $challenge_bin); $response = $ctx->hexdigest; # send response print $server "response $response\n"; # read status return unless $status = read_server_cooked(); print "$status\n"; return 1; } sub a_update { my $new_ip = shift; # send update command print $server "a_update online $hostname $new_ip\n"; # read status return unless $status = read_server_cooked(); # print "$status\n"; return 1; } sub mini_disconnect { # last step, send exit command print $server "exit\n"; return unless $status = read_server_cooked(); # print "$status\n"; close($server); return 1; } ################################### # # Main Entry # # avoid duplicate process if (-e $lock_file) { print STDERR "ERROR: Another copy of miniUpdate is running\n"; exit; } else { open(LF, "> $lock_file"); print LF "$$\n"; close(LF); } Proc::Daemon::Init; # switch to daemon mode logwrite(""); logwrite(""); logwrite("*** miniUpdate $version Started ***"); $SIG{'TERM'} = \&prg_exit; while (1) { # compare ip to determine whether update or not $new_ip = get_ipaddr(); if (($last_ip ne $new_ip) or ((time() - $last_update) > $max_interval)){ # connect do { logwrite("### ERROR ### Unable to connect to miniDNS Update server."); goto RETRY; } unless mini_connect(); # update do { logwrite("### ERROR ### Update request failed."); goto RETRY; } unless a_update($new_ip); logwrite("$hostname mapped to $new_ip"); $last_ip = $new_ip; $last_update = time(); # reset last update time # disconnect mini_disconnect(); } else { # print "No need to update\n"; } # sleep for $retry_period seconds RETRY: sleep $retry_period; } # endless loop