Home > Mobile >  How to programmatically get IPv4 address of a host with getaddrinfo
How to programmatically get IPv4 address of a host with getaddrinfo

Time:11-23

Using an example from the manual I am trying to write a simple program that gets a list of IPv4 address of a host:

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main() {
    struct addrinfo hints;
    struct addrinfo *result, *rp;

    /* Obtain address(es) matching host/port */

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = AF_INET;    /* IPv4 */
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = 0;
    hints.ai_protocol = 0;          /* Any protocol */

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

    /* getaddrinfo() returns a list of address structures.
       Try each address until we successfully connect(2).
       If socket(2) (or connect(2)) fails, we (close the socket
       and) try the next address. */

    for (rp = result; rp != NULL; rp = rp->ai_next) {
        puts(rp->ai_addr->sa_data);
    }

    if (rp == NULL) {
        fprintf(stderr, "No address succeeded\n");
        exit(EXIT_FAILURE);
    }

    return 0;
}

I was expecting to get a list of IP addresses from the for loop. But, unfortunately, the program outputs empty lines.

What is wrong?

CodePudding user response:

The getaddrinfo() functions returns a linked list of struct addrinfo. Each entry has a struct_sockaddr *ai_addr member, which is a generic structure for representing network addresses. You need to cast that to a protocol specific structure -- in this case, struct sockaddr_in, which looks like this:

struct sockaddr_in {
   sa_family_t    sin_family; /* address family: AF_INET */
   in_port_t      sin_port;   /* port in network byte order */
   struct in_addr sin_addr;   /* internet address */
};

struct in_addr {
   uint32_t       s_addr;     /* address in network byte order */
};

In struct sockaddr_in, the ip address is represented as a 32-bit integer; to convert that into the common dotted-octet format, we need to use the inet_ntoa function, making your code look like this:

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

int main() {
    struct addrinfo hints;
    struct addrinfo *result, *rp;

    /* Obtain address(es) matching host/port */

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = AF_INET;    /* IPv4 */
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = 0;
    hints.ai_protocol = 0;          /* Any protocol */

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

    /* getaddrinfo() returns a list of address structures.
       Try each address until we successfully connect(2).
       If socket(2) (or connect(2)) fails, we (close the socket
       and) try the next address. */

    for (rp = result; rp != NULL; rp = rp->ai_next) {
        printf("%s\n", inet_ntoa(((struct sockaddr_in*)rp->ai_addr)->sin_addr));
    }

    return 0;
}

Note that I've removed the if (rp == NULL) bit from the end, because after the for loop rp is always equal to NULL (that's the exit condition of the loop).

Running the above code produces:

142.250.80.78

To make the code present both ipv4 and ipv6 addresses you would use inet_ntop instead of inet_ntoa; that would look like:

char dst[1024];

for (rp = result; rp != NULL; rp = rp->ai_next) {
    if (rp->ai_family == AF_INET) {
        if (NULL != inet_ntop(AF_INET, &((struct sockaddr_in *)rp->ai_addr)->sin_addr, dst, sizeof(dst))) {
            printf("ipv4: %s\n", dst);
        } else {
            perror("inet_ntop");
        }
    } else if (rp->ai_family == AF_INET6) {
        if (NULL != inet_ntop(AF_INET6, &((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr, dst, sizeof(dst))) {
            printf("ipv6: %s\n", dst);
        } else {
            perror("inet_ntop");
        }
    } else {
        printf("wtf? %d", rp->ai_family);
    }
}

The above code would output:

ipv6: 2607:f8b0:4006:81f::200e
ipv4: 142.250.80.110
  • Related