lib/net/dns/resolver.rb in net-dns-0.8.0 vs lib/net/dns/resolver.rb in net-dns-0.9.0

- old
+ new

@@ -25,11 +25,10 @@ end end module Net module DNS - include Logger::Severity # = Net::DNS::Resolver - DNS resolver class # # The Net::DNS::Resolver class implements a complete DNS resolver written @@ -85,11 +84,10 @@ # # # C Shell # % setenv RES_OPTIONS "retrans:3 retry:2 debug" # class Resolver - class Error < StandardError end class NoResponseError < Error end @@ -97,34 +95,32 @@ # An hash with the defaults values of almost all the # configuration parameters of a resolver object. See # the description for each parameter to have an # explanation of its usage. Defaults = { - :config_file => "/etc/resolv.conf", - :log_file => $stdout, - :port => 53, - :searchlist => [], - :nameservers => [IPAddr.new("127.0.0.1")], - :domain => "", - :source_port => 0, - :source_address => IPAddr.new("0.0.0.0"), - :source_address_inet6 => IPAddr.new('::'), - :retry_interval => 5, - :retry_number => 4, - :recursive => true, - :defname => true, - :dns_search => true, - :use_tcp => false, - :ignore_truncated => false, - :packet_size => 512, - :tcp_timeout => TcpTimeout.new(5), - :udp_timeout => UdpTimeout.new(5), - } + config_file: "/etc/resolv.conf", + log_file: $stdout, + port: 53, + searchlist: [], + nameservers: [IPAddr.new("127.0.0.1")], + domain: "", + source_port: 0, + source_address: IPAddr.new("0.0.0.0"), + source_address_inet6: IPAddr.new('::'), + retry_interval: 5, + retry_number: 4, + recursive: true, + defname: true, + dns_search: true, + use_tcp: false, + ignore_truncated: false, + packet_size: 512, + tcp_timeout: TcpTimeout.new(5), + udp_timeout: UdpTimeout.new(5), + }.freeze - class << self - C = Object.const_get(defined?(RbConfig) ? :RbConfig : :Config)::CONFIG # Quick resolver method. Bypass the configuration using # the defaults. # @@ -140,14 +136,12 @@ # because the comparison will fail when running on JRuby. # On JRuby RUBY_PLATFORM == 'java'. def platform_windows? !!(C["host_os"] =~ /msdos|mswin|djgpp|mingw/i) end - end - # Creates a new resolver object. # # Argument +config+ can either be empty or be an hash with # some configuration parameters. To know what each parameter # do, look at the description of each. @@ -253,12 +247,10 @@ # 2) ENV variables # 3) config file # 4) defaults (and /etc/resolv.conf for config) #------------------------------------------------------------ - - #------------------------------------------------------------ # Parsing config file #------------------------------------------------------------ parse_config_file @@ -268,14 +260,15 @@ parse_environment_variables #------------------------------------------------------------ # Parsing arguments #------------------------------------------------------------ - config.each do |key,val| - next if key == :log_file or key == :config_file + config.each do |key, val| + next if (key == :log_file) || (key == :config_file) + begin - eval "self.#{key.to_s} = val" + eval "self.#{key} = val" rescue NoMethodError raise ArgumentError, "Option #{key} not valid" end end end @@ -307,11 +300,11 @@ case arg when String @config[:searchlist] = [arg] if valid? arg @logger.info "Searchlist changed to value #{@config[:searchlist].inspect}" when Array - @config[:searchlist] = arg if arg.all? {|x| valid? x} + @config[:searchlist] = arg if arg.all? { |x| valid? x } @logger.info "Searchlist changed to value #{@config[:searchlist].inspect}" else raise ArgumentError, "Wrong argument format, neither String nor Array" end end @@ -323,11 +316,11 @@ # def nameservers @config[:nameservers].map(&:to_s) end - alias_method :nameserver, :nameservers + alias nameserver nameservers # Set the list of resolver nameservers. # +arg+ can be a single ip address or an array of addresses. # # res.nameservers = "192.168.0.1" @@ -354,30 +347,31 @@ @config[:nameservers] = [arg] @logger.info "Nameservers list changed to value #{@config[:nameservers].inspect}" when Array @config[:nameservers] = [] arg.each do |x| - @config[:nameservers] << case x - when String - begin - IPAddr.new(x) - rescue ArgumentError - nameservers_from_name(arg) - return - end - when IPAddr - x - else - raise ArgumentError, "Wrong argument format" - end + val = case x + when String + begin + IPAddr.new(x) + rescue ArgumentError + nameservers_from_name(arg) + return + end + when IPAddr + x + else + raise ArgumentError, "Wrong argument format" + end + @config[:nameservers] << val end @logger.info "Nameservers list changed to value #{@config[:nameservers].inspect}" else raise ArgumentError, "Wrong argument format, neither String, Array nor IPAddr" end end - alias_method("nameserver=","nameservers=") + alias_method("nameserver=", "nameservers=") # Return a string with the default domain. def domain @config[:domain].inspect end @@ -406,11 +400,11 @@ # res.port = 10053 # # The default is port 53. # def port=(num) - if (0..65535).include? num + if (0..65_535).cover? num @config[:port] = num @logger.info "Port number changed to #{num}" else raise ArgumentError, "Wrong port number #{num}" end @@ -438,11 +432,12 @@ # def source_port=(num) unless root? raise ResolverPermissionError, "Are you root?" end - if (0..65535).include?(num) + + if (0..65_535).cover?(num) @config[:source_port] = num else raise ArgumentError, "Wrong port number #{num}" end end @@ -460,11 +455,11 @@ # Get the local ipv6 address from which the resolver sends queries # def source_address_inet6 @config[:source_address_inet6].to_s end - + # Set the local source address from which the resolver sends its queries. # # res.source_address = "172.16.100.1" # res.source_address = IPAddr.new("172.16.100.1") # @@ -490,13 +485,13 @@ unless addr.respond_to? :to_s raise ArgumentError, "Wrong address argument #{addr}" end begin - port = rand(64000)+1024 + port = rand(1024..65_023) @logger.warn "Try to determine state of source address #{addr} with port #{port}" - a = TCPServer.new(addr.to_s,port) + a = TCPServer.new(addr.to_s, port) rescue SystemCallError => e case e.errno when 98 # Port already in use! @logger.warn "Port already in use" retry @@ -550,11 +545,11 @@ end # Set the number of times the resolver will try a query. # Default 4 times. def retry_number=(num) - if num.kind_of? Integer and num > 0 + if num.is_a?(Integer) && (num > 0) @config[:retry_number] = num @logger.info "Retrasmissions number changed to #{num}" else raise ArgumentError, "Retry value must be a positive integer" end @@ -569,45 +564,45 @@ # puts "recursive query" # def recursive? @config[:recursive] end - alias_method :recurse, :recursive? - alias_method :recursive, :recursive? + alias recurse recursive? + alias recursive recursive? # Sets whether or not the resolver should perform recursive # queries. Default is true. # # res.recursive = false # perform non-recursive query # def recursive=(bool) case bool - when TrueClass,FalseClass + when TrueClass, FalseClass @config[:recursive] = bool @logger.info("Recursive state changed to #{bool}") else raise ArgumentError, "Argument must be boolean" end end - alias_method :recurse=, :recursive= + alias recurse= recursive= # Return a string representing the resolver state, suitable # for printing on the screen. # # puts "Resolver state:" # puts res.state # def state str = ";; RESOLVER state:\n;; " i = 1 - @config.each do |key,val| - if key == :log_file or key == :config_file - str << "#{key}: #{val} \t" + @config.each do |key, val| + str << if (key == :log_file) || (key == :config_file) + "#{key}: #{val} \t" else - str << "#{key}: #{eval(key.to_s)} \t" + "#{key}: #{eval(key.to_s)} \t" end - str << "\n;; " if i % 2 == 0 + str << "\n;; " if i.even? i += 1 end str end alias print state @@ -631,11 +626,11 @@ # # Default is true. # def defname=(bool) case bool - when TrueClass,FalseClass + when TrueClass, FalseClass @config[:defname] = bool @logger.info("Defname state changed to #{bool}") else raise ArgumentError, "Argument must be boolean" end @@ -643,33 +638,33 @@ # Get the state of the dns_search flag. def dns_search @config[:dns_search] end - alias_method :dnsrch, :dns_search + alias dnsrch dns_search # Set the flag +dns_search+ in a boolean state. If +dns_search+ # is true, when using the Resolver#search method will be applied # the search list. Default is true. def dns_search=(bool) case bool - when TrueClass,FalseClass + when TrueClass, FalseClass @config[:dns_search] = bool @logger.info("DNS search state changed to #{bool}") else raise ArgumentError, "Argument must be boolean" end end - alias_method("dnsrch=","dns_search=") + alias_method("dnsrch=", "dns_search=") # Get the state of the use_tcp flag. # def use_tcp? @config[:use_tcp] end - alias_method :usevc, :use_tcp? - alias_method :use_tcp, :use_tcp? + alias usevc use_tcp? + alias use_tcp use_tcp? # If +use_tcp+ is true, the resolver will perform all queries # using TCP virtual circuits instead of UDP datagrams, which # is the default for the DNS protocol. # @@ -679,11 +674,11 @@ # # Default is false. # def use_tcp=(bool) case bool - when TrueClass,FalseClass + when TrueClass, FalseClass @config[:use_tcp] = bool @logger.info("Use tcp flag changed to #{bool}") else raise ArgumentError, "Argument must be boolean" end @@ -691,15 +686,15 @@ alias usevc= use_tcp= def ignore_truncated? @config[:ignore_truncated] end - alias_method :ignore_truncated, :ignore_truncated? + alias ignore_truncated ignore_truncated? def ignore_truncated=(bool) case bool - when TrueClass,FalseClass + when TrueClass, FalseClass @config[:ignore_truncated] = bool @logger.info("Ignore truncated flag changed to #{bool}") else raise ArgumentError, "Argument must be boolean" end @@ -805,11 +800,11 @@ # res.logger = log # # Note that this will destroy the precedent logger. # def logger=(logger) - if logger.kind_of? Logger + if logger.is_a? Logger @logger.close @logger = logger else raise ArgumentError, "Argument must be an instance of Logger class" end @@ -859,35 +854,33 @@ # packet = res.search("192.168.10.254") # # Returns a Net::DNS::Packet object. If you need to examine the response packet # whether it contains any answers or not, use the Resolver#query method instead. # - def search(name,type=Net::DNS::A,cls=Net::DNS::IN) + def search(name, type = Net::DNS::A, cls = Net::DNS::IN) + return query(name, type, cls) if name.class == IPAddr - return query(name,type,cls) if name.class == IPAddr - # If the name contains at least one dot then try it as is first. if name.include? "." @logger.debug "Search(#{name},#{Net::DNS::RR::Types.new(type)},#{Net::DNS::RR::Classes.new(cls)})" - ans = query(name,type,cls) + ans = query(name, type, cls) return ans if ans.header.anCount > 0 end # If the name doesn't end in a dot then apply the search list. - if name !~ /\.$/ and @config[:dns_search] + if name !~ /\.$/ && @config[:dns_search] @config[:searchlist].each do |domain| newname = name + "." + domain @logger.debug "Search(#{newname},#{Net::DNS::RR::Types.new(type)},#{Net::DNS::RR::Classes.new(cls)})" - ans = query(newname,type,cls) + ans = query(newname, type, cls) return ans if ans.header.anCount > 0 end end # Finally, if the name has no dots then try it as is. @logger.debug "Search(#{name},#{Net::DNS::RR::Types.new(type)},#{Net::DNS::RR::Classes.new(cls)})" - query(name+".",type,cls) - + query(name + ".", type, cls) end # Performs a DNS query for the given name; the search list # is not applied. If the name doesn't contain any dots and # +defname+ is true then the default domain will be appended. @@ -910,23 +903,21 @@ # # Returns a Net::DNS::Packet object. If you need to examine the response # packet whether it contains any answers or not, use the Resolver#query # method instead. # - def query(name,type=Net::DNS::A,cls=Net::DNS::IN) + def query(name, type = Net::DNS::A, cls = Net::DNS::IN) + return send(name, type, cls) if name.class == IPAddr - return send(name,type,cls) if name.class == IPAddr - # If the name doesn't contain any dots then append the default domain. - if name !~ /\./ and name !~ /:/ and @config[:defname] + if name !~ /\./ && name !~ /:/ && @config[:defname] name += "." + @config[:domain] end @logger.debug "Query(#{name},#{Net::DNS::RR::Types.new(type)},#{Net::DNS::RR::Classes.new(cls)})" - send(name,type,cls) - + send(name, type, cls) end # Performs a DNS query for the given name. Neither the # searchlist nor the default domain will be appended. # @@ -948,23 +939,23 @@ # If the name is an IP address (Ipv4 or IPv6), in the form of a string # or a IPAddr object, then an appropriate PTR query will be performed: # # ip = IPAddr.new("172.16.100.2") # packet = res.query(ip) - # + # # packet = res.query("172.16.100.2") # # Use +packet.header.ancount+ or +packet.answer+ to find out if there # were any records in the answer section. # def query(argument, type = Net::DNS::A, cls = Net::DNS::IN) - if @config[:nameservers].size == 0 + if @config[:nameservers].empty? raise Resolver::Error, "No nameservers specified!" end method = :query_udp - packet = if argument.kind_of? Net::DNS::Packet + packet = if argument.is_a? Net::DNS::Packet argument else make_query_packet(argument, type, cls) end @@ -1002,32 +993,32 @@ @logger.warn "AXFR query, switching to TCP" method = :query_tcp end end - ans = self.send(method, packet, packet_data) + ans = send(method, packet, packet_data) unless ans message = "No response from nameservers list" @logger.fatal(message) raise NoResponseError, message end - @logger.info "Received #{ans[0].size} bytes from #{ans[1][2]+":"+ans[1][1].to_s}" - response = Net::DNS::Packet.parse(ans[0],ans[1]) + @logger.info "Received #{ans[0].size} bytes from #{ans[1][2] + ':' + ans[1][1].to_s}" + response = Net::DNS::Packet.parse(ans[0], ans[1]) - if response.header.truncated? and not ignore_truncated? + if response.header.truncated? && !ignore_truncated? @logger.warn "Packet truncated, retrying using TCP" self.use_tcp = true begin - return query(argument,type,cls) + return query(argument, type, cls) ensure self.use_tcp = false end end - return response + response end # Performs a zone transfer for the zone passed as a parameter. # # It is actually only a wrapper to a send with type set as Net::DNS::AXFR, @@ -1050,34 +1041,35 @@ def mx(name, cls = Net::DNS::IN) arr = [] query(name, Net::DNS::MX, cls).answer.each do |entry| arr << entry if entry.type == 'MX' end - arr.sort_by { |a| a.preference } + arr.sort_by(&:preference) end private # Parses a configuration file specified as the argument. def parse_config_file if self.class.platform_windows? require 'win32/resolv' arr = Win32::Resolv.get_resolv_info - self.domain = arr[0].to_s + self.domain = arr[0][0] self.nameservers = arr[1] else nameservers = [] IO.foreach(@config[:config_file]) do |line| - line.gsub!(/\s*[;#].*/,"") + line.gsub!(/\s*[;#].*/, "") next unless line =~ /\S/ + case line when /^\s*domain\s+(\S+)/ - self.domain = $1 + self.domain = Regexp.last_match(1) when /^\s*search\s+(.*)/ - self.searchlist = $1.split(" ") + self.searchlist = Regexp.last_match(1).split(" ") when /^\s*nameserver\s+(.*)/ - nameservers << $1.split(" ") + nameservers << Regexp.last_match(1).split(" ") end end self.nameservers = nameservers.flatten end end @@ -1093,11 +1085,11 @@ if ENV['LOCALDOMAIN'] self.domain = ENV['LOCALDOMAIN'] end if ENV['RES_OPTIONS'] ENV['RES_OPTIONS'].split(" ").each do |opt| - name,val = opt.split(":") + name, val = opt.split(":") begin eval("self.#{name} = #{val}") rescue NoMethodError raise ArgumentError, "Invalid ENV option #{name}" end @@ -1143,48 +1135,47 @@ packet end def query_tcp(packet, packet_data) - ans = nil length = [packet_data.size].pack("n") @config[:nameservers].each do |ns| begin buffer = "" - socket = Socket.new(Socket::AF_INET,Socket::SOCK_STREAM,0) - socket.bind(Socket.pack_sockaddr_in(@config[:source_port],@config[:source_address].to_s)) + socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0) + socket.bind(Socket.pack_sockaddr_in(@config[:source_port], @config[:source_address].to_s)) - sockaddr = Socket.pack_sockaddr_in(@config[:port],ns.to_s) + sockaddr = Socket.pack_sockaddr_in(@config[:port], ns.to_s) @config[:tcp_timeout].timeout do socket.connect(sockaddr) @logger.info "Contacting nameserver #{ns} port #{@config[:port]}" - socket.write(length+packet_data) + socket.write(length + packet_data) ans = socket.recv(Net::DNS::INT16SZ) len = ans.unpack("n")[0] @logger.info "Receiving #{len} bytes..." if len == 0 @logger.warn "Receiving 0 lenght packet from nameserver #{ns}, trying next." next end - while (buffer.size < len) + while buffer.size < len left = len - buffer.size - temp,from = socket.recvfrom(left) + temp, from = socket.recvfrom(left) buffer += temp end unless buffer.size == len @logger.warn "Malformed packet from nameserver #{ns}, trying next." next end end - return [buffer,["",@config[:port],ns.to_s,ns.to_s]] + return [buffer, ["", @config[:port], ns.to_s, ns.to_s]] rescue TimeoutError @logger.warn "Nameserver #{ns} not responding within TCP timeout, trying next one" next ensure socket.close @@ -1192,13 +1183,13 @@ end end def query_udp(packet, packet_data) socket4 = UDPSocket.new - socket4.bind(@config[:source_address].to_s,@config[:source_port]) + socket4.bind(@config[:source_address].to_s, @config[:source_port]) socket6 = UDPSocket.new(Socket::AF_INET6) - socket6.bind(@config[:source_address_inet6].to_s,@config[:source_port]) + socket6.bind(@config[:source_address_inet6].to_s, @config[:source_port]) ans = nil response = "" @config[:nameservers].each do |ns| begin @@ -1227,9 +1218,8 @@ raise ArgumentError, "Invalid domain name #{name}" else true end end - end end end