diff options
-rw-r--r-- | .travis.yml | 1 | ||||
-rw-r--r-- | Rakefile | 1 | ||||
-rw-r--r-- | lib/ipaddress.rb | 90 | ||||
-rw-r--r-- | lib/ipaddress/ipv4.rb | 18 | ||||
-rw-r--r-- | lib/ipaddress/ipv6.rb | 30 | ||||
-rw-r--r-- | lib/ipaddress/prefix.rb | 2 | ||||
-rw-r--r-- | test/ipaddress/ipv4_test.rb | 34 | ||||
-rw-r--r-- | test/ipaddress/ipv6_test.rb | 60 | ||||
-rw-r--r-- | test/ipaddress/mongoid_test.rb | 6 | ||||
-rw-r--r-- | test/ipaddress/prefix_test.rb | 2 | ||||
-rw-r--r-- | test/ipaddress_test.rb | 31 |
11 files changed, 250 insertions, 25 deletions
diff --git a/.travis.yml b/.travis.yml index bb08cdb..cd30252 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,6 @@ rvm: - 2.4 install: - gem install bundler -- gem uninstall rake -x - gem install rake - bundle install script: @@ -9,6 +9,7 @@ Rake::TestTask.new(:test) do |test| test.libs << 'lib' << 'test' test.pattern = 'test/**/*_test.rb' test.verbose = true + test.warning = true end begin diff --git a/lib/ipaddress.rb b/lib/ipaddress.rb index 19b526f..99591d7 100644 --- a/lib/ipaddress.rb +++ b/lib/ipaddress.rb @@ -68,14 +68,14 @@ module IPAddress # def self.ntoa(uint) unless(uint.is_a? Numeric and uint <= 0xffffffff and uint >= 0) - raise(::ArgumentError, "not a long integer: #{uint.inspect}") - end - ret = [] - 4.times do - ret.unshift(uint & 0xff) - uint >>= 8 - end - ret.join('.') + raise(::ArgumentError, "not a long integer: #{uint.inspect}") + end + ret = [] + 4.times do + ret.unshift(uint & 0xff) + uint >>= 8 + end + ret.join('.') end # @@ -102,22 +102,88 @@ module IPAddress self.kind_of? IPAddress::IPv6 end + + # + # Checks if the given string is either a valid IP, either a valid IPv4 subnet + # + # Example: + # + # IPAddress::valid? "10.0.0.0/24" + # #=> true + # + # IPAddress::valid? "2002::1" + # #=> true + # + # IPAddress::valid? "10.0.0.256" + # #=> false + # + # IPAddress::valid? "10.0.0.0/999" + # #=> false + # + def self.valid?(addr) + valid_ip?(addr) || valid_ipv4_subnet?(addr) || valid_ipv6_subnet?(addr) + end + # # Checks if the given string is a valid IP address, # either IPv4 or IPv6 # # Example: # - # IPAddress::valid? "2002::1" + # IPAddress::valid_ip? "2002::1" # #=> true # - # IPAddress::valid? "10.0.0.256" + # IPAddress::valid_ip? "10.0.0.256" # #=> false # - def self.valid?(addr) + def self.valid_ip?(addr) valid_ipv4?(addr) || valid_ipv6?(addr) end - + + # + # Checks if the given string is a valid IPv4 subnet + # + # Example: + # + # IPAdress::valid_ipv4_subnet? "10.0.0.0/24" + # #=> true + # + # IPAdress::valid_ipv4_subnet? "10.0.0.0/255.255.255.0" + # #=> true + # + # IPAdress::valid_ipv4_subnet? "10.0.0.0/64" + # #=> false + # + def self.valid_ipv4_subnet?(addr) + ip, netmask = addr.split("/") + + valid_ipv4?(ip) && (!(netmask =~ /\A([12]?\d|3[0-2])\z/).nil? || valid_ipv4_netmask?(netmask)) + end + + # + # Checks if the given string is a valid IPv6 subnet + # + # Example: + # + # IPAdress::valid_ipv6_subnet? "::/0" + # #=> true + # + # IPAdress::valid_ipv6_subnet? "dead:beef:cafe:babe::/64" + # #=> true + # + # IPAdress::valid_ipv6_subnet? "2001::1/129" + # #=> false + # + def self.valid_ipv6_subnet?(addr) + ip, netmask = addr.split("/") + + netmask = Integer(netmask, 10) + + valid_ipv6?(ip) && netmask >= 0 && netmask <= 128 + rescue ArgumentError + false + end + # # Checks if the given string is a valid IPv4 address # diff --git a/lib/ipaddress/ipv4.rb b/lib/ipaddress/ipv4.rb index 080f3d6..70d1a51 100644 --- a/lib/ipaddress/ipv4.rb +++ b/lib/ipaddress/ipv4.rb @@ -61,6 +61,7 @@ module IPAddress; # IPAddress::IPv4.new "10.0.0.1/255.0.0.0" # def initialize(str) + raise ArgumentError, "Nil IP" unless str ip, netmask = str.split("/") # Check the ip and remove white space @@ -510,6 +511,7 @@ module IPAddress; # #=> ["10.100.100.1/8","10.100.100.1/16","172.16.0.1/16"] # def <=>(oth) + return nil unless oth.is_a?(self.class) return prefix <=> oth.prefix if to_u32 == oth.to_u32 to_u32 <=> oth.to_u32 end @@ -650,6 +652,20 @@ module IPAddress; end # + # Checks if an IPv4 address objects belongs + # to a link-local network RFC3927 + # + # Example: + # + # ip = IPAddress "169.254.0.1" + # ip.link_local? + # #=> true + # + def link_local? + [self.class.new("169.254.0.0/16")].any? {|i| i.include? self} + end + + # # Returns the IP address in in-addr.arpa format # for DNS lookups # @@ -772,7 +788,7 @@ module IPAddress; # # we can calculate the subnets with a /26 prefix # - # ip.subnets(26).map{&:to_string) + # ip.subnet(26).map{&:to_string) # #=> ["172.16.10.0/26", "172.16.10.64/26", # "172.16.10.128/26", "172.16.10.192/26"] # diff --git a/lib/ipaddress/ipv6.rb b/lib/ipaddress/ipv6.rb index 284308c..f427954 100644 --- a/lib/ipaddress/ipv6.rb +++ b/lib/ipaddress/ipv6.rb @@ -87,6 +87,7 @@ module IPAddress; # ip6 = IPAddress "2001:db8::8:800:200c:417a/64" # def initialize(str) + raise ArgumentError, "Nil IP" unless str ip, netmask = str.split("/") if str =~ /:.+\./ @@ -423,6 +424,34 @@ module IPAddress; @prefix == 128 and @compressed == "::1" end + # + # Checks if an IPv6 address objects belongs + # to a link-local network RFC4291 + # + # Example: + # + # ip = IPAddress "fe80::1" + # ip.link_local? + # #=> true + # + def link_local? + [self.class.new("fe80::/64")].any? {|i| i.include? self} + end + + # + # Checks if an IPv6 address objects belongs + # to a unique-local network RFC4193 + # + # Example: + # + # ip = IPAddress "fc00::1" + # ip.unique_local? + # #=> true + # + def unique_local? + [self.class.new("fc00::/7")].any? {|i| i.include? self} + end + # # Returns true if the address is a mapped address # @@ -495,6 +524,7 @@ module IPAddress; # #=> ["2001:db8:1::1/64","2001:db8:1::1/65","2001:db8:2::1/64"] # def <=>(oth) + return nil unless oth.is_a?(self.class) return prefix <=> oth.prefix if to_u128 == oth.to_u128 to_u128 <=> oth.to_u128 end diff --git a/lib/ipaddress/prefix.rb b/lib/ipaddress/prefix.rb index 136f2d3..e819800 100644 --- a/lib/ipaddress/prefix.rb +++ b/lib/ipaddress/prefix.rb @@ -78,7 +78,7 @@ module IPAddress end end - end # class Prefix + end # class Prefix class Prefix32 < Prefix diff --git a/test/ipaddress/ipv4_test.rb b/test/ipaddress/ipv4_test.rb index df60bb1..19e0450 100644 --- a/test/ipaddress/ipv4_test.rb +++ b/test/ipaddress/ipv4_test.rb @@ -74,6 +74,22 @@ class IPv4Test < Minitest::Test "10.32.0.1" => ["10.32.0.253", 253], "192.0.0.0" => ["192.1.255.255", 131072] } + + @link_local = [ + "169.254.0.0", + "169.254.255.255", + "169.254.12.34", + "169.254.0.0/16", + "169.254.0.0/17"] + + @not_link_local = [ + "127.0.0.1", + "127.0.1.1", + "192.168.0.100", + "169.255.0.0", + "169.254.0.0/15", + "0.0.0.0", + "255.255.255.255"] end @@ -83,7 +99,7 @@ class IPv4Test < Minitest::Test assert_instance_of @klass, ip end assert_instance_of IPAddress::Prefix32, @ip.prefix - assert_raises (ArgumentError) do + assert_raises(ArgumentError) do @klass.new end end @@ -92,6 +108,7 @@ class IPv4Test < Minitest::Test @invalid_ipv4.each do |i| assert_raises(ArgumentError) {@klass.new(i)} end + assert_raises (ArgumentError) {@klass.new(nil)} assert_raises (ArgumentError) {@klass.new("10.0.0.0/asd")} end @@ -309,6 +326,15 @@ class IPv4Test < Minitest::Test assert_equal false, @klass.new("192.0.0.2/24").private? end + def test_method_link_local? + @link_local.each do |addr| + assert_equal true, @klass.new(addr).link_local? + end + @not_link_local.each do |addr| + assert_equal false, @klass.new(addr).link_local? + end + end + def test_method_octet assert_equal 172, @ip[0] assert_equal 16, @ip[1] @@ -372,6 +398,12 @@ class IPv4Test < Minitest::Test ip3 = @klass.new("10.0.0.0/8") arr = ["10.0.0.0/8","10.0.0.0/16","10.0.0.0/24"] assert_equal arr, [ip1,ip2,ip3].sort.map{|s| s.to_string} + # compare with alien thing + ip1 = @klass.new('127.0.0.1') + ip2 = IPAddress::IPv6.new('::1') + not_ip = String + assert_equal nil, ip1 <=> ip2 + assert_equal nil, ip1 <=> not_ip end def test_method_minus diff --git a/test/ipaddress/ipv6_test.rb b/test/ipaddress/ipv6_test.rb index 3990129..9d7736d 100644 --- a/test/ipaddress/ipv6_test.rb +++ b/test/ipaddress/ipv6_test.rb @@ -26,9 +26,7 @@ class IPv6Test < Minitest::Test "::1" => 1, "0:0:0:0:0:0:0:0" => 0, "0:0:0::0:0:0" => 0, - "::" => 0, - "1080:0:0:0:8:800:200C:417A" => 21932261930451111902915077091070067066, - "1080::8:800:200C:417A" => 21932261930451111902915077091070067066} + "::" => 0} @invalid_ipv6 = [":1:2:3:4:5:6:7", ":1:2:3:4:5:6:7", @@ -44,6 +42,37 @@ class IPv6Test < Minitest::Test @network = @klass.new "2001:db8:8:800::/64" @arr = [8193,3512,0,0,8,2048,8204,16762] @hex = "20010db80000000000080800200c417a" + + @link_local = [ + "fe80::", + "fe80::1", + "fe80::208:74ff:feda:625c", + "fe80::/64", + "fe80::/65"] + + @not_link_local = [ + "::", + "::1", + "ff80:03:02:01::", + "2001:db8::8:800:200c:417a", + "fe80::/63"] + + @unique_local = [ + "fc00::/7", + "fc00::/8", + "fd00::/8", + "fd12:3456:789a:1::1", + "fd12:3456:789a:1::/64", + "fc00::1"] + + @not_unique_local = [ + "fc00::/6", + "::", + "::1", + "fe80::", + "fe80::1", + "fe80::/64"] + end def test_attribute_address @@ -58,6 +87,7 @@ class IPv6Test < Minitest::Test end assert_equal 64, @ip.prefix + assert_raises(ArgumentError) {@klass.new nil } assert_raises(ArgumentError) { @klass.new "::10.1.1.1" } @@ -216,6 +246,24 @@ class IPv6Test < Minitest::Test assert_equal false, @ip.loopback? end + def test_method_link_local? + @link_local.each do |addr| + assert_equal true, @klass.new(addr).link_local? + end + @not_link_local.each do |addr| + assert_equal false, @klass.new(addr).link_local? + end + end + + def test_method_unique_local? + @unique_local.each do |addr| + assert_equal true, @klass.new(addr).unique_local? + end + @not_unique_local.each do |addr| + assert_equal false, @klass.new(addr).unique_local? + end + end + def test_method_network @networks.each do |addr,net| ip = @klass.new addr @@ -284,6 +332,12 @@ class IPv6Test < Minitest::Test arr = ["2001:db8:1::1/64","2001:db8:1::1/65", "2001:db8:1::2/64","2001:db8:2::1/64"] assert_equal arr, [ip1,ip2,ip3,ip4].sort.map{|s| s.to_string} + # compare with alien thing + ip1 = @klass.new('::1') + ip2 = IPAddress::IPv4.new('127.0.0.1') + not_ip = String + assert_equal nil, ip1 <=> ip2 + assert_equal nil, ip1 <=> not_ip end def test_classmethod_expand diff --git a/test/ipaddress/mongoid_test.rb b/test/ipaddress/mongoid_test.rb index b463e84..4f32093 100644 --- a/test/ipaddress/mongoid_test.rb +++ b/test/ipaddress/mongoid_test.rb @@ -38,7 +38,7 @@ class MongoidTest < Minitest::Test @invalid_values.each do |invalid_value| # Invalid addresses should serialize to nil - assert_equal nil, IPAddress.mongoize(invalid_value) + assert_nil IPAddress.mongoize(invalid_value) end end @@ -57,7 +57,7 @@ class MongoidTest < Minitest::Test @invalid_values.each do |invalid_value| # Invalid stored value should be loaded as nil - assert_equal nil, IPAddress.demongoize(invalid_value) + assert_nil IPAddress.demongoize(invalid_value) end end @@ -67,4 +67,4 @@ class MongoidTest < Minitest::Test assert_equal IPAddress.mongoize(@valid_network4), IPAddress.evolve(@valid_network4) end -end
\ No newline at end of file +end diff --git a/test/ipaddress/prefix_test.rb b/test/ipaddress/prefix_test.rb index 1a0e277..f0a3d8c 100644 --- a/test/ipaddress/prefix_test.rb +++ b/test/ipaddress/prefix_test.rb @@ -132,7 +132,7 @@ class Prefix128Test < Minitest::Test end def test_initialize - assert_raises (ArgumentError) do + assert_raises(ArgumentError) do @klass.new 129 end assert_instance_of @klass, @klass.new(64) diff --git a/test/ipaddress_test.rb b/test/ipaddress_test.rb index 862c889..d5bdf60 100644 --- a/test/ipaddress_test.rb +++ b/test/ipaddress_test.rb @@ -59,6 +59,17 @@ class IPAddressTest < Minitest::Test end def test_module_method_valid? + assert_equal true, IPAddress::valid?("10.0.0.0/24") + assert_equal true, IPAddress::valid?("10.0.0.0/255.255.255.0") + assert_equal false, IPAddress::valid?("10.0.0.0/64") + assert_equal false, IPAddress::valid?("10.0.0.0/255.255.255.256") + assert_equal true, IPAddress::valid?("::/0") + assert_equal true, IPAddress::valid?("2002::1/128") + assert_equal true, IPAddress::valid?("dead:beef:cafe:babe::/64") + assert_equal false, IPAddress::valid?("2002::1/129") + end + + def test_module_method_valid_ip? assert_equal true, IPAddress::valid?("10.0.0.1") assert_equal true, IPAddress::valid?("10.0.0.0") assert_equal true, IPAddress::valid?("2002::1") @@ -69,14 +80,30 @@ class IPAddressTest < Minitest::Test assert_equal false, IPAddress::valid?("10.0") assert_equal false, IPAddress::valid?("2002:::1") assert_equal false, IPAddress::valid?("2002:516:2:200") - end - def test_module_method_valid_ipv4_netmark? + def test_module_method_valid_ipv4_netmask? assert_equal true, IPAddress::valid_ipv4_netmask?("255.255.255.0") assert_equal false, IPAddress::valid_ipv4_netmask?("10.0.0.1") end + def test_module_method_valid_ipv4_subnet? + assert_equal true, IPAddress::valid_ipv4_subnet?("10.0.0.0/255.255.255.0") + assert_equal true, IPAddress::valid_ipv4_subnet?("10.0.0.0/0") + assert_equal true, IPAddress::valid_ipv4_subnet?("10.0.0.0/32") + assert_equal false, IPAddress::valid_ipv4_subnet?("10.0.0.0/ABC") + assert_equal false, IPAddress::valid_ipv4_subnet?("10.0.0.1") + assert_equal false, IPAddress::valid_ipv4_subnet?("10.0.0.0/33") + assert_equal false, IPAddress::valid_ipv4_subnet?("10.0.0.256/24") + assert_equal false, IPAddress::valid_ipv4_subnet?("10.0.0.0/255.255.255.256") + end + + def test_module_method_valid_ipv6_subnet? + assert_equal true, IPAddress::valid_ipv6_subnet?("::/0") + assert_equal true, IPAddress::valid_ipv6_subnet?("2002::1/128") + assert_equal true, IPAddress::valid_ipv6_subnet?("dead:beef:cafe:babe::/64") + assert_equal false, IPAddress::valid_ipv6_subnet?("2002::1/129") + end end |