Question

Suppose the following code, which mimics the basic functionality of the resolveip utility:

#define _POSIX_SOURCE     /* getaddrinfo() */
#include <sys/types.h>    /* getaddrinfo(), struct addrinfo, struct sockaddr */
#include <sys/socket.h>   /* getaddrinfo(), struct addrinfo, struct sockaddr, AF_* */
#include <netdb.h>        /* getaddrinfo(), struct addrinfo, struct sockaddr */
#include <arpa/inet.h>    /* inet_ntop() */
#include <stdio.h>        /* fprintf(), printf(), perror(), stderr */
#include <stdlib.h>       /* EXIT_SUCCESS */

int main(int argc, char** argv) {
  for(int i = 1; i < argc; ++i) { /* For each hostname */

    char* hostname = argv[i];    
    struct addrinfo* res;  /* We retrieve the addresses */
    if(getaddrinfo(hostname, NULL, NULL, &res) == -1) {
      perror("getaddrinfo");
      continue;
    }

    for(; res->ai_next; res = res->ai_next) { /* We print the addresses */
      switch(res->ai_addr->sa_family) {
        case AF_INET: {
          struct in_addr addr = ((struct sockaddr_in*)(res->ai_addr))->sin_addr;
          char buffer[17]; printf("%s: %s\n", hostname, inet_ntop(AF_INET, &addr, buffer, 17)); break;
        } case AF_INET6: {
          struct in6_addr addr = ((struct sockaddr_in6*)(res->ai_addr))->sin6_addr;
          char buffer[40]; printf("%s: %s\n", hostname, inet_ntop(AF_INET6, &addr, buffer, 40)); break;
        } default: {
          fprintf(stderr, "%s: Unknown address family\n", hostname);
        }
      }
    }

    freeaddrinfo(res); /* We release the allocated resources */

  } return EXIT_SUCCESS;
}

The above code invoked with google.com as its first and only argument will output something akin to:

google.com: 173.194.35.87
google.com: 173.194.35.87
google.com: 173.194.35.87
google.com: 173.194.35.95
google.com: 173.194.35.95
google.com: 173.194.35.95
google.com: 173.194.35.88
google.com: 173.194.35.88
google.com: 173.194.35.88
google.com: 2a00:1450:4008:800::101f
google.com: 2a00:1450:4008:800::101f

Suppose we want to get rid of the duplicate entries. Let us thus create a structure containing hints about what sort of results we wish to retrieve. The following modification does not affect the output in any way, since the structure is initialized to default values as mandated by the getaddrinfo(3) manpage:

struct addrinfo hints = {
  .ai_family = AF_UNSPEC,
  .ai_socktype = 0,
  .ai_protocol = 0,
  .ai_flags = (AI_V4MAPPED | AI_ADDRCONFIG)
}; if(getaddrinfo(hostname, NULL, &hints, &res) == -1) {
  perror("getaddrinfo");
  continue;
}

Let us now filter out the duplicate entries by specifying the ai_socktype field to an arbitrary value:

struct addrinfo hints = {
  .ai_family = AF_UNSPEC,
  .ai_socktype = SOCK_DGRAM,
  .ai_protocol = 0,
  .ai_flags = (AI_V4MAPPED | AI_ADDRCONFIG)
};

Alas, we have now lost the IPv6 address:

google.com: 173.194.35.87
google.com: 173.194.35.95
google.com: 173.194.35.88

Let us now revert back to the original hint-less version:

if(getaddrinfo(hostname, NULL, NULL, &res) == -1) {
  perror("getaddrinfo");
  continue;
}

And let us now use manual filtering instead:

for(; res->ai_next; res = res->ai_next) {
  if(res->ai_socktype != SOCK_DGRAM) continue;
  ...
}

And now everything works the way it is supposed to:

google.com: 173.194.35.87
google.com: 173.194.35.95
google.com: 173.194.35.88
google.com: 2a00:1450:4008:801::101f

I am curious whence the discrepancy between passing a hint to the getaddrinfo(3) function and manual filtering of the returned records arises. Tested on linux kernel 3.8.0-32 with glibc 2.17.

Was it helpful?

Solution

Your for loop check is wrong, you're always skipping the last entry - which in your case happens to be the IPv6 address.

 for(; res->ai_next; res = res->ai_next) {

needs to be

 for(; res; res = res->ai_next) { 
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top