lib/net/dns/resolver.rb in net-dns-0.3 vs lib/net/dns/resolver.rb in net-dns-0.4
- old
+ new
@@ -9,10 +9,11 @@
require 'ipaddr'
require 'logger'
require 'net/dns/packet'
require 'net/dns/resolver/timeouts'
+alias old_send send
module Net # :nodoc:
module DNS
include Logger::Severity
@@ -102,11 +103,11 @@
:retry_number => 4,
:recursive => true,
:defname => true,
:dns_search => true,
:use_tcp => false,
- :ignore_trucated => false,
+ :ignore_truncated => false,
:packet_size => 512,
:tcp_timeout => TcpTimeout.new(120),
:udp_timeout => UdpTimeout.new(0)}
# Create a new resolver object.
@@ -656,14 +657,14 @@
raise ResolverArgumentError, "Argument must be boolean"
end
end
alias usevc= use_tcp=
- def ignore_trucated?
- @config[:ignore_trucated]
+ def ignore_truncated?
+ @config[:ignore_truncated]
end
- alias_method :ignore_trucated, :ignore_trucated?
+ alias_method :ignore_truncated, :ignore_truncated?
def ignore_truncated=(bool)
case bool
when TrueClass,FalseClass
@config[:ignore_truncated] = bool
@@ -888,11 +889,10 @@
send(name,type,cls)
end
-
# Performs a DNS query for the given name. Neither the
# searchlist nor the default domain will be appended.
#
# The argument list can be either a Net::DNS::Packet object
# or a name string plus optional type and class, which if
@@ -922,10 +922,12 @@
def send(argument,type=Net::DNS::A,cls=Net::DNS::IN)
if @config[:nameservers].size == 0
raise ResolverError, "No nameservers specified!"
end
+ method = :send_udp
+
if argument.kind_of? Net::DNS::Packet
packet = argument
else
packet = make_query_packet(argument,type,cls)
end
@@ -937,37 +939,91 @@
# Choose whether use TCP, UDP or RAW
if packet_size > @config[:packet_size] # Must use TCP, either plain or raw
if @raw # Use raw sockets?
@logger.info "Sending #{packet_size} bytes using TCP over RAW socket"
- ans = send_raw_tcp(packet,packet_data)
+ method = :send_raw_tcp
else
@logger.info "Sending #{packet_size} bytes using TCP"
- ans = send_tcp(packet,packet_data)
+ method = :send_tcp
end
else # Packet size is inside the boundaries
if @raw # Use raw sockets?
@logger.info "Sending #{packet_size} bytes using UDP over RAW socket"
- ans = send_raw_udp(packet,packet_data)
+ method = :send_raw_udp
elsif use_tcp? # User requested TCP
@logger.info "Sending #{packet_size} bytes using TCP"
- ans = send_tcp(packet,packet_data)
+ method = :send_tcp
else # Finally use UDP
@logger.info "Sending #{packet_size} bytes using UDP"
- ans = send_udp(packet,packet_data)
end
end
- if ans.header.truncated? and not ignore_truncated?
+ if type == Net::DNS::AXFR
+ if @raw
+ @logger.warn "AXFR query, switching to TCP over RAW socket"
+ method = :send_raw_tcp
+ else
+ @logger.warn "AXFR query, switching to TCP"
+ method = :send_tcp
+ end
+ end
+
+ ans = self.old_send(method,packet,packet_data)
+
+ unless ans
+ @logger.fatal "No response from nameservers list: aborting"
+ raise NoResponseError
+ 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])
+
+ if response.header.truncated? and not ignore_truncated?
@logger.warn "Packet truncated, retrying using TCP"
- ans = send_tcp(packet,packet_data)
+ self.use_tcp = true
+ begin
+ return send(argument,type,cls)
+ ensure
+ self.use_tcp = false
+ end
end
- ans
+ return 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,
+ # since it is using the same infrastucture.
+ #
+ def axfr(name,cls=Net::DNS::IN)
+ @logger.info "Requested AXFR transfer, zone #{name} class #{cls}"
+ send(name,Net::DNS::AXFR,cls)
+ end
+
+ #
+ # Performs an MX query for the domain name passed as parameter.
+ #
+ # It actually uses the same methods a normal Resolver query would
+ # use, but automatically sort the results based on preferences
+ # and returns an ordered array.
+ #
+ # Example:
+ #
+ # res = Net::DNS::Resolver.new
+ # res.mx("google.com")
+ #
+ def mx(name,cls=Net::DNS::IN)
+ arr = []
+ send(name, Net::DNS::MX, cls).answer.each do |entry|
+ arr << entry if entry.type == 'MX'
+ end
+ return arr.sort_by {|a| a.preference}
+ end
+
private
# Parse a configuration file specified as the argument.
#
def parse_config_file
@@ -1057,98 +1113,73 @@
def send_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))
- # Generate new TCP socket or use persistent one
- if @config[:persistent_tcp] and @@tcp_socket
- socket = @@tcp_socket
- @logger.info "Using persistent socket #{socket.inspect}"
- else
- socket = Socket.new(Socket::AF_INET,Socket::SOCK_STREAM,0)
- socket.bind(Socket.pack_sockaddr_in(@config[:source_port],@config[:source_address].to_s))
- @@tcp_socket = socket if @config[:persistent_tcp]
- end
-
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)
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)
+ left = len - buffer.size
+ temp,from = socket.recvfrom(left)
+ buffer += temp
+ end
- ans = socket.recv(len)
- unless ans.size == len
+ unless buffer.size == len
@logger.warn "Malformed packet from nameserver #{ns}, trying next."
next
end
end
-
- ans = [ans,["",@config[:port],ns.to_s,ns.to_s]]
- @logger.info "Received #{ans[0].size} bytes from #{ans[1][2]+":"+ans[1][1].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 unless @config[:persistent_tcp]
+ socket.close
end
end
-
- if ans
- return Net::DNS::Packet.parse(ans[0],ans[1])
- else
- @logger.fatal "No response from nameservers list: aborting"
- raise NoResponseError
- end
end
-
def send_udp(packet,packet_data)
- # Generate new UDP socket or use persistent one
- if @config[:persistent_udp] and @@udp_socket
- socket = @@udp_socket
- @logger.info "Using persistent socket #{socket.inspect}"
- else
- socket = UDPSocket.new
- socket.bind(@config[:source_address].to_s,@config[:source_port])
- @@udp_socket = socket if @config[:persistent_udp]
- end
-
+ socket = UDPSocket.new
+ socket.bind(@config[:source_address].to_s,@config[:source_port])
+
ans = nil
response = ""
@config[:nameservers].each do |ns|
begin
@config[:udp_timeout].timeout do
@logger.info "Contacting nameserver #{ns} port #{@config[:port]}"
socket.send(packet_data,0,ns.to_s,@config[:port])
ans = socket.recvfrom(@config[:packet_size])
end
- if ans
- @logger.info "Received #{ans[0].size} bytes from #{ans[1][2]+":"+ans[1][1].to_s}"
- break
- end
+ break if ans
rescue TimeoutError
@logger.warn "Nameserver #{ns} not responding within UDP timeout, trying next one"
next
end
end
- if ans
- return Net::DNS::Packet.parse(ans[0],ans[1])
- else
- @logger.fatal "No response from nameservers list: aborting"
- raise NoResponseError
- end
+ ans
end
def valid?(name)
if name =~ /[^-\w\.]/
raise ResolverArgumentError, "Invalid domain name #{name}"