PageRenderTime 60ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/ruby/1.9/resolv.rb

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