home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2007 September / PCWSEP07.iso / Software / Linux / Linux Mint 3.0 Light / LinuxMint-3.0-Light.iso / casper / filesystem.squashfs / usr / lib / ruby / 1.8 / resolv.rb < prev    next >
Encoding:
Ruby Source  |  2006-05-06  |  48.9 KB  |  1,885 lines

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