класс IPAddr
IPAddr предоставляет набор методов для работы с IP-адресом. Поддерживаются как IPv4, так и IPv6.
Пример
require 'ipaddr' ipaddr1 = IPAddr.new "3ffe:505:2::1" p ipaddr1 #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0001/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff> p ipaddr1.to_s #=> "3ffe:505:2::1" ipaddr2 = ipaddr1.mask(48) #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0000/ffff:ffff:ffff:0000:0000:0000:0000:0000> p ipaddr2.to_s #=> "3ffe:505:2::" ipaddr3 = IPAddr.new "192.168.2.0/24" p ipaddr3 #=> #<IPAddr: IPv4:192.168.2.0/255.255.255.0>
Константы
- IN4MASK
-
32-битная маска для IPv4
- IN6FORMAT
-
Строка формата для IPv6
- IN6MASK
-
128-битная маска для IPv6
- RE_IPV4ADDRLIKE
-
Regexpвнутренне используется для разбора IPv4 адреса. - RE_IPV6ADDRLIKE_COMPRESSED
-
Regexpвнутренне используется для разбора IPv6 адреса. - RE_IPV6ADDRLIKE_FULL
-
Regexpвнутренне используется для разбора IPv6 адреса. - VERSION
Атрибуты
Возвращает семейство адресов данного IP-адреса.
Публичные методы класса
# File lib/ipaddr.rb, line 585
def initialize(addr = '::', family = Socket::AF_UNSPEC)
@mask_addr = nil
if !addr.kind_of?(String)
case family
when Socket::AF_INET, Socket::AF_INET6
set(addr.to_i, family)
@mask_addr = (family == Socket::AF_INET) ? IN4MASK : IN6MASK
return
when Socket::AF_UNSPEC
raise AddressFamilyError, "address family must be specified"
else
raise AddressFamilyError, "unsupported address family: #{family}"
end
end
prefix, prefixlen = addr.split('/', 2)
if prefix =~ /\A\[(.*)\]\z/i
prefix = $1
family = Socket::AF_INET6
end
if prefix =~ /\A(.*)(%\w+)\z/
prefix = $1
zone_id = $2
family = Socket::AF_INET6
end
# It seems AI_NUMERICHOST doesn't do the job.
#Socket.getaddrinfo(left, nil, Socket::AF_INET6, Socket::SOCK_STREAM, nil,
# Socket::AI_NUMERICHOST)
@addr = @family = nil
if family == Socket::AF_UNSPEC || family == Socket::AF_INET
@addr = in_addr(prefix)
if @addr
@family = Socket::AF_INET
end
end
if !@addr && (family == Socket::AF_UNSPEC || family == Socket::AF_INET6)
@addr = in6_addr(prefix)
@family = Socket::AF_INET6
end
@zone_id = zone_id
if family != Socket::AF_UNSPEC && @family != family
raise AddressFamilyError, "address family mismatch"
end
if prefixlen
mask!(prefixlen)
else
@mask_addr = (@family == Socket::AF_INET) ? IN4MASK : IN6MASK
end
end Создаёт новый объект ipaddr, либо из удобочитаемой строки представления IP-адреса, либо из упакованного значения in_addr с последующим семейством адресов.
В первом случае, распознаются следующие форматы: "адрес", "адрес/длина_префикса" и "адрес/маска", где IPv6-адрес может быть заключён в квадратные скобки (‘[’ и ‘]'). Если указан длина префикса или маска, возвращается замаскированный IP-адрес. Хотя семейство адресов определяется автоматически из указанной строки, вы можете указать его явно, используя необязательный второй аргумент.
В противном случае, IP-адрес генерируется из упакованного значения in_addr и семейства адресов.
Класс IPAddr определяет множество методов и операторов, некоторые из которых, такие как &, |, include? и ==, принимают строку или упакованное значение in_addr вместо объекта IPAddr.
# File lib/ipaddr.rb, line 107 def self.new_ntoh(addr) return new(ntop(addr)) end
Создаёт новый ipaddr, содержащий заданную строку IP-адреса в сетевом порядке байтов.
# File lib/ipaddr.rb, line 113
def self.ntop(addr)
case addr.size
when 4
addr.unpack('C4').join('.')
when 16
IN6FORMAT % addr.unpack('n8')
else
raise AddressFamilyError, "unsupported address family"
end
end Преобразует строку IP-адреса в сетевом порядке байтов в удобочитаемый вид.
Методы Публичного Экземпляра
# File lib/ipaddr.rb, line 125 def &(other) return self.clone.set(@addr & coerce_other(other).to_i) end
Возвращает новый ipaddr, созданный с помощью побитового И.
# File lib/ipaddr.rb, line 140 def <<(num) return self.clone.set(addr_mask(@addr << num)) end
Возвращает новый ipaddr, созданный с помощью побитового сдвига влево.
# File lib/ipaddr.rb, line 382 def <=>(other) other = coerce_other(other) rescue nil else @addr <=> other.to_i if other.family == @family end
Сравнивает ipaddr с другим.
# File lib/ipaddr.rb, line 150 def ==(other) other = coerce_other(other) rescue false else @family == other.family && @addr == other.to_i end
Возвращает true, если два ipaddr равны.
# File lib/ipaddr.rb, line 135 def >>(num) return self.clone.set(@addr >> num) end
Возвращает новый ipaddr, созданный с помощью побитового сдвига вправо.
# File lib/ipaddr.rb, line 392 def eql?(other) return self.class == other.class && self.hash == other.hash && self == other end
Проверяет равенство, используемое методом Hash.
# File lib/ipaddr.rb, line 228
def hton
case @family
when Socket::AF_INET
return [@addr].pack('N')
when Socket::AF_INET6
return (0..7).map { |i|
(@addr >> (112 - 16 * i)) & 0xffff
}.pack('n8')
else
raise AddressFamilyError, "unsupported address family"
end
end Возвращает строку в сетевом порядке байтов для IP-адреса.
# File lib/ipaddr.rb, line 176 def include?(other) other = coerce_other(other) return false unless other.family == family range = to_range other = other.to_range range.begin <= other.begin && range.end >= other.end end
Возвращает true, если заданный ipaddr находится в диапазоне.
Например:
require 'ipaddr'
net1 = IPAddr.new("192.168.2.0/24")
net2 = IPAddr.new("192.168.2.100")
net3 = IPAddr.new("192.168.3.0")
net4 = IPAddr.new("192.168.2.0/16")
p net1.include?(net2) #=> true
p net1.include?(net3) #=> false
p net1.include?(net4) #=> false
p net4.include?(net1) #=> true
# File lib/ipaddr.rb, line 448
def inspect
case @family
when Socket::AF_INET
af = "IPv4"
when Socket::AF_INET6
af = "IPv6"
zone_id = @zone_id.to_s
else
raise AddressFamilyError, "unsupported address family"
end
return sprintf("#<%s: %s:%s%s/%s>", self.class.name,
af, _to_string(@addr), zone_id, _to_string(@mask_addr))
end Возвращает строку с удобочитаемым представлением ipaddr. («#<IPAddr: family:address/mask>»)
# File lib/ipaddr.rb, line 361
def ip6_arpa
if !ipv6?
raise InvalidAddressError, "not an IPv6 address: #{@addr}"
end
return _reverse + ".ip6.arpa"
end Возвращает строку для обратного поиска DNS, совместимую с RFC3172.
# File lib/ipaddr.rb, line 369
def ip6_int
if !ipv6?
raise InvalidAddressError, "not an IPv6 address: #{@addr}"
end
return _reverse + ".ip6.int"
end Возвращает строку для обратного поиска DNS, совместимую с RFC1886.
# File lib/ipaddr.rb, line 242 def ipv4? return @family == Socket::AF_INET end
Возвращает true, если ipaddr — это IPv4-адрес.
# File lib/ipaddr.rb, line 329
def ipv4_compat
warn "IPAddr\##{__callee__} is obsolete", uplevel: 1 if $VERBOSE
if !ipv4?
raise InvalidAddressError, "not an IPv4 address: #{@addr}"
end
return self.clone.set(@addr, Socket::AF_INET6)
end Возвращает новый ipaddr, преобразованный из коренного IPv4-адреса в IPv4-совместимый IPv6-адрес.
# File lib/ipaddr.rb, line 301
def ipv4_compat?
warn "IPAddr\##{__callee__} is obsolete", uplevel: 1 if $VERBOSE
_ipv4_compat?
end Возвращает true, если ipaddr — это IPv4-совместимый IPv6-адрес.
# File lib/ipaddr.rb, line 318
def ipv4_mapped
if !ipv4?
raise InvalidAddressError, "not an IPv4 address: #{@addr}"
end
clone = self.clone.set(@addr | 0xffff00000000, Socket::AF_INET6)
clone.instance_variable_set(:@mask_addr, @mask_addr | 0xffffffffffffffffffffffff00000000)
clone
end Возвращает новый ipaddr, преобразованный из коренного IPv4-адреса в IPv4-отображенный IPv6-адрес.
# File lib/ipaddr.rb, line 296 def ipv4_mapped? return ipv6? && (@addr >> 32) == 0xffff end
Возвращает true, если ipaddr — это IPv4-отображенный IPv6-адрес.
# File lib/ipaddr.rb, line 247 def ipv6? return @family == Socket::AF_INET6 end
Возвращает true, если ipaddr — это IPv6-адрес.
# File lib/ipaddr.rb, line 284
def link_local?
case @family
when Socket::AF_INET
@addr & 0xffff0000 == 0xa9fe0000 # 169.254.0.0/16
when Socket::AF_INET6
@addr & 0xffc0_0000_0000_0000_0000_0000_0000_0000 == 0xfe80_0000_0000_0000_0000_0000_0000_0000
else
raise AddressFamilyError, "unsupported address family"
end
end Возвращает true, если ipaddr — это адрес локальной сети. IPv4-адреса в 169.254.0.0/16, зарезервированные RFC 3927, и адреса Link-Local IPv6 Unicast в fe80::/10, зарезервированные RFC 4291, считаются адресами локальной сети.
# File lib/ipaddr.rb, line 252
def loopback?
case @family
when Socket::AF_INET
@addr & 0xff000000 == 0x7f000000
when Socket::AF_INET6
@addr == 1
else
raise AddressFamilyError, "unsupported address family"
end
end Возвращает true, если ipaddr — это адрес обратной связи.
# File lib/ipaddr.rb, line 160 def mask(prefixlen) return self.clone.mask!(prefixlen) end
Возвращает новый ipaddr, созданный путем маскирования IP-адреса с заданным prefixlen/маской сети. (например, 8, 64, “255.255.255.0” и т.д.)
# File lib/ipaddr.rb, line 340
def native
if !ipv4_mapped? && !_ipv4_compat?
return self
end
return self.clone.set(@addr & IN4MASK, Socket::AF_INET)
end Возвращает новый ipaddr, созданный путем преобразования IPv6-адреса в коренной IPv4-адрес. Если IP-адрес не является IPv4-отображенным или IPv4-совместимым IPv6-адресом, возвращает self.
# File lib/ipaddr.rb, line 463 def netmask _to_string(@mask_addr) end
Возвращает маску сети в строковом формате, например, 255.255.0.0
# File lib/ipaddr.rb, line 418
def prefix
case @family
when Socket::AF_INET
n = IN4MASK ^ @mask_addr
i = 32
when Socket::AF_INET6
n = IN6MASK ^ @mask_addr
i = 128
else
raise AddressFamilyError, "unsupported address family"
end
while n.positive?
n >>= 1
i -= 1
end
i
end Возвращает длину префикса в битах для ipaddr.
# File lib/ipaddr.rb, line 437
def prefix=(prefix)
case prefix
when Integer
mask!(prefix)
else
raise InvalidPrefixError, "prefix must be an integer: #{@addr}"
end
end Устанавливает длину префикса в битах
# File lib/ipaddr.rb, line 267
def private?
case @family
when Socket::AF_INET
@addr & 0xff000000 == 0x0a000000 || # 10.0.0.0/8
@addr & 0xfff00000 == 0xac100000 || # 172.16.0.0/12
@addr & 0xffff0000 == 0xc0a80000 # 192.168.0.0/16
when Socket::AF_INET6
@addr & 0xfe00_0000_0000_0000_0000_0000_0000_0000 == 0xfc00_0000_0000_0000_0000_0000_0000_0000
else
raise AddressFamilyError, "unsupported address family"
end
end Возвращает true, если ipaddr — это приватный адрес. IPv4-адреса в 10.0.0.0/8, 172.16.0.0/12 и 192.168.0.0/16, как определено в RFC 1918, и IPv6-адреса уникальных локальных адресов в fc00::/7, как определено в RFC 4193, считаются приватными.
# File lib/ipaddr.rb, line 349
def reverse
case @family
when Socket::AF_INET
return _reverse + ".in-addr.arpa"
when Socket::AF_INET6
return ip6_arpa
else
raise AddressFamilyError, "unsupported address family"
end
end Возвращает строку для обратного поиска DNS. Для IPv6-адреса возвращает строку в формате RFC3172.
# File lib/ipaddr.rb, line 377 def succ return self.clone.set(@addr + 1, @family) end
Возвращает преемника ipaddr.
# File lib/ipaddr.rb, line 186 def to_i return @addr end
Возвращает целочисленное представление ipaddr.
# File lib/ipaddr.rb, line 402
def to_range
begin_addr = (@addr & @mask_addr)
case @family
when Socket::AF_INET
end_addr = (@addr | (IN4MASK ^ @mask_addr))
when Socket::AF_INET6
end_addr = (@addr | (IN6MASK ^ @mask_addr))
else
raise AddressFamilyError, "unsupported address family"
end
self.class.new(begin_addr, @family)..self.class.new(end_addr, @family)
end Создаёт объект Range для сетевого адреса.
# File lib/ipaddr.rb, line 191
def to_s
str = to_string
return str if ipv4?
str.gsub!(/\b0{1,3}([\da-f]+)\b/i, '\1')
loop do
break if str.sub!(/\A0:0:0:0:0:0:0:0\z/, '::')
break if str.sub!(/\b0:0:0:0:0:0:0\b/, ':')
break if str.sub!(/\b0:0:0:0:0:0\b/, ':')
break if str.sub!(/\b0:0:0:0:0\b/, ':')
break if str.sub!(/\b0:0:0:0\b/, ':')
break if str.sub!(/\b0:0:0\b/, ':')
break if str.sub!(/\b0:0\b/, ':')
break
end
str.sub!(/:{3,}/, '::')
if /\A::(ffff:)?([\da-f]{1,4}):([\da-f]{1,4})\z/i =~ str
str = sprintf('::%s%d.%d.%d.%d', $1, $2.hex / 256, $2.hex % 256, $3.hex / 256, $3.hex % 256)
end
str
end Возвращает строку, содержащую представление IP-адреса.
# File lib/ipaddr.rb, line 217
def to_string
str = _to_string(@addr)
if @family == Socket::AF_INET6
str << zone_id.to_s
end
return str
end Возвращает строку, содержащую представление IP-адреса в канонической форме.
# File lib/ipaddr.rb, line 469
def zone_id
if @family == Socket::AF_INET6
@zone_id
else
raise InvalidAddressError, "not an IPv6 address"
end
end Возвращает идентификатор зоны IPv6, если он есть. Вызывает исключение InvalidAddressError, если это не IPv6-адрес.
# File lib/ipaddr.rb, line 479
def zone_id=(zid)
if @family == Socket::AF_INET6
case zid
when nil, /\A%(\w+)\z/
@zone_id = zid
else
raise InvalidAddressError, "invalid zone identifier for address"
end
else
raise InvalidAddressError, "not an IPv6 address"
end
end Возвращает идентификатор зоны IPv6, если он есть. Вызывает исключение InvalidAddressError, если это не IPv6-адрес.
# File lib/ipaddr.rb, line 130 def |(other) return self.clone.set(@addr | coerce_other(other).to_i) end
Возвращает новый ipaddr, построенный с помощью побитового ИЛИ.
# File lib/ipaddr.rb, line 145 def ~ return self.clone.set(addr_mask(~@addr)) end
Возвращает новый ipaddr, построенный с помощью побитового отрицания.
Защищенные методы экземпляров
# File lib/ipaddr.rb, line 521
def mask!(mask)
case mask
when String
case mask
when /\A(0|[1-9]+\d*)\z/
prefixlen = mask.to_i
when /\A\d+\z/
raise InvalidPrefixError, "leading zeros in prefix"
else
m = IPAddr.new(mask)
if m.family != @family
raise InvalidPrefixError, "address family is not same: #{@addr}"
end
@mask_addr = m.to_i
n = @mask_addr ^ m.instance_variable_get(:@mask_addr)
unless ((n + 1) & n).zero?
raise InvalidPrefixError, "invalid mask #{mask}: #{@addr}"
end
@addr &= @mask_addr
return self
end
else
prefixlen = mask
end
case @family
when Socket::AF_INET
if prefixlen < 0 || prefixlen > 32
raise InvalidPrefixError, "invalid length: #{@addr}"
end
masklen = 32 - prefixlen
@mask_addr = ((IN4MASK >> masklen) << masklen)
when Socket::AF_INET6
if prefixlen < 0 || prefixlen > 128
raise InvalidPrefixError, "invalid length: #{@addr}"
end
masklen = 128 - prefixlen
@mask_addr = ((IN6MASK >> masklen) << masklen)
else
raise AddressFamilyError, "unsupported address family"
end
@addr = ((@addr >> masklen) << masklen)
return self
end Устанавливает текущую маску сети на заданную маску.
# File lib/ipaddr.rb, line 497
def set(addr, *family)
case family[0] ? family[0] : @family
when Socket::AF_INET
if addr < 0 || addr > IN4MASK
raise InvalidAddressError, "invalid address: #{@addr}"
end
when Socket::AF_INET6
if addr < 0 || addr > IN6MASK
raise InvalidAddressError, "invalid address: #{@addr}"
end
else
raise AddressFamilyError, "unsupported address family"
end
@addr = addr
if family[0]
@family = family[0]
if @family == Socket::AF_INET
@mask_addr &= IN4MASK
end
end
return self
end Устанавливает внутренний сохранённый IP-адрес, +@addr+, на заданный addr. Параметр addr валидируется с помощью первого family члена, который является Socket::AF_INET или Socket::AF_INET6.
Приватные методы экземпляров
# File lib/ipaddr.rb, line 306
def _ipv4_compat?
if !ipv6? || (@addr >> 32) != 0
return false
end
a = (@addr & IN4MASK)
return a != 0 && a != 1
end # File lib/ipaddr.rb, line 708
def _reverse
case @family
when Socket::AF_INET
return (0..3).map { |i|
(@addr >> (8 * i)) & 0xff
}.join('.')
when Socket::AF_INET6
return ("%.32x" % @addr).reverse!.gsub!(/.(?!$)/, '\&.')
else
raise AddressFamilyError, "unsupported address family"
end
end # File lib/ipaddr.rb, line 721
def _to_string(addr)
case @family
when Socket::AF_INET
return (0..3).map { |i|
(addr >> (24 - 8 * i)) & 0xff
}.join('.')
when Socket::AF_INET6
return (("%.32x" % addr).gsub!(/.{4}(?!$)/, '\&:'))
else
raise AddressFamilyError, "unsupported address family"
end
end # File lib/ipaddr.rb, line 697
def addr_mask(addr)
case @family
when Socket::AF_INET
return addr & IN4MASK
when Socket::AF_INET6
return addr & IN6MASK
else
raise AddressFamilyError, "unsupported address family"
end
end # File lib/ipaddr.rb, line 634
def coerce_other(other)
case other
when IPAddr
other
when String
self.class.new(other)
else
self.class.new(other, @family)
end
end # File lib/ipaddr.rb, line 660
def in6_addr(left)
case left
when RE_IPV6ADDRLIKE_FULL
if $2
addr = in_addr($~[2,4])
left = $1 + ':'
else
addr = 0
end
right = ''
when RE_IPV6ADDRLIKE_COMPRESSED
if $4
left.count(':') <= 6 or raise InvalidAddressError, "invalid address: #{@addr}"
addr = in_addr($~[4,4])
left = $1
right = $3 + '0:0'
else
left.count(':') <= ($1.empty? || $2.empty? ? 8 : 7) or
raise InvalidAddressError, "invalid address: #{@addr}"
left = $1
right = $2
addr = 0
end
else
raise InvalidAddressError, "invalid address: #{@addr}"
end
l = left.split(':')
r = right.split(':')
rest = 8 - l.size - r.size
if rest < 0
return nil
end
(l + Array.new(rest, '0') + r).inject(0) { |i, s|
i << 16 | s.hex
} | addr
end # File lib/ipaddr.rb, line 645
def in_addr(addr)
case addr
when Array
octets = addr
else
m = RE_IPV4ADDRLIKE.match(addr) or return nil
octets = m.captures
end
octets.inject(0) { |i, s|
(n = s.to_i) < 256 or raise InvalidAddressError, "invalid address: #{@addr}"
s.match(/\A0./) and raise InvalidAddressError, "zero-filled number in IPv4 address is ambiguous: #{@addr}"
i << 8 | n
}
end
Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.