PageRenderTime 54ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/resolv.rb

https://github.com/wanabe/ruby
Ruby | 2910 lines | 1948 code | 464 blank | 498 comment | 137 complexity | ed2b47764cec1f71db28cb80823bc778 MD5 | raw file
Possible License(s): LGPL-2.1, AGPL-3.0, 0BSD, Unlicense, GPL-2.0, BSD-3-Clause

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

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

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