Identifying the Other End of a Socket (Perl Cookbook, 2nd Edition)

## 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

Copyright © 2003 O'Reilly & Associates. All rights reserved.