класс 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 643
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 с последующим указанием семейства адресов.
В первом случае распознаются следующие допустимые форматы: «address», «address/prefixlen» и «address/mask»; IPv6-адрес можно заключить в квадратные скобки (‘[’ и ‘]’). Если указан prefixlen или mask, возвращается 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 114
def self.ntop(addr)
if addr.is_a?(String) && addr.encoding != Encoding::BINARY
raise InvalidAddressError, "invalid encoding (given #{addr.encoding}, expected BINARY)"
end
case addr.bytesize
when 4
addr.unpack('C4').join('.')
when 16
IN6FORMAT % addr.unpack('n8')
else
raise AddressFamilyError, "unsupported address family"
end
end Преобразует строковую форму IP-адреса в сетевом порядке байтов в понятное человеку представление. Ожидается, что строка будет иметь кодировку Encoding::ASCII_8BIT (BINARY).
Общедоступные методы экземпляра
# File lib/ipaddr.rb, line 130 def &(other) return self.clone.set(@addr & coerce_other(other).to_i) end
Возвращает новый объект ipaddr, полученный побитовой операцией AND.
# File lib/ipaddr.rb, line 145 def <<(num) return self.clone.set(addr_mask(@addr << num)) end
Возвращает новый объект ipaddr, полученный побитовым сдвигом влево.
# File lib/ipaddr.rb, line 422 def <=>(other) other = coerce_other(other) rescue nil else @addr <=> other.to_i if other.family == @family end
Сравнивает объект ipaddr с другим объектом.
# File lib/ipaddr.rb, line 155 def ==(other) other = coerce_other(other) rescue false else @family == other.family && @addr == other.to_i end
Возвращает true, если два объекта ipaddr равны.
# File lib/ipaddr.rb, line 140 def >>(num) return self.clone.set(@addr >> num) end
Возвращает новый объект ipaddr, полученный побитовым сдвигом вправо.
# File lib/ipaddr.rb, line 135 def |(other) return self.clone.set(@addr | coerce_other(other).to_i) end
Возвращает новый объект ipaddr, полученный побитовой операцией OR.
# File lib/ipaddr.rb, line 150 def ~ return self.clone.set(addr_mask(~@addr)) end
Возвращает новый объект ipaddr, полученный побитовым отрицанием.
# File lib/ipaddr.rb, line 231
def as_json(*)
if ipv4? && prefix == 32
to_s
elsif ipv6? && prefix == 128
to_s
else
cidr
end
end Возвращает строку с представлением IP-адреса и префиксом.
# File lib/ipaddr.rb, line 248
def cidr
"#{to_s}/#{prefix}"
end Возвращает строку с представлением IP-адреса в нотации CIDR
# File lib/ipaddr.rb, line 432 def eql?(other) return self.class == other.class && self.hash == other.hash && self == other end
Проверяет равенство, используемое Hash.
# File lib/ipaddr.rb, line 253
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 181 def include?(other) other = coerce_other(other) return false unless other.family == family begin_addr <= other.begin_addr && end_addr >= other.end_addr 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 477
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 401
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 409
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 267 def ipv4? return @family == Socket::AF_INET end
Возвращает true, если объект ipaddr является IPv4-адресом.
# File lib/ipaddr.rb, line 369
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 341
def ipv4_compat?
warn "IPAddr\##{__callee__} is obsolete", uplevel: 1 if $VERBOSE
_ipv4_compat?
end Возвращает true, если объект ipaddr является IPv4-совместимым IPv6-адресом.
# File lib/ipaddr.rb, line 358
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 336 def ipv4_mapped? return ipv6? && (@addr >> 32) == 0xffff end
Возвращает true, если объект ipaddr является IPv4-отображенным IPv6-адресом.
# File lib/ipaddr.rb, line 272 def ipv6? return @family == Socket::AF_INET6 end
Возвращает true, если объект ipaddr является IPv6-адресом.
# File lib/ipaddr.rb, line 321
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 || # fe80::/10
(@addr & 0xffff_0000_0000 == 0xffff_0000_0000 && (
@addr & 0xffff0000 == 0xa9fe0000 # ::ffff:169.254.0.0/16
))
else
raise AddressFamilyError, "unsupported address family"
end
end Возвращает true, если объект ipaddr является адресом локального канала. IPv4-адреса из диапазона 169.254.0.0/16, зарезервированного RFC 3927, и локальные одноадресные IPv6-адреса из диапазона fe80::/10, зарезервированного RFC 4291, считаются адресами локального канала. IPv4-адреса локального канала в диапазоне IPv4-отображенных IPv6-адресов также считаются адресами локального канала.
# File lib/ipaddr.rb, line 279
def loopback?
case @family
when Socket::AF_INET
@addr & 0xff000000 == 0x7f000000 # 127.0.0.1/8
when Socket::AF_INET6
@addr == 1 || # ::1
(@addr & 0xffff_0000_0000 == 0xffff_0000_0000 && (
@addr & 0xff000000 == 0x7f000000 # ::ffff:127.0.0.1/8
))
else
raise AddressFamilyError, "unsupported address family"
end
end Возвращает true, если объект ipaddr является адресом обратной петли. IPv4-адреса обратной петли в диапазоне IPv4-отображенных IPv6-адресов также считаются адресами обратной петли.
# File lib/ipaddr.rb, line 165 def mask(prefixlen) return self.clone.mask!(prefixlen) end
Возвращает новый объект ipaddr, полученный маскированием IP-адреса заданной длиной префикса или маской подсети. (например, 8, 64, “255.255.255.0” и т. д.)
# File lib/ipaddr.rb, line 380
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 492 def netmask _to_string(@mask_addr) end
Возвращает маску подсети в строковом формате, например 255.255.0.0
# File lib/ipaddr.rb, line 447
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 466
def prefix=(prefix)
case prefix
when Integer
mask!(prefix)
else
raise InvalidPrefixError, "prefix must be an integer"
end
end Задает длину префикса в битах
# File lib/ipaddr.rb, line 298
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 ||
(@addr & 0xffff_0000_0000 == 0xffff_0000_0000 && (
@addr & 0xff000000 == 0x0a000000 || # ::ffff:10.0.0.0/8
@addr & 0xfff00000 == 0xac100000 || # ::ffff::172.16.0.0/12
@addr & 0xffff0000 == 0xc0a80000 # ::ffff::192.168.0.0/16
))
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, считаются частными. Частные IPv4-адреса в диапазоне IPv4-отображенных IPv6-адресов также считаются частными.
# File lib/ipaddr.rb, line 389
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 417 def succ return self.clone.set(@addr + 1, @family) end
Возвращает следующий адрес после объекта ipaddr.
# File lib/ipaddr.rb, line 189 def to_i return @addr end
Возвращает целочисленное представление объекта ipaddr.
# File lib/ipaddr.rb, line 242
def to_json(*a)
%Q{"#{as_json(*a)}"}
end Возвращает строку JSON с представлением IP-адреса.
# File lib/ipaddr.rb, line 442 def to_range self.class.new(begin_addr, @family)..self.class.new(end_addr, @family) end
Создает объект Range для сетевого адреса.
# File lib/ipaddr.rb, line 194
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 220
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 497
def wildcard_mask
case @family
when Socket::AF_INET
mask = IN4MASK ^ @mask_addr
when Socket::AF_INET6
mask = IN6MASK ^ @mask_addr
else
raise AddressFamilyError, "unsupported address family"
end
_to_string(mask)
end Возвращает маску подстановки в строковом формате, например 0.0.255.255
# File lib/ipaddr.rb, line 512
def zone_id
if @family == Socket::AF_INET6
@zone_id
else
raise InvalidAddressError, "not an IPv6 address"
end
end Возвращает идентификатор зоны IPv6, если он задан. Если адрес не является IPv6-адресом, вызывает исключение InvalidAddressError.
# File lib/ipaddr.rb, line 522
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, если он задан. Если адрес не является IPv6-адресом, вызывает исключение InvalidAddressError.
Защищенные методы экземпляра
# File lib/ipaddr.rb, line 537 def begin_addr @addr & @mask_addr end
# File lib/ipaddr.rb, line 541
def end_addr
case @family
when Socket::AF_INET
@addr | (IN4MASK ^ @mask_addr)
when Socket::AF_INET6
@addr | (IN6MASK ^ @mask_addr)
else
raise AddressFamilyError, "unsupported address family"
end
end # File lib/ipaddr.rb, line 579
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"
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}"
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"
end
masklen = 32 - prefixlen
@mask_addr = ((IN4MASK >> masklen) << masklen)
when Socket::AF_INET6
if prefixlen < 0 || prefixlen > 128
raise InvalidPrefixError, "invalid length"
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 555
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 Задает +@addr+ — внутренне хранящийся IP-адрес — равным указанному addr. Параметр addr проверяется с помощью первого элемента family, которым является Socket::AF_INET или Socket::AF_INET6.
Приватные методы экземпляра
# File lib/ipaddr.rb, line 346
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 766
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 779
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 755
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 692
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 718
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 703
def in_addr(addr)
case addr
when Array
octets = addr
else
RE_IPV4ADDRLIKE.match?(addr) or return nil
octets = addr.split('.')
end
octets.inject(0) { |i, s|
(n = s.to_i) < 256 or raise InvalidAddressError, "invalid address: #{@addr}"
(s != '0') && s.start_with?('0') and raise InvalidAddressError, "zero-filled number in IPv4 address is ambiguous: #{@addr}"
i << 8 | n
}
end
Ruby Core © 1993–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.