37

when I do this

ip = request.env["REMOTE_ADDR"]

I get the client's IP address it it. But what if I want to validate whether the value in the variable is really an IP? How do I do that?

Please help. Thanks in advance. And sorry if this question is repeated, I didn't take the effort of finding it...

EDIT

What about IPv6 IP's??

ecoologic
  • 10,202
  • 3
  • 62
  • 66
Rohit
  • 5,631
  • 4
  • 31
  • 59
  • 2
    next time better take some effort for finding existing one. Here are two http://stackoverflow.com/questions/106179/regular-expression-to-match-hostname-or-ip-address and http://stackoverflow.com/questions/2999282/regular-expression-to-match-ip-address-wildcard – ankitjaininfo Sep 03 '10 at 10:58

13 Answers13

52

Ruby has already the needed Regex in the standard library. Checkout resolv.

require "resolv"

"192.168.1.1"   =~ Resolv::IPv4::Regex ? true : false #=> true
"192.168.1.500" =~ Resolv::IPv4::Regex ? true : false #=> false

"ff02::1"    =~ Resolv::IPv6::Regex ? true : false #=> true
"ff02::1::1" =~ Resolv::IPv6::Regex ? true : false #=> false

If you like it the short way ...

require "resolv"

!!("192.168.1.1"   =~ Resolv::IPv4::Regex) #=> true
!!("192.168.1.500" =~ Resolv::IPv4::Regex) #=> false

!!("ff02::1"    =~ Resolv::IPv6::Regex) #=> true
!!("ff02::1::1" =~ Resolv::IPv6::Regex) #=> false

Have fun!

Update (2018-10-08):

From the comments below i love the very short version:

!!(ip_string =~ Regexp.union([Resolv::IPv4::Regex, Resolv::IPv6::Regex]))

Very elegant with rails (also an answer from below):

validates :ip,
          :format => {
            :with => Regexp.union(Resolv::IPv4::Regex, Resolv::IPv6::Regex)
          }
wingfire
  • 791
  • 5
  • 9
  • Just to simplify things, `((ip_string =~ Resolv::IPv4::Regex) || (ip_string =~ Resolv::IPv6::Regex)) ? true : false` – gitb Sep 02 '14 at 20:20
  • 3
    Simplify it further `!!((ip_string =~ Resolv::IPv4::Regex) || (ip_string =~ Resolv::IPv6::Regex))` – jesal Apr 21 '15 at 18:28
  • 2
    I prefer `!!(ip_string =~ Regexp.union([Resolv::IPv4::Regex, Resolv::IPv6::Regex]))` – Sam Nov 03 '16 at 12:47
  • Very helpful answer. – Jigar Bhatt Nov 25 '20 at 10:35
  • FYI `Resolv::AddressRegex` exists (since ruby 1) and is the union of v4 and v6, so you can use that instead of combining them yourself. [code](https://github.com/ruby/ruby/blob/2b39640b0bbf7459b305d8a98bb01f197975b8d9/lib/resolv.rb#L2907) – salty Oct 31 '22 at 20:31
44

Why not let a library validate it for you? You shouldn't introduce complex regular expressions that are impossible to maintain.

% gem install ipaddress

Then, in your application

require "ipaddress"

IPAddress.valid? "192.128.0.12"
#=> true

IPAddress.valid? "192.128.0.260"
#=> false

# Validate IPv6 addresses without additional work.
IPAddress.valid? "ff02::1"
#=> true

IPAddress.valid? "ff02::ff::1"
#=> false


IPAddress.valid_ipv4? "192.128.0.12"
#=> true

IPAddress.valid_ipv6? "192.128.0.12"
#=> false

You can also use Ruby's built-in IPAddr class, but it doesn't lend itself very well for validation.

Of course, if the IP address is supplied to you by the application server or framework, there is no reason to validate at all. Simply use the information that is given to you, and handle any exceptions gracefully.

Tilo
  • 33,354
  • 5
  • 79
  • 106
molf
  • 73,644
  • 13
  • 135
  • 118
15
require 'ipaddr'
!(IPAddr.new(str) rescue nil).nil?

I use it for quick check because it uses built in library. Supports both ipv4 and ipv6. It is not very strict though, it says '999.999.999.999' is valid, for example. See the winning answer if you need more precision.

Evgenii
  • 36,389
  • 27
  • 134
  • 170
  • `!!IPAddr.new(str) rescue false` would be better code – Tim Kretschmer Apr 05 '17 at 07:23
  • 1
    Upvoted as at least the current library (Ruby 2.4.2) correctly validates addresses only if all groups of digits are inside the permissible ranges (0 to 255 for IPv4). `IPAddr.new '999.999.999.999'` => `IPAddr::InvalidAddressError: invalid address` – MatzFan Dec 11 '17 at 13:51
11

As most of the answers don't speak about IPV6 validation, I had the similar problem. I solved it by using the Ruby Regex Library, as @wingfire mentionned it.

But I also used the Regexp Library to use it's union method as explained here

I so have this code for a validation :

validates :ip, :format => { 
                  :with => Regexp.union(Resolv::IPv4::Regex, Resolv::IPv6::Regex)
                }

Hope this can help someone !

Philippe B.
  • 364
  • 2
  • 8
5

Use http://www.ruby-doc.org/stdlib-1.9.3/libdoc/ipaddr/rdoc/IPAddr.html it performs validation for you. Just rescue the exception with false and you know that it was invalid.

1.9.3p194 :002 > IPAddr.new('1.2.3.4')
 => #<IPAddr: IPv4:1.2.3.4/255.255.255.255> 
1.9.3p194 :003 > IPAddr.new('1.2.3.a')
ArgumentError: invalid address
  from /usr/local/rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/ipaddr.rb:496:in `rescue in initialize'
  from /usr/local/rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/ipaddr.rb:493:in `initialize'
  from (irb):3:in `new'
  from (irb):3
  from /usr/local/rvm/rubies/ruby-1.9.3-p194/bin/irb:16:in `<main>'
mar
  • 51
  • 1
  • 1
5
require 'ipaddr'

def is_ip?(ip)
  !!IPAddr.new(ip) rescue false
end

is_ip?("192.168.0.1")
=> true

is_ip?("www.google.com")
=> false

Or, if you don't mind extending core classes:

require 'ipaddr'

class String
  def is_ip?
    !!IPAddr.new(self) rescue false
  end
end

"192.168.0.1".is_ip?
=> true

"192.168.0.512".is_ip?
=> false
Knotty66
  • 1,711
  • 2
  • 12
  • 4
4

All answers above asume IPv4... you must ask yourself how wise it is to limit you app to IPv4 by adding these kind of checks in this day of the net migrating to IPv6.

If you ask me: Don't validate it at all. Instead just pass the string as-is to the network components that will be using the IP address and let them do the validation. Catch the exceptions they will throw when it is wrong and use that information to tell the user what happened. Don't re-invent the wheel, build upon the work of others.

Stijn de Witt
  • 40,192
  • 13
  • 79
  • 80
  • btw, what u get in `request.env['REMOTE_ADDR']` will always be a valid IP address, unless the web-server mess up with it! There is no need to check – ankitjaininfo Sep 03 '10 at 11:28
  • That's the best advice. Programmers are obsessed by syntactic validation but should not: pass it to the programs which will use it... and validate it. – bortzmeyer Mar 03 '13 at 09:01
3

Try this

Use IPAddr

require 'ipaddr'
true if IPAddr.new(ip) rescue false
Rahul Patel
  • 1,386
  • 1
  • 14
  • 16
2

This regular expression I use which I found here

/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/

Gerhard
  • 6,850
  • 8
  • 51
  • 81
0

for match a valid IP adress with regexp use

^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$

instead of

^([01]?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\.([01]?[0-9][0-9]?|2[0-4][0-9]|25[0-5])){3}$

because many regex engine match the first possibility in the OR sequence

you can try your regex engine : 10.48.0.200

test the difference here

Alban
  • 3,105
  • 5
  • 31
  • 46
0

IP address in a string form must contain exactly four numbers, separated by dots. Each number must be in a range between 0 and 255, inclusive.

squadette
  • 8,177
  • 4
  • 28
  • 39
  • also remember that you have to check for local network ips and other reserved ip addresses like 192.168.x.x and 127.0.0.1 – corroded Sep 03 '10 at 10:53
  • why? you actually can access your own site from your own network :) (or even with lynx on localhost). – squadette Sep 03 '10 at 15:46
0

Validate using regular expression:

\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}
ankitjaininfo
  • 11,961
  • 7
  • 52
  • 75
0

for ipv4

def ipv4?(str)
   nums = str.split('.')
   reg = /^\d$|^[1-9]\d$|^1\d\d$|^2[0-4]\d$|^25[0-5]$/
   nums.length == 4 && (nums.count {|n| reg.match?(n)}) == 4
end
Suhas C V
  • 104
  • 1
  • 5