views:

2009

answers:

10

For a communication between two hosts, I need to send the IP address of my host to the other site. The problem is that if I request my IP address, it might be that I get back my local loopback IP addres (127.x.x.x) , not the network (ethernet) IP address.

I use the following code:

char myhostname[32];


gethostname(myhostname, 32);
hp = gethostbyname(myhostname);
unsigned my_ip = *(unsigned*)(hp->h_addr);

if( (my_ip % 256) == 127) {
  /* Wrong IP adress as it's 127.x.x.x */
  printf("Error, local IP address!");
  return;
}

The only way to solve it is to make sure my hostname in /etc/hosts is behind the real network address, not the local loopback (the default for e.g. Ubuntu).

Is there a way to solve this without relying on the content of /etc/hosts?

Edit: I changed the above code so it makes use of getaddrinfo, but I still get back the loopback device's number (127.0,0,1) instead of the real IP address:

struct addrinfo hint = {0};
struct addrinfo *aip = NULL;
unsigned ip = 0;
struct sockaddr_in *sinp = NULL;

hint.ai_family = AF_INET; /* IPv4 */
hint.ai_socktype = SOCK_STREAM;

if(getaddrinfo(hostname, NULL, &hint, &aip) != 0) {
    return 0;
}
sinp = (struct sockaddr_in *) aip->ai_addr;
ip   = *(unsigned *) &sinp->sin_addr;

(I used to get back a list of 3 addrinfo's with the three SOCK_STREAM,SOCK_DGRAM and SOCK_RAW, but the hint prevents that)

So my question still stands...

+1  A: 

Look at this: http://stackoverflow.com/questions/613471/discovering-public-ip-programatically

Note that in some cases a computer can have more than one non-loopback IP address, and in that case the answers to that question tell you how to get the one that is exposed to the internet.

David Zaslavsky
I need to get it from C, not from a shell script or java
Roalt
The accepted answer, at least, is still relevant - you can access that website from C.
David Zaslavsky
+3  A: 

Not an answer, but a relevant comment: be aware that as soon as you start sending addressing information in the content of packets, you run the risk of making your application unable to work across NAT:ing routers and/or through firewalls.

These technologies rely on the information in IP packet headers to keep track of the traffic, and if applications exchange addressing information inside packets, where they remain invisible to this inspection, they might break.

Of course, this might be totally irrelevant to your application, but I thought it worth pointing out in this context.

unwind
Yes, it is irrelevant as it always runs on a local LAN, however your point is relevant for larger scale applications indeed.
Roalt
Be careful: code has a way of lasting long beyond your original intent. Just be aware that someday, someone is going to want to run it beyond the local lan. (This may not make any sense, but they'll want to do it).
Michael Kohne
A: 

Even if the computer has only one physical network interface (an assumption that may or may not hold, even netbooks have two - ethernet and WLAN), VPNs can add even more IP adresses. Anyway, the host on the other side should be able to determine the IP your host used to contact it.

ammoQ
A: 

Use getaddrinfo()

vartec
+3  A: 

The originating address will be included in the packet sent... there's no need to duplicate this information. It's obtained when accepting the communication from the remote host (see beej's guide to networking, specifically the part on accept())

workmad3
+9  A: 

There is POSIX function getaddrinfo() that returns linked list of addresses for given hostname, so you just need to go through that list and find non-loopback address.

See man getaddrinfo.

qrdl
A: 

You're almost there. I'm not sure how you're getting my_ip from hp.

gethostbyname() returns a pointer to a hostent structure which has an h_addr_list field.

The h_addr_list field is a null-terminated list of all the ip addresses bound to that host.

I think you're getting the loopback address because it's the first entry in h_addr_list.

EDIT: It should work something like this:

gethostname(myhostname, 32);
hp = gethostbyname(myhostname);
unsigned my_ip = *(unsigned*)(hp->h_addr);

for (int i = 0; hp->h_addr_list[i] != 0; ++i) {
  if (hp->h_addr_list[i] != INADDR_LOOPBACK) {
    // hp->addr_list[i] is a non-loopback address
  }
}
// no address found
Ferruccio
gethostbyname is deprecated since april 1997... (release of RFC 2133)
bortzmeyer
I added the line that got the my_ip from hp now to the original question.
Roalt
A: 

If /etc/hosts is still there and still the same, looking for all entries of h_addr_list won't help.

gc
What do you mean, wat should or shouldn't be placed in /etc/hosts ? The problem is that on our normal system (RHEL), the hostname is not placed after the 127.0.0.1 entry (but the real eth0 address), but on systems like Ubuntu, they put the hostname after the 127.0.0.1 entry.
Roalt
Try get rid of (rename it if in doubt) the file /etc/hosts, then you will get the eth0 IP directly, if that is acceptable for you. Probably the worse choice, but if you just have one machine to worry about...
gc
A: 

Your new code hardwires the use of IPv4 (in the hint.ai_family field) which is a terrible idea.

Apart from that, you're close, you just should loop through the results of getaddrinfo. Your code just gets the first IP address but there is an aip->ai_next field to follow...

struct addrinfo {
       ...
       struct addrinfo *ai_next;       /* next structure in linked list */
};
bortzmeyer
Putting a breakpoint after the getaddrinfo structure and browsing through the ai_next does not reveal all the different ip address, I only get either the local address or the real ethernet address, depending where the hostname in /etc/hosts is placed.
Roalt
As I said in a comment to the question, the *algorith* is flawed, anyway. My answer was just to explain how to implement the flawed algorithm :-)
bortzmeyer
+1  A: 

I just ran into a situation where when only /etc/hosts has information in it and when I used getaddrinfo to get the IP address list, it returned 127.0.0.1 each time. As it turned out, the hostname was aliased to localhost...something often easy to overlook. Here's what happened:

The /etc/hosts file:
127.0.0.1 localhost.localdomain localhost foo
::1 localhost6.localdomain6 localhost6
172.16.1.248 foo
172.16.1.249 bie
172.16.1.250 bletch

So, now, when you call getaddrinfo with host="foo", it returns 127.0.0.1 3 times. The error here, is that foo appears both on the line with "127.0.0.1" and "172.16.1.248". Once I removed foo from the line with "127.0.0.1" things worked fine.

Hope this helps someone.

belaire
Yes, that could have very well been the problem, I found out myself also. Your system should take care of making sure your hostname is set to the right ip address. So it might be ok with no ethernet connection, but once an extra connection is added the /etc/hosts should then be changed by the system.
Roalt