/lib/ruby/lib/resolv.rb

http://android-ruby.googlecode.com/ · Ruby · 2262 lines · 1570 code · 344 blank · 348 comment · 92 complexity · 6bd75e9e33095804209dca0604fc1b7d MD5 · raw file

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