PageRenderTime 62ms CodeModel.GetById 35ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/ipaddr.rb

https://github.com/ahwuyeah/ruby
Ruby | 658 lines | 459 code | 67 blank | 132 comment | 82 complexity | 8f2503f7b4f2adff4a1f05c9cdfa2881 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-3.0, Unlicense, GPL-2.0
  1. #
  2. # ipaddr.rb - A class to manipulate an IP address
  3. #
  4. # Copyright (c) 2002 Hajimu UMEMOTO <ume@mahoroba.org>.
  5. # Copyright (c) 2007, 2009, 2012 Akinori MUSHA <knu@iDaemons.org>.
  6. # All rights reserved.
  7. #
  8. # You can redistribute and/or modify it under the same terms as Ruby.
  9. #
  10. # $Id$
  11. #
  12. # Contact:
  13. # - Akinori MUSHA <knu@iDaemons.org> (current maintainer)
  14. #
  15. # TODO:
  16. # - scope_id support
  17. #
  18. require 'socket'
  19. # IPAddr provides a set of methods to manipulate an IP address. Both IPv4 and
  20. # IPv6 are supported.
  21. #
  22. # == Example
  23. #
  24. # require 'ipaddr'
  25. #
  26. # ipaddr1 = IPAddr.new "3ffe:505:2::1"
  27. #
  28. # p ipaddr1 #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0001/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>
  29. #
  30. # p ipaddr1.to_s #=> "3ffe:505:2::1"
  31. #
  32. # ipaddr2 = ipaddr1.mask(48) #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0000/ffff:ffff:ffff:0000:0000:0000:0000:0000>
  33. #
  34. # p ipaddr2.to_s #=> "3ffe:505:2::"
  35. #
  36. # ipaddr3 = IPAddr.new "192.168.2.0/24"
  37. #
  38. # p ipaddr3 #=> #<IPAddr: IPv4:192.168.2.0/255.255.255.0>
  39. class IPAddr
  40. # 32 bit mask for IPv4
  41. IN4MASK = 0xffffffff
  42. # 128 bit mask for IPv4
  43. IN6MASK = 0xffffffffffffffffffffffffffffffff
  44. # Format string for IPv6
  45. IN6FORMAT = (["%.4x"] * 8).join(':')
  46. # Regexp _internally_ used for parsing IPv4 address.
  47. RE_IPV4ADDRLIKE = %r{
  48. \A
  49. (\d+) \. (\d+) \. (\d+) \. (\d+)
  50. \z
  51. }x
  52. # Regexp _internally_ used for parsing IPv6 address.
  53. RE_IPV6ADDRLIKE_FULL = %r{
  54. \A
  55. (?:
  56. (?: [\da-f]{1,4} : ){7} [\da-f]{1,4}
  57. |
  58. ( (?: [\da-f]{1,4} : ){6} )
  59. (\d+) \. (\d+) \. (\d+) \. (\d+)
  60. )
  61. \z
  62. }xi
  63. # Regexp _internally_ used for parsing IPv6 address.
  64. RE_IPV6ADDRLIKE_COMPRESSED = %r{
  65. \A
  66. ( (?: (?: [\da-f]{1,4} : )* [\da-f]{1,4} )? )
  67. ::
  68. ( (?:
  69. ( (?: [\da-f]{1,4} : )* )
  70. (?:
  71. [\da-f]{1,4}
  72. |
  73. (\d+) \. (\d+) \. (\d+) \. (\d+)
  74. )
  75. )? )
  76. \z
  77. }xi
  78. # Generic IPAddr related error. Exceptions raised in this class should
  79. # inherit from Error.
  80. class Error < ArgumentError; end
  81. # Raised when the provided IP address is an invalid address.
  82. class InvalidAddressError < Error; end
  83. # Raised when the address family is invalid such as an address with an
  84. # unsupported family, an address with an inconsistent family, or an address
  85. # who's family cannot be determined.
  86. class AddressFamilyError < Error; end
  87. # Raised when the address is an invalid length.
  88. class InvalidPrefixError < InvalidAddressError; end
  89. # Returns the address family of this IP address.
  90. attr_reader :family
  91. # Creates a new ipaddr containing the given network byte ordered
  92. # string form of an IP address.
  93. def IPAddr::new_ntoh(addr)
  94. return IPAddr.new(IPAddr::ntop(addr))
  95. end
  96. # Convert a network byte ordered string form of an IP address into
  97. # human readable form.
  98. def IPAddr::ntop(addr)
  99. case addr.size
  100. when 4
  101. s = addr.unpack('C4').join('.')
  102. when 16
  103. s = IN6FORMAT % addr.unpack('n8')
  104. else
  105. raise AddressFamilyError, "unsupported address family"
  106. end
  107. return s
  108. end
  109. # Returns a new ipaddr built by bitwise AND.
  110. def &(other)
  111. return self.clone.set(@addr & coerce_other(other).to_i)
  112. end
  113. # Returns a new ipaddr built by bitwise OR.
  114. def |(other)
  115. return self.clone.set(@addr | coerce_other(other).to_i)
  116. end
  117. # Returns a new ipaddr built by bitwise right-shift.
  118. def >>(num)
  119. return self.clone.set(@addr >> num)
  120. end
  121. # Returns a new ipaddr built by bitwise left shift.
  122. def <<(num)
  123. return self.clone.set(addr_mask(@addr << num))
  124. end
  125. # Returns a new ipaddr built by bitwise negation.
  126. def ~
  127. return self.clone.set(addr_mask(~@addr))
  128. end
  129. # Returns true if two ipaddrs are equal.
  130. def ==(other)
  131. other = coerce_other(other)
  132. return @family == other.family && @addr == other.to_i
  133. end
  134. # Returns a new ipaddr built by masking IP address with the given
  135. # prefixlen/netmask. (e.g. 8, 64, "255.255.255.0", etc.)
  136. def mask(prefixlen)
  137. return self.clone.mask!(prefixlen)
  138. end
  139. # Returns true if the given ipaddr is in the range.
  140. #
  141. # e.g.:
  142. # require 'ipaddr'
  143. # net1 = IPAddr.new("192.168.2.0/24")
  144. # net2 = IPAddr.new("192.168.2.100")
  145. # net3 = IPAddr.new("192.168.3.0")
  146. # p net1.include?(net2) #=> true
  147. # p net1.include?(net3) #=> false
  148. def include?(other)
  149. other = coerce_other(other)
  150. if ipv4_mapped?
  151. if (@mask_addr >> 32) != 0xffffffffffffffffffffffff
  152. return false
  153. end
  154. mask_addr = (@mask_addr & IN4MASK)
  155. addr = (@addr & IN4MASK)
  156. family = Socket::AF_INET
  157. else
  158. mask_addr = @mask_addr
  159. addr = @addr
  160. family = @family
  161. end
  162. if other.ipv4_mapped?
  163. other_addr = (other.to_i & IN4MASK)
  164. other_family = Socket::AF_INET
  165. else
  166. other_addr = other.to_i
  167. other_family = other.family
  168. end
  169. if family != other_family
  170. return false
  171. end
  172. return ((addr & mask_addr) == (other_addr & mask_addr))
  173. end
  174. alias === include?
  175. # Returns the integer representation of the ipaddr.
  176. def to_i
  177. return @addr
  178. end
  179. # Returns a string containing the IP address representation.
  180. def to_s
  181. str = to_string
  182. return str if ipv4?
  183. str.gsub!(/\b0{1,3}([\da-f]+)\b/i, '\1')
  184. loop do
  185. break if str.sub!(/\A0:0:0:0:0:0:0:0\z/, '::')
  186. break if str.sub!(/\b0:0:0:0:0:0:0\b/, ':')
  187. break if str.sub!(/\b0:0:0:0:0:0\b/, ':')
  188. break if str.sub!(/\b0:0:0:0:0\b/, ':')
  189. break if str.sub!(/\b0:0:0:0\b/, ':')
  190. break if str.sub!(/\b0:0:0\b/, ':')
  191. break if str.sub!(/\b0:0\b/, ':')
  192. break
  193. end
  194. str.sub!(/:{3,}/, '::')
  195. if /\A::(ffff:)?([\da-f]{1,4}):([\da-f]{1,4})\z/i =~ str
  196. str = sprintf('::%s%d.%d.%d.%d', $1, $2.hex / 256, $2.hex % 256, $3.hex / 256, $3.hex % 256)
  197. end
  198. str
  199. end
  200. # Returns a string containing the IP address representation in
  201. # canonical form.
  202. def to_string
  203. return _to_string(@addr)
  204. end
  205. # Returns a network byte ordered string form of the IP address.
  206. def hton
  207. case @family
  208. when Socket::AF_INET
  209. return [@addr].pack('N')
  210. when Socket::AF_INET6
  211. return (0..7).map { |i|
  212. (@addr >> (112 - 16 * i)) & 0xffff
  213. }.pack('n8')
  214. else
  215. raise AddressFamilyError, "unsupported address family"
  216. end
  217. end
  218. # Returns true if the ipaddr is an IPv4 address.
  219. def ipv4?
  220. return @family == Socket::AF_INET
  221. end
  222. # Returns true if the ipaddr is an IPv6 address.
  223. def ipv6?
  224. return @family == Socket::AF_INET6
  225. end
  226. # Returns true if the ipaddr is an IPv4-mapped IPv6 address.
  227. def ipv4_mapped?
  228. return ipv6? && (@addr >> 32) == 0xffff
  229. end
  230. # Returns true if the ipaddr is an IPv4-compatible IPv6 address.
  231. def ipv4_compat?
  232. if !ipv6? || (@addr >> 32) != 0
  233. return false
  234. end
  235. a = (@addr & IN4MASK)
  236. return a != 0 && a != 1
  237. end
  238. # Returns a new ipaddr built by converting the native IPv4 address
  239. # into an IPv4-mapped IPv6 address.
  240. def ipv4_mapped
  241. if !ipv4?
  242. raise InvalidAddressError, "not an IPv4 address"
  243. end
  244. return self.clone.set(@addr | 0xffff00000000, Socket::AF_INET6)
  245. end
  246. # Returns a new ipaddr built by converting the native IPv4 address
  247. # into an IPv4-compatible IPv6 address.
  248. def ipv4_compat
  249. if !ipv4?
  250. raise InvalidAddressError, "not an IPv4 address"
  251. end
  252. return self.clone.set(@addr, Socket::AF_INET6)
  253. end
  254. # Returns a new ipaddr built by converting the IPv6 address into a
  255. # native IPv4 address. If the IP address is not an IPv4-mapped or
  256. # IPv4-compatible IPv6 address, returns self.
  257. def native
  258. if !ipv4_mapped? && !ipv4_compat?
  259. return self
  260. end
  261. return self.clone.set(@addr & IN4MASK, Socket::AF_INET)
  262. end
  263. # Returns a string for DNS reverse lookup. It returns a string in
  264. # RFC3172 form for an IPv6 address.
  265. def reverse
  266. case @family
  267. when Socket::AF_INET
  268. return _reverse + ".in-addr.arpa"
  269. when Socket::AF_INET6
  270. return ip6_arpa
  271. else
  272. raise AddressFamilyError, "unsupported address family"
  273. end
  274. end
  275. # Returns a string for DNS reverse lookup compatible with RFC3172.
  276. def ip6_arpa
  277. if !ipv6?
  278. raise InvalidAddressError, "not an IPv6 address"
  279. end
  280. return _reverse + ".ip6.arpa"
  281. end
  282. # Returns a string for DNS reverse lookup compatible with RFC1886.
  283. def ip6_int
  284. if !ipv6?
  285. raise InvalidAddressError, "not an IPv6 address"
  286. end
  287. return _reverse + ".ip6.int"
  288. end
  289. # Returns the successor to the ipaddr.
  290. def succ
  291. return self.clone.set(@addr + 1, @family)
  292. end
  293. # Compares the ipaddr with another.
  294. def <=>(other)
  295. other = coerce_other(other)
  296. return nil if other.family != @family
  297. return @addr <=> other.to_i
  298. end
  299. include Comparable
  300. # Checks equality used by Hash.
  301. def eql?(other)
  302. return self.class == other.class && self.hash == other.hash && self == other
  303. end
  304. # Returns a hash value used by Hash, Set, and Array classes
  305. def hash
  306. return ([@addr, @mask_addr].hash << 1) | (ipv4? ? 0 : 1)
  307. end
  308. # Creates a Range object for the network address.
  309. def to_range
  310. begin_addr = (@addr & @mask_addr)
  311. case @family
  312. when Socket::AF_INET
  313. end_addr = (@addr | (IN4MASK ^ @mask_addr))
  314. when Socket::AF_INET6
  315. end_addr = (@addr | (IN6MASK ^ @mask_addr))
  316. else
  317. raise AddressFamilyError, "unsupported address family"
  318. end
  319. return clone.set(begin_addr, @family)..clone.set(end_addr, @family)
  320. end
  321. # Returns a string containing a human-readable representation of the
  322. # ipaddr. ("#<IPAddr: family:address/mask>")
  323. def inspect
  324. case @family
  325. when Socket::AF_INET
  326. af = "IPv4"
  327. when Socket::AF_INET6
  328. af = "IPv6"
  329. else
  330. raise AddressFamilyError, "unsupported address family"
  331. end
  332. return sprintf("#<%s: %s:%s/%s>", self.class.name,
  333. af, _to_string(@addr), _to_string(@mask_addr))
  334. end
  335. protected
  336. # Set +@addr+, the internal stored ip address, to given +addr+. The
  337. # parameter +addr+ is validated using the first +family+ member,
  338. # which is +Socket::AF_INET+ or +Socket::AF_INET6+.
  339. def set(addr, *family)
  340. case family[0] ? family[0] : @family
  341. when Socket::AF_INET
  342. if addr < 0 || addr > IN4MASK
  343. raise InvalidAddressError, "invalid address"
  344. end
  345. when Socket::AF_INET6
  346. if addr < 0 || addr > IN6MASK
  347. raise InvalidAddressError, "invalid address"
  348. end
  349. else
  350. raise AddressFamilyError, "unsupported address family"
  351. end
  352. @addr = addr
  353. if family[0]
  354. @family = family[0]
  355. end
  356. return self
  357. end
  358. # Set current netmask to given mask.
  359. def mask!(mask)
  360. if mask.kind_of?(String)
  361. if mask =~ /^\d+$/
  362. prefixlen = mask.to_i
  363. else
  364. m = IPAddr.new(mask)
  365. if m.family != @family
  366. raise InvalidPrefixError, "address family is not same"
  367. end
  368. @mask_addr = m.to_i
  369. @addr &= @mask_addr
  370. return self
  371. end
  372. else
  373. prefixlen = mask
  374. end
  375. case @family
  376. when Socket::AF_INET
  377. if prefixlen < 0 || prefixlen > 32
  378. raise InvalidPrefixError, "invalid length"
  379. end
  380. masklen = 32 - prefixlen
  381. @mask_addr = ((IN4MASK >> masklen) << masklen)
  382. when Socket::AF_INET6
  383. if prefixlen < 0 || prefixlen > 128
  384. raise InvalidPrefixError, "invalid length"
  385. end
  386. masklen = 128 - prefixlen
  387. @mask_addr = ((IN6MASK >> masklen) << masklen)
  388. else
  389. raise AddressFamilyError, "unsupported address family"
  390. end
  391. @addr = ((@addr >> masklen) << masklen)
  392. return self
  393. end
  394. private
  395. # Creates a new ipaddr object either from a human readable IP
  396. # address representation in string, or from a packed in_addr value
  397. # followed by an address family.
  398. #
  399. # In the former case, the following are the valid formats that will
  400. # be recognized: "address", "address/prefixlen" and "address/mask",
  401. # where IPv6 address may be enclosed in square brackets (`[' and
  402. # `]'). If a prefixlen or a mask is specified, it returns a masked
  403. # IP address. Although the address family is determined
  404. # automatically from a specified string, you can specify one
  405. # explicitly by the optional second argument.
  406. #
  407. # Otherwise an IP address is generated from a packed in_addr value
  408. # and an address family.
  409. #
  410. # The IPAddr class defines many methods and operators, and some of
  411. # those, such as &, |, include? and ==, accept a string, or a packed
  412. # in_addr value instead of an IPAddr object.
  413. def initialize(addr = '::', family = Socket::AF_UNSPEC)
  414. if !addr.kind_of?(String)
  415. case family
  416. when Socket::AF_INET, Socket::AF_INET6
  417. set(addr.to_i, family)
  418. @mask_addr = (family == Socket::AF_INET) ? IN4MASK : IN6MASK
  419. return
  420. when Socket::AF_UNSPEC
  421. raise AddressFamilyError, "address family must be specified"
  422. else
  423. raise AddressFamilyError, "unsupported address family: #{family}"
  424. end
  425. end
  426. prefix, prefixlen = addr.split('/')
  427. if prefix =~ /^\[(.*)\]$/i
  428. prefix = $1
  429. family = Socket::AF_INET6
  430. end
  431. # It seems AI_NUMERICHOST doesn't do the job.
  432. #Socket.getaddrinfo(left, nil, Socket::AF_INET6, Socket::SOCK_STREAM, nil,
  433. # Socket::AI_NUMERICHOST)
  434. @addr = @family = nil
  435. if family == Socket::AF_UNSPEC || family == Socket::AF_INET
  436. @addr = in_addr(prefix)
  437. if @addr
  438. @family = Socket::AF_INET
  439. end
  440. end
  441. if !@addr && (family == Socket::AF_UNSPEC || family == Socket::AF_INET6)
  442. @addr = in6_addr(prefix)
  443. @family = Socket::AF_INET6
  444. end
  445. if family != Socket::AF_UNSPEC && @family != family
  446. raise AddressFamilyError, "address family mismatch"
  447. end
  448. if prefixlen
  449. mask!(prefixlen)
  450. else
  451. @mask_addr = (@family == Socket::AF_INET) ? IN4MASK : IN6MASK
  452. end
  453. end
  454. def coerce_other(other)
  455. case other
  456. when IPAddr
  457. other
  458. when String
  459. self.class.new(other)
  460. else
  461. self.class.new(other, @family)
  462. end
  463. end
  464. def in_addr(addr)
  465. case addr
  466. when Array
  467. octets = addr
  468. else
  469. m = RE_IPV4ADDRLIKE.match(addr) or return nil
  470. octets = m.captures
  471. end
  472. octets.inject(0) { |i, s|
  473. (n = s.to_i) < 256 or raise InvalidAddressError, "invalid address"
  474. s.match(/\A0./) and raise InvalidAddressError, "zero-filled number in IPv4 address is ambiguous"
  475. i << 8 | n
  476. }
  477. end
  478. def in6_addr(left)
  479. case left
  480. when RE_IPV6ADDRLIKE_FULL
  481. if $2
  482. addr = in_addr($~[2,4])
  483. left = $1 + ':'
  484. else
  485. addr = 0
  486. end
  487. right = ''
  488. when RE_IPV6ADDRLIKE_COMPRESSED
  489. if $4
  490. left.count(':') <= 6 or raise InvalidAddressError, "invalid address"
  491. addr = in_addr($~[4,4])
  492. left = $1
  493. right = $3 + '0:0'
  494. else
  495. left.count(':') <= ($1.empty? || $2.empty? ? 8 : 7) or
  496. raise InvalidAddressError, "invalid address"
  497. left = $1
  498. right = $2
  499. addr = 0
  500. end
  501. else
  502. raise InvalidAddressError, "invalid address"
  503. end
  504. l = left.split(':')
  505. r = right.split(':')
  506. rest = 8 - l.size - r.size
  507. if rest < 0
  508. return nil
  509. end
  510. (l + Array.new(rest, '0') + r).inject(0) { |i, s|
  511. i << 16 | s.hex
  512. } | addr
  513. end
  514. def addr_mask(addr)
  515. case @family
  516. when Socket::AF_INET
  517. return addr & IN4MASK
  518. when Socket::AF_INET6
  519. return addr & IN6MASK
  520. else
  521. raise AddressFamilyError, "unsupported address family"
  522. end
  523. end
  524. def _reverse
  525. case @family
  526. when Socket::AF_INET
  527. return (0..3).map { |i|
  528. (@addr >> (8 * i)) & 0xff
  529. }.join('.')
  530. when Socket::AF_INET6
  531. return ("%.32x" % @addr).reverse!.gsub!(/.(?!$)/, '\&.')
  532. else
  533. raise AddressFamilyError, "unsupported address family"
  534. end
  535. end
  536. def _to_string(addr)
  537. case @family
  538. when Socket::AF_INET
  539. return (0..3).map { |i|
  540. (addr >> (24 - 8 * i)) & 0xff
  541. }.join('.')
  542. when Socket::AF_INET6
  543. return (("%.32x" % addr).gsub!(/.{4}(?!$)/, '\&:'))
  544. else
  545. raise AddressFamilyError, "unsupported address family"
  546. end
  547. end
  548. end
  549. unless Socket.const_defined? :AF_INET6
  550. class Socket < BasicSocket
  551. # IPv6 protocol family
  552. AF_INET6 = Object.new
  553. end
  554. class << IPSocket
  555. private
  556. def valid_v6?(addr)
  557. case addr
  558. when IPAddr::RE_IPV6ADDRLIKE_FULL
  559. if $2
  560. $~[2,4].all? {|i| i.to_i < 256 }
  561. else
  562. true
  563. end
  564. when IPAddr::RE_IPV6ADDRLIKE_COMPRESSED
  565. if $4
  566. addr.count(':') <= 6 && $~[4,4].all? {|i| i.to_i < 256}
  567. else
  568. addr.count(':') <= 7
  569. end
  570. else
  571. false
  572. end
  573. end
  574. alias getaddress_orig getaddress
  575. public
  576. # Returns a +String+ based representation of a valid DNS hostname,
  577. # IPv4 or IPv6 address.
  578. #
  579. # IPSocket.getaddress 'localhost' #=> "::1"
  580. # IPSocket.getaddress 'broadcasthost' #=> "255.255.255.255"
  581. # IPSocket.getaddress 'www.ruby-lang.org' #=> "221.186.184.68"
  582. # IPSocket.getaddress 'www.ccc.de' #=> "2a00:1328:e102:ccc0::122"
  583. def getaddress(s)
  584. if valid_v6?(s)
  585. s
  586. else
  587. getaddress_orig(s)
  588. end
  589. end
  590. end
  591. end