Is this a local IP?

Started by
7 comments, last by Zipster 13 years, 11 months ago
Hey folks. I recently had to write a function to check if an IP address "belongs" to the local computer. I'm not sure if that's the correct phrasing, but hopefully you'll get an idea what I need from what I have written so far:
// Assume 'ip' is in network byte-order
bool Is_Local_IP(unsigned long ip)
{
	struct hostent *hp = 0;
	struct in_addr **blist = 0;
	char	hostname[256] = {'\0'};
	struct in_addr ipv4addr;
	memset(&ipv4addr,0,sizeof(ipv4addr));

	if(gethostname(hostname, sizeof(hostname))==-1) {
		return false;
	} 

	if(std::isdigit(hostname[0]))	{
		ipv4addr.s_addr = inet_addr(hostname);
		hp = gethostbyaddr((const char*)&ipv4addr,sizeof(ipv4addr.s_addr),AF_INET);
	} else {
		hp = gethostbyname(hostname);
	}
	if(!hp) {
		return false;
	}

	blist = (struct in_addr **)hp->h_addr_list;
	while(*blist != NULL) {
		if((**blist).s_addr == ip) {
			return true;
		}
		++blist;
	}
	
	return false;
}
The issue is that I haven't done a ton of low-level network programming in the past, so my two biggest concerns are: 1) Are there any edge or corner cases where this code will fail with a false-negative (or even a false-positive, but that seems less likely)? 2) Is this more code than there needs to be to perform the same task? It seems as though you should be able to get a hostent directly without first getting the host name, but that could just be the way the API is designed. I already know that reverse look-ups can be unreliable, but I'm more concerned with making sure the code itself isn't missing anything. All help or insight is appreciated [smile]
Advertisement
A think a more reliable way would be to get the address of each interface, and compare the address you have against those of all the interfaces.
enum Bool { True, False, FileNotFound };
Quote:Original post by hplus0603
A think a more reliable way would be to get the address of each interface, and compare the address you have against those of all the interfaces.

I did a bit of Googling, and item 3.3 on this page seems to suggest that both methods (WSAIoctl with SIO_GET_INTERFACE_LIST and what I have) will retrieve the same list of addresses, with the third one being the most portable. Is there any advantage to enumerating the interfaces manually?
Your code doesn't handle loopback addresses. e.g. 127.0.0.1 always points to the local machine (by definition), but it doesn't show up in hostent info. I don't know if you care about this case - it depends on where you're getting the mysterious IP addresses in the first place. All the info I'm giving is for winsock btw. I haven't tried it on another OS.

This is what I came up with:
// Assume 'ip' is in network byte-orderbool Is_Local_IP(u_long ip){    bool                match = false;    hostent *           hp;    std::vector<u_long> addrs;    hp = gethostbyname(NULL);    if (hp)    {        u_long ** addr_list = (u_long **) hp->h_addr_list;        for ( ; *addr_list; ++addr_list)            addrs.push_back(**addr_list);    }    hp = gethostbyaddr((char *) &ip, sizeof(ip), AF_INET);	    if (hp)        hp = gethostbyname(hp->h_name);    if (hp)        match = std::find(addrs.begin(), addrs.end(), * (u_long *) hp->h_addr) != addrs.end();	return match;}

Basically you save away the local address (gethostbyname(NULL) will get you the local host). Then you lookup your mysterious IP, canonicalize it by looking up the resulting name (gethostbyaddr on a loopback address gives you back the loopback address instead of the "real" address...), then see if you can find the resulting address in the saved list.

This works for me for both my real IP and the loopback. It has the downside of if you try to check an address for a machine that doesn't exist it will sit there and stall waiting until it times out.

BTW, you can't compare the names directly because gethostbyname(NULL) is returning the local machine name only while the others are returning the fully qualified name.
-Mike
Quote:Original post by Anon Mike
Your code doesn't handle loopback addresses. e.g. 127.0.0.1 always points to the local machine (by definition), but it doesn't show up in hostent info. I don't know if you care about this case - it depends on where you're getting the mysterious IP addresses in the first place.

Indeed, I noticed that after reading the link I posted, so I added an explicit check for a 127.x.x.x address. However your code is much better because it doesn't required checking against any magic numbers [smile] I'm also a fan of the clever "canonization".
Enumerating the local interfaces will always get the configured addresses of the local interfaces, without going through DNS.

Going through DNS is slower, and much less reliable, because there will often not be any reverse DNS at all (for private addresses like 192.x that you get from your NAT router/DHCP) or the reverse DNS will not match the forward DNS (common with certain ISPs).
enum Bool { True, False, FileNotFound };
Quote:Original post by hplus0603
Enumerating the local interfaces will always get the configured addresses of the local interfaces, without going through DNS.

Going through DNS is slower, and much less reliable, because there will often not be any reverse DNS at all (for private addresses like 192.x that you get from your NAT router/DHCP) or the reverse DNS will not match the forward DNS (common with certain ISPs).

Ah, so at least for the local machine I don't have to do a reverse DNS. But it still can't be avoided for the "mysterious IP", correct?
If the mysterious IP is not the address of a local interface, then it's by definition the address of a remote machine, which may or may not have reverse DNS set up. However, your initial problem statement was just to figure out whether a received address was "local" or not. If it's not local, then you know the answer, and don't need to do any other look-up.
enum Bool { True, False, FileNotFound };
Quote:Original post by hplus0603
If the mysterious IP is not the address of a local interface, then it's by definition the address of a remote machine, which may or may not have reverse DNS set up. However, your initial problem statement was just to figure out whether a received address was "local" or not. If it's not local, then you know the answer, and don't need to do any other look-up.

Right, I'm not sure what I was thinking when I asked about a second lookup :)

This topic is closed to new replies.

Advertisement