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

/lib/resolv.rb

http://github.com/MacRuby/MacRuby
Ruby | 2340 lines | 1636 code | 346 blank | 358 comment | 101 complexity | 718bafde98e0738e2a076b8a88fe261c MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, LGPL-2.1

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

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

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