Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

IP Address Sanity

by Anonymous Monk
on Nov 07, 2002 at 22:04 UTC ( [id://211264]=perlquestion: print w/replies, xml ) Need Help??

Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hey, I am writing a little piece of code to check the sanity of an IP address. I haven't got it to break yet, so I was wondering if anyone else would have a go and see if there is anything wrong. Thanks!

#!/usr/bin/perl use strict; print "What IP address/Domain Name do you want to access: "; $_ = <>; my $ip = SanitizeIP($_); print "$ip is sane.\n"; exit; sub SanitizeIP { chomp ($_); my ($a,$c,$d,$e,$f); my $b = 0; if ($_ =~ /[a-zA-Z]+/) { print "$_\n"; $_ = gethostbyname($_); ($c,$d,$e,$f) = unpack('C4', $_); $_ = $c . "." . $d . "." . $e . "." . $f; } my @_temp = split(/\./,$_); undef $_; foreach $a(@_temp) { if ($a =~ /\b\d\b|\b\d\d\b|\b\d\d\d\b/) { if (($a >= 0) and ($a <= 255)) { $_ = $_ . int($a); $_ = $_ . "." unless ($b > 2); $b++; } else { print "This is an illegal IP address -- or it sure see +ms like one.\n"; exit; } } else { print "This is an illegal IP address -- or it sure seems l +ike one.\n"; exit; } } if (($_ eq "") or ($_ eq " ")) { print "The Address did not resolve, or there was something wro +ng with what you typed.\n"; exit; } return $_; }

Replies are listed 'Best First'.
Re: IP Address Sanity
by jdporter (Paladin) on Nov 07, 2002 at 23:34 UTC
    The problem with your general approach is that IP addresses aren't really textual strings, they're 32-bit integers. The dotted-quad is just one convenient representation. But it's not the only one.

    A proper solution to your stated need is to use the inet_aton and inet_ntoa functions provided by the Socket module, which is part of the standard library.

    inet_aton translates any valid IP address -- regardless of notation format -- or domain name into a packed string containing the 32-bit integer. Such a string can be converted into a dotted-quad representation by the inet_ntoa function.

    Example:

    use Socket; # load inet_aton, inet_ntoa $_ = <>; # read user's input chomp; # convert to packed string. succeeds if valid address. my $ip = inet_aton( $_ ); # length will be 4 for IPv4 addresses. length($ip) or die "Sorry, '$_' is invalid.\n"; $ip = inet_ntoa($ip); # convert to dotted quad. print "$_ is valid; its IP address is $ip\n";
Re: IP Address Sanity
by Wonko the sane (Deacon) on Nov 07, 2002 at 22:33 UTC
    This bit of code is what I like to use to check the validity of an IP address.

    This is a variation of one from the the Great Book "Mastering Regular Expressions" by Jeffrey Friedl,
    which I highly recommend.

    unless ( $ip =~ m/^(?:[1]?\d\d?|2[0-4]\d|25[0-5])\. (?:[1]?\d\d?|2[0-4]\d|25[0-5])\. (?:[1]?\d\d?|2[0-4]\d|25[0-5])\. (?:[1]?\d\d?|2[0-4]\d|25[0-5])$ /x ) { # do something }
    This catches everything except for '0.0.0.0' ip's

    which you can handle easily by one addtional check to your test routine.

    if ( $ip =~ /^0.0.0.0$/ ) { # do something }
    Best Regards,
    Wonko
      Your pattern does match 0.0.0.0 from what I can tell. And this slight change makes it robust against arbitrary prefix zeros to boot.
      /^ 0*?(?:1?\d\d?|2[0-4]\d|25[0-5])\. 0*?(?:1?\d\d?|2[0-4]\d|25[0-5])\. 0*?(?:1?\d\d?|2[0-4]\d|25[0-5])\. 0*?(?:1?\d\d?|2[0-4]\d|25[0-5]) $/x;
      --- demerphq
      my friends call me, usually because I'm late....
        0.0.0.0

        is not actually a valid IP address. Which is why I mentioned that my pattern did not catch this (as invalid that is). The pattern matches it, but it should not. :-)

        Leading 0's in an IP are also considered to be invalid.

        1.003.003.123 is invalid and should be expressed without leading 0's

        Best Regards,
        Wonko

Re: IP Address Sanity
by fokat (Deacon) on Nov 08, 2002 at 04:51 UTC
    Appart from the suggestions from the fellow monks, I should point you at NetAddr::IP. You can very easily see wether something is (or can be converted to) an IP address by saying:

    use NetAddr::IP; if (defined NetAddr::IP->new($myip)) { ... Your code goes here ... } else { die "Do not feed me with garbage!\n"; }
    Also, you want to accept IP addresses for something. NetAddr::IP makes most of what you might want to do easy. There's also a Tutorial within the monastery about how to do stuff with this.

    You can download the module from here or any friendly CPAN mirror.

    Disclaimer: I wrote NetAddr::IP so I might be a bit biased :) Best regards

    -lem, but some call me fokat

Re: IP Address Sanity
by FamousLongAgo (Friar) on Nov 07, 2002 at 22:42 UTC
    These pass as valid IP addresses:
    10. 22. 109 934324 23832 23.104 200. 12.21.3 0.^^&7".34.55-1
    Try this:
    while ( <DATA> ) { chomp; print $_, ": ", check_ip( $_ ), "\n"; } sub check_ip { my ( $ip ) = @_; return "Wrong format" unless $ip =~ /^(\d{1,3}\.){3}\d{1,3}$/; return "Illegal value" if grep { $_ > 255 } split /\./, $ip; return "Weird zeroes" if grep { /^0[0-9]/ } split /\./, $ip; return "OK" } __DATA__ 0.^^&7".34.55-1 fda.er32.223.1 10.00.120.13 32.294.233.12 -02.35.54.54 41.41.22.50
Re: IP Address Sanity
by lestrrat (Deacon) on Nov 07, 2002 at 22:42 UTC

    I don't like the regexp approach for this type of problems, I find it easier to dissect things one by one...

    #!/usr/local/bin/perl use strict; sub ipverify { my( $ip ) = @_; if($ip =~ /[^\d.]/) { die "Found illegal characters in ip: $ip"; } my @octets = split( /\./, $ip ); if(@octets != 4) { die "number of octets must be 4"; } foreach my $octet (@octets) { ## numerical sanity if($octet < 0 || $octet > 255) { die "octet $octet is out of range ( 0 <= x <= 255 )"; } ## $octet should not start with a 0 if it evaluates to ## less than 100 if( $octet < 100 ) { ## if 0, octet should be single digit if($octet == 0 && length($octet) != 1) { die "bad representation '$octet'. rewrite as '0'"; } if($octet =~ /^0+(\d+)$/) { die "bad representation '$octet'. rewrite as '$1'"; } } } return 1; } sub hostname_verify { my($hostname) = @_; my $iaddr = gethostbyname($hostname); if(!$iaddr) { die "Hostname '$hostname' did not resolve"; } return 1; } sub main { while(1) { print "Type in IP address or hostname to verify (enter to quit +): "; chomp(my $input = <STDIN>); if(!length($input)) { last; } if($input =~ /\D/) { eval{ hostname_verify($input) }; } else { eval{ ip_verify($input) }; } if($@) { (my $err = $@) =~ s/ at .*$//; print STDERR " ERROR: $err\n"; } else { print "$input is a valid address\n"; } } } main();
      The test for an octet < 0 is not necessary, since a negative number won't get past the initial regex test.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://211264]
Approved by VSarkiss
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others admiring the Monastery: (2)
As of 2024-04-25 19:22 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found