PageRenderTime 58ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/ruby/lib/resolv.rb

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