[H-GEN] Bash script testing for changed IP address randomly fails

Bruce Campbell bc at humbug.org.au
Thu Jan 19 07:10:51 EST 2006


On Thu, 19 Jan 2006, David Powell (Moondrake) wrote:

> I think you're right.  This is very strange.  I've reverted back to the
> original version of the script (what I posted last night - without the
> c++ style comment) and I can't get it to return a false positive now.  I
> haven't edited the file that was causing the errors at all (I just
> reinstated a backup)

Use rcs on all of your config files and scripts; you'll feel much safer 
in the long run.

> , but after 40 or so runs, it's not giving any false
> alarms.

Your script has three obvious problems.  Firstly, as Jason pointed out, 
you are relying on a resource that is not under your control, which has a 
dependency on your connection being up.  Secondly, you are trusting in the 
output of said command to have the IP address in the same place each time 
it is run.  Thirdly, you are not checking to see if you do in fact have an 
IP(v4) address.

Since you've chosen parsing the output of http://checkip.dyndns.org/ as 
your particular poison, we've got the second and third problems to deal 
with.  In your experience, you've obviously 'always' seen the IP address 
in field 6 previously, eg:

 	<html><head><title>Current IP Check</title></head><body>Current IP
 	Address: 1.2.3.4</body></html>

However, sometimes (took 5 runs for me) it returns:

 	<html><head><title>Current IP Check</title></head><body>Current IP
 	Address: unknown, 1.2.3.4</body></html>

Thus, $ip_now will be assigned the string 'unknown,' in such a situation.

So, addressing the second and third problems, the following perl snippet 
will return an IPv4 address from the above output:

 	ip_now=$(curl http://checkip.dyndns.org/ 2>/dev/null) | perl -e
 	'my $flag=0; while(<>){ next if( $flag );
 	next unless( m/Current\s+IP\D*(\d+\.\d+\.\d+\.\d+)\D*/ );
 	print "$1"; $flag++; }'

Note that if the output changes, or curl is unable to reach that URL 
(first problem), then $ip_now will be assigned the empty string.  Since 
we've now changed the third problem to one of ensuring that you have a 
string (since the perl snippet only returns an IPv4 address), we can fix 
this by using instead:

 	if [ "$ip_now" != "$ip_old" -a ! -z "$ip_now" ]

Eg, only update the record of old and current IP addresses and send email 
only if they differ, and you have something in the string.

Viola; your script will now work as intended.

However, we still have a lingering sense of unease, in that we haven't 
solved the first problem.  It scraps against one of the core tenets of 
sysadmin administration, being, don't trust anything you don't control.

In this case, you are relying on the supplied URL (or alternatives as 
suggested in the thread) to maintain the same, or similar output.  When 
that output changes, your script will stop working.

The first question to be asked towards solving this is, do you _need_ to 
reference an external resource that is not under your control?  Since you 
mention that the intent is to keep yourself informed of changes in the 
server's IP address, then presumably the answer is no, you do not need to 
reference an external URL, unless there is some weird and funky IP 
masquarading running on a seperate router.

I would suggest using instead:

 	interface=eth0
 	ip_now=ifconfig $interface | perl -e
 	'my $flag=0; while(<>){ next if( $flag );
 	next unless( m/inet\D*(\d+\.\d+\.\d+\.\d+)\D*/ );
 	print "$1"; $flag++; }'

This will work on any decent OS, which your installation of linux may or 
may not be.  Change the pattern match as appropriate.

--==--
Bruce.




More information about the General mailing list