Recipe 17.7 Identifying the Other End of a Socket
17.7.1 Problem
You have a socket and want to identify
the machine at the other end.
17.7.2 Solution
If you're only interested in the IP address of the remote machine,
use:
use Socket;
$other_end = getpeername(SOCKET)
or die "Couldn't identify other end: $!\n";
($port, $iaddr) = unpack_sockaddr_in($other_end);
$ip_address = inet_ntoa($iaddr);
If you want its actual hostname,
use:
use Socket;
$other_end = getpeername(SOCKET)
or die "Couldn't identify other end: $!\n";
($port, $iaddr) = unpack_sockaddr_in($other_end);
$actual_ip = inet_ntoa($iaddr);
$claimed_hostname = gethostbyaddr($iaddr, AF_INET);
@name_lookup = gethostbyname($claimed_hostname)
or die "Could not look up $claimed_hostname : $!\n";
@resolved_ips = map { inet_ntoa($_) }
@name_lookup[ 4 .. $#name_lookup ];
17.7.3 Discussion
For a long time, figuring out who connected to you was considered
more straightforward than it really is. The
getpeername function returns the IP address of the
remote machine in a packed binary structure (or
undef if an error occurred). To unpack it, use
inet_ntoa. If you want the name of the remote end,
call gethostbyaddr to look up the name of the
machine in the DNS tables, right?
Not really. That's only half the solution. Because a name lookup goes
to the name's owner's DNS server and a lookup of an IP address goes
to the address's owner's DNS server, you have to contend with the
possibility that the machine that connected to you is giving
incorrect names. For instance, the machine
evil.crackers.org could belong to malevolent
cyberpirates who tell their DNS server that its IP address
(1.2.3.4) should be identified as
trusted.dod.gov. If your program trusts
trusted.dod.gov, a connection from
evil.crackers.org will cause
getpeername to return the right IP address
(1.2.3.4), but gethostbyaddr
will return the duplicitous name.
To avoid
this problem, we take the (possibly deceitful) name returned by
gethostbyaddr and look it up again with
gethostbyname. In the case of
evil.crackers.org, the lookup of
trusted.dod.gov will be done through
dod.gov's DNS servers, and will return the real IP
address(es) for trusted.dod.gov. Because many
machines have more than one IP address (multihomed web servers are
the obvious example), we can't use the simplified form of
gethostbyname:
$packed_ip = gethostbyname($name) or die "Couldn't look up $name : $!\n";
$ip_address = inet_ntoa($packed_ip);
So far we've assumed we're dealing with an Internet domain
application. You can also call getpeername on a
Unix domain socket. If the other end called bind,
you'll get the filename they bound to. If the other end
didn't call bind, however,
getpeername may return an empty string (unpacked),
a packed string with oddball garbage in it, or
undef to indicate an error, or your computer may
reboot. (These possibilities are listed in descending order of
probability and desirability.) This is what we in the computer
business call "undefined behavior."
Even this level of paranoia and mistrust isn't enough. It's still
possible for people to fake out DNS servers they don't directly
control, so don't use hostnames for identification or authentication.
True paranoiacs and misanthropes use cryptographically secure
methods.
17.7.4 See Also
The gethostbyaddr,
gethostbyname, and getpeername
functions in Chapter 29 of Programming Perl
and in perlfunc(1); the
inet_ntoa function in the standard Socket module;
the documentation for the standard IO::Socket and Net::hostnet
modules
|