#!/usr/bin/perl -Tw # updatedns - ChangeIP.com Auto Client Script # by Tristan Nixon ( corvi42@silencegreys.com ) # based very loosely upon the script by: Bob Lee (crazyboblee@hotmail.com) # License: http://www.gnu.org/copyleft/gpl.html # # I have tried to make this program responsible in # such a way that it will be optimal for both running as a cron # task to update the dns should your ip change, and as part of a # network init script # it will also update your /etc/hosts file so that your hostname # is set correctly for local resolution # ( ie it adds an entry with the ppp ip when you go online, and modifies this to a local address when offline ) # sometimes apache or other daemons # complain if you're locally using a hostname that doesn't properly resolve, # this should solve this problem # # usage: updatedns # # if the command is run without arguments, it will # check if it is neccessary to update the dns # ( ie if the ppp interface has a different ip from the dns record ) # and then do so this is how it should be run if you want to set # or update a dns record when your machine goes online or when your ip changes # # if it is run with an argument: 'updatedns offline' # ( or any argument that resolves to true in perl: 'updatedns 1' or 'updatedns foobar' etc. ) # then it will set the dns record to point to $public_offlineip # # all of this is logged to /var/log/updatedns.log # # you must edit the variables below to get this thing working for you # important variables: # $uid - your changeip.com userid # $pwd - your changeip.com password # $hostname - the hostname to which you want your computer to be bound # $offlineip - the ip for your hostname so you can get to your own machine when you're offline # $public_offlineip - the ip others should go to when you're offline # $iface - your dynamic / dialup interface, this should be fine as it is for most people # $hostsfile - location of your local hosts file, this should be fine for most people # $logfile - the location of the logfile, this should be fine for most people # $dnsserver - the server & port to go to to update - I don't see why you'd want to change this ## ## ## START MAIN PROGRAM ## ## ## # make the environment safe clean_env(); $ENV{PATH} = '/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin'; # variables my $offline = $ARGV[0] ? 1 : 0; my $uid = ''; # set this to your changeip.com userid my $pwd = ''; # set this to your changeip.com password my $hostname = ''; # set this to your hostname my $offlineip = '127.0.0.1'; # set this to your own local interface my $public_offlineip = $offlineip; # set this to the ip other should go to when you're offline my $iface = 'ppp0'; my $hostsfile = '/etc/hosts'; my $logfile = '/var/log/updatedns.log'; my $dnsserver = 'www.changeip.com:443'; my $pppip = get_pppip(); my $dns_entry = get_dns(); # if we already match the dns record, exit now ( ! $offline && $pppip eq $dns_entry ) and exit( 0 ); # update hosts file ( $offline ) and hostsfile_offline(); ( ! $offline ) and hostsfile_online(); # update the dns update_dns(); ## ## ## END MAIN PROGRAM ## ## ## # subroutines sub clean_env { foreach my $key ( %ENV ){ $ENV{$key} = untaint( $ENV{$key} ); } } sub untaint{ my $stringToClean = $_[0]; ( ! defined( $stringToClean ) || $stringToClean eq '' ) and return ''; # remove invalid characters $stringToClean =~ s/[;&"`\$\\<>\|]//g; # remove leading and trailing white space $stringToClean =~ s/^\s+//; $stringToClean =~ s/\s+$//; # untaint $stringToClean = $1 if ($stringToClean =~ /^(.*)$/); return $stringToClean; # now all clean =) } sub get_pppip { my $pppstr = `ifconfig $iface | grep addr:`; ( $pppstr =~ /addr:(\S+)/ ) or die("get_pppip failed\n"); return $1; } sub get_dns { my $dnsstr = `host $hostname`; ( $dnsstr =~ /(\S+)\n?$/ ) or die "get_dns failed\n"; return $1; } sub hostsfile_offline { open( HOSTS, $hostsfile ) or die("could not open $hostsfile\n"); my @hosts = ; close( HOSTS ); my $hostsize = scalar( @hosts ); for( my $i = 0; $i < $hostsize; $i++ ){ # add hostname to offlineip entry ( $hosts[$i] =~ /^$offlineip/ ) and $hosts[$i] =~ s/\s*\n/\t$hostname\n/; } open( HOSTS, ">$hostsfile" ) or die("could not open $hostsfile for writing\n"); print HOSTS @hosts; close( HOSTS ); } sub hostsfile_online { open( HOSTS, $hostsfile ) or die("could not open $hostsfile\n"); my @hosts = ; close( HOSTS ); my $hostsize = scalar( @hosts ); for( my $i = 0; $i < $hostsize; $i++ ){ my $line = shift( @hosts ); # remove hostname from offlineip entry ( $line =~ /^$offlineip/ ) and $line =~ s/\t$hostname//; # any others with the hostname should be removed also ( $line =~ /$hostname/ ) and $line = ''; $line and push( @hosts, $line ); } open( HOSTS, ">$hostsfile" ) or die("could not open $hostsfile for writing\n"); print HOSTS @hosts; close( HOSTS ); } sub update_dns { my $getstring = "GET /update.asp?u=$uid&p=$pwd&cmd=update&hostname=$hostname&ip="; ( $offline ) and $getstring .= $public_offlineip; ( ! $offline ) and $getstring .= $pppip; my $cmd = qq~echo "$getstring" | openssl s_client -quiet -connect $dnsserver 2>&1~; # run the update command my @output = `$cmd`; my $outputsize = scalar( @output ); my $crop = 0; for( my $i = 0; $i < $outputsize; $i++ ){ my $line = shift( @output ); $line =~ /^[\s\n]*$/ and $line = ''; $line =~ //i and $crop = 1; $line =~ /<\/html>/i and $crop = 0; $line =~ /<\/html>/i and $line = ''; $crop and $line = ''; $line and push( @output, $line ); } # update the logfile my @time = localtime( time ); my $date = "$time[3]/". ( $time[4] + 1 ) .'/'. ( $time[5] + 1900 ) ." $time[2]:$time[1]:$time[0]"; my $state = $offline ? 'logging off' : 'logging on'; my $status = $? ? 'failure' : 'success'; open( LOG, ">>$logfile" ) or die "could not open $logfile for appending\n"; print LOG qq~ # updatedns $state $date # returned: @output # $status! ~; close( LOG ); $? and exit( $? ); }