PageRenderTime 59ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/ruby/1.9/resolv.rb

http://github.com/jruby/jruby
Ruby | 2363 lines | 1651 code | 347 blank | 365 comment | 103 complexity | a21aa2e5cce413fbe453d71e6623a139 MD5 | raw file
Possible License(s): GPL-3.0, BSD-3-Clause, GPL-2.0, JSON, LGPL-2.1

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

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

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