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

/lib/ruby/1.8/resolv.rb

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

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