7

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?

BlueDiary9
  • 95
  • 2
  • 6
ahat
  • 343
  • 3
  • 9
  • 1
    Summary of the answer @fluter gives below: a hostname can have more than one IP address. – Andrew Henle Nov 24 '16 at 11:58
  • 1
    And one IP address can have different configurations, `getaddrinfo` returns address configurations instead of IP address. – fluter Nov 24 '16 at 12:05

1 Answers1

18

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);
    return 0;
}


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

Kyrol
  • 3,475
  • 7
  • 34
  • 46
fluter
  • 13,238
  • 8
  • 62
  • 100
  • I tried your code, but I get the address to be 2.0.0.0. I understand now why I am getting the different addresses after printing out the socket and protocol etc. But running your code I am not getting the same output as you and it seems that for your protocol you are supposed to outputting hexadecimal, but your output is a decimal. Based on the difference in outputs I am guessing something just got lost in translation? Any help would be nice. If need be I can paste the output that I am seeing. – ahat Nov 26 '16 at 02:47
  • I got the address to be 2.0.0.0 where you are outputting 8.8.4.4 (I know this is the right ip). – ahat Nov 26 '16 at 02:58
  • no problem! I figured it was something minor like that! Now it works (and makes sense) and I can follow what getnameinfo is doing! Thanks! :) – ahat Nov 26 '16 at 03:36
  • the answer must be pinned as a reference! +1 – Soner from The Ottoman Empire May 13 '19 at 19:08