There are professional packages that do this, and do it well. But I wanted to create my own solution just for the learning experience. This is a set of quick-n-dirty scripts that post my router's IP address to a remote webhost so that I can find my home network while at a remote computer. I really mean quick-n-dirty; this is just a set of little hacks... mostly a proof of concept.

Problem:

Your broadband connection assigns a dynamic IP. You use a standalone consumer-grade router that maintains the connection with your ISP, and then your local machines are all assigned a subnet IP, so your PC's don't really know (or care) what IP address your ISP has assigned you.

Furthermore, you need to know the IP address of your network from the outside. For example, on the road you need to grab a webpage served by a machine behind your router, but you don't know what IP address your ISP assigned your router today. Another example might be that you need to log into your router's web interface externally, but don't know what IP address to use.

The solution:

The following code is divided into five segments. Three are Perl, and two deal with procmail filtering. ...two are CGI, and one is set in motion via the Windows equivilant of cron; Task Scheduler. First, you need a web host with a static IP that allows CGI scripting. On the host, place two scripts: ipreflect.cgi which just posts a webpage divulging the client's IP (if available). The second script placed on the host is currentip.cgi. This script reads an IP logfile maintained on the web host that keeps track of your IP address.

Next let's talk about the local machine side. On one of the PC's on your home LAN you need to place the script named iplog.pl. This script polls ipreflect.cgi to discover your router's current IP address. The script then records this IP address to a local logfile. And if the IP address is different from the one previously recorded, the script sends an email to your webhost account containing your new IP address.

Now for the final details.... You'll need to set up a .forward file and a .procmailrc file on the web host so that when this New IP email arrives at the web host, the email is logged into a file that currentip.cgi can read.

Now I'll post the code.....


Remote webhost:

.forward
|exec /usr/bin/procmail


.procmailrc
:0 * ^Subject:.*\[IP Log\] /home/your_homedir/path_to_file/ip.txt


Now in your remote cgi-bin directory:

ipreflect.cgi

#!/usr/bin/perl print "Content-Type: text/html\n\n"; print defined $ENV{REMOTE_ADDR} ? $ENV{REMOTE_ADDR} : "Not Available" , "\n";


currentip.cgi

#!/usr/bin/perl use strict; use warnings; my $logfile = '/home/your_account/path_to_file/ip.txt'; open my $inlog, "<", $logfile or die "Couldn't open $logfile.\n$!"; my $ip = ''; while ( my $line = <$inlog> ) { next unless $line =~ m/^IP:\s*\d+\.\d+\.\d+\.\d+\s*$/; chomp $line; $ip = $line; } close $inlog; print "Content-Type: text/html\n\n"; print << "HTML"; <body> <h2>$ip</h2> </body> HTML


Your local PC

iplog.pl

#!/perl/bin/perl.exe use strict; use warnings; use LWP::Simple; use MIME::Lite; my $reflector = "http://your.webhost.com/cgi-bin/ipreflect.cgi"; my $logfile = "ip.log"; # Get IP from an IP reflector server. my $ip = get $reflector; chomp $ip; my $now = localtime; $ip = "Reflector server down." unless ( $ip =~ m/^\d+\.\d+\.\d+\.\d+$/ or $ip =~ m/Not Available/i ); # Read log to determine if the IP changed since last test. my $ip_changed = ""; if ( -e $logfile ) { open my $inlog, "<", $logfile or die "Can't read logfile.\n$!"; while ( <$inlog> ) { next unless eof; chomp; my $last_ip = ( split /\s+=>\s+/ )[1]; $last_ip =~ s/\*$//; $ip_changed = ( $last_ip ne $ip ) ? "*" : ""; } close $inlog; } else { # If the logfile doesn't exist, assume IP changed. # This forces an update to the remote server anytime # the logfile is deleted. Remember, for a fresh start # at both ends, just delete the local logfile. $ip_changed = "*"; } # Update log. open my $log, ">>", $logfile or die "Can't open output logfile.\n$!"; print "$now => $ip$ip_changed\n"; # Screen output. print $log "$now => $ip$ip_changed\n"; # Logfile output. close $log or die "Couldn't close output file.\n$!"; # If the IP changed, send the new IP to remote server via email. if ( $ip_changed and $ip =~ m/^\d+\.\d+\.\d+\.\d+$/ ) { my $msg = MIME::Lite->new( From =>'your@email.address', To =>'your.email@address.on.web.host', Subject =>"[IP Log] $now", Data =>"Updated: $now\nIP: $ip\n" ); MIME::Lite->send( 'smtp', "your.isp.smtp.server", Timeout => 60 ); $msg->send; }

Final words

I set iplog.pl up in a Windows Task Scheduler job on my local PC to run once per hour. Every hour it will poll the remote webhost to see what IP address the webhost thinks my router is coming from. The results will be written to the logfile. And if the IP has changed, notification will be emailed to my webhost account email address. procmail will catch that message and save it to a logfile. The next time I access currentip.cgi at my webhost, I'll see my current router IP, even if I'm checking remotely.

For this to be effective, the router must be configured with Port Forwarding so that it will know which service requests to pass on to which PC's on my local network. Frankly, I don't use it for much of anything other than testing CGI scripts on my local machine from a remote machine when I'm away from the local machine. It's mostly just a testing tool for me, and something to tinker with.

But I thought the method used was just crazy enough to be worth posting. Comments welcome.

The biggest shortcoming this has is that the local logfile, and the remote one, will grow and grow forever. Perhaps if I was serious about using this I would build tools to keep them reasonably sized.

The other shortcoming is the fact that it requires that the user actually have some external web host that allows cgi scripting.

ipreflect.cgi could just as easily have been written as a html document with server-side-includes, but I was on a CGI kick and just wrote it as a CGI script.

Why did I use ipreflect.cgi on a remote host, rather than allowing iplog.pl to just poll my router through its built-in web interface? That would be because I have a Netgear WGR614v4 router. Its web interface requires javascript to work properly, and I have no idea where to begin on programming a javascript enabled web-scraper script. Thus, this convoluted mess you see posted here. ;)

For a working example of this script in action see http://davido.perlmonk.org/ipreflect.cgi

Enjoy.


Dave


In reply to Broadband Dynamic IP Poster by davido

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.