3

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

Roalt
  • 8,330
  • 7
  • 41
  • 53
  • 2
    gethostbyname is deprecated for many, many years (one of the reasons being that it works with only one address family). As mentioned by qrdl, you should use getaddrinfo – bortzmeyer Mar 09 '09 at 21:46
  • Ok, thanks for the info. This original code is 12+ years old. – Roalt Mar 10 '09 at 10:09

10 Answers10

9

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
  • 34,062
  • 14
  • 56
  • 86
4

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
  • 391,730
  • 64
  • 469
  • 606
  • Yes, it is irrelevant as it always runs on a local LAN, however your point is relevant for larger scale applications indeed. – Roalt Mar 09 '09 at 13:26
  • 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 Mar 10 '09 at 10:18
4

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
  • 25,101
  • 4
  • 35
  • 56
3

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.

  • 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 Jan 09 '10 at 19:45
1

Look at this: Discovering public IP programmatically

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.

Community
  • 1
  • 1
David Z
  • 128,184
  • 27
  • 255
  • 279
0

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.

Erich Kitzmueller
  • 36,381
  • 5
  • 80
  • 102
0

Use getaddrinfo()

vartec
  • 131,205
  • 36
  • 218
  • 244
0

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
  • 98,941
  • 38
  • 226
  • 299
0

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

gc .
  • 415
  • 4
  • 9
  • 18
  • 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 Apr 01 '09 at 06:08
  • 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 . Apr 01 '09 at 11:52
0

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
  • 34,164
  • 12
  • 67
  • 91
  • 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 Apr 01 '09 at 06:09
  • 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 Apr 01 '09 at 11:44