user442020
user442020

Reputation:

When i do getaddrinfo for localhost, I don't receive 127.0.0.1

I am still learning sockets and am unclear why this doesn't print out 127.0.0.1. Even if I replace the word localhost with 127.0.0.1 I receive some other ip's which I guess are my router or something. I always thought this should return 127.0.0.1. Here's the output I receive:

hostname: 28.30.0.0
hostname: 28.30.0.0
hostname: 28.30.0.0
hostname: 28.30.0.0
hostname: 16.2.0.0
hostname: 16.2.0.0

Here is the basic code:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>

int main()
{
    struct addrinfo* feed_server = NULL;

    getaddrinfo("localhost", NULL, NULL, &feed_server);
    struct addrinfo *res;
    for(res = feed_server; res != NULL; res = res->ai_next)
    {   
        printf("hostname: %s\n", inet_ntoa(*((struct in_addr*)(res->ai_addr))));
    } 

    return 0;
}

Upvotes: 5

Views: 21431

Answers (3)

Matt
Matt

Reputation: 1901

There are two problems with the original code:

  1. The ai_addr member points to a sockaddr and not a struct in_addr so casting it like that will always produce incorrect results.
  2. Unless you pass a hints that is not NULL and with the af_family member set to AF_INET, you cannot expect all returned addresses to be IPv4 (struct sockaddr_in type). So you can provide the hints to specify IPv4 or check the af_family member of the resulting addrinfo structs.

One thing I typically see at least on Linux systems is that getaddrinfo for localhost usually returns the IPv6 ::1 address first.

From the addresses being printed I can tell you are running on an OS that includes the sockaddrs length in the struct. For example the definition of struct sockaddr on OS X is:

 struct sockaddr {
      __uint8_t       sa_len;         /* total length */
      sa_family_t     sa_family;      /* [XSI] address family */
      char            sa_data[14];    /* [XSI] addr value (actually larger) */
 };

For both struct sockaddr_in and sockaddr_in6 the very next member after sa_family is the port which is always two bytes. So when you cast either of these structs to a struct in_addr you will get an address that is sa_len.sa_family.0.0 (assuming you don't provide a port to getaddrinfo - if you provide a port the 0.0 will be replaced with the ports byte values).

So gettaddr info is returning you two IPv6 addresses: 28.30.0.0 - sizeof struct sockaddr_in6 = 28 and af_family = 30

and two IPv4 addresses: 16.2.0.0 - sizeof struct sockaddr_in = 16 and af_family = 2

To do this properly you could do what the other answer said and use getnameinfo. However using inet_ntop (not inet_ntoa) can be equally as good.

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h> /* for memset */

int main()
{
   char addr_buf[64];
   struct addrinfo* feed_server = NULL;

   memset(addr_buf, 0, sizeof(addr_buf));

   getaddrinfo("localhost", NULL, NULL, &feed_server);
   struct addrinfo *res;
   for(res = feed_server; res != NULL; res = res->ai_next)
   {   
       if ( res->ai_family == AF_INET )
       {
          inet_ntop(AF_INET, &((struct sockaddr_in *)res->ai_addr)->sin_addr, addr_buf, sizeof(addr_buf));
       }
       else
       {
          inet_ntop(AF_INET6, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, addr_buf, sizeof(addr_buf));
       }

       printf("hostname: %s\n", addr_buf); 
   } 

   return 0;
}

```

Upvotes: 6

Sergiy Zaschipas
Sergiy Zaschipas

Reputation: 182

You should use hints for call of getaddrinfo. Because to resolve "localhost" or any /etc/hosts record hints.af_family must be set to AF_INET.

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>

int main()
{

   struct addrinfo hints;
   memset(&hints, 0, sizeof(struct addrinfo));
   hints.ai_family = AF_INET;
   getaddrinfo("localhost", NULL, &hints, &feed_server);
   struct addrinfo *res;
   for(res = feed_server; res != NULL; res = res->ai_next){   
      struct sockaddr_in* saddr = (struct sockaddr_in*)res->ai_addr;
      printf("hostname: %s\n", inet_ntoa(saddr->sin_addr))
   } 
   return 0;
}

Upvotes: 3

John Ledbetter
John Ledbetter

Reputation: 14183

res->ai_addr is of type struct sockaddr*, not struct in_addr*.

You need to do something like this:

for(res = feed_server; res != NULL; res = res->ai_next)
{
    /* ideally look at the sa_family here to make sure it is AF_INET before casting */
    struct sockaddr_in* saddr = (struct sockaddr_in*)res->ai_addr;
    printf("hostname: %s\n", inet_ntoa(saddr->sin_addr));
} 

Upvotes: 4

Related Questions