diff options
author | bluemonk <ceresa@ieee.org> | 2010-07-18 11:29:36 +0200 |
---|---|---|
committer | bluemonk <ceresa@ieee.org> | 2010-07-18 11:30:53 +0200 |
commit | 2169d58e12815c9dec0b1ced515fa4b0e36ef10a (patch) | |
tree | 091efd999a7269b3e77fd725f2e8c34e70c2f4e7 /lib | |
parent | ec7ebc79397865db434cccd809c5eac0529fde07 (diff) | |
download | ipaddress-2169d58e12815c9dec0b1ced515fa4b0e36ef10a.tar.gz |
Lots of changes towards 0.6.0
Diffstat (limited to 'lib')
-rw-r--r-- | lib/ipaddress.rb | 146 | ||||
-rw-r--r-- | lib/ipaddress/extensions/extensions.rb | 4 | ||||
-rw-r--r-- | lib/ipaddress/ipbase.rb | 83 | ||||
-rw-r--r-- | lib/ipaddress/ipv4.rb | 181 | ||||
-rw-r--r-- | lib/ipaddress/ipv6.rb | 82 | ||||
-rw-r--r-- | lib/ipaddress/prefix.rb | 39 |
6 files changed, 367 insertions, 168 deletions
diff --git a/lib/ipaddress.rb b/lib/ipaddress.rb index 50336f0..d7397f6 100644 --- a/lib/ipaddress.rb +++ b/lib/ipaddress.rb @@ -1,9 +1,136 @@ -$LOAD_PATH.unshift(File.dirname(__FILE__)) -$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) +# +# = IPAddress +# +# A ruby library to manipulate IPv4 and IPv6 addresses +# +# +# Package:: IPAddress +# Author:: Marco Ceresa <ceresa@ieee.org> +# License:: Ruby License +# +#-- +# +#++ -require 'ipaddress/ipbase' require 'ipaddress/ipv4' require 'ipaddress/ipv6' +require 'ipaddress/extensions/extensions' + + +module IPAddress + + NAME = "IPAddress" + GEM = "ipaddress" + AUTHORS = ["Marco Ceresa <ceresa@ieee.org>"] + + # + # Parse the argument string to create a new + # IPv4, IPv6 or Mapped IP object + # + # ip = IPAddress.parse "172.16.10.1/24" + # ip6 = IPAddress.parse "2001:db8::8:800:200c:417a/64" + # ip_mapped = IPAddress.parse "::ffff:172.16.10.1/128" + # + # All the object created will be instances of the + # correct class: + # + # ip.class + # #=> IPAddress::IPv4 + # ip6.class + # #=> IPAddress::IPv6 + # ip_mapped.class + # #=> IPAddress::IPv6::Mapped + # + def IPAddress::parse(str) + case str + when /:.+\./ + IPAddress::IPv6::Mapped.new(str) + else + begin + IPAddress::IPv4.new(str) + rescue ArgumentError + IPAddress::IPv6.new(str) + end + end + end + + # + # Checks if the given string is a valid IP address, + # either IPv4 or IPv6 + # + # Example: + # + # IPAddress::valid? "2002::1" + # #=> true + # + # IPAddress::valid? "10.0.0.256" + # #=> false + # + def self.valid?(addr) + valid_ipv4?(addr) || valid_ipv6?(addr) + end + + # + # Checks if the given string is a valid IPv4 address + # + # Example: + # + # IPAddress::valid_ipv4? "2002::1" + # #=> false + # + # IPAddress::valid_ipv4? "172.16.10.1" + # #=> true + # + def self.valid_ipv4?(addr) + if /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\Z/ =~ addr + return $~.captures.all? {|i| i.to_i < 256} + end + false + end + + # + # Checks if the argument is a valid IPv4 netmask + # expressed in dotted decimal format. + # + # IPAddress.valid_ipv4_netmask? "255.255.0.0" + # #=> true + # + def self.valid_ipv4_netmask?(addr) + arr = addr.split(".").map{|i| i.to_i}.pack("CCCC").unpack("B*").first.scan(/01/) + arr.empty? && valid_ipv4?(addr) + rescue + return false + end + + # + # Checks if the given string is a valid IPv6 address + # + # Example: + # + # IPAddress::valid_ipv6? "2002::1" + # #=> true + # + # IPAddress::valid_ipv6? "2002::DEAD::BEEF" + # #=> false + # + def self.valid_ipv6?(addr) + # IPv6 (normal) + return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*\Z/ =~ addr + return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ addr + return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ addr + # IPv6 (IPv4 compat) + return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:/ =~ addr && valid_ipv4?($') + return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ addr && valid_ipv4?($') + return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ addr && valid_ipv4?($') + false + end + + def self.deprecate(message = nil) # :nodoc: + message ||= "You are using deprecated behavior which will be removed from the next major or minor release." + warn("DEPRECATION WARNING: #{message}") + end + +end # module IPAddress # # IPAddress is a wrapper method built around @@ -35,18 +162,7 @@ require 'ipaddress/ipv6' # #=> IPAddress::IPv6::Mapped # def IPAddress(str) - case str - when /:.+\./ - IPAddress::IPv6::Mapped.new(str) - else - begin - IPAddress::IPv4.new(str) - rescue ArgumentError - IPAddress::IPv6.new(str) - end - end + IPAddress::parse str end - - diff --git a/lib/ipaddress/extensions/extensions.rb b/lib/ipaddress/extensions/extensions.rb index 3090244..0d70bda 100644 --- a/lib/ipaddress/extensions/extensions.rb +++ b/lib/ipaddress/extensions/extensions.rb @@ -7,8 +7,8 @@ class Integer Math::log2(self).to_i == Math::log2(self) end - def closest_power_of_2 - self.upto(32) do |i| + def closest_power_of_2(limit=32) + self.upto(limit) do |i| return i if i.power_of_2? end end diff --git a/lib/ipaddress/ipbase.rb b/lib/ipaddress/ipbase.rb deleted file mode 100644 index 8c3d272..0000000 --- a/lib/ipaddress/ipbase.rb +++ /dev/null @@ -1,83 +0,0 @@ -require 'ipaddress/extensions/extensions' - -module IPAddress - - # - # Checks if the given string is a valid IP address, - # either IPv4 or IPv6 - # - # Example: - # - # IPAddress::valid? "2002::1" - # #=> true - # - # IPAddress::valid? "10.0.0.256" - # #=> false - # - def self.valid?(addr) - valid_ipv4?(addr) || valid_ipv6?(addr) - end - - # - # Checks if the given string is a valid IPv4 address - # - # Example: - # - # IPAddress::valid_ipv4? "2002::1" - # #=> false - # - # IPAddress::valid_ipv4? "172.16.10.1" - # #=> true - # - def self.valid_ipv4?(addr) - if /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\Z/ =~ addr - return $~.captures.all? {|i| i.to_i < 256} - end - false - end - - # - # Checks if the argument is a valid IPv4 netmark - # expressed in dotted decimal format. - # - # IPAddress.valid_ipv4_netmask? "255.255.0.0" - # #=> true - # - def self.valid_ipv4_netmask?(addr) - arr = addr.split(".").map{|i| i.to_i}.pack("CCCC").unpack("B*").first.scan(/01/) - arr.empty? && valid_ipv4?(addr) - rescue - return false - end - - # - # Checks if the given string is a valid IPv6 address - # - # Example: - # - # IPAddress::valid_ipv6? "2002::1" - # #=> true - # - # IPAddress::valid_ipv6? "2002::DEAD::BEEF" - # #=> false - # - def self.valid_ipv6?(addr) - # IPv6 (normal) - return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*\Z/ =~ addr - return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ addr - return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ addr - # IPv6 (IPv4 compat) - return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:/ =~ addr && valid_ipv4?($') - return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ addr && valid_ipv4?($') - return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ addr && valid_ipv4?($') - false - end - - class IPBase; end - -end # module IPAddress - - - - - diff --git a/lib/ipaddress/ipv4.rb b/lib/ipaddress/ipv4.rb index f6365f4..e94c34c 100644 --- a/lib/ipaddress/ipv4.rb +++ b/lib/ipaddress/ipv4.rb @@ -1,4 +1,3 @@ -require 'ipaddress/ipbase' require 'ipaddress/prefix' module IPAddress; @@ -15,7 +14,7 @@ module IPAddress; # # Class IPAddress::IPv4 is used to handle IPv4 type addresses. # - class IPv4 < IPBase + class IPv4 include IPAddress include Enumerable @@ -102,6 +101,7 @@ module IPAddress; # as a string. # # ip = IPAddress("172.16.100.4/22") + # # ip.address # #=> "172.16.100.4" # @@ -114,8 +114,10 @@ module IPAddress; # as a IPAddress::Prefix32 object # # ip = IPAddress("172.16.100.4/22") + # # ip.prefix # #=> 22 + # # ip.prefix.class # #=> IPAddress::Prefix32 # @@ -132,10 +134,12 @@ module IPAddress; # mask. # # ip = IPAddress("172.16.100.4") + # # puts ip # #=> 172.16.100.4/16 # # ip.prefix = 22 + # # puts ip # #=> 172.16.100.4/22 # @@ -147,6 +151,7 @@ module IPAddress; # Returns the address as an array of decimal values # # ip = IPAddress("172.16.100.4") + # # ip.octets # #=> [172, 16, 100, 4] # @@ -155,21 +160,37 @@ module IPAddress; end # + # Returns a string with the address portion of + # the IPv4 object + # + # ip = IPAddress("172.16.100.4/22") + # + # ip.to_s + # #=> "172.16.100.4" + # + def to_s + @address + end + + # # Returns a string with the IP address in canonical # form. # # ip = IPAddress("172.16.100.4/22") - # ip.to_s + # + # ip.to_string # #=> "172.16.100.4/22" # - def to_s + def to_string "#@address/#@prefix" end + # # Returns the prefix as a string in IP format # # ip = IPAddress("172.16.100.4/22") + # # ip.netmask # #=> "255.255.252.0" # @@ -183,10 +204,12 @@ module IPAddress; # object. # # ip = IPAddress("172.16.100.4") + # # puts ip # #=> 172.16.100.4/16 # # ip.netmask = "255.255.252.0" + # # puts ip # #=> 172.16.100.4/22 # @@ -203,6 +226,7 @@ module IPAddress; # structure. # # ip = IPAddress("10.0.0.0/8") + # # ip.to_u32 # #=> 167772160 # @@ -216,6 +240,7 @@ module IPAddress; # in a network byte order format. # # ip = IPAddress("172.16.10.1/24") + # # ip.data # #=> "\254\020\n\001" # @@ -237,6 +262,7 @@ module IPAddress; # Returns the octet specified by index # # ip = IPAddress("172.16.100.50/24") + # # ip[0] # #=> 172 # ip[1] @@ -256,6 +282,7 @@ module IPAddress; # as a string containing a sequence of 0 and 1 # # ip = IPAddress("127.0.0.1") + # # ip.bits # #=> "01111111000000000000000000000001" # @@ -267,8 +294,9 @@ module IPAddress; # Returns the broadcast address for the given IP. # # ip = IPAddress("172.16.10.64/24") + # # ip.broadcast.to_s - # #=> "172.16.10.255/24" + # #=> "172.16.10.255" # def broadcast self.class.parse_u32(broadcast_u32, @prefix) @@ -278,10 +306,12 @@ module IPAddress; # Checks if the IP address is actually a network # # ip = IPAddress("172.16.10.64/24") + # # ip.network? # #=> false # # ip = IPAddress("172.16.10.64/26") + # # ip.network? # #=> true # @@ -294,8 +324,9 @@ module IPAddress; # for the given IP. # # ip = IPAddress("172.16.10.64/24") + # # ip.network.to_s - # #=> "172.16.10.0/24" + # #=> "172.16.10.0" # def network self.class.parse_u32(network_u32, @prefix) @@ -309,15 +340,17 @@ module IPAddress; # host IP address is 192.168.100.1. # # ip = IPAddress("192.168.100.0/24") + # # ip.first.to_s - # #=> "192.168.100.1/24" + # #=> "192.168.100.1" # # The object IP doesn't need to be a network: the method # automatically gets the network number from it # # ip = IPAddress("192.168.100.50/24") + # # ip.first.to_s - # #=> "192.168.100.1/24" + # #=> "192.168.100.1" # def first self.class.parse_u32(network_u32+1, @prefix) @@ -329,18 +362,20 @@ module IPAddress; # last host IP address in the range. # # Example: given the 192.168.100.0/24 network, the last - # host IP address is 192.168.100.1. + # host IP address is 192.168.100.254 # # ip = IPAddress("192.168.100.0/24") + # # ip.last.to_s - # #=> "192.168.100.254/24" + # #=> "192.168.100.254" # # The object IP doesn't need to be a network: the method # automatically gets the network number from it # # ip = IPAddress("192.168.100.50/24") + # # ip.last.to_s - # #=> "192.168.100.254/24" + # #=> "192.168.100.254" # def last self.class.parse_u32(broadcast_u32-1, @prefix) @@ -350,9 +385,10 @@ module IPAddress; # Iterates over all the hosts IP addresses for the given # network (or IP address). # - # ip = IPaddress("10.0.0.1/29") + # ip = IPAddress("10.0.0.1/29") + # # ip.each do |i| - # p i + # p i.to_s # end # #=> "10.0.0.1" # #=> "10.0.0.2" @@ -374,7 +410,8 @@ module IPAddress; # The object yielded is a new IPv4 object created # from the iteration. # - # ip = IPaddress("10.0.0.1/29") + # ip = IPAddress("10.0.0.1/29") + # # ip.each do |i| # p i.address # end @@ -406,8 +443,8 @@ module IPAddress; # Example: # # ip1 = IPAddress "10.100.100.1/8" - # ip2 = IPAddress ""172.16.0.1/16" - # ip3 = IPAddress ""10.100.100.1/16" + # ip2 = IPAddress "172.16.0.1/16" + # ip3 = IPAddress "10.100.100.1/16" # # ip1 < ip2 # #=> true @@ -434,7 +471,8 @@ module IPAddress; # in the network. It also counts the network # address and the broadcast address. # - # ip = IPaddress("10.0.0.1/29") + # ip = IPAddress("10.0.0.1/29") + # # ip.size # #=> 8 # @@ -446,7 +484,8 @@ module IPAddress; # Returns an array with the IP addresses of # all the hosts in the network. # - # ip = IPaddress("10.0.0.1/29") + # ip = IPAddress("10.0.0.1/29") + # # ip.hosts.map {|i| i.address} # #=> ["10.0.0.1", # #=> "10.0.0.2", @@ -462,7 +501,8 @@ module IPAddress; # # Returns the network number in Unsigned 32bits format # - # ip = IPaddress("10.0.0.1/29") + # ip = IPAddress("10.0.0.1/29") + # # ip.network_u32 # #=> 167772160 # @@ -474,6 +514,7 @@ module IPAddress; # Returns the broadcast address in Unsigned 32bits format # # ip = IPaddress("10.0.0.1/29") + # # ip.broadcast_u32 # #=> 167772167 # @@ -490,6 +531,7 @@ module IPAddress; # ip = IPAddress("192.168.10.100/24") # # addr = IPAddress("192.168.10.102/24") + # # ip.include? addr # #=> true # @@ -505,12 +547,14 @@ module IPAddress; # for DNS lookups # # ip = IPAddress("172.16.100.50/24") + # # ip.reverse # #=> "50.100.16.172.in-addr.arpa" # def reverse @octets.reverse.join(".") + ".in-addr.arpa" end + alias_method :arpa, :reverse # # Subnetting a network @@ -524,7 +568,8 @@ module IPAddress; # networks will be divided evenly from the supernet. # # network = IPAddress("172.16.10.0/24") - # network / 4 # implies map{|i| i.to_s} + # + # network / 4 # implies map{|i| i.to_string} # #=> ["172.16.10.0/26", # "172.16.10.64/26", # "172.16.10.128/26", @@ -535,7 +580,8 @@ module IPAddress; # other networks with the remaining addresses. # # network = IPAddress("172.16.10.0/24") - # network / 3 # implies map{|i| i.to_s} + # + # network / 3 # implies map{|i| i.to_string} # #=> ["172.16.10.0/26", # "172.16.10.64/26", # "172.16.10.128/25"] @@ -546,7 +592,6 @@ module IPAddress; unless (1..(2**(32-prefix.to_i))).include? subnets raise ArgumentError, "Value #{subnets} out of range" end - calculate_subnets(subnets) end alias_method :/, :subnet @@ -564,13 +609,13 @@ module IPAddress; # # you can supernet it with a new /23 prefix # - # ip.supernet(23).to_s + # ip.supernet(23).to_string # #=> "172.16.10.0/23" # # However if you supernet it with a /22 prefix, the # network address will change: # - # ip.supernet(22).to_s + # ip.supernet(22).to_string # #=> "172.16.8.0/22" # def supernet(new_prefix) @@ -583,6 +628,14 @@ module IPAddress; # Returns the difference between two IP addresses # in unsigned int 32 bits format # + # Example: + # + # ip1 = IPAddress("172.16.10.0/24") + # ip2 = IPAddress("172.16.11.0/24") + # + # puts ip1 - ip2 + # #=> 256 + # def -(oth) return (to_u32 - oth.to_u32).abs end @@ -596,14 +649,21 @@ module IPAddress; # # ip1 = IPAddress("172.16.10.1/24") # ip2 = IPAddress("172.16.11.2/24") - # puts ip1 + ip2 - # #=>"172.16.10.0/23" + # + # p (ip1 + ip2).map {|i| i.to_string} + # #=> ["172.16.10.0/23"] # # If the networks are not contiguous, returns # the two network numbers from the objects # + # ip1 = IPAddress("10.0.0.1/24") + # ip2 = IPAddress("10.0.2.1/24") + # + # p (ip1 + ip2).map {|i| i.to_string} + # #=> ["10.0.0.0/24","10.0.2.0/24"] + # def +(oth) - self.class.summarize(self,oth) + aggregate(*[self,oth].sort.map{|i| i.network}) end # @@ -614,11 +674,12 @@ module IPAddress; # Example: # # ip = IPAddress("10.0.0.1/24") + # # ip.a? # #=> true # def a? - CLASSFUL.index(8) === bits + CLASSFUL.key(8) === bits end # @@ -629,11 +690,12 @@ module IPAddress; # Example: # # ip = IPAddress("172.16.10.1/24") + # # ip.b? # #=> true # def b? - CLASSFUL.index(16) === bits + CLASSFUL.key(16) === bits end # @@ -644,11 +706,12 @@ module IPAddress; # Example: # # ip = IPAddress("192.168.1.1/30") + # # ip.c? # #=> true # def c? - CLASSFUL.index(24) === bits + CLASSFUL.key(24) === bits end # @@ -658,6 +721,7 @@ module IPAddress; # Example: # # ip = IPAddress("172.16.10.1/24") + # # ip.to_ipv6 # #=> "ac10:0a01" # @@ -670,14 +734,16 @@ module IPAddress; # unsigned 32bits integer. # # ip = IPAddress::IPv4::parse_u32(167772160) + # # ip.prefix = 8 - # ip.to_s + # ip.to_string # #=> "10.0.0.0/8" # # The +prefix+ parameter is optional: # # ip = IPAddress::IPv4::parse_u32(167772160, 8) - # ip.to_s + # + # ip.to_string # #=> "10.0.0.0/8" # def self.parse_u32(u32, prefix=nil) @@ -699,7 +765,7 @@ module IPAddress; # ip = IPAddress::IPv4::parse_data "\254\020\n\001" # ip.prefix = 24 # - # ip.to_s + # ip.to_string # #=> "172.16.10.1/24" # def self.parse_data(str) @@ -713,10 +779,10 @@ module IPAddress; # Example: # # str = "foobar172.16.10.1barbaz" - # ip = self.extract str + # ip = IPAddress::IPv4::extract str # # ip.to_s - # #=> "172.16.10.1/16" + # #=> "172.16.10.1" # def self.extract(str) self.new REGEXP.match(str).to_s @@ -770,7 +836,8 @@ module IPAddress; # ip2 = IPAddress("10.0.1.1/24") # ip3 = IPAddress("10.0.2.1/24") # ip4 = IPAddress("10.0.3.1/24") - # IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).to_s + # + # IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).to_string # #=> "10.0.0.0/22", # # But the following networks can't be summarized in a single network: @@ -779,27 +846,28 @@ module IPAddress; # ip2 = IPAddress("10.0.2.1/24") # ip3 = IPAddress("10.0.3.1/24") # ip4 = IPAddress("10.0.4.1/24") - # IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).map{|i| i.to_s} + # + # IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).map{|i| i.to_string} # #=> ["10.0.1.0/24","10.0.2.0/23","10.0.4.0/24"] # def self.summarize(*args) # one network? no need to summarize - return args.flatten.first if args.size == 1 + return [args.first.network] if args.size == 1 - result, arr, last = [], args.sort, args.sort.last.network - arr.each_cons(2) do |x,y| - snet = x.supernet(x.prefix.to_i-1) - if snet.include? y - result << snet - else - result << x.network unless result.any?{|i| i.include? x} - end + i = 0 + result = args.dup.sort.map{|i| i.network} + while i < result.size-1 + sum = result[i] + result[i+1] + result[i..i+1] = sum.first if sum.size == 1 + i += 1 end - result << last unless result.any?{|i| i.include? last} + result.flatten! if result.size == args.size + # nothing more to summarize return result else + # keep on summarizing return self.summarize(*result) end end @@ -815,12 +883,12 @@ module IPAddress; def prefix_from_ip(ip) bits = bits_from_address(ip) - CLASSFUL.each {|reg,prefix| return prefix if bits =~ reg} + CLASSFUL.each {|reg,prefix| return Prefix32.new(prefix) if bits =~ reg} end def calculate_subnets(subnets) po2 = subnets.closest_power_of_2 - new_prefix = @prefix.to_i + Math::log2(po2).to_i + new_prefix = @prefix + Math::log2(po2).to_i networks = Array.new (0..po2-1).each do |i| mul = i * (2**(32-new_prefix)) @@ -843,6 +911,21 @@ module IPAddress; end return dup.reverse end + + def aggregate(ip1,ip2) + if ip1.include? ip2 + return [ip1] + else + snet = ip1.supernet(ip1.prefix-1) + arr1 = ip1.subnet(2**(ip2.prefix-ip1.prefix)).map{|i| i.to_string} + arr2 = snet.subnet(2**(ip2.prefix-snet.prefix)).map{|i| i.to_string} + if (arr2 - [ip2.to_string] - arr1).empty? + return [snet] + else + return [ip1, ip2] + end + end + end end # class IPv4 end # module IPAddress diff --git a/lib/ipaddress/ipv6.rb b/lib/ipaddress/ipv6.rb index 337b9e4..8424d53 100644 --- a/lib/ipaddress/ipv6.rb +++ b/lib/ipaddress/ipv6.rb @@ -1,4 +1,3 @@ -require 'ipaddress/ipbase' require 'ipaddress/prefix' module IPAddress; @@ -59,7 +58,7 @@ module IPAddress; # portion. # # - class IPv6 < IPBase + class IPv6 include IPAddress include Enumerable @@ -148,28 +147,29 @@ module IPAddress; # if the object was created using the default prefix # of 128 bits. # - # ip = IPAddress("2001:db8::8:800:200c:417a") - # puts ip - # #=> 2001:db8::8:800:200c:417a/128 + # ip6 = IPAddress("2001:db8::8:800:200c:417a") # - # ip.prefix = 64 - # puts ip - # #=> 2001:db8::8:800:200c:417a/64 + # puts ip6.to_string + # #=> "2001:db8::8:800:200c:417a/128" + # + # ip6.prefix = 64 + # puts ip6.to_string + # #=> "2001:db8::8:800:200c:417a/64" # def prefix=(num) @prefix = Prefix128.new(num) end # - # Unlike its counterpart IPv6#to_s method, IPv6#to_string + # Unlike its counterpart IPv6#to_string method, IPv6#to_string_uncompressed # returns the whole IPv6 address and prefix in an uncompressed form # # ip6 = IPAddress "2001:db8::8:800:200c:417a/64" # - # ip6.to_string + # ip6.to_string_uncompressed # #=> "2001:0db8:0000:0000:0008:0800:200c:417a/64" # - def to_string + def to_string_uncompressed "#@address/#@prefix" end @@ -177,13 +177,26 @@ module IPAddress; # Returns the IPv6 address in a human readable form, # using the compressed address. # - # ip6 = IPAddress "2001:db8::8:800:200c:417a/64" + # ip6 = IPAddress "2001:0db8:0000:0000:0008:0800:200c:417a/64" # # ip6.to_string # #=> "2001:db8::8:800:200c:417a/64" # + def to_string + "#@compressed/#@prefix" + end + + # + # Returns the IPv6 address in a human readable form, + # using the compressed address. + # + # ip6 = IPAddress "2001:db8::8:800:200c:417a/64" + # + # ip6.to_s + # #=> "2001:db8::8:800:200c:417a" + # def to_s - "#{compressed}/#@prefix" + @compressed end # @@ -204,10 +217,12 @@ module IPAddress; # True if the IPv6 address is a network # # ip6 = IPAddress "2001:db8::8:800:200c:417a/64" + # # ip6.network? # #=> false # # ip6 = IPAddress "2001:db8:8:800::/64" + # # ip6.network? # #=> true # @@ -219,6 +234,7 @@ module IPAddress; # Returns the 16-bits value specified by index # # ip = IPAddress("2001:db8::8:800:200c:417a/64") + # # ip[0] # #=> 8193 # ip[1] @@ -250,6 +266,7 @@ module IPAddress; # in a network byte order format. # # ip6 = IPAddress "2001:db8::8:800:200c:417a/64" + # # ip6.data # #=> " \001\r\270\000\000\000\000\000\b\b\000 \fAz" # @@ -283,6 +300,21 @@ module IPAddress; end # + # Returns the IPv6 address in a DNS reverse lookup + # string, as per RFC3172 and RFC2874. + # + # ip6 = IPAddress "3ffe:505:2::f") + # + # ip6.reverse + # #=> "f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.arpa" + # + def reverse + to_hex.reverse.gsub(/./){|c| c+"."} + "ip6.arpa" + end + alias_method :arpa, :reverse + + + # # Compressed form of the IPv6 address # # ip6 = IPAddress "2001:db8::8:800:200c:417a/64" @@ -325,10 +357,10 @@ module IPAddress; # Returns the address portion of an IP in binary format, # as a string containing a sequence of 0 and 1 # - # ip = IPAddress("2001:db8::8:800:200c:417a") + # ip6 = IPAddress("2001:db8::8:800:200c:417a") # - # ip.bits - # #=> "01111111000000000000000000000001" + # ip6.bits + # #=> "0010000000000001000011011011100000 [...] " # def bits data.unpack("B*").first @@ -392,7 +424,7 @@ module IPAddress; # # With that data you can create a new IPv6 object: # - # ip6 = IPAddress::IPv4::parse_data " \001\r\270\000\000\000\000\000\b\b\000 \fAz" + # ip6 = IPAddress::IPv6::parse_data " \001\r\270\000\000\000\000\000\b\b\000 \fAz" # ip6.prefix = 64 # # ip6.to_s @@ -660,9 +692,23 @@ module IPAddress; # ip6 = IPAddress "::ffff:172.16.10.1/128" # # ip6.to_s - # #=> "::FFFF:172.16.10.1/128" + # #=> "::FFFF:172.16.10.1" # def to_s + "::ffff:#{@ipv4.address}" + end + + # + # Similar to IPv6#to_string, but prints out the IPv4 address + # in dotted decimal format + # + # + # ip6 = IPAddress "::ffff:172.16.10.1/128" + # + # ip6.to_s + # #=> "::FFFF:172.16.10.1/128" + # + def to_string "::ffff:#{@ipv4.address}/#@prefix" end diff --git a/lib/ipaddress/prefix.rb b/lib/ipaddress/prefix.rb index 61e684b..ed0cda0 100644 --- a/lib/ipaddress/prefix.rb +++ b/lib/ipaddress/prefix.rb @@ -11,7 +11,7 @@ module IPAddress # # =DESCRIPTION # - # IPAddresS::Prefix is the parent class for IPAddress::Prefix32 + # IPAddress::Prefix is the parent class for IPAddress::Prefix32 # and IPAddress::Prefix128, defining some modules in common for # both the subclasses. # @@ -24,23 +24,60 @@ module IPAddress attr_reader :prefix + # + # Creates a new general prefix + # def initialize(num) @prefix = num.to_i end + # + # Returns a string with the prefix + # def to_s "#@prefix" end alias_method :inspect, :to_s + # + # Returns the prefix + # def to_i @prefix end + # + # Compare the prefix + # def <=>(oth) @prefix <=> oth.to_i end + # + # Sums two prefixes or a prefix to a + # number, returns a Fixnum + # + def +(oth) + if oth.is_a? Fixnum + self.prefix + oth + else + self.prefix + oth.prefix + end + end + + # + # Returns the difference between two + # prefixes, or a prefix and a number, + # as a Fixnum + # + def -(oth) + if oth.is_a? Fixnum + self.prefix - oth + else + (self.prefix - oth.prefix).abs + end + end + end # class Prefix |