9

I want to get the IP address of the computer my program is launched on, to be able then to send it to a client, but I always get 0.0.0.1 instead of the real IP address (like 127.0.0.1 for instance).

I'm currently able to get the port, but not the IP address.

How can I get it?

The best solution would be to be able to get it with a sockaddr_in. Here's what I'm currently doing:

int open_connection(char* ip, int* port)
{
  int                   sock;
  struct sockaddr_in    sin;
  socklen_t             len;
  int                   i;

  i = 0;
  len = sizeof(sin);
  if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    return (-1);
  bzero(&sin, sizeof(struct sockaddr_in));
  sin.sin_family = AF_INET;
  if (bind(sock, (struct sockaddr *) &sin, sizeof(sin)) != 0)
    perror("Error on bind");
  if (getsockname(sock, (struct sockaddr *)&sin, &len) != 0)
    perror("Error on getsockname");
  strcpy(ip, inet_ntoa(sin.sin_addr)); // IP = 0.0.0.0
  *port = sin.sin_port;
  return (sock);
}

EDIT: I understand I was going on the wrong way with my way of thinking. So my question is: What's the best way to get your own IP address?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Julien Fouilhé
  • 2,583
  • 3
  • 30
  • 56
  • 1
    If the client is talking to you... he already has your ip address. I'm confused why you need to send it explicitly. – Pyrce Apr 10 '13 at 00:22
  • 1
    @Pyrce I'm making a FTP server, and when entering passive mode, I need to send the IP address and the port the server is listening to... Even if it is the same IP address... – Julien Fouilhé Apr 10 '13 at 00:25
  • 2
    Maybe this old question? http://stackoverflow.com/questions/212528/get-the-ip-address-of-the-machine I think bind to 0.0.0.0 is a specific thing, though, which means bind on all IP addresses - I doubt you'd get anything else out of bind since that's what you passed in. – Rup Apr 10 '13 at 00:28
  • 3
    Actually you can still use `getsockname()` but call it on the socket for the client connection, not the socket for the bind. That'll show you which of your interfaces the client is talking to, which I assume will be the same one they'll need to use for the auxiliary data connection. – Rup Apr 10 '13 at 00:30
  • @Rup Ok, and then get the ip address for this interface with `ioctl`? Can you post it as an answer so I can validate? Thanks. – Julien Fouilhé Apr 10 '13 at 00:34
  • As similar question here: http://stackoverflow.com/q/10956890/694576 – alk Apr 10 '13 at 08:44

4 Answers4

15

When you bind() a socket to 0.0.0.0, that is the only IP the socket has available when calling getsockname(). It means the socket is bound to all local interfaces. In order to get a specific IP from a socket, it has to be bound to a specific IP.

Using the socket API to get the machine's local IP(s) is the wrong approach anyway. A common mistake is to use gethostname() with gethostbyname() or getaddrinfo() to get the local IP list. Usually that works, but it has some hidden gotchas that can cause false information, but people tend to ignore that fact, or don't even know about it in the first place (I didn't know about it for years, but then I learned better).

Instead, you really should use platform-specific APIs for enumerating the local networking interfaces. That will provide more reliable information. Windows has GetAdaptersInfo() and GetAdaptersAddresses(). Other platforms have getifaddrs(). Those will tell you what local IPs are available. You can then bind() a socket to 0.0.0.0 in order to accept clients on any of those IPs, or bind() to a specific IP to accept clients only on that IP.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Could you elaborate on the "*... hidden gotchas ...*" in `getaddrinfo()`, please? – alk Apr 10 '13 at 06:20
  • `gethostbyname()` and `getaddrinfo()` are designed to use DNS lookups to resolve hostnames to IPs. They are not meant to retrieve info about localhost. It is possible for a) a user to alter/corrupt the OS's DNS cache (hosts file, etc), and b) a PC's hostname to be configured to not have an IP at all or to map to multiple IPs which may not all belong to the PC (think load balancing, etc). Those functions are not 100% guaranteed to return valid local IPs when passed a local hostname in all setups. Functions like `GetAdaptersInfo()` and `getifsaddrs()` are designed for that purpose instead. – Remy Lebeau Apr 10 '13 at 08:31
  • Oh yes, the **external** DNS involved is what you are basically referring to. I do agree on this. Thanks for clarifing. – alk Apr 10 '13 at 08:41
  • That works perfectly (`getifaddrs()`), and it is easy to implement this, thanks ! – Julien Fouilhé Apr 11 '13 at 10:01
  • on Windows this code does not result into a DNS query and returns all local IP addresses: gethostname(hostname, 255); for (getaddrinfo(hostname, 0, 0, &ai); ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET) continue; // now use ai->ai_addr – bebbo Aug 16 '14 at 20:15
  • `gethostbyname()` and `getaddrinfo()` on Windows are subject to the same pitfalls I mentioned earlier. Just because you usually do not see them happen does not mean they cannot happen at all (and I have seen reports of them happening, even on Windows). The *correct* way to get local IPs on Windows is to use `GetAdaptersInfo()` or `GetAdaptersAddresses()`. – Remy Lebeau Aug 17 '14 at 03:07
  • This was *super* helpful for me. I was using RakNet in C++ for networking and their code internally called `gethostbyname()`. On certain networks, the call was taking a *very* long time (~30s) just to get the local IP address. It seems like one of your "gotchas". Changing their internal code to use `getifaddrs()` instead appeared to fix the problem. Thanks so much for posting this answer! – aardvarkk Jun 01 '16 at 21:50
5

The sockets API allows you to enumerate the IP addresses assigned to your network interfaces, but it will not tell you what you "real IP" is if you are connecting to the Internet from behind a router.

The only way to know it is by asking someone outside. Thats how servers like FileZilla FTP Server do that. They instruct you to configure the URL to a "ip.php" script like this one in the server's settings so it can ask the Internet whats its public IP address, to use in Passive Mode.

You can also consider using STUN, a protocol widely used in VoIP to discover public IP.

Havenard
  • 27,022
  • 5
  • 36
  • 62
  • 1
    +1. In case OP wants more options, I personally like [ifconfig.me](http://ifconfig.me), and [canhazip.com](http://canhazip.com). – jweyrich Apr 10 '13 at 00:42
  • Thanks,that's not adapted for my current needs (it's a school project, and it is not meant to work from the internet). I'll see if I have time to implement it. – Julien Fouilhé Apr 10 '13 at 00:44
1

You could call ioctl(sock, SIOCGIFADDR, adr)

see netdevice(7)

Grandswiss
  • 93
  • 1
  • 4
0

Following @Remy Lebeau's answer I wrote a function that return current machine's address. I have only tested this on macOS High Sierra.

interfaec can be anything among lo0, en0, etc.

ipVersion can be AF_INET or AF_INET6.

long int getInternalAddress(char* interface, sa_family_t ipVersion)
{
  struct ifaddrs *ifaddrHead, *ifaddr;
  /* int_8 */
  sa_family_t family;
  int n;
  char *interfaceName;

  if (getifaddrs(&ifaddrHead) != 0)
  {
    fprintf(stderr, "ifaddrs error");
  }

  /* iterate through address list */
  for (ifaddr = ifaddrHead, n = 0; ifaddr != NULL; ifaddr = ifaddr->ifa_next, n++)
  {
    family = ifaddr->ifa_addr->sa_family;
    interfaceName = ifaddr->ifa_name;

    if (!family || family != ipVersion || strcmp(interfaceName, interface)) continue;

    struct sockaddr *addr = ifaddr->ifa_addr;
    struct sockaddr_in* addr_in = (struct sockaddr_in*) addr;
    long int address = addr_in->sin_addr.s_addr;

    freeifaddrs(ifaddrHead);

    return address;
  }

  freeifaddrs(ifaddrHead);

  return 0;
}

To use it,

int main()
{
  long int address = getInternalAddress((char*) &"en0", AF_INET);
  printf("%li\n", address);

  return 0;
}

I'm still a beginner in C, if there is anything wrong please tell me.

Yuan Fu
  • 191
  • 2
  • 15