PageRenderTime 71ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/resolv.rb

http://github.com/ruby/ruby
Ruby | 2884 lines | 1929 code | 461 blank | 494 comment | 136 complexity | a50c3844fe9437602508ef27a477b3c6 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, AGPL-3.0

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 s = sender_for(from, msg)
  605. break
  606. else
  607. # unexpected DNS message ignored
  608. end
  609. end
  610. return msg, s.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. lazy_initialize
  669. sock = @socks_hash[host.index(':') ? "::" : "0.0.0.0"]
  670. return nil if !sock
  671. service = [host, port]
  672. id = DNS.allocate_request_id(host, port)
  673. request = msg.encode
  674. request[0,2] = [id].pack('n')
  675. return @senders[[service, id]] =
  676. Sender.new(request, data, sock, host, port)
  677. end
  678. def close
  679. @mutex.synchronize {
  680. if @initialized
  681. super
  682. @senders.each_key {|service, id|
  683. DNS.free_request_id(service[0], service[1], id)
  684. }
  685. @initialized = false
  686. end
  687. }
  688. end
  689. class Sender < Requester::Sender # :nodoc:
  690. def initialize(msg, data, sock, host, port)
  691. super(msg, data, sock)
  692. @host = host
  693. @port = port
  694. end
  695. attr_reader :data
  696. def send
  697. raise "@sock is nil." if @sock.nil?
  698. @sock.send(@msg, 0, @host, @port)
  699. end
  700. end
  701. end
  702. class ConnectedUDP < Requester # :nodoc:
  703. def initialize(host, port=Port)
  704. super()
  705. @host = host
  706. @port = port
  707. @mutex = Thread::Mutex.new
  708. @initialized = false
  709. end
  710. def lazy_initialize
  711. @mutex.synchronize {
  712. next if @initialized
  713. @initialized = true
  714. is_ipv6 = @host.index(':')
  715. sock = UDPSocket.new(is_ipv6 ? Socket::AF_INET6 : Socket::AF_INET)
  716. @socks = [sock]
  717. sock.do_not_reverse_lookup = true
  718. DNS.bind_random_port(sock, is_ipv6 ? "::" : "0.0.0.0")
  719. sock.connect(@host, @port)
  720. }
  721. self
  722. end
  723. def recv_reply(readable_socks)
  724. lazy_initialize
  725. reply = readable_socks[0].recv(UDPSize)
  726. return reply, nil
  727. end
  728. def sender(msg, data, host=@host, port=@port)
  729. lazy_initialize
  730. unless host == @host && port == @port
  731. raise RequestError.new("host/port don't match: #{host}:#{port}")
  732. end
  733. id = DNS.allocate_request_id(@host, @port)
  734. request = msg.encode
  735. request[0,2] = [id].pack('n')
  736. return @senders[[nil,id]] = Sender.new(request, data, @socks[0])
  737. end
  738. def close
  739. @mutex.synchronize do
  740. if @initialized
  741. super
  742. @senders.each_key {|from, id|
  743. DNS.free_request_id(@host, @port, id)
  744. }
  745. @initialized = false
  746. end
  747. end
  748. end
  749. class Sender < Requester::Sender # :nodoc:
  750. def send
  751. raise "@sock is nil." if @sock.nil?
  752. @sock.send(@msg, 0)
  753. end
  754. attr_reader :data
  755. end
  756. end
  757. class MDNSOneShot < UnconnectedUDP # :nodoc:
  758. def sender(msg, data, host, port=Port)
  759. lazy_initialize
  760. id = DNS.allocate_request_id(host, port)
  761. request = msg.encode
  762. request[0,2] = [id].pack('n')
  763. sock = @socks_hash[host.index(':') ? "::" : "0.0.0.0"]
  764. return @senders[id] =
  765. UnconnectedUDP::Sender.new(request, data, sock, host, port)
  766. end
  767. def sender_for(addr, msg)
  768. lazy_initialize
  769. @senders[msg.id]
  770. end
  771. end
  772. class TCP < Requester # :nodoc:
  773. def initialize(host, port=Port)
  774. super()
  775. @host = host
  776. @port = port
  777. sock = TCPSocket.new(@host, @port)
  778. @socks = [sock]
  779. @senders = {}
  780. end
  781. def recv_reply(readable_socks)
  782. len = readable_socks[0].read(2).unpack('n')[0]
  783. reply = @socks[0].read(len)
  784. return reply, nil
  785. end
  786. def sender(msg, data, host=@host, port=@port)
  787. unless host == @host && port == @port
  788. raise RequestError.new("host/port don't match: #{host}:#{port}")
  789. end
  790. id = DNS.allocate_request_id(@host, @port)
  791. request = msg.encode
  792. request[0,2] = [request.length, id].pack('nn')
  793. return @senders[[nil,id]] = Sender.new(request, data, @socks[0])
  794. end
  795. class Sender < Requester::Sender # :nodoc:
  796. def send
  797. @sock.print(@msg)
  798. @sock.flush
  799. end
  800. attr_reader :data
  801. end
  802. def close
  803. super
  804. @senders.each_key {|from,id|
  805. DNS.free_request_id(@host, @port, id)
  806. }
  807. end
  808. end
  809. ##
  810. # Indicates a problem with the DNS request.
  811. class RequestError < StandardError
  812. end
  813. end
  814. class Config # :nodoc:
  815. def initialize(config_info=nil)
  816. @mutex = Thread::Mutex.new
  817. @config_info = config_info
  818. @initialized = nil
  819. @timeouts = nil
  820. end
  821. def timeouts=(values)
  822. if values
  823. values = Array(values)
  824. values.each do |t|
  825. Numeric === t or raise ArgumentError, "#{t.inspect} is not numeric"
  826. t > 0.0 or raise ArgumentError, "timeout=#{t} must be positive"
  827. end
  828. @timeouts = values
  829. else
  830. @timeouts = nil
  831. end
  832. end
  833. def Config.parse_resolv_conf(filename)
  834. nameserver = []
  835. search = nil
  836. ndots = 1
  837. File.open(filename, 'rb') {|f|
  838. f.each {|line|
  839. line.sub!(/[#;].*/, '')
  840. keyword, *args = line.split(/\s+/)
  841. next unless keyword
  842. case keyword
  843. when 'nameserver'
  844. nameserver += args
  845. when 'domain'
  846. next if args.empty?
  847. search = [args[0]]
  848. when 'search'
  849. next if args.empty?
  850. search = args
  851. when 'options'
  852. args.each {|arg|
  853. case arg
  854. when /\Andots:(\d+)\z/
  855. ndots = $1.to_i
  856. end
  857. }
  858. end
  859. }
  860. }
  861. return { :nameserver => nameserver, :search => search, :ndots => ndots }
  862. end
  863. def Config.default_config_hash(filename="/etc/resolv.conf")
  864. if File.exist? filename
  865. config_hash = Config.parse_resolv_conf(filename)
  866. else
  867. if /mswin|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM
  868. require 'win32/resolv'
  869. search, nameserver = Win32::Resolv.get_resolv_info
  870. config_hash = {}
  871. config_hash[:nameserver] = nameserver if nameserver
  872. config_hash[:search] = [search].flatten if search
  873. end
  874. end
  875. config_hash || {}
  876. end
  877. def lazy_initialize
  878. @mutex.synchronize {
  879. unless @initialized
  880. @nameserver_port = []
  881. @search = nil
  882. @ndots = 1
  883. case @config_info
  884. when nil
  885. config_hash = Config.default_config_hash
  886. when String
  887. config_hash = Config.parse_resolv_conf(@config_info)
  888. when Hash
  889. config_hash = @config_info.dup
  890. if String === config_hash[:nameserver]
  891. config_hash[:nameserver] = [config_hash[:nameserver]]
  892. end
  893. if String === config_hash[:search]
  894. config_hash[:search] = [config_hash[:search]]
  895. end
  896. else
  897. raise ArgumentError.new("invalid resolv configuration: #{@config_info.inspect}")
  898. end
  899. if config_hash.include? :nameserver
  900. @nameserver_port = config_hash[:nameserver].map {|ns| [ns, Port] }
  901. end
  902. if config_hash.include? :nameserver_port
  903. @nameserver_port = config_hash[:nameserver_port].map {|ns, port| [ns, (port || Port)] }
  904. end
  905. @search = config_hash[:search] if config_hash.include? :search
  906. @ndots = config_hash[:ndots] if config_hash.include? :ndots
  907. if @nameserver_port.empty?
  908. @nameserver_port << ['0.0.0.0', Port]
  909. end
  910. if @search
  911. @search = @search.map {|arg| Label.split(arg) }
  912. else
  913. hostname = Socket.gethostname
  914. if /\./ =~ hostname
  915. @search = [Label.split($')]
  916. else
  917. @search = [[]]
  918. end
  919. end
  920. if !@nameserver_port.kind_of?(Array) ||
  921. @nameserver_port.any? {|ns_port|
  922. !(Array === ns_port) ||
  923. ns_port.length != 2
  924. !(String === ns_port[0]) ||
  925. !(Integer === ns_port[1])
  926. }
  927. raise ArgumentError.new("invalid nameserver config: #{@nameserver_port.inspect}")
  928. end
  929. if !@search.kind_of?(Array) ||
  930. !@search.all? {|ls| ls.all? {|l| Label::Str === l } }
  931. raise ArgumentError.new("invalid search config: #{@search.inspect}")
  932. end
  933. if !@ndots.kind_of?(Integer)
  934. raise ArgumentError.new("invalid ndots config: #{@ndots.inspect}")
  935. end
  936. @initialized = true
  937. end
  938. }
  939. self
  940. end
  941. def single?
  942. lazy_initialize
  943. if @nameserver_port.length == 1
  944. return @nameserver_port[0]
  945. else
  946. return nil
  947. end
  948. end
  949. def nameserver_port
  950. @nameserver_port
  951. end
  952. def generate_candidates(name)
  953. candidates = nil
  954. name = Name.create(name)
  955. if name.absolute?
  956. candidates = [name]
  957. else
  958. if @ndots <= name.length - 1
  959. candidates = [Name.new(name.to_a)]
  960. else
  961. candidates = []
  962. end
  963. candidates.concat(@search.map {|domain| Name.new(name.to_a + domain)})
  964. fname = Name.create("#{name}.")
  965. if !candidates.include?(fname)
  966. candidates << fname
  967. end
  968. end
  969. return candidates
  970. end
  971. InitialTimeout = 5
  972. def generate_timeouts
  973. ts = [InitialTimeout]
  974. ts << ts[-1] * 2 / @nameserver_port.length
  975. ts << ts[-1] * 2
  976. ts << ts[-1] * 2
  977. return ts
  978. end
  979. def resolv(name)
  980. candidates = generate_candidates(name)
  981. timeouts = @timeouts || generate_timeouts
  982. begin
  983. candidates.each {|candidate|
  984. begin
  985. timeouts.each {|tout|
  986. @nameserver_port.each {|nameserver, port|
  987. begin
  988. yield candidate, tout, nameserver, port
  989. rescue ResolvTimeout
  990. end
  991. }
  992. }
  993. raise ResolvError.new("DNS resolv timeout: #{name}")
  994. rescue NXDomain
  995. end
  996. }
  997. rescue ResolvError
  998. end
  999. end
  1000. ##
  1001. # Indicates no such domain was found.
  1002. class NXDomain < ResolvError
  1003. end
  1004. ##
  1005. # Indicates some other unhandled resolver error was encountered.
  1006. class OtherResolvError < ResolvError
  1007. end
  1008. end
  1009. module OpCode # :nodoc:
  1010. Query = 0
  1011. IQuery = 1
  1012. Status = 2
  1013. Notify = 4
  1014. Update = 5
  1015. end
  1016. module RCode # :nodoc:
  1017. NoError = 0
  1018. FormErr = 1
  1019. ServFail = 2
  1020. NXDomain = 3
  1021. NotImp = 4
  1022. Refused = 5
  1023. YXDomain = 6
  1024. YXRRSet = 7
  1025. NXRRSet = 8
  1026. NotAuth = 9
  1027. NotZone = 10
  1028. BADVERS = 16
  1029. BADSIG = 16
  1030. BADKEY = 17
  1031. BADTIME = 18
  1032. BADMODE = 19
  1033. BADNAME = 20
  1034. BADALG = 21
  1035. end
  1036. ##
  1037. # Indicates that the DNS response was unable to be decoded.
  1038. class DecodeError < StandardError
  1039. end
  1040. ##
  1041. # Indicates that the DNS request was unable to be encoded.
  1042. class EncodeError < StandardError
  1043. end
  1044. module Label # :nodoc:
  1045. def self.split(arg)
  1046. labels = []
  1047. arg.scan(/[^\.]+/) {labels << Str.new($&)}
  1048. return labels
  1049. end
  1050. class Str # :nodoc:
  1051. def initialize(string)
  1052. @string = string
  1053. # case insensivity of DNS labels doesn't apply non-ASCII characters. [RFC 4343]
  1054. # This assumes @string is given in ASCII compatible encoding.
  1055. @downcase = string.b.downcase
  1056. end
  1057. attr_reader :string, :downcase
  1058. def to_s
  1059. return @string
  1060. end
  1061. def inspect
  1062. return "#<#{self.class} #{self}>"
  1063. end
  1064. def ==(other)
  1065. return self.class == other.class && @downcase == other.downcase
  1066. end
  1067. def eql?(other)
  1068. return self == other
  1069. end
  1070. def hash
  1071. return @downcase.hash
  1072. end
  1073. end
  1074. end
  1075. ##
  1076. # A representation of a DNS name.
  1077. class Name
  1078. ##
  1079. # Creates a new DNS name from +arg+. +arg+ can be:
  1080. #
  1081. # Name:: returns +arg+.
  1082. # String:: Creates a new Name.
  1083. def self.create(arg)
  1084. case arg
  1085. when Name
  1086. return arg
  1087. when String
  1088. return Name.new(Label.split(arg), /\.\z/ =~ arg ? true : false)
  1089. else
  1090. raise ArgumentError.new("cannot interpret as DNS name: #{arg.inspect}")
  1091. end
  1092. end
  1093. def initialize(labels, absolute=true) # :nodoc:
  1094. labels = labels.map {|label|
  1095. case label
  1096. when String then Label::Str.new(label)
  1097. when Label::Str then label
  1098. else
  1099. raise ArgumentError, "unexpected label: #{label.inspect}"
  1100. end
  1101. }
  1102. @labels = labels
  1103. @absolute = absolute
  1104. end
  1105. def inspect # :nodoc:
  1106. "#<#{self.class}: #{self}#{@absolute ? '.' : ''}>"
  1107. end
  1108. ##
  1109. # True if this name is absolute.
  1110. def absolute?
  1111. return @absolute
  1112. end
  1113. def ==(other) # :nodoc:
  1114. return false unless Name === other
  1115. return false unless @absolute == other.absolute?
  1116. return @labels == other.to_a
  1117. end
  1118. alias eql? == # :nodoc:
  1119. ##
  1120. # Returns true if +other+ is a subdomain.
  1121. #
  1122. # Example:
  1123. #
  1124. # domain = Resolv::DNS::Name.create("y.z")
  1125. # p Resolv::DNS::Name.create("w.x.y.z").subdomain_of?(domain) #=> true
  1126. # p Resolv::DNS::Name.create("x.y.z").subdomain_of?(domain) #=> true
  1127. # p Resolv::DNS::Name.create("y.z").subdomain_of?(domain) #=> false
  1128. # p Resolv::DNS::Name.create("z").subdomain_of?(domain) #=> false
  1129. # p Resolv::DNS::Name.create("x.y.z.").subdomain_of?(domain) #=> false
  1130. # p Resolv::DNS::Name.create("w.z").subdomain_of?(domain) #=> false
  1131. #
  1132. def subdomain_of?(other)
  1133. raise ArgumentError, "not a domain name: #{other.inspect}" unless Name === other
  1134. return false if @absolute != other.absolute?
  1135. other_len = other.length
  1136. return false if @labels.length <= other_len
  1137. return @labels[-other_len, other_len] == other.to_a
  1138. end
  1139. def hash # :nodoc:
  1140. return @labels.hash ^ @absolute.hash
  1141. end
  1142. def to_a # :nodoc:
  1143. return @labels
  1144. end
  1145. def length # :nodoc:
  1146. return @labels.length
  1147. end
  1148. def [](i) # :nodoc:
  1149. return @labels[i]
  1150. end
  1151. ##
  1152. # returns the domain name as a string.
  1153. #
  1154. # The domain name doesn't have a trailing dot even if the name object is
  1155. # absolute.
  1156. #
  1157. # Example:
  1158. #
  1159. # p Resolv::DNS::Name.create("x.y.z.").to_s #=> "x.y.z"
  1160. # p Resolv::DNS::Name.create("x.y.z").to_s #=> "x.y.z"
  1161. def to_s
  1162. return @labels.join('.')
  1163. end
  1164. end
  1165. class Message # :nodoc:
  1166. @@identifier = -1
  1167. def initialize(id = (@@identifier += 1) & 0xffff)
  1168. @id = id
  1169. @qr = 0
  1170. @opcode = 0
  1171. @aa = 0
  1172. @tc = 0
  1173. @rd = 0 # recursion desired
  1174. @ra = 0 # recursion available
  1175. @rcode = 0
  1176. @question = []
  1177. @answer = []
  1178. @authority = []
  1179. @additional = []
  1180. end
  1181. attr_accessor :id, :qr, :opcode, :aa, :tc, :rd, :ra, :rcode
  1182. attr_reader :question, :answer, :authority, :additional
  1183. def ==(other)
  1184. return @id == other.id &&
  1185. @qr == other.qr &&
  1186. @opcode == other.opcode &&
  1187. @aa == other.aa &&
  1188. @tc == other.tc &&
  1189. @rd == other.rd &&
  1190. @ra == other.ra &&
  1191. @rcode == other.rcode &&
  1192. @question == other.question &&
  1193. @answer == other.answer &&
  1194. @authority == other.authority &&
  1195. @additional == other.additional
  1196. end
  1197. def add_question(name, typeclass)
  1198. @question << [Name.create(name), typeclass]
  1199. end
  1200. def each_question
  1201. @question.each {|name, typeclass|
  1202. yield name, typeclass
  1203. }
  1204. end
  1205. def add_answer(name, ttl, data)
  1206. @answer << [Name.create(name), ttl, data]
  1207. end
  1208. def each_answer
  1209. @answer.each {|name, ttl, data|
  1210. yield name, ttl, data
  1211. }
  1212. end
  1213. def add_authority(name, ttl, data)
  1214. @authority << [Name.create(name), ttl, data]
  1215. end
  1216. def each_authority
  1217. @authority.each {|name, ttl, data|
  1218. yield name, ttl, data
  1219. }
  1220. end
  1221. def add_additional(name, ttl, data)
  1222. @additional << [Name.create(name), ttl, data]
  1223. end
  1224. def each_additional
  1225. @additional.each {|name, ttl, data|
  1226. yield name, ttl, data
  1227. }
  1228. end
  1229. def each_resource
  1230. each_answer {|name, ttl, data| yield name, ttl, data}
  1231. each_authority {|name, ttl, data| yield name, ttl, data}
  1232. each_additional {|name, ttl, data| yield name, ttl, data}
  1233. end
  1234. def encode
  1235. return MessageEncoder.new {|msg|
  1236. msg.put_pack('nnnnnn',
  1237. @id,
  1238. (@qr & 1) << 15 |
  1239. (@opcode & 15) << 11 |
  1240. (@aa & 1) << 10 |
  1241. (@tc & 1) << 9 |
  1242. (@rd & 1) << 8 |
  1243. (@ra & 1) << 7 |
  1244. (@rcode & 15),
  1245. @question.length,
  1246. @answer.length,
  1247. @authority.length,
  1248. @additional.length)
  1249. @question.each {|q|
  1250. name, typeclass = q
  1251. msg.put_name(name)
  1252. msg.put_pack('nn', typeclass::TypeValue, typeclass::ClassValue)
  1253. }
  1254. [@answer, @authority, @additional].each {|rr|
  1255. rr.each {|r|
  1256. name, ttl, data = r
  1257. msg.put_name(name)
  1258. msg.put_pack('nnN', data.class::TypeValue, data.class::ClassValue, ttl)
  1259. msg.put_length16 {data.encode_rdata(msg)}
  1260. }
  1261. }
  1262. }.to_s
  1263. end
  1264. class MessageEncoder # :nodoc:
  1265. def initialize
  1266. @data = ''.dup
  1267. @names = {}
  1268. yield self
  1269. end
  1270. def to_s
  1271. return @data
  1272. end
  1273. def put_bytes(d)
  1274. @data << d
  1275. end
  1276. def put_pack(template, *d)
  1277. @data << d.pack(template)
  1278. end
  1279. def put_length16
  1280. length_index = @data.length
  1281. @data << "\0\0"
  1282. data_start = @data.length
  1283. yield
  1284. data_end = @data.length
  1285. @data[length_index, 2] = [data_end - data_start].pack("n")
  1286. end
  1287. def put_string(d)
  1288. self.put_pack("C", d.length)
  1289. @data << d
  1290. end
  1291. def put_string_list(ds)
  1292. ds.each {|d|
  1293. self.put_string(d)
  1294. }
  1295. end
  1296. def put_name(d)
  1297. put_labels(d.to_a)
  1298. end
  1299. def put_labels(d)
  1300. d.each_index {|i|
  1301. domain = d[i..-1]
  1302. if idx = @names[domain]
  1303. self.put_pack("n", 0xc000 | idx)
  1304. return
  1305. else
  1306. if @data.length < 0x4000
  1307. @names[domain] = @data.length
  1308. end
  1309. self.put_label(d[i])
  1310. end
  1311. }
  1312. @data << "\0"
  1313. end
  1314. def put_label(d)
  1315. self.put_string(d.to_s)
  1316. end
  1317. end
  1318. def Message.decode(m)
  1319. o = Message.new(0)
  1320. MessageDecoder.new(m) {|msg|
  1321. id, flag, qdcount, ancount, nscount, arcount =
  1322. msg.get_unpack('nnnnnn')
  1323. o.id = id
  1324. o.qr = (flag >> 15) & 1
  1325. o.opcode = (flag >> 11) & 15
  1326. o.aa = (flag >> 10) & 1
  1327. o.tc = (flag >> 9) & 1
  1328. o.rd = (flag >> 8) & 1
  1329. o.ra = (flag >> 7) & 1
  1330. o.rcode = flag & 15
  1331. (1..qdcount).each {
  1332. name, typeclass = msg.get_question
  1333. o.add_question(name, typeclass)
  1334. }
  1335. (1..ancount).each {
  1336. name, ttl, data = msg.get_rr
  1337. o.add_answer(name, ttl, data)
  1338. }
  1339. (1..nscount).each {
  1340. name, ttl, data = msg.get_rr
  1341. o.add_authority(name, ttl, data)
  1342. }
  1343. (1..arcount).each {
  1344. name, ttl, data = msg.get_rr
  1345. o.add_additional(name, ttl, data)
  1346. }
  1347. }
  1348. return o
  1349. end
  1350. class MessageDecoder # :nodoc:
  1351. def initialize(data)
  1352. @data = data
  1353. @index = 0
  1354. @limit = data.bytesize
  1355. yield self
  1356. end
  1357. def inspect
  1358. "\#<#{self.class}: #{@data.byteslice(0, @index).inspect} #{@data.byteslice(@index..-1).inspect}>"
  1359. end
  1360. def get_length16
  1361. len, = self.get_unpack('n')
  1362. save_limit = @limit
  1363. @limit = @index + len
  1364. d = yield(len)
  1365. if @index < @limit
  1366. raise DecodeError.new("junk exists")
  1367. elsif @limit < @index
  1368. raise DecodeError.new("limit exceeded")
  1369. end
  1370. @limit = save_limit
  1371. return d
  1372. end
  1373. def get_bytes(len = @limit - @index)
  1374. raise DecodeError.new("limit exceeded") if @limit < @index + len
  1375. d = @data.byteslice(@index, len)
  1376. @index += len
  1377. return d
  1378. end
  1379. def get_unpack(template)
  1380. len = 0
  1381. template.each_byte {|byte|
  1382. byte = "%c" % byte
  1383. case byte
  1384. when ?c, ?C
  1385. len += 1
  1386. when ?n
  1387. len += 2
  1388. when ?N
  1389. len += 4
  1390. else
  1391. raise StandardError.new("unsupported template: '#{byte.chr}' in '#{template}'")
  1392. end
  1393. }
  1394. raise DecodeError.new("limit exceeded") if @limit < @index + len
  1395. arr = @data.unpack("@#{@index}#{template}")
  1396. @index += len
  1397. return arr
  1398. end
  1399. def get_string
  1400. raise DecodeError.new("limit exceeded") if @limit <= @index
  1401. len = @data.getbyte(@index)
  1402. raise DecodeError.new("limit exceeded") if @limit < @index + 1 + len
  1403. d = @data.byteslice(@index + 1, len)
  1404. @index += 1 + len
  1405. return d
  1406. end
  1407. def get_string_list
  1408. strings = []
  1409. while @index < @limit
  1410. strings << self.get_string
  1411. end
  1412. strings
  1413. end
  1414. def get_name
  1415. return Name.new(self.get_labels)
  1416. end
  1417. def get_labels
  1418. prev_index = @index
  1419. save_index = nil
  1420. d = []
  1421. while true
  1422. raise DecodeError.new("limit exceeded") if @limit <= @index
  1423. case @data.getbyte(@index)
  1424. when 0
  1425. @index += 1
  1426. if save_index
  1427. @index = save_index
  1428. end
  1429. return d
  1430. when 192..255
  1431. idx = self.get_unpack('n')[0] & 0x3fff
  1432. if prev_index <= idx
  1433. raise DecodeError.new("non-backward name pointer")
  1434. end
  1435. prev_index = idx
  1436. if !save_index
  1437. save_index = @index
  1438. end
  1439. @index = idx
  1440. else
  1441. d << self.get_label
  1442. end
  1443. end
  1444. end
  1445. def get_label
  1446. return Label::Str.new(self.get_string)
  1447. end
  1448. def get_question
  1449. name = self.get_name
  1450. type, klass = self.get_unpack("nn")
  1451. return name, Resource.get_class(type, klass)
  1452. end
  1453. def get_rr
  1454. name = self.get_name
  1455. type, klass, ttl = self.get_unpack('nnN')
  1456. typeclass = Resource.get_class(type, klass)
  1457. res = self.get_length16 do
  1458. begin
  1459. typeclass.decode_rdata self
  1460. rescue => e
  1461. raise DecodeError, e.message, e.backtrace
  1462. end
  1463. end
  1464. res.instance_variable_set :@ttl, ttl
  1465. return name, ttl, res
  1466. end
  1467. end
  1468. end
  1469. ##
  1470. # A DNS query abstract class.
  1471. class Query
  1472. def encode_rdata(msg) # :nodoc:
  1473. raise EncodeError.new("#{self.class} is query.")
  1474. end
  1475. def self.decode_rdata(msg) # :nodoc:
  1476. raise DecodeError.new("#{self.class} is query.")
  1477. end
  1478. end
  1479. ##
  1480. # A DNS resource abstract class.
  1481. class Resource < Query
  1482. ##
  1483. # Remaining Time To Live for this Resource.
  1484. attr_reader :ttl
  1485. ClassHash = {} # :nodoc:
  1486. def encode_rdata(msg) # :nodoc:
  1487. raise NotImplementedError.new
  1488. end
  1489. def self.decode_rdata(msg) # :nodoc:
  1490. raise NotImplementedError.new
  1491. end
  1492. def ==(other) # :nodoc:
  1493. return false unless self.class == other.class
  1494. s_ivars = self.instance_variables
  1495. s_ivars.sort!
  1496. s_ivars.delete :@ttl
  1497. o_ivars = other.instance_variables
  1498. o_ivars.sort!
  1499. o_ivars.delete :@ttl
  1500. return s_ivars == o_ivars &&
  1501. s_ivars.collect {|name| self.instance_variable_get name} ==
  1502. o_ivars.collect {|name| other.instance_variable_get name}
  1503. end
  1504. def eql?(other) # :nodoc:
  1505. return self == other
  1506. end
  1507. def hash # :nodoc:
  1508. h = 0
  1509. vars = self.instance_variables
  1510. vars.delete :@ttl
  1511. vars.each {|name|
  1512. h ^= self.instance_variable_get(name).hash
  1513. }
  1514. return h
  1515. end
  1516. def self.get_class(type_value, class_value) # :nodoc:
  1517. return ClassHash[[type_value, class_value]] ||
  1518. Generic.create(type_value, class_value)
  1519. end
  1520. ##
  1521. # A generic resource abstract class.
  1522. class Generic < Resource
  1523. ##
  1524. # Creates a new generic resource.
  1525. def initialize(data)
  1526. @data = data
  1527. end
  1528. ##
  1529. # Data for this generic resource.
  1530. attr_reader :data
  1531. def encode_rdata(msg) # :nodoc:
  1532. msg.put_bytes(data)
  1533. end
  1534. def self.decode_rdata(msg) # :nodoc:
  1535. return self.new(msg.get_bytes)
  1536. end
  1537. def self.create(type_value, class_value) # :nodoc:
  1538. c = Class.new(Generic)
  1539. c.const_set(:TypeValue, type_value)
  1540. c.const_set(:ClassValue, class_value)
  1541. Generic.const_set("Type#{type_value}_Class#{class_value}", c)
  1542. ClassHash[[type_value, class_value]] = c
  1543. return c
  1544. end
  1545. end
  1546. ##
  1547. # Domain Name resource abstract class.
  1548. class DomainName < Resource
  1549. ##
  1550. # Creates a new DomainName from +name+.
  1551. def initialize(name)
  1552. @name = name
  1553. end
  1554. ##
  1555. # The name of this DomainName.
  1556. attr_reader :name
  1557. def encode_rdata(msg) # :nodoc:
  1558. msg.put_name(@name)
  1559. end
  1560. def self.decode_rdata(msg) # :nodoc:
  1561. return self.new(msg.get_name)
  1562. end
  1563. end
  1564. # Standard (class generic) RRs
  1565. ClassValue = nil # :nodoc:
  1566. ##
  1567. # An authoritative name server.
  1568. class NS < DomainName
  1569. TypeValue = 2 # :nodoc:
  1570. end
  1571. ##
  1572. # The canonical name for an alias.
  1573. class CNAME < DomainName
  1574. TypeValue = 5 # :nodoc:
  1575. end
  1576. ##
  1577. # Start Of Authority resource.
  1578. class SOA < Resource
  1579. TypeValue = 6 # :nodoc:
  1580. ##
  1581. # Creates a new SOA record. See the attr documentation for the
  1582. # details of each argument.
  1583. def initialize(mname, rname, serial, refresh, retry_, expire, minimum)
  1584. @mname = mname
  1585. @rname = rname
  1586. @serial = serial
  1587. @refresh = refresh
  1588. @retry = retry_
  1589. @expire = expire
  1590. @minimum = minimum
  1591. end
  1592. ##
  1593. # Name of the host where the master zone file for this zone resides.
  1594. attr_reader :mname
  1595. ##
  1596. # The person responsible for this domain name.
  1597. attr_reader :rname
  1598. ##
  1599. # The version number of the zone file.
  1600. attr_reader :serial
  1601. ##
  1602. # How often, in seconds, a secondary name server is to check for
  1603. # updates from the primary name server.
  1604. attr_reader :refresh
  1605. ##
  1606. # How often, in seconds, a secondary name server is to retry after a
  1607. # failure to check for a refresh.
  1608. attr_reader :retry
  1609. ##
  1610. # Time in seconds that a secondary name server is to use the data
  1611. # before refreshing from the primary name server.
  1612. attr_reader :expire
  1613. ##
  1614. # The minimum number of seconds to be used for TTL values in RRs.
  1615. attr_reader :minimum
  1616. def encode_rdata(msg) # :nodoc:
  1617. msg.put_name(@mname)
  1618. msg.put_name(@rname)
  1619. msg.put_pack('NNNNN', @serial, @refresh, @retry, @expire, @minimum)
  1620. end
  1621. def self.decode_rdata(msg) # :nodoc:
  1622. mname = msg.get_name
  1623. rname = msg.get_name
  1624. serial, refresh, retry_, expire, minimum = msg.get_unpack('NNNNN')
  1625. return self.new(
  1626. mname, rname, serial, refresh, retry_, expire, minimum)
  1627. end
  1628. end
  1629. ##
  1630. # A Pointer to another DNS name.

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