ahat ahat - 14 days ago 5
C Question

Why are there multiple results from getaddrinfo?

I am trying to create a simple program that gets the ip address given a certain hostname:

My code snipped is attached below:

#include<stdio.h>
#include<stdlib.h>
#include<stdio.h>
#include<netdb.h>
#include<sys/socket.h>
#include<errno.h>
#include<arpa/inet.h>
#include<string.h>
#include<unistd.h>

int main(int argc, char *argv[]) {
if(argc<2){
printf("Please provide a hostname.\n");
exit(1);
}

char *hostname = argv[1];
char ip[100];
get_ip(hostname,ip);
printf("%s resolved to %s\n",hostname,ip);
}

int get_ip(char *hostname,char *ip){
struct sockaddr_in *h;
int sockfd;
struct addrinfo hints, *servinfo,*res;
struct addrinfo *iter;
int rv;

memset(&hints,0,sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if((rv = getaddrinfo(hostname,"http",&hints, &res))!=0){
fprintf(stderr, "getaddrinfo %s\n",gai_strerror(rv));
return 1;
}
for(iter=res;iter != NULL;iter=iter->ai_next){
printf("%p\n",iter->ai_next);

h=(struct sockaddr_in *)iter->ai_addr;
strcpy(ip,inet_ntoa(h->sin_addr));
printf("%s\n",ip);
}
freeaddrinfo(res);
return 0;
}


I enter in the following arguments:

gcc get_ip_addr.c -o get_ip_addr;
./get_ip_addr google-public-dns-b.google.com


This results in:

0x2475330
8.8.4.4
(nil)
0.0.0.0


When i remove the "http" and &hints and set them to NULL I get the following results:

0x1b63310
8.8.4.4
0x1b63360
8.8.4.4
0x1b633b0
8.8.4.4
0x1b63410
0.0.0.0
0x1b63470
0.0.0.0
(nil)
0.0.0.0


So when I set the service and hints to NULL the getaddrinfo returns multiple possible results I don't understand why I am getting multiple ip addresses instead of just getting one ip address?
Any help would be greatly appreciated! Thanks!

Answer

By setting the service and hints field to NULL, you are asking getaddrinfo to return all possible addresses, which includes IP and IPv6, with different socket types, what you output is just the dot representation of ip address, and that's only part of the entire address structure getaddrinfo returned. If you look carefully the returned addresses, you will notice that each address is actually different. For example, following program output 6 addresses in total:

int main(int argc, char *argv[])
{
    struct addrinfo *result, *rp;
    int s;

    s = getaddrinfo("google-public-dns-b.google.com", NULL, NULL, &result);
    if (s != 0) {
            fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
            exit(EXIT_FAILURE);
    }

    char addr[1024];
    for (rp = result; rp != NULL; rp = rp->ai_next) {
            printf("flags: 0x%x\tfamily: %d\tsocktype: %d\tprotocol: %d\n",
                            rp->ai_flags,
                            rp->ai_family,
                            rp->ai_socktype,
                            rp->ai_protocol);
            s = getnameinfo(rp->ai_addr, rp->ai_addrlen, addr, sizeof addr, NULL, 0, NI_NUMERICHOST);
            if (s != 0) {
                    printf("getnameinfo error:%d\n", s);
                    continue;
            }
            printf("addr: %s\n", addr);
    }

    freeaddrinfo(result);
}


$ ./a.out
flags: 0x28 family: 2       socktype: 1     protocol: 6
addr: 8.8.4.4
flags: 0x28 family: 2       socktype: 2     protocol: 17
addr: 8.8.4.4
flags: 0x28 family: 2       socktype: 3     protocol: 0
addr: 8.8.4.4
flags: 0x28 family: 10      socktype: 1     protocol: 6
addr: 2001:4860:4860::8844
flags: 0x28 family: 10      socktype: 2     protocol: 17
addr: 2001:4860:4860::8844
flags: 0x28 family: 10      socktype: 3     protocol: 0
addr: 2001:4860:4860::8844

As you can see, each address returned are different, in terms of combination of address, family, socktype and protocol. If you would like to return one specific type of address, use hints to restrict the returned addresses to what you want. Each data field explained:

Family:

#define PF_INET         2       /* IP protocol family.  */
#define PF_INET6        10      /* IP version 6.  */
#define AF_INET         PF_INET
#define AF_INET6        PF_INET6

Socktypes:

enum __socket_type
{
  SOCK_STREAM = 1,              /* Sequenced, reliable, connection-based
                               byte streams.  */
  SOCK_DGRAM = 2,               /* Connectionless, unreliable datagrams
                               of fixed maximum length.  */
  SOCK_RAW = 3,                 /* Raw protocol interface.  */
....
}

Protocols:

enum {
  IPPROTO_IP = 0,               /* Dummy protocol for TCP               */

  IPPROTO_TCP = 6,              /* Transmission Control Protocol        */

  IPPROTO_UDP = 17,             /* User Datagram Protocol               */
}

Another issue, you were see "0.0.0.0" in your output because you are using inet_ntoa on IPv6 address while this function only support IPv4, and thus it has been deprecated, you should consider using inet_ntop or getnameinfo which supports both address families.