0

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?

1 Answer 1

3

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
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.