469,366 Members | 2,236 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 469,366 developers. It's quick & easy.

How to validate an IP address...

numberwhun
3,503 Expert Mod 2GB
Hello everyone! I am still learning (TONS every day) and having an absolute blast. Unfortunately, I have an issue that is puzzling and bewildering me.

Seeing as how the best way to learn is to re-invent the wheel, I am trying to write a script to validate an IP address (IPv4) as valid. I am only in the first part of this, where I validate that each of the octets has 1 to 3 digits. Well, this just isn't working.

I have tried the following 3 different regular expressions:

1. m/\d{1,2}\.\d{1,3}\.\d{1,3}\.\d{1,3}/
2. m/\d\d?\d?\.\d\d?\d?\.\d\d?\d?\.\d\d?\d?/
3. m/\d(\d\d?)?\.\d(\d\d?)?\.\d(\d\d?)?\.\d(\d\d?)?/

The last two were ideas taken directly from the "Mastering Regular Expressions" book. It validates that 192.168.0.1 is valid, but all 3 also validate the 1922.168.0.1 is valid as well. I just don't understand why.

I am using Active Perl 5.8.8 on Windows XP and also Perl 5.6.1 on Solaris 9. Any help or guidance as to WHY this is not working correctly would be greatly appreciated.


UPDATE: Ok, after I went and finished my first cup of tea of the day, I took a look again at my regex and decided to try putting a ^ at the beginning and VOILA!! Success!! Now, why does it work with, but not without the ^, especially when the regex is matching from beginning to end of the string? Also, I tested them and all 3 of the above regex's work for matching the format of an IP.



Regards,

Jeff
Jun 5 '07 #1
20 19642
KevinADC
4,059 Expert 2GB
You probably should be using ^ (beginning of string) and $ (end of string) to validate the IP:

/^pattern$/

otherwise you are validating partial matches will will be true for a wider range of patterns.

Expand|Select|Wrap|Line Numbers
  1. $v = 'foobar';
  2. print 'true foo' if ($v =~ /foo/);
  3. print 'true ^foo' if ($v =~ /^foo/);
  4. print 'true ^foo$' if ($v =~ /^foo$/);
Jun 5 '07 #2
numberwhun
3,503 Expert Mod 2GB
You probably should be using ^ (beginning of string) and $ (end of string) to validate the IP:

/^pattern$/

otherwise you are validating partial matches will will be true for a wider range of patterns.

That does make sense now that I think about it. I had changed it and added the ^, that is how I got it to work. Hey, lesson learned. Thanks!!

Jeff
Jun 5 '07 #3
miller
1,089 Expert 1GB
Greetings Jeff,

I certainly understand the compulsion behind attempts to reinvent the wheel. However, if you really would like to learn, it often helps to examine someone else's wheel first. They might have thought of many things that you hadn't considered before.

In this case for example, I did a quick search for IP on CPAN, and one module came up first.

cpan Data::Validate::IP

Upon examination of this package's documentation, one function comes up immediately: is_ipv4. Now, based off the name, this looks like exactly what you want. But the easiest way for me to tell this is to simply click on the "Source" link at the top of the page and see what the function does. Doing this and searching for "sub is_ipv4" reveals:

Expand|Select|Wrap|Line Numbers
  1. sub is_ipv4 {
  2.     my $self = shift if ref($_[0]); 
  3.     my $value = shift;
  4.  
  5.     return unless defined($value);
  6.  
  7.     my(@octets) = $value =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
  8.     return unless (@octets == 4);
  9.     foreach (@octets) {
  10.         return unless ($_ >= 0 && $_ <= 255);
  11.     }
  12.  
  13.     return join('.', @octets);
  14. }
  15.  
Now as you see, this module not only uses anchors, but also tests to see if the octets are in the currect range. There are probably many ways to accomplish this type of test, but this looks like a good one. Although the final return value logic appears redundant, as that is equivalent to $value.

Wheel taken apart, and reassembled. Ta-da!

- Miller
Jun 5 '07 #4
KevinADC
4,059 Expert 2GB
Although the final return value logic appears redundant, as that is equivalent to $value.
Might be for tainted/untainted reasons.
Jun 5 '07 #5
miller
1,089 Expert 1GB
Might be for tainted/untainted reasons.
Could you explain that please? I honestly believe that it's just redundant logic, but would be curious to at least know what you mean by tainted/untainted.

- Miller
Jun 5 '07 #6
KevinADC
4,059 Expert 2GB
Well, I didn't look at anymore than the code you posted so my use of "Might be" is a disclaimer. ;)

I assume you know what tainted and untainted data is.

$value may still be "tainted" when passed to the function. If so, the following line "untaints" it:

Expand|Select|Wrap|Line Numbers
  1.  my(@octets) = $value =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
After checking (and passing) the ranges the array is joined as a string and sent back. If $value were sent back it would still be tainted and perl would bark at you for using tainted data if $value were used in some insecure way.

If $value were untainted before all this, returning the joined array would seem redundant.

http://perldoc.perl.org/perlsec.html
Jun 5 '07 #7
miller
1,089 Expert 1GB
I assume you know what tainted and untainted data is.
Uhh .... well .... you see ... the thing is ... I thought I did ... but I really didn't.

Doing a quick google search was not enlightening. However, reading the documentation you provided on Perl Security filled in the gaps in my knowledge. I'm not sure I'll ever have use for such functionality, but it's good to know about all the same.

Thanks,
- Miller
Jun 5 '07 #8
numberwhun
3,503 Expert Mod 2GB
Greetings Jeff,

Wheel taken apart, and reassembled. Ta-da!

- Miller
Thanks Miller, especially for your reply! I did see that module and upon doing the QA testing on the algorithm I came up with I realized that in this code:


foreach (@octets) {
return unless ($_ >= 0 && $_ <= 255);
}


The return statement can acutlly be shortened to be the following:

return unless ($_ <= 255);

The reason for this is because his original regex, which is the one I had originally put together, tests for a minimum 1 but up to a maximum of 3 digits. That said, it won't match a negative number because the minus sign is not a digit. I actually tested this and it works fine. Funny how a different point of view can help make other code more efficient.

Thanks again to all that responded.
Jun 6 '07 #9
miller
1,089 Expert 1GB
The return statement can acutlly be shortened to be the following:

return unless ($_ <= 255);

...

Thanks again to all that responded.
Right you are. However, I'm thinking that this might be one of those instances where including the logic to ensure that the number is greater than or equal to 0 is effectively just documentation. Yes, the regex would ensure this itself, but including that in the range test implies that he really means it to be 0 to 255 and not 1-255.

Good eye, and your welcome.

- Miller
Jun 6 '07 #10
velaga
3
Hi all,
This works to validate IP .

Expand|Select|Wrap|Line Numbers
  1. #!/bin/perl
  2. use strict;
  3.  
  4. print "enter Addr \t:\n";
  5. my $input = <STDIN>;
  6. chop $input;
  7.  
  8. $input=~ s/\s+//g;
  9.  
  10. if ($input =~ m{^
  11.     (\d{1,2}|1\d{1,2}|2[0-4]\d|25[0-5])        # Number 0-255
  12.     \.
  13.     (\d{1,2}|1\d{1,2}|2[0-4]\d|25[0-5])
  14.     \.
  15.     (\d{1,2}|1\d{1,2}|2[0-4]\d|25[0-5])
  16.     \.
  17.     (\d{1,2}|1\d{1,2}|2[0-4]\d|25[0-5])
  18.     $
  19. }x ) {
  20.     print "$input is valid IP !!!!\n";
  21.     exit;
  22. }
  23.  
This gets you rid of white spaces entered !
Jul 31 '07 #11
numberwhun
3,503 Expert Mod 2GB
First, a couple of things, in my opinion, wouldn't it be better to chomp() the input and just remove the newline instead of chop()ing off the last character and returning it? Also, when entering code into the forum, always put the code between code tags. When you are typing up your response, see the "Reply Guidelines" off to the right for an example. If you don't, people like our friendly Moderator, Miller, have to come in behind you and edit your post to add them.

As for your regex, while I am sure it works, WOW!!!, that is one long way to go for something that was done earlier in the post by 3 other shorter regex's. I am not putting yours down in any way, please know that, but "Laziness, Impatience, and Hubris" aren't part of a Developer's life for nothing. :-)

Regards,

Jeff
Jul 31 '07 #12
miller
1,089 Expert 1GB
This works to validate IP .
Greetings Velaga,

That would indeed work for IP validation, and accomplishes the logic in single regex. If you wanted to shorten it some you could remove one of your or'd conditions by doing the following:

Expand|Select|Wrap|Line Numbers
  1. if ($input =~ m{^
  2.     (1?\d{1,2}|2[0-4]\d|25[0-5])        # Number 0-255
  3.     \.
  4.     (1?\d{1,2}|2[0-4]\d|25[0-5])
  5.     \.
  6.     (1?\d{1,2}|2[0-4]\d|25[0-5])
  7.     \.
  8.     (1?\d{1,2}|2[0-4]\d|25[0-5])
  9.     $
  10. }x ) {
  11.     print "$input is valid IP !!!!\n";
  12.     exit;
  13. }
  14.  
However, while it's possible to do all the logic in a regex, it's a lot simpler and even faster to just do the range validation outside.

Or even better, just use the standard modules for such tests, and don't worry about how it's implemented.

Thanks for sharing your solution,
- Miller
Jul 31 '07 #13
velaga
3
Hi miller

Thanks for beautifing code ! .
Do we have option where we can READ & WRITE a file in PERL ? ( like O_RDWR in C)

Thanks,
- velaga
Aug 1 '07 #14
numberwhun
3,503 Expert Mod 2GB
Sure do. You will want to read the perldoc page for the open() function. In fact, you will find a lot of information on the perldoc site that will get you going if you look at the left hand navigation.

Regards,

Jeff
Aug 1 '07 #15
miller
1,089 Expert 1GB
Read up the link that Jeff provided.

I personally make sure to use IO::File whenever I'm doing more than basic reading and writing.

Expand|Select|Wrap|Line Numbers
  1. my $fh = IO::File->new($file, O_RDWR|O_CREAT|O_EXCL);
  2.  
- Miller
Aug 1 '07 #16
velaga
3
Hi miller


How do we find non ascii characters using regualr expressions ?
Example ;70 *H 80 A IND COMMON MODE


Regards,
PraVeen V
Aug 16 '07 #17
numberwhun
3,503 Expert Mod 2GB
First, if you have an additional question that is not relevant to the current thread (which was solved), then please post all new and additional questions to a new thread so they don't get lost.

As for your question, you may want to go over to refcards.com and check out their Perl Regular Expression Quick Reference Card. That is a very good reference that I have hanging on my cubicle wall right in front of me. That does go over escape sequences for other chracters.

Regards,

Jeff
Aug 16 '07 #18
Expand|Select|Wrap|Line Numbers
  1. $ip=<STDIN>;
  2. if(($text =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/) && ($1 <= 255 && $2 <= 255 && $3 <= 255 && $4 <= 255 )){
  3.  
  4. print "ip address is $ip" ;
  5. }
  6.  
  7. else
  8. {
  9. print "wrong ip address";
  10. }
May 23 '12 #19
Expand|Select|Wrap|Line Numbers
  1. chomp($ip=<STDIN>);
  2. if(($ip =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/) && ($1 <= 255 && $2 <= 255 && $3 <= 255 && $4 <= 255 )){
  3.  
  4. print "ip address is $ip" ;
  5. }
  6.  
  7. else
  8. {
  9. print "wrong ip address";
  10. }
May 23 '12 #20
RonB
589 Expert Mod 512MB
Hello gaurav 007,

Did you realize that this was a 5 year old thread that you resurrected?

Personally I prefer to use a module for things like this.

Data::Validate::IP - ipv4 and ipv6 validation methods
Expand|Select|Wrap|Line Numbers
  1.   use Data::Validate::IP qw(is_ipv4 is_ipv6);
  2.  
  3.   if(is_ipv4($suspect)){
  4.         print "Looks like an ipv4 address";
  5.   } else {
  6.         print "Not an ipv4 address\n";
  7.   }
  8.  
  9.   if(is_ipv6($suspect)){
  10.         print "Looks like an ipv6 address";
  11.   } else {
  12.         print "Not an ipv6 address\n";
  13.   }
  14.  
  15.  
  16.   # or as an object
  17.   my $v = Data::Validate::IP->new();
  18.  
  19.   die "not an ipv4 ip" unless ($v->is_ipv4('domain.com'));
  20.  
  21.   die "not an ipv6 ip" unless ($v->is_ipv6('domain.com'));
May 23 '12 #21

Post your reply

Sign in to post your reply or Sign up for a free account.

Similar topics

25 posts views Thread by Dynamo | last post: by
12 posts views Thread by Dag Sunde | last post: by
5 posts views Thread by Dave | last post: by
6 posts views Thread by yochessyo | last post: by
23 posts views Thread by codefire | last post: by
3 posts views Thread by Sushant Panda | last post: by
reply views Thread by zhoujie | last post: by
reply views Thread by suresh191 | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.