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

/lib/ruby/1.9/resolv.rb

https://github.com/chrisa/jruby
Ruby | 2276 lines | 1583 code | 345 blank | 348 comment | 97 complexity | 8c97d7011ed3ad301b0c658ad5e091a0 MD5 | raw file
Possible License(s): GPL-3.0, JSON

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

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