/lib/ruby/1.8/resolv.rb

http://github.com/jruby/jruby · Ruby · 2295 lines · 1599 code · 346 blank · 350 comment · 98 complexity · 281e87abf63b556c3140815ac480dcca MD5 · raw file

Large files are truncated click here to view the full file

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