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