PageRenderTime 59ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/18/resolv.rb

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

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