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 having an uproarious good time at the Monastery: (4)
As of 2025-06-13 23:06 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?
    erzuuliAnonymous Monks are no longer allowed to use Super Search, due to an excessive use of this resource by robots.