Reputation: 1828
I have this Perl code snippet:
my $serverSocket = new IO::Socket::INET(
LocalPort => 53,
Proto => 'udp',
ReuseAddr => 1,
) or Error("Could't open server socket.");
print "Server started.\n";
my $clientQuery;
while (1) {
$serverSocket->recv($clientQuery, 1024, 0);
my $clientQueryParsed = IncomingDNSPacket::parseDNSPacket($clientQuery);
my @questions = @{ $clientQueryParsed->{questions} };
my @answers = map {getAnswer($_, $forwarder)} @questions;
my $errorCode = @answers == 0 ? 3 : '';
my $clientResponseParsed = {
errorCode => $errorCode,
id => $clientQueryParsed->{id},
questions => \@questions,
answerRRs => \@answers,
additionalRRs => [],
authorityRRs => [],
};
my $clientResponse = OutgoingDNSPacket::makeAnswer(%$clientResponseParsed);
$serverSocket->send($clientResponse, 0);
}
Sometimes $serverSocket->send($clientResponse, 0);
returns this error: send: Cannot determine peer address
. I tried to google it and found that this situation can be if socket was closed by remote host. But this is not TCP, this is UDP. Why do I get this error?
EDIT: Here's code of getAnswer
:
sub getAnswer {
my $question = shift;
my $forwarder = shift;
my @answer;
@answer = $cache -> get($question);
if (@answer == 0) {
my $forwarderPacket = getAnswerFromForwarder($question);
$cache -> add ($forwarderPacket);
@answer = $cache -> get($question);
}
return @answer;
}
sub getAnswerFromForwarder {
my $question = shift;
my $forwQuery = OutgoingDNSPacket::makeQuestion(id => 0, questions => [$question]);
my $forwSock = new IO::Socket::INET (
PeerAddr => $forwarder,
PeerPort => 53,
Proto => 'udp'
) or Error("Couldn't create forwarder socket");
$forwSock -> send($forwQuery, 0);
my $select = new IO::Select;
$select -> add ($forwSock);
my @socks = $select -> can_read(2);
if (@socks > 0) {
my $forwAnswer;
$forwSock -> recv($forwAnswer, 1024, 0);
my $forwUnpacked = IncomingDNSPacket::parseDNSPacket($forwAnswer);
return $forwUnpacked;
} else {
return {
errorCode => 3,
questions => [],
answerRRs => [],
authorityRRs => [],
additionalRRs => []
};
}
}
$forwSock
's send
can be unsuccessful. And if it is unsuccessful, I can't send anything from $serverSocket
. Why?
Upvotes: 0
Views: 2071
Reputation: 58741
Short Answer
Your problem is that you are not handling $udpSocket->recv(...)
errors, and this is confounding your later send
.
Most likely the recv
fails with ECONNREFUSED. You want to ignore this error and try again, and do the same for the send
call, too.
Longer Answer
To clarify, your call to send
does not return the error "Cannot determine peer address" — it croak
s with that message.
IO::Socket remembers the remote peer address after a successful call to $io_socket->recv()
. This peer is the implicit destination for the next call to $io_socket->send()
. An unsuccessful recv
, however, leaves no peer address available, and the following send
simply croak
s for want of a destination.
(Your code doesn't choke because the recv
buffer, $clientQuery
, is unmodified after a failed recv
. You are processing the previously recv
d packet again.)
Now, because this is UDP, the underlying error probably pertains to a previous failed send
. Most likely you sent a datagram, an ICMP "dest unreach" came back sometime later, and the subsequent send
/recv
call on the socket reports this as ECONNREFUSED.
Pseudocoded Answer
use Errno; # for %!
#use caution; this is untested and not thoroughly thought through
while (1) {
my $ok = $udpSocket->recv($buf, $len, 0);
if (! $ok) {
redo if $!{ECONNREFUSED}; # Error from a previous UDP send
die "recv: $!\n";
}
...
do {
$ok = $udpSocket->send($buf, 0);
} while (! $ok and $!{ECONNREFUSED});
}
Upvotes: 2