PageRenderTime 66ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/Merlin/External/Languages/Ruby/redist-libs/ruby/1.8/resolv.rb

https://github.com/jxnmaomao/ironruby
Ruby | 1884 lines | 1469 code | 184 blank | 231 comment | 90 complexity | 1cb8f368d96e54753225f96862491f15 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. =begin
  2. = resolv library
  3. resolv.rb is a resolver library written in Ruby.
  4. Since it is written in Ruby, it is thread-aware.
  5. I.e. it can resolv many hostnames concurrently.
  6. It is possible to lookup various resources of DNS using DNS module directly.
  7. == example
  8. p Resolv.getaddress("www.ruby-lang.org")
  9. p Resolv.getname("210.251.121.214")
  10. Resolv::DNS.open {|dns|
  11. p dns.getresources("www.ruby-lang.org", Resolv::DNS::Resource::IN::A).collect {|r| r.address}
  12. p dns.getresources("ruby-lang.org", Resolv::DNS::Resource::IN::MX).collect {|r| [r.exchange.to_s, r.preference]}
  13. }
  14. == Resolv class
  15. === class methods
  16. --- Resolv.getaddress(name)
  17. --- Resolv.getaddresses(name)
  18. --- Resolv.each_address(name) {|address| ...}
  19. They lookups IP addresses of ((|name|)) which represents a hostname
  20. as a string by default resolver.
  21. getaddress returns first entry of lookupped addresses.
  22. getaddresses returns lookupped addresses as an array.
  23. each_address iterates over lookupped addresses.
  24. --- Resolv.getname(address)
  25. --- Resolv.getnames(address)
  26. --- Resolv.each_name(address) {|name| ...}
  27. lookups hostnames of ((|address|)) which represents IP address as a string.
  28. getname returns first entry of lookupped names.
  29. getnames returns lookupped names as an array.
  30. each_names iterates over lookupped names.
  31. == Resolv::Hosts class
  32. hostname resolver using /etc/hosts format.
  33. === class methods
  34. --- Resolv::Hosts.new(hosts='/etc/hosts')
  35. === methods
  36. --- Resolv::Hosts#getaddress(name)
  37. --- Resolv::Hosts#getaddresses(name)
  38. --- Resolv::Hosts#each_address(name) {|address| ...}
  39. address lookup methods.
  40. --- Resolv::Hosts#getname(address)
  41. --- Resolv::Hosts#getnames(address)
  42. --- Resolv::Hosts#each_name(address) {|name| ...}
  43. hostnames lookup methods.
  44. == Resolv::DNS class
  45. DNS stub resolver.
  46. === class methods
  47. --- Resolv::DNS.new(config_info=nil)
  48. ((|config_info|)) should be nil, a string or a hash.
  49. If nil is given, /etc/resolv.conf and platform specific information is used.
  50. If a string is given, it should be a filename which format is same as /etc/resolv.conf.
  51. If a hash is given, it may contains information for nameserver, search and ndots as follows.
  52. Resolv::DNS.new({:nameserver=>["210.251.121.21"], :search=>["ruby-lang.org"], :ndots=>1})
  53. --- Resolv::DNS.open(config_info=nil)
  54. --- Resolv::DNS.open(config_info=nil) {|dns| ...}
  55. === methods
  56. --- Resolv::DNS#close
  57. --- Resolv::DNS#getaddress(name)
  58. --- Resolv::DNS#getaddresses(name)
  59. --- Resolv::DNS#each_address(name) {|address| ...}
  60. address lookup methods.
  61. ((|name|)) must be a instance of Resolv::DNS::Name or String. Lookupped
  62. address is represented as an instance of Resolv::IPv4 or Resolv::IPv6.
  63. --- Resolv::DNS#getname(address)
  64. --- Resolv::DNS#getnames(address)
  65. --- Resolv::DNS#each_name(address) {|name| ...}
  66. hostnames lookup methods.
  67. ((|address|)) must be a instance of Resolv::IPv4, Resolv::IPv6 or String.
  68. Lookupped name is represented as an instance of Resolv::DNS::Name.
  69. --- Resolv::DNS#getresource(name, typeclass)
  70. --- Resolv::DNS#getresources(name, typeclass)
  71. --- Resolv::DNS#each_resource(name, typeclass) {|resource| ...}
  72. They lookup DNS resources of ((|name|)).
  73. ((|name|)) must be a instance of Resolv::Name or String.
  74. ((|typeclass|)) should be one of follows:
  75. * Resolv::DNS::Resource::IN::ANY
  76. * Resolv::DNS::Resource::IN::NS
  77. * Resolv::DNS::Resource::IN::CNAME
  78. * Resolv::DNS::Resource::IN::SOA
  79. * Resolv::DNS::Resource::IN::HINFO
  80. * Resolv::DNS::Resource::IN::MINFO
  81. * Resolv::DNS::Resource::IN::MX
  82. * Resolv::DNS::Resource::IN::TXT
  83. * Resolv::DNS::Resource::IN::ANY
  84. * Resolv::DNS::Resource::IN::A
  85. * Resolv::DNS::Resource::IN::WKS
  86. * Resolv::DNS::Resource::IN::PTR
  87. * Resolv::DNS::Resource::IN::AAAA
  88. Lookupped resource is represented as an instance of (a subclass of)
  89. Resolv::DNS::Resource.
  90. (Resolv::DNS::Resource::IN::A, etc.)
  91. == Resolv::DNS::Resource::IN::NS class
  92. --- name
  93. == Resolv::DNS::Resource::IN::CNAME class
  94. --- name
  95. == Resolv::DNS::Resource::IN::SOA class
  96. --- mname
  97. --- rname
  98. --- serial
  99. --- refresh
  100. --- retry
  101. --- expire
  102. --- minimum
  103. == Resolv::DNS::Resource::IN::HINFO class
  104. --- cpu
  105. --- os
  106. == Resolv::DNS::Resource::IN::MINFO class
  107. --- rmailbx
  108. --- emailbx
  109. == Resolv::DNS::Resource::IN::MX class
  110. --- preference
  111. --- exchange
  112. == Resolv::DNS::Resource::IN::TXT class
  113. --- data
  114. == Resolv::DNS::Resource::IN::A class
  115. --- address
  116. == Resolv::DNS::Resource::IN::WKS class
  117. --- address
  118. --- protocol
  119. --- bitmap
  120. == Resolv::DNS::Resource::IN::PTR class
  121. --- name
  122. == Resolv::DNS::Resource::IN::AAAA class
  123. --- address
  124. == Resolv::DNS::Name class
  125. === class methods
  126. --- Resolv::DNS::Name.create(name)
  127. === methods
  128. --- Resolv::DNS::Name#to_s
  129. == Resolv::DNS::Resource class
  130. == Resolv::IPv4 class
  131. === class methods
  132. --- Resolv::IPv4.create(address)
  133. === methods
  134. --- Resolv::IPv4#to_s
  135. --- Resolv::IPv4#to_name
  136. === constants
  137. --- Resolv::IPv4::Regex
  138. regular expression for IPv4 address.
  139. == Resolv::IPv6 class
  140. === class methods
  141. --- Resolv::IPv6.create(address)
  142. === methods
  143. --- Resolv::IPv6#to_s
  144. --- Resolv::IPv6#to_name
  145. === constants
  146. --- Resolv::IPv6::Regex
  147. regular expression for IPv6 address.
  148. == Bugs
  149. * NIS is not supported.
  150. * /etc/nsswitch.conf is not supported.
  151. * IPv6 is not supported.
  152. =end
  153. require 'socket'
  154. require 'fcntl'
  155. require 'timeout'
  156. require 'thread'
  157. class Resolv
  158. def self.getaddress(name)
  159. DefaultResolver.getaddress(name)
  160. end
  161. def self.getaddresses(name)
  162. DefaultResolver.getaddresses(name)
  163. end
  164. def self.each_address(name, &block)
  165. DefaultResolver.each_address(name, &block)
  166. end
  167. def self.getname(address)
  168. DefaultResolver.getname(address)
  169. end
  170. def self.getnames(address)
  171. DefaultResolver.getnames(address)
  172. end
  173. def self.each_name(address, &proc)
  174. DefaultResolver.each_name(address, &proc)
  175. end
  176. def initialize(resolvers=[Hosts.new, DNS.new])
  177. @resolvers = resolvers
  178. end
  179. def getaddress(name)
  180. each_address(name) {|address| return address}
  181. raise ResolvError.new("no address for #{name}")
  182. end
  183. def getaddresses(name)
  184. ret = []
  185. each_address(name) {|address| ret << address}
  186. return ret
  187. end
  188. def each_address(name)
  189. if AddressRegex =~ name
  190. yield name
  191. return
  192. end
  193. yielded = false
  194. @resolvers.each {|r|
  195. r.each_address(name) {|address|
  196. yield address.to_s
  197. yielded = true
  198. }
  199. return if yielded
  200. }
  201. end
  202. def getname(address)
  203. each_name(address) {|name| return name}
  204. raise ResolvError.new("no name for #{address}")
  205. end
  206. def getnames(address)
  207. ret = []
  208. each_name(address) {|name| ret << name}
  209. return ret
  210. end
  211. def each_name(address)
  212. yielded = false
  213. @resolvers.each {|r|
  214. r.each_name(address) {|name|
  215. yield name.to_s
  216. yielded = true
  217. }
  218. return if yielded
  219. }
  220. end
  221. class ResolvError < StandardError
  222. end
  223. class ResolvTimeout < TimeoutError
  224. end
  225. class Hosts
  226. if /mswin32|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM
  227. require 'win32/resolv'
  228. DefaultFileName = Win32::Resolv.get_hosts_path
  229. else
  230. DefaultFileName = '/etc/hosts'
  231. end
  232. def initialize(filename = DefaultFileName)
  233. @filename = filename
  234. @mutex = Mutex.new
  235. @initialized = nil
  236. end
  237. def lazy_initialize
  238. @mutex.synchronize {
  239. unless @initialized
  240. @name2addr = {}
  241. @addr2name = {}
  242. open(@filename) {|f|
  243. f.each {|line|
  244. line.sub!(/#.*/, '')
  245. addr, hostname, *aliases = line.split(/\s+/)
  246. next unless addr
  247. addr.untaint
  248. hostname.untaint
  249. @addr2name[addr] = [] unless @addr2name.include? addr
  250. @addr2name[addr] << hostname
  251. @addr2name[addr] += aliases
  252. @name2addr[hostname] = [] unless @name2addr.include? hostname
  253. @name2addr[hostname] << addr
  254. aliases.each {|n|
  255. n.untaint
  256. @name2addr[n] = [] unless @name2addr.include? n
  257. @name2addr[n] << addr
  258. }
  259. }
  260. }
  261. @name2addr.each {|name, arr| arr.reverse!}
  262. @initialized = true
  263. end
  264. }
  265. self
  266. end
  267. def getaddress(name)
  268. each_address(name) {|address| return address}
  269. raise ResolvError.new("#{@filename} has no name: #{name}")
  270. end
  271. def getaddresses(name)
  272. ret = []
  273. each_address(name) {|address| ret << address}
  274. return ret
  275. end
  276. def each_address(name, &proc)
  277. lazy_initialize
  278. if @name2addr.include?(name)
  279. @name2addr[name].each(&proc)
  280. end
  281. end
  282. def getname(address)
  283. each_name(address) {|name| return name}
  284. raise ResolvError.new("#{@filename} has no address: #{address}")
  285. end
  286. def getnames(address)
  287. ret = []
  288. each_name(address) {|name| ret << name}
  289. return ret
  290. end
  291. def each_name(address, &proc)
  292. lazy_initialize
  293. if @addr2name.include?(address)
  294. @addr2name[address].each(&proc)
  295. end
  296. end
  297. end
  298. class DNS
  299. # STD0013 (RFC 1035, etc.)
  300. # ftp://ftp.isi.edu/in-notes/iana/assignments/dns-parameters
  301. Port = 53
  302. UDPSize = 512
  303. DNSThreadGroup = ThreadGroup.new
  304. def self.open(*args)
  305. dns = new(*args)
  306. return dns unless block_given?
  307. begin
  308. yield dns
  309. ensure
  310. dns.close
  311. end
  312. end
  313. def initialize(config_info=nil)
  314. @mutex = Mutex.new
  315. @config = Config.new(config_info)
  316. @initialized = nil
  317. end
  318. def lazy_initialize
  319. @mutex.synchronize {
  320. unless @initialized
  321. @config.lazy_initialize
  322. if nameserver = @config.single?
  323. @requester = Requester::ConnectedUDP.new(nameserver)
  324. else
  325. @requester = Requester::UnconnectedUDP.new
  326. end
  327. @initialized = true
  328. end
  329. }
  330. self
  331. end
  332. def close
  333. @mutex.synchronize {
  334. if @initialized
  335. @requester.close if @requester
  336. @requester = nil
  337. @initialized = false
  338. end
  339. }
  340. end
  341. def getaddress(name)
  342. each_address(name) {|address| return address}
  343. raise ResolvError.new("DNS result has no information for #{name}")
  344. end
  345. def getaddresses(name)
  346. ret = []
  347. each_address(name) {|address| ret << address}
  348. return ret
  349. end
  350. def each_address(name)
  351. each_resource(name, Resource::IN::A) {|resource| yield resource.address}
  352. end
  353. def getname(address)
  354. each_name(address) {|name| return name}
  355. raise ResolvError.new("DNS result has no information for #{address}")
  356. end
  357. def getnames(address)
  358. ret = []
  359. each_name(address) {|name| ret << name}
  360. return ret
  361. end
  362. def each_name(address)
  363. case address
  364. when Name
  365. ptr = address
  366. when IPv4::Regex
  367. ptr = IPv4.create(address).to_name
  368. when IPv6::Regex
  369. ptr = IPv6.create(address).to_name
  370. else
  371. raise ResolvError.new("cannot interpret as address: #{address}")
  372. end
  373. each_resource(ptr, Resource::IN::PTR) {|resource| yield resource.name}
  374. end
  375. def getresource(name, typeclass)
  376. each_resource(name, typeclass) {|resource| return resource}
  377. raise ResolvError.new("DNS result has no information for #{name}")
  378. end
  379. def getresources(name, typeclass)
  380. ret = []
  381. each_resource(name, typeclass) {|resource| ret << resource}
  382. return ret
  383. end
  384. def each_resource(name, typeclass, &proc)
  385. lazy_initialize
  386. q = Queue.new
  387. senders = {}
  388. begin
  389. @config.resolv(name) {|candidate, tout, nameserver|
  390. msg = Message.new
  391. msg.rd = 1
  392. msg.add_question(candidate, typeclass)
  393. unless sender = senders[[candidate, nameserver]]
  394. sender = senders[[candidate, nameserver]] =
  395. @requester.sender(msg, candidate, q, nameserver)
  396. end
  397. sender.send
  398. reply = reply_name = nil
  399. timeout(tout, ResolvTimeout) { reply, reply_name = q.pop }
  400. case reply.rcode
  401. when RCode::NoError
  402. extract_resources(reply, reply_name, typeclass, &proc)
  403. return
  404. when RCode::NXDomain
  405. raise Config::NXDomain.new(reply_name.to_s)
  406. else
  407. raise Config::OtherResolvError.new(reply_name.to_s)
  408. end
  409. }
  410. ensure
  411. @requester.delete(q)
  412. end
  413. end
  414. def extract_resources(msg, name, typeclass)
  415. if typeclass < Resource::ANY
  416. n0 = Name.create(name)
  417. msg.each_answer {|n, ttl, data|
  418. yield data if n0 == n
  419. }
  420. end
  421. yielded = false
  422. n0 = Name.create(name)
  423. msg.each_answer {|n, ttl, data|
  424. if n0 == n
  425. case data
  426. when typeclass
  427. yield data
  428. yielded = true
  429. when Resource::CNAME
  430. n0 = data.name
  431. end
  432. end
  433. }
  434. return if yielded
  435. msg.each_answer {|n, ttl, data|
  436. if n0 == n
  437. case data
  438. when typeclass
  439. yield data
  440. end
  441. end
  442. }
  443. end
  444. class Requester
  445. def initialize
  446. @senders = {}
  447. end
  448. def close
  449. thread, sock, @thread, @sock = @thread, @sock
  450. begin
  451. if thread
  452. thread.kill
  453. thread.join
  454. end
  455. ensure
  456. sock.close if sock
  457. end
  458. end
  459. def delete(arg)
  460. case arg
  461. when Sender
  462. @senders.delete_if {|k, s| s == arg }
  463. when Queue
  464. @senders.delete_if {|k, s| s.queue == arg }
  465. else
  466. raise ArgumentError.new("neither Sender or Queue: #{arg}")
  467. end
  468. end
  469. class Sender
  470. def initialize(msg, data, sock, queue)
  471. @msg = msg
  472. @data = data
  473. @sock = sock
  474. @queue = queue
  475. end
  476. attr_reader :queue
  477. def recv(msg)
  478. @queue.push([msg, @data])
  479. end
  480. end
  481. class UnconnectedUDP < Requester
  482. def initialize
  483. super()
  484. @sock = UDPSocket.new
  485. @sock.fcntl(Fcntl::F_SETFD, 1) if defined? Fcntl::F_SETFD
  486. @id = {}
  487. @id.default = -1
  488. @thread = Thread.new {
  489. DNSThreadGroup.add Thread.current
  490. loop {
  491. reply, from = @sock.recvfrom(UDPSize)
  492. msg = begin
  493. Message.decode(reply)
  494. rescue DecodeError
  495. STDERR.print("DNS message decoding error: #{reply.inspect}\n")
  496. next
  497. end
  498. if s = @senders[[[from[3],from[1]],msg.id]]
  499. s.recv msg
  500. else
  501. #STDERR.print("non-handled DNS message: #{msg.inspect} from #{from.inspect}\n")
  502. end
  503. }
  504. }
  505. end
  506. def sender(msg, data, queue, host, port=Port)
  507. service = [host, port]
  508. id = Thread.exclusive {
  509. @id[service] = (@id[service] + 1) & 0xffff
  510. }
  511. request = msg.encode
  512. request[0,2] = [id].pack('n')
  513. return @senders[[service, id]] =
  514. Sender.new(request, data, @sock, host, port, queue)
  515. end
  516. class Sender < Requester::Sender
  517. def initialize(msg, data, sock, host, port, queue)
  518. super(msg, data, sock, queue)
  519. @host = host
  520. @port = port
  521. end
  522. def send
  523. @sock.send(@msg, 0, @host, @port)
  524. end
  525. end
  526. end
  527. class ConnectedUDP < Requester
  528. def initialize(host, port=Port)
  529. super()
  530. @host = host
  531. @port = port
  532. @sock = UDPSocket.new(host.index(':') ? Socket::AF_INET6 : Socket::AF_INET)
  533. @sock.connect(host, port)
  534. @sock.fcntl(Fcntl::F_SETFD, 1) if defined? Fcntl::F_SETFD
  535. @id = -1
  536. @thread = Thread.new {
  537. DNSThreadGroup.add Thread.current
  538. loop {
  539. reply = @sock.recv(UDPSize)
  540. msg = begin
  541. Message.decode(reply)
  542. rescue DecodeError
  543. STDERR.print("DNS message decoding error: #{reply.inspect}")
  544. next
  545. end
  546. if s = @senders[msg.id]
  547. s.recv msg
  548. else
  549. #STDERR.print("non-handled DNS message: #{msg.inspect}")
  550. end
  551. }
  552. }
  553. end
  554. def sender(msg, data, queue, host=@host, port=@port)
  555. unless host == @host && port == @port
  556. raise RequestError.new("host/port don't match: #{host}:#{port}")
  557. end
  558. id = Thread.exclusive { @id = (@id + 1) & 0xffff }
  559. request = msg.encode
  560. request[0,2] = [id].pack('n')
  561. return @senders[id] = Sender.new(request, data, @sock, queue)
  562. end
  563. class Sender < Requester::Sender
  564. def send
  565. @sock.send(@msg, 0)
  566. end
  567. end
  568. end
  569. class TCP < Requester
  570. def initialize(host, port=Port)
  571. super()
  572. @host = host
  573. @port = port
  574. @sock = TCPSocket.new
  575. @sock.connect(host, port)
  576. @sock.fcntl(Fcntl::F_SETFD, 1) if defined? Fcntl::F_SETFD
  577. @id = -1
  578. @senders = {}
  579. @thread = Thread.new {
  580. DNSThreadGroup.add Thread.current
  581. loop {
  582. len = @sock.read(2).unpack('n')
  583. reply = @sock.read(len)
  584. msg = begin
  585. Message.decode(reply)
  586. rescue DecodeError
  587. STDERR.print("DNS message decoding error: #{reply.inspect}")
  588. next
  589. end
  590. if s = @senders[msg.id]
  591. s.push msg
  592. else
  593. #STDERR.print("non-handled DNS message: #{msg.inspect}")
  594. end
  595. }
  596. }
  597. end
  598. def sender(msg, data, queue, host=@host, port=@port)
  599. unless host == @host && port == @port
  600. raise RequestError.new("host/port don't match: #{host}:#{port}")
  601. end
  602. id = Thread.exclusive { @id = (@id + 1) & 0xffff }
  603. request = msg.encode
  604. request[0,2] = [request.length, id].pack('nn')
  605. return @senders[id] = Sender.new(request, data, @sock, queue)
  606. end
  607. class Sender < Requester::Sender
  608. def send
  609. @sock.print(@msg)
  610. @sock.flush
  611. end
  612. end
  613. end
  614. class RequestError < StandardError
  615. end
  616. end
  617. class Config
  618. def initialize(config_info=nil)
  619. @mutex = Mutex.new
  620. @config_info = config_info
  621. @initialized = nil
  622. end
  623. def Config.parse_resolv_conf(filename)
  624. nameserver = []
  625. search = nil
  626. ndots = 1
  627. open(filename) {|f|
  628. f.each {|line|
  629. line.sub!(/[#;].*/, '')
  630. keyword, *args = line.split(/\s+/)
  631. args.each { |arg|
  632. arg.untaint
  633. }
  634. next unless keyword
  635. case keyword
  636. when 'nameserver'
  637. nameserver += args
  638. when 'domain'
  639. next if args.empty?
  640. search = [args[0]]
  641. when 'search'
  642. next if args.empty?
  643. search = args
  644. when 'options'
  645. args.each {|arg|
  646. case arg
  647. when /\Andots:(\d+)\z/
  648. ndots = $1.to_i
  649. end
  650. }
  651. end
  652. }
  653. }
  654. return { :nameserver => nameserver, :search => search, :ndots => ndots }
  655. end
  656. def Config.default_config_hash(filename="/etc/resolv.conf")
  657. if File.exist? filename
  658. config_hash = Config.parse_resolv_conf(filename)
  659. else
  660. if /mswin32|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM
  661. search, nameserver = Win32::Resolv.get_resolv_info
  662. config_hash = {}
  663. config_hash[:nameserver] = nameserver if nameserver
  664. config_hash[:search] = [search].flatten if search
  665. end
  666. end
  667. config_hash
  668. end
  669. def lazy_initialize
  670. @mutex.synchronize {
  671. unless @initialized
  672. @nameserver = []
  673. @search = nil
  674. @ndots = 1
  675. case @config_info
  676. when nil
  677. config_hash = Config.default_config_hash
  678. when String
  679. config_hash = Config.parse_resolv_conf(@config_info)
  680. when Hash
  681. config_hash = @config_info.dup
  682. if String === config_hash[:nameserver]
  683. config_hash[:nameserver] = [config_hash[:nameserver]]
  684. end
  685. if String === config_hash[:search]
  686. config_hash[:search] = [config_hash[:search]]
  687. end
  688. else
  689. raise ArgumentError.new("invalid resolv configuration: #{@config_info.inspect}")
  690. end
  691. @nameserver = config_hash[:nameserver] if config_hash.include? :nameserver
  692. @search = config_hash[:search] if config_hash.include? :search
  693. @ndots = config_hash[:ndots] if config_hash.include? :ndots
  694. @nameserver = ['0.0.0.0'] if @nameserver.empty?
  695. if @search
  696. @search = @search.map {|arg| Label.split(arg) }
  697. else
  698. hostname = Socket.gethostname
  699. if /\./ =~ hostname
  700. @search = [Label.split($')]
  701. else
  702. @search = [[]]
  703. end
  704. end
  705. if !@nameserver.kind_of?(Array) ||
  706. !@nameserver.all? {|ns| String === ns }
  707. raise ArgumentError.new("invalid nameserver config: #{@nameserver.inspect}")
  708. end
  709. if !@search.kind_of?(Array) ||
  710. !@search.all? {|ls| ls.all? {|l| Label::Str === l } }
  711. raise ArgumentError.new("invalid search config: #{@search.inspect}")
  712. end
  713. if !@ndots.kind_of?(Integer)
  714. raise ArgumentError.new("invalid ndots config: #{@ndots.inspect}")
  715. end
  716. @initialized = true
  717. end
  718. }
  719. self
  720. end
  721. def single?
  722. lazy_initialize
  723. if @nameserver.length == 1
  724. return @nameserver[0]
  725. else
  726. return nil
  727. end
  728. end
  729. def generate_candidates(name)
  730. candidates = nil
  731. name = Name.create(name)
  732. if name.absolute?
  733. candidates = [name]
  734. else
  735. if @ndots <= name.length - 1
  736. candidates = [Name.new(name.to_a)]
  737. else
  738. candidates = []
  739. end
  740. candidates.concat(@search.map {|domain| Name.new(name.to_a + domain)})
  741. end
  742. return candidates
  743. end
  744. InitialTimeout = 5
  745. def generate_timeouts
  746. ts = [InitialTimeout]
  747. ts << ts[-1] * 2 / @nameserver.length
  748. ts << ts[-1] * 2
  749. ts << ts[-1] * 2
  750. return ts
  751. end
  752. def resolv(name)
  753. candidates = generate_candidates(name)
  754. timeouts = generate_timeouts
  755. begin
  756. candidates.each {|candidate|
  757. begin
  758. timeouts.each {|tout|
  759. @nameserver.each {|nameserver|
  760. begin
  761. yield candidate, tout, nameserver
  762. rescue ResolvTimeout
  763. end
  764. }
  765. }
  766. raise ResolvError.new("DNS resolv timeout: #{name}")
  767. rescue NXDomain
  768. end
  769. }
  770. rescue ResolvError
  771. end
  772. end
  773. class NXDomain < ResolvError
  774. end
  775. class OtherResolvError < ResolvError
  776. end
  777. end
  778. module OpCode
  779. Query = 0
  780. IQuery = 1
  781. Status = 2
  782. Notify = 4
  783. Update = 5
  784. end
  785. module RCode
  786. NoError = 0
  787. FormErr = 1
  788. ServFail = 2
  789. NXDomain = 3
  790. NotImp = 4
  791. Refused = 5
  792. YXDomain = 6
  793. YXRRSet = 7
  794. NXRRSet = 8
  795. NotAuth = 9
  796. NotZone = 10
  797. BADVERS = 16
  798. BADSIG = 16
  799. BADKEY = 17
  800. BADTIME = 18
  801. BADMODE = 19
  802. BADNAME = 20
  803. BADALG = 21
  804. end
  805. class DecodeError < StandardError
  806. end
  807. class EncodeError < StandardError
  808. end
  809. module Label
  810. def self.split(arg)
  811. labels = []
  812. arg.scan(/[^\.]+/) {labels << Str.new($&)}
  813. return labels
  814. end
  815. class Str
  816. def initialize(string)
  817. @string = string
  818. @downcase = string.downcase
  819. end
  820. attr_reader :string, :downcase
  821. def to_s
  822. return @string
  823. end
  824. def inspect
  825. return "#<#{self.class} #{self.to_s}>"
  826. end
  827. def ==(other)
  828. return @downcase == other.downcase
  829. end
  830. def eql?(other)
  831. return self == other
  832. end
  833. def hash
  834. return @downcase.hash
  835. end
  836. end
  837. end
  838. class Name
  839. def self.create(arg)
  840. case arg
  841. when Name
  842. return arg
  843. when String
  844. return Name.new(Label.split(arg), /\.\z/ =~ arg ? true : false)
  845. else
  846. raise ArgumentError.new("cannot interpret as DNS name: #{arg.inspect}")
  847. end
  848. end
  849. def initialize(labels, absolute=true)
  850. @labels = labels
  851. @absolute = absolute
  852. end
  853. def inspect
  854. "#<#{self.class}: #{self.to_s}#{@absolute ? '.' : ''}>"
  855. end
  856. def absolute?
  857. return @absolute
  858. end
  859. def ==(other)
  860. return false unless Name === other
  861. return @labels == other.to_a && @absolute == other.absolute?
  862. end
  863. alias eql? ==
  864. # tests subdomain-of relation.
  865. #
  866. # domain = Resolv::DNS::Name.create("y.z")
  867. # p Resolv::DNS::Name.create("w.x.y.z").subdomain_of?(domain) #=> true
  868. # p Resolv::DNS::Name.create("x.y.z").subdomain_of?(domain) #=> true
  869. # p Resolv::DNS::Name.create("y.z").subdomain_of?(domain) #=> false
  870. # p Resolv::DNS::Name.create("z").subdomain_of?(domain) #=> false
  871. # p Resolv::DNS::Name.create("x.y.z.").subdomain_of?(domain) #=> false
  872. # p Resolv::DNS::Name.create("w.z").subdomain_of?(domain) #=> false
  873. #
  874. def subdomain_of?(other)
  875. raise ArgumentError, "not a domain name: #{other.inspect}" unless Name === other
  876. return false if @absolute != other.absolute?
  877. other_len = other.length
  878. return false if @labels.length <= other_len
  879. return @labels[-other_len, other_len] == other.to_a
  880. end
  881. def hash
  882. return @labels.hash ^ @absolute.hash
  883. end
  884. def to_a
  885. return @labels
  886. end
  887. def length
  888. return @labels.length
  889. end
  890. def [](i)
  891. return @labels[i]
  892. end
  893. # returns the domain name as a string.
  894. #
  895. # The domain name doesn't have a trailing dot even if the name object is
  896. # absolute.
  897. #
  898. # p Resolv::DNS::Name.create("x.y.z.").to_s #=> "x.y.z"
  899. # p Resolv::DNS::Name.create("x.y.z").to_s #=> "x.y.z"
  900. #
  901. def to_s
  902. return @labels.join('.')
  903. end
  904. end
  905. class Message
  906. @@identifier = -1
  907. def initialize(id = (@@identifier += 1) & 0xffff)
  908. @id = id
  909. @qr = 0
  910. @opcode = 0
  911. @aa = 0
  912. @tc = 0
  913. @rd = 0 # recursion desired
  914. @ra = 0 # recursion available
  915. @rcode = 0
  916. @question = []
  917. @answer = []
  918. @authority = []
  919. @additional = []
  920. end
  921. attr_accessor :id, :qr, :opcode, :aa, :tc, :rd, :ra, :rcode
  922. attr_reader :question, :answer, :authority, :additional
  923. def ==(other)
  924. return @id == other.id &&
  925. @qr == other.qr &&
  926. @opcode == other.opcode &&
  927. @aa == other.aa &&
  928. @tc == other.tc &&
  929. @rd == other.rd &&
  930. @ra == other.ra &&
  931. @rcode == other.rcode &&
  932. @question == other.question &&
  933. @answer == other.answer &&
  934. @authority == other.authority &&
  935. @additional == other.additional
  936. end
  937. def add_question(name, typeclass)
  938. @question << [Name.create(name), typeclass]
  939. end
  940. def each_question
  941. @question.each {|name, typeclass|
  942. yield name, typeclass
  943. }
  944. end
  945. def add_answer(name, ttl, data)
  946. @answer << [Name.create(name), ttl, data]
  947. end
  948. def each_answer
  949. @answer.each {|name, ttl, data|
  950. yield name, ttl, data
  951. }
  952. end
  953. def add_authority(name, ttl, data)
  954. @authority << [Name.create(name), ttl, data]
  955. end
  956. def each_authority
  957. @authority.each {|name, ttl, data|
  958. yield name, ttl, data
  959. }
  960. end
  961. def add_additional(name, ttl, data)
  962. @additional << [Name.create(name), ttl, data]
  963. end
  964. def each_additional
  965. @additional.each {|name, ttl, data|
  966. yield name, ttl, data
  967. }
  968. end
  969. def each_resource
  970. each_answer {|name, ttl, data| yield name, ttl, data}
  971. each_authority {|name, ttl, data| yield name, ttl, data}
  972. each_additional {|name, ttl, data| yield name, ttl, data}
  973. end
  974. def encode
  975. return MessageEncoder.new {|msg|
  976. msg.put_pack('nnnnnn',
  977. @id,
  978. (@qr & 1) << 15 |
  979. (@opcode & 15) << 11 |
  980. (@aa & 1) << 10 |
  981. (@tc & 1) << 9 |
  982. (@rd & 1) << 8 |
  983. (@ra & 1) << 7 |
  984. (@rcode & 15),
  985. @question.length,
  986. @answer.length,
  987. @authority.length,
  988. @additional.length)
  989. @question.each {|q|
  990. name, typeclass = q
  991. msg.put_name(name)
  992. msg.put_pack('nn', typeclass::TypeValue, typeclass::ClassValue)
  993. }
  994. [@answer, @authority, @additional].each {|rr|
  995. rr.each {|r|
  996. name, ttl, data = r
  997. msg.put_name(name)
  998. msg.put_pack('nnN', data.class::TypeValue, data.class::ClassValue, ttl)
  999. msg.put_length16 {data.encode_rdata(msg)}
  1000. }
  1001. }
  1002. }.to_s
  1003. end
  1004. class MessageEncoder
  1005. def initialize
  1006. @data = ''
  1007. @names = {}
  1008. yield self
  1009. end
  1010. def to_s
  1011. return @data
  1012. end
  1013. def put_bytes(d)
  1014. @data << d
  1015. end
  1016. def put_pack(template, *d)
  1017. @data << d.pack(template)
  1018. end
  1019. def put_length16
  1020. length_index = @data.length
  1021. @data << "\0\0"
  1022. data_start = @data.length
  1023. yield
  1024. data_end = @data.length
  1025. @data[length_index, 2] = [data_end - data_start].pack("n")
  1026. end
  1027. def put_string(d)
  1028. self.put_pack("C", d.length)
  1029. @data << d
  1030. end
  1031. def put_string_list(ds)
  1032. ds.each {|d|
  1033. self.put_string(d)
  1034. }
  1035. end
  1036. def put_name(d)
  1037. put_labels(d.to_a)
  1038. end
  1039. def put_labels(d)
  1040. d.each_index {|i|
  1041. domain = d[i..-1]
  1042. if idx = @names[domain]
  1043. self.put_pack("n", 0xc000 | idx)
  1044. return
  1045. else
  1046. @names[domain] = @data.length
  1047. self.put_label(d[i])
  1048. end
  1049. }
  1050. @data << "\0"
  1051. end
  1052. def put_label(d)
  1053. self.put_string(d.string)
  1054. end
  1055. end
  1056. def Message.decode(m)
  1057. o = Message.new(0)
  1058. MessageDecoder.new(m) {|msg|
  1059. id, flag, qdcount, ancount, nscount, arcount =
  1060. msg.get_unpack('nnnnnn')
  1061. o.id = id
  1062. o.qr = (flag >> 15) & 1
  1063. o.opcode = (flag >> 11) & 15
  1064. o.aa = (flag >> 10) & 1
  1065. o.tc = (flag >> 9) & 1
  1066. o.rd = (flag >> 8) & 1
  1067. o.ra = (flag >> 7) & 1
  1068. o.rcode = flag & 15
  1069. (1..qdcount).each {
  1070. name, typeclass = msg.get_question
  1071. o.add_question(name, typeclass)
  1072. }
  1073. (1..ancount).each {
  1074. name, ttl, data = msg.get_rr
  1075. o.add_answer(name, ttl, data)
  1076. }
  1077. (1..nscount).each {
  1078. name, ttl, data = msg.get_rr
  1079. o.add_authority(name, ttl, data)
  1080. }
  1081. (1..arcount).each {
  1082. name, ttl, data = msg.get_rr
  1083. o.add_additional(name, ttl, data)
  1084. }
  1085. }
  1086. return o
  1087. end
  1088. class MessageDecoder
  1089. def initialize(data)
  1090. @data = data
  1091. @index = 0
  1092. @limit = data.length
  1093. yield self
  1094. end
  1095. def get_length16
  1096. len, = self.get_unpack('n')
  1097. save_limit = @limit
  1098. @limit = @index + len
  1099. d = yield(len)
  1100. if @index < @limit
  1101. raise DecodeError.new("junk exists")
  1102. elsif @limit < @index
  1103. raise DecodeError.new("limit exceeded")
  1104. end
  1105. @limit = save_limit
  1106. return d
  1107. end
  1108. def get_bytes(len = @limit - @index)
  1109. d = @data[@index, len]
  1110. @index += len
  1111. return d
  1112. end
  1113. def get_unpack(template)
  1114. len = 0
  1115. template.each_byte {|byte|
  1116. case byte
  1117. when ?c, ?C
  1118. len += 1
  1119. when ?n
  1120. len += 2
  1121. when ?N
  1122. len += 4
  1123. else
  1124. raise StandardError.new("unsupported template: '#{byte.chr}' in '#{template}'")
  1125. end
  1126. }
  1127. raise DecodeError.new("limit exceeded") if @limit < @index + len
  1128. arr = @data.unpack("@#{@index}#{template}")
  1129. @index += len
  1130. return arr
  1131. end
  1132. def get_string
  1133. len = @data[@index]
  1134. raise DecodeError.new("limit exceeded") if @limit < @index + 1 + len
  1135. d = @data[@index + 1, len]
  1136. @index += 1 + len
  1137. return d
  1138. end
  1139. def get_string_list
  1140. strings = []
  1141. while @index < @limit
  1142. strings << self.get_string
  1143. end
  1144. strings
  1145. end
  1146. def get_name
  1147. return Name.new(self.get_labels)
  1148. end
  1149. def get_labels(limit=nil)
  1150. limit = @index if !limit || @index < limit
  1151. d = []
  1152. while true
  1153. case @data[@index]
  1154. when 0
  1155. @index += 1
  1156. return d
  1157. when 192..255
  1158. idx = self.get_unpack('n')[0] & 0x3fff
  1159. if limit <= idx
  1160. raise DecodeError.new("non-backward name pointer")
  1161. end
  1162. save_index = @index
  1163. @index = idx
  1164. d += self.get_labels(limit)
  1165. @index = save_index
  1166. return d
  1167. else
  1168. d << self.get_label
  1169. end
  1170. end
  1171. return d
  1172. end
  1173. def get_label
  1174. return Label::Str.new(self.get_string)
  1175. end
  1176. def get_question
  1177. name = self.get_name
  1178. type, klass = self.get_unpack("nn")
  1179. return name, Resource.get_class(type, klass)
  1180. end
  1181. def get_rr
  1182. name = self.get_name
  1183. type, klass, ttl = self.get_unpack('nnN')
  1184. typeclass = Resource.get_class(type, klass)
  1185. return name, ttl, self.get_length16 {typeclass.decode_rdata(self)}
  1186. end
  1187. end
  1188. end
  1189. class Query
  1190. def encode_rdata(msg)
  1191. raise EncodeError.new("#{self.class} is query.")
  1192. end
  1193. def self.decode_rdata(msg)
  1194. raise DecodeError.new("#{self.class} is query.")
  1195. end
  1196. end
  1197. class Resource < Query
  1198. ClassHash = {}
  1199. def encode_rdata(msg)
  1200. raise NotImplementedError.new
  1201. end
  1202. def self.decode_rdata(msg)
  1203. raise NotImplementedError.new
  1204. end
  1205. def ==(other)
  1206. return self.class == other.class &&
  1207. self.instance_variables == other.instance_variables &&
  1208. self.instance_variables.collect {|name| self.instance_eval name} ==
  1209. other.instance_variables.collect {|name| other.instance_eval name}
  1210. end
  1211. def eql?(other)
  1212. return self == other
  1213. end
  1214. def hash
  1215. h = 0
  1216. self.instance_variables.each {|name|
  1217. h ^= self.instance_eval("#{name}.hash")
  1218. }
  1219. return h
  1220. end
  1221. def self.get_class(type_value, class_value)
  1222. return ClassHash[[type_value, class_value]] ||
  1223. Generic.create(type_value, class_value)
  1224. end
  1225. class Generic < Resource
  1226. def initialize(data)
  1227. @data = data
  1228. end
  1229. attr_reader :data
  1230. def encode_rdata(msg)
  1231. msg.put_bytes(data)
  1232. end
  1233. def self.decode_rdata(msg)
  1234. return self.new(msg.get_bytes)
  1235. end
  1236. def self.create(type_value, class_value)
  1237. c = Class.new(Generic)
  1238. c.const_set(:TypeValue, type_value)
  1239. c.const_set(:ClassValue, class_value)
  1240. Generic.const_set("Type#{type_value}_Class#{class_value}", c)
  1241. ClassHash[[type_value, class_value]] = c
  1242. return c
  1243. end
  1244. end
  1245. class DomainName < Resource
  1246. def initialize(name)
  1247. @name = name
  1248. end
  1249. attr_reader :name
  1250. def encode_rdata(msg)
  1251. msg.put_name(@name)
  1252. end
  1253. def self.decode_rdata(msg)
  1254. return self.new(msg.get_name)
  1255. end
  1256. end
  1257. # Standard (class generic) RRs
  1258. ClassValue = nil
  1259. class NS < DomainName
  1260. TypeValue = 2
  1261. end
  1262. class CNAME < DomainName
  1263. TypeValue = 5
  1264. end
  1265. class SOA < Resource
  1266. TypeValue = 6
  1267. def initialize(mname, rname, serial, refresh, retry_, expire, minimum)
  1268. @mname = mname
  1269. @rname = rname
  1270. @serial = serial
  1271. @refresh = refresh
  1272. @retry = retry_
  1273. @expire = expire
  1274. @minimum = minimum
  1275. end
  1276. attr_reader :mname, :rname, :serial, :refresh, :retry, :expire, :minimum
  1277. def encode_rdata(msg)
  1278. msg.put_name(@mname)
  1279. msg.put_name(@rname)
  1280. msg.put_pack('NNNNN', @serial, @refresh, @retry, @expire, @minimum)
  1281. end
  1282. def self.decode_rdata(msg)
  1283. mname = msg.get_name
  1284. rname = msg.get_name
  1285. serial, refresh, retry_, expire, minimum = msg.get_unpack('NNNNN')
  1286. return self.new(
  1287. mname, rname, serial, refresh, retry_, expire, minimum)
  1288. end
  1289. end
  1290. class PTR < DomainName
  1291. TypeValue = 12
  1292. end
  1293. class HINFO < Resource
  1294. TypeValue = 13
  1295. def initialize(cpu, os)
  1296. @cpu = cpu
  1297. @os = os
  1298. end
  1299. attr_reader :cpu, :os
  1300. def encode_rdata(msg)
  1301. msg.put_string(@cpu)
  1302. msg.put_string(@os)
  1303. end
  1304. def self.decode_rdata(msg)
  1305. cpu = msg.get_string
  1306. os = msg.get_string
  1307. return self.new(cpu, os)
  1308. end
  1309. end
  1310. class MINFO < Resource
  1311. TypeValue = 14
  1312. def initialize(rmailbx, emailbx)
  1313. @rmailbx = rmailbx
  1314. @emailbx = emailbx
  1315. end
  1316. attr_reader :rmailbx, :emailbx
  1317. def encode_rdata(msg)
  1318. msg.put_name(@rmailbx)
  1319. msg.put_name(@emailbx)
  1320. end
  1321. def self.decode_rdata(msg)
  1322. rmailbx = msg.get_string
  1323. emailbx = msg.get_string
  1324. return self.new(rmailbx, emailbx)
  1325. end
  1326. end
  1327. class MX < Resource
  1328. TypeValue= 15
  1329. def initialize(preference, exchange)
  1330. @preference = preference
  1331. @exchange = exchange
  1332. end
  1333. attr_reader :preference, :exchange
  1334. def encode_rdata(msg)
  1335. msg.put_pack('n', @preference)
  1336. msg.put_name(@exchange)
  1337. end
  1338. def self.decode_rdata(msg)
  1339. preference, = msg.get_unpack('n')
  1340. exchange = msg.get_name
  1341. return self.new(preference, exchange)
  1342. end
  1343. end
  1344. class TXT < Resource
  1345. TypeValue = 16
  1346. def initialize(first_string, *rest_strings)
  1347. @strings = [first_string, *rest_strings]
  1348. end
  1349. attr_reader :strings
  1350. def data
  1351. @strings[0]
  1352. end
  1353. def encode_rdata(msg)
  1354. msg.put_string_list(@strings)
  1355. end
  1356. def self.decode_rdata(msg)
  1357. strings = msg.get_string_list
  1358. return self.new(*strings)
  1359. end
  1360. end
  1361. class ANY < Query
  1362. TypeValue = 255
  1363. end
  1364. ClassInsensitiveTypes = [
  1365. NS, CNAME, SOA, PTR, HINFO, MINFO, MX, TXT, ANY
  1366. ]
  1367. # ARPA Internet specific RRs
  1368. module IN
  1369. ClassValue = 1
  1370. ClassInsensitiveTypes.each {|s|
  1371. c = Class.new(s)
  1372. c.const_set(:TypeValue, s::TypeValue)
  1373. c.const_set(:ClassValue, ClassValue)
  1374. ClassHash[[s::TypeValue, ClassValue]] = c
  1375. self.const_set(s.name.sub(/.*::/, ''), c)
  1376. }
  1377. class A < Resource
  1378. ClassHash[[TypeValue = 1, ClassValue = ClassValue]] = self
  1379. def initialize(address)
  1380. @address = IPv4.create(address)
  1381. end
  1382. attr_reader :address
  1383. def encode_rdata(msg)
  1384. msg.put_bytes(@address.address)
  1385. end
  1386. def self.decode_rdata(msg)
  1387. return self.new(IPv4.new(msg.get_bytes(4)))
  1388. end
  1389. end
  1390. class WKS < Resource
  1391. ClassHash[[TypeValue = 11, ClassValue = ClassValue]] = self
  1392. def initialize(address, protocol, bitmap)
  1393. @address = IPv4.create(address)
  1394. @protocol = protocol
  1395. @bitmap = bitmap
  1396. end
  1397. attr_reader :address, :protocol, :bitmap
  1398. def encode_rdata(msg)
  1399. msg.put_bytes(@address.address)
  1400. msg.put_pack("n", @protocol)
  1401. msg.put_bytes(@bitmap)
  1402. end
  1403. def self.decode_rdata(msg)
  1404. address = IPv4.new(msg.get_bytes(4))
  1405. protocol, = msg.get_unpack("n")
  1406. bitmap = msg.get_bytes
  1407. return self.new(address, protocol, bitmap)
  1408. end
  1409. end
  1410. class AAAA < Resource
  1411. ClassHash[[TypeValue = 28, ClassValue = ClassValue]] = self
  1412. def initialize(address)
  1413. @address = IPv6.create(address)
  1414. end
  1415. attr_reader :address
  1416. def encode_rdata(msg)
  1417. msg.put_bytes(@address.address)
  1418. end
  1419. def self.decode_rdata(msg)
  1420. return self.new(IPv6.new(msg.get_bytes(16)))
  1421. end
  1422. end
  1423. # SRV resource record defined in RFC 2782
  1424. #
  1425. # These records identify the hostname and port that a service is
  1426. # available at.
  1427. #
  1428. # The format is:
  1429. # _Service._Proto.Name TTL Class SRV Priority Weight Port Target
  1430. #
  1431. # The fields specific to SRV are defined in RFC 2782 as meaning:
  1432. # - +priority+ The priority of this target host. A client MUST attempt
  1433. # to contact the target host with the lowest-numbered priority it can
  1434. # reach; target hosts with the same priority SHOULD be tried in an
  1435. # order defined by the weight field. The range is 0-65535. Note that
  1436. # it is not widely implemented and should be set to zero.
  1437. #
  1438. # - +weight+ A server selection mechanism. The weight field specifies
  1439. # a relative weight for entries with the same priority. Larger weights
  1440. # SHOULD be given a proportionately higher probability of being
  1441. # selected. The range of this number is 0-65535. Domain administrators
  1442. # SHOULD use Weight 0 when there isn't any server selection to do, to
  1443. # make the RR easier to read for humans (less noisy). Note that it is
  1444. # not widely implemented and should be set to zero.
  1445. #
  1446. # - +port+ The port on this target host of this service. The range is 0-
  1447. # 65535.
  1448. #
  1449. # - +target+ The domain name of the target host. A target of "." means
  1450. # that the service is decidedly not available at this domain.
  1451. class SRV < Resource
  1452. ClassHash[[TypeValue = 33, ClassValue = ClassValue]] = self
  1453. # Create a SRV resource record.
  1454. def initialize(priority, weight, port, target)
  1455. @priority = priority.to_int
  1456. @weight = weight.to_int
  1457. @port = port.to_int
  1458. @target = Name.create(target)
  1459. end
  1460. attr_reader :priority, :weight, :port, :target
  1461. def encode_rdata(msg)
  1462. msg.put_pack("n", @priority)
  1463. msg.put_pack("n", @weight)
  1464. msg.put_pack("n", @port)
  1465. msg.put_name(@target)
  1466. end
  1467. def self.decode_rdata(msg)
  1468. priority, = msg.get_unpack("n")
  1469. weight, = msg.get_unpack("n")
  1470. port, = msg.get_unpack("n")
  1471. target = msg.get_name
  1472. return self.new(priority, weight, port, target)
  1473. end
  1474. end
  1475. end
  1476. end
  1477. end
  1478. class IPv4
  1479. Regex = /\A(\d+)\.(\d+)\.(\d+)\.(\d+)\z/
  1480. def self.create(arg)
  1481. case arg
  1482. when IPv4
  1483. return arg
  1484. when Regex
  1485. if (0..255) === (a = $1.to_i) &&
  1486. (0..255) === (b = $2.to_i) &&
  1487. (0..255) === (c = $3.to_i) &&
  1488. (0..255) === (d = $4.to_i)
  1489. return self.new([a, b, c, d].pack("CCCC"))
  1490. else
  1491. raise ArgumentError.new("IPv4 address with invalid value: " + arg)
  1492. end
  1493. else
  1494. raise ArgumentError.new("cannot interpret as IPv4 address: #{arg.inspect}")
  1495. end
  1496. end
  1497. def initialize(address)
  1498. unless address.kind_of?(String) && address.length == 4
  1499. raise ArgumentError.new('IPv4 address must be 4 bytes')
  1500. end
  1501. @address = address
  1502. end
  1503. attr_reader :address
  1504. def to_s
  1505. return sprintf("%d.%d.%d.%d", *@address.unpack("CCCC"))
  1506. end
  1507. def inspect
  1508. return "#<#{self.class} #{self.to_s}>"
  1509. end
  1510. def to_name
  1511. return DNS::Name.create(
  1512. '%d.%d.%d.%d.in-addr.arpa.' % @address.unpack('CCCC').reverse)
  1513. end
  1514. def ==(other)
  1515. return @address == other.address
  1516. end
  1517. def eql?(other)
  1518. return self == other
  1519. end
  1520. def hash
  1521. return @address.hash
  1522. end
  1523. end
  1524. class IPv6
  1525. Regex_8Hex = /\A
  1526. (?:[0-9A-Fa-f]{1,4}:){7}
  1527. [0-9A-Fa-f]{1,4}
  1528. \z/x
  1529. Regex_CompressedHex = /\A
  1530. ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::
  1531. ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)
  1532. \z/x
  1533. Regex_6Hex4Dec = /\A
  1534. ((?:[0-9A-Fa-f]{1,4}:){6,6})
  1535. (\d+)\.(\d+)\.(\d+)\.(\d+)
  1536. \z/x
  1537. Regex_CompressedHex4Dec = /\A
  1538. ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::
  1539. ((?:[0-9A-Fa-f]{1,4}:)*)
  1540. (\d+)\.(\d+)\.(\d+)\.(\d+)
  1541. \z/x
  1542. Regex = /
  1543. (?:#{Regex_8Hex}) |
  1544. (?:#{Regex_CompressedHex}) |
  1545. (?:#{Regex_6Hex4Dec}) |
  1546. (?:#{Regex_CompressedHex4Dec})/x
  1547. def self.create(arg)
  1548. case arg
  1549. when IPv6
  1550. return arg
  1551. when String
  1552. address = ''
  1553. if Regex_8Hex =~ arg
  1554. arg.scan(/[0-9A-Fa-f]+/) {|hex| address << [hex.hex].pack('n')}
  1555. elsif Regex_CompressedHex =~ arg
  1556. prefix = $1
  1557. suffix = $2
  1558. a1 = ''
  1559. a2 = ''
  1560. prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')}
  1561. suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')}
  1562. omitlen = 16 - a1.length - a2.length
  1563. address << a1 << "\0" * omitlen << a2
  1564. elsif Regex_6Hex4Dec =~ arg
  1565. prefix, a, b, c, d = $1, $2.to_i, $3.to_i, $4.to_i, $5.to_i
  1566. if (0..255) === a && (0..255) === b && (0..255) === c && (0..255) === d
  1567. prefix.scan(/[0-9A-Fa-f]+/) {|hex| address << [hex.hex].pack('n')}
  1568. address << [a, b, c, d].pack('CCCC')
  1569. else
  1570. raise ArgumentError.new("not numeric IPv6 address: " + arg)
  1571. end
  1572. elsif Regex_CompressedHex4Dec =~ arg
  1573. prefix, suffix, a, b, c, d = $1, $2, $3.to_i, $4.to_i, $5.to_i, $6.to_i
  1574. if (0..255) === a && (0..255) === b && (0..255) === c && (0..255) === d
  1575. a1 = ''
  1576. a2 = ''
  1577. prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')}
  1578. suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')}
  1579. omitlen = 12 - a1.length - a2.length
  1580. address << a1 << "\0" * omitlen << a2 << [a, b, c, d].pack('CCCC')
  1581. else
  1582. raise ArgumentError.new("not numeric IPv6 address: " + arg)
  1583. end
  1584. else
  1585. raise ArgumentError.new("not numeric IPv6 address: " + arg)
  1586. end
  1587. return IPv6.new(address)
  1588. else
  1589. raise ArgumentError.new("cannot interpret as IPv6 address: #{arg.inspect}")
  1590. end
  1591. end
  1592. def initialize(address)
  1593. unless address.kind_of?(String) && address.length == 16
  1594. raise ArgumentError.new('IPv6 address must be 16 bytes')
  1595. end
  1596. @address = address
  1597. end
  1598. attr_reader :address
  1599. def to_s
  1600. address = sprintf("%X:%X:%X:%X:%X:%X:%X:%X", *@address.unpack("nnnnnnnn"))
  1601. unless address.sub!(/(^|:)0(:0)+(:|$)/, '::')
  1602. address.sub!(/(^|:)0(:|$)/, '::')
  1603. end
  1604. return address
  1605. end
  1606. def inspect
  1607. return "#<#{self.class} #{self.to_s}>"
  1608. end
  1609. def to_name
  1610. # ip6.arpa should be searched too. [RFC3152]
  1611. return DNS::Name.new(
  1612. @address.unpack("H32")[0].split(//).reverse + ['ip6', 'int'])
  1613. end
  1614. def ==(other)
  1615. return @address == other.address
  1616. end
  1617. def eql?(other)
  1618. return self == other
  1619. end
  1620. def hash
  1621. return @address.hash
  1622. en

Large files files are truncated, but you can click here to view the full file