I have a program (too complex to present here) that uses an UDP socket created by IO::Socket::INET->new()
with both, Local...
and Peer...
address and port. So expectation is that a plain $sock->send($data, $flags)
will send $data
to the peer address specified when creating the socket. That seems to work.
However when I try to send an individual packet to a different destination by using $sock->send($data, $flags, $dest)
, $dest
seems to be ignored (and the packet is being sdent to the socket's peer address). I added lots of debug messages, and the parameter $dest
is passed correctly to send
, but strace
shows that sendto()
is called with NULL
for sockaddr
and 0
for socklen
.
perldoc -f send
doesn't help me. So why is the destination address ignored?
As requested, here's the (somewhat extra verbose) send_packet code:
$RE_saddr = qr/^(.+):(\d+)$/;
sub send_packet($$;$)
{
my ($sock, $packet, $dest) = @_;
my @params = ($packet, 0);
if (defined($dest)) {
if (my ($addr, $port) = $dest =~ $RE_saddr) {
if (my $addr_bin = inet_aton($addr)) {
if (defined($dest = sockaddr_in($port, $addr_bin))) {
push(@params, $dest);
} else {
warn "bad address or socket for $addr:$port";
}
} else {
warn "bad address $addr";
}
} else {
warn "bad destination address $dest";
}
}
return 1
if ($sock->send(@params));
return undef;
}
So obviously the destination is passed as "host : port" (one string without blanks (blanks are due to markup deficits here)).
If you use Peer*
when creating an IO::Socket::INET
then connect()
will be called, which sets the peername of the socket. On such a socket you'll never have to specify the remote socket address, because it has a default one.
IO::Socket::send() has a "feature": it ignores the TO
parameter when the socket has a valid peername:
my $r = defined(getpeername($sock))
? send($sock, $_[1], $flags)
: send($sock, $_[1], $flags, $peer);
That change was introduced in IO 1.12, which unfortunately predates the history of the current git repository:
Modified IO::Socket::send so not to pass 4 arguments to send if the socket is connected
If you don't like this behaviour, you'll have to use raw CORE::send()
instead, i.e.:
#!/usr/bin/perl
use warnings;
use strict;
use IO::Socket::INET;
use Socket qw(pack_sockaddr_in);
my $client = IO::Socket::INET->new(
PeerAddr => '127.0.0.1',
PeerPort => 2000,
Proto => 'udp',
) or die "client socket: $!\n";
my $addr = pack_sockaddr_in(2000, inet_aton('127.0.0.1'));
$client->send('ABCD', 0)
or die "IO::Socket::send() no addr: $!\n";
$client->send('ABCD', 0, $addr)
or die "IO::Socket::send() with addr: $!\n";
send($client, 'ABCD', 0, $addr)
or die "send() with addr: $!\n";
exit 0;
Test run:
$ strace -e sendto,connect,socket perl dummy.pl
socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, IPPROTO_UDP) = 4
connect(4, {sa_family=AF_INET, sin_port=htons(2000), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
sendto(4, "ABCD", 4, 0, NULL, 0) = 4
sendto(4, "ABCD", 4, 0, NULL, 0) = 4
sendto(4, "ABCD", 4, 0, {sa_family=AF_INET, sin_port=htons(2000), sin_addr=inet_addr("127.0.0.1")}, 16) = 4
The strace from my Linux machine indicates that send()
is mapped to sendto()
, because the Perl code does call send() and sendto().
UPDATE: I've created the upstream ticket #133936.