getaddrinfo() returning unexpected results

32 Views Asked by At

I'm trying to learn network programming in C, I'm currently working on an echo server, a server which sends back any string it receives, I have the following code:

You can skip the entire code section to get to the actual problem

server.c

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

#define PORT "8080"
#define BUFFLEN 1024

char *strin(FILE *stream);
void printaddrs(struct addrinfo *res);

int main() {
    struct addrinfo hints, *res;
    // sockfd -> server file descriptor (serverfd)
    int sockfd, clientfd;
    char buffer[BUFFLEN];

    memset(&hints, 0, sizeof(hints)); // Make sure 'hints' is empty
    // Fill in the 'hints' addrinfo structure
    hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 (IPv4 or IPv6)
    hints.ai_socktype = SOCK_STREAM; // TCP
    hints.ai_flags = AI_PASSIVE; // Use my address
    printf("'hints' filled successfully!\n");

    if (getaddrinfo(NULL, PORT, &hints, &res) != 0) {
        printf("getaddrinfo(): Error!\n");
    }
    printf("getaddrinfo(): Success!\n");

    // print all addresses returned by getaddrinfo()
    printaddrs(res);

    printf("Select an address to bind to: ");
    // strin -> takes input from FILE *stream no matter how long it is!!!
    char *id = strin(stdin);
    if (id == NULL) {
        printf("No input!\n");
        freeaddrinfo(res);
        free(id);
        return 1;
    }
    struct sockaddr_in *ipv4;
    struct sockaddr_in6 *ipv6;
    char addrstr[INET6_ADDRSTRLEN];
    struct addrinfo *p;
    int count = 0, type, family;
    char ipstr[INET6_ADDRSTRLEN], ipver[5];

    for (; res != NULL; res = res->ai_next) {
        if (atoi(id) == count) {
            if (res->ai_family == AF_INET) { // IPv4
                ipv4 = (struct sockaddr_in *) res->ai_addr;
                inet_ntop(res->ai_family, ipv4, addrstr, sizeof(addrstr));

            } else if (res->ai_family == AF_INET6) { // IPv6
                ipv6 = (struct sockaddr_in6 *) res->ai_addr;
                inet_ntop(res->ai_family, ipv6, addrstr, sizeof(addrstr));
            }
            printf("Chosen address: %s\n", addrstr);
            break;
        }
        strcpy(addrstr, "");
        count++;
    }
    free(id);

    if (strcmp(addrstr, "") == 0) {
        printf("Couldn't find entered id.\n");
        freeaddrinfo(res);
        return 1;
    }

    // Create the socket
    sockfd = socket(res->ai_family, res->ai_socktype, 0);
    if (sockfd == -1) {
        printf("socket(): Failure!\n");
        freeaddrinfo(res);
        return 1;
    }
    printf("socket(): Success!\n");

    // Bind the socket to an IP address and a port
    if (bind(sockfd, res->ai_addr, res->ai_addrlen) == -1) {
        printf("bind(): Failure!\n");
        freeaddrinfo(res);
        return 1;
    }
    printf("bind(): Success!\n");

    if (listen(sockfd, 5) == -1) {
        printf("listen(): Failure!\n");
        freeaddrinfo(res);
        return 1;
    }

    printf("Socket listening on port %s...\n", PORT);

    struct sockaddr *clientaddr;
    char clientaddrstr[INET6_ADDRSTRLEN];
    while (1) {
        clientfd = accept(sockfd, clientaddr, (int *)sizeof(clientaddr));
        if (clientfd == -1) {
            printf("accept(): Failure!\n");
            freeaddrinfo(res);
            return 1;
        }
        printf("accept(): Success!\n");
        inet_ntop(clientaddr->sa_family, clientaddr, clientaddrstr, sizeof(clientaddrstr));
        printf("Accepted a conenction to %s\n", clientaddrstr);
    }
    freeaddrinfo(res);
    return 0;
}

client.c

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

#define PORT "8080"
#define BUFFLEN 1024

/** TODO
 * getaddrinfo() to get my address
 */

void printaddrs(struct addrinfo *res);
char *strin(FILE *stream);

int main() {
    printf("Choose an address to connect to: ");
    char *servaddr = strin(stdin);
    // Initialize variables to be used later
    struct addrinfo hints, *res;
    int clientfd;

    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;

    if (getaddrinfo(servaddr, PORT, &hints, &res) != 0) {
        printf("getaddrinfo(): Failure!\n");
        free(servaddr);
        freeaddrinfo(res);
        return 1;
    }

    printf("getaddrinfo(): Success!\n");

    clientfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

    if (clientfd == -1) {
        printf("socket(): Failure!\n");
        freeaddrinfo(res);
        return 1;
    }
    printf("socket(): Success!\n");

    if (connect(clientfd, res->ai_addr, res->ai_addrlen) == -1) {
        printf("connect(): Failure!\n");
        freeaddrinfo(res);
        return 1;
    }
    printf("connect(): Success!\n");
    printf("Connecting to %s...\n", servaddr);
    free(servaddr);

    freeaddrinfo(res);
    return 0;
}

Here's the printaddrs() function

void printaddrs(struct addrinfo *res) {
    struct addrinfo *p;
    int count = 0;
    char ipstr[INET6_ADDRSTRLEN], ipver[5];
    for (p = res; p != NULL; p = p->ai_next) {
        if (p->ai_family == AF_INET) { // IPv4
            struct sockaddr_in *ipv4 = (struct sockaddr_in *) p->ai_addr;
            inet_ntop(p->ai_family, ipv4, ipstr, sizeof(ipstr));
            strcpy(ipver, "ipv4");
        } else if (p->ai_family == AF_INET6) { // IPv6
            struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *) p->ai_addr;
            inet_ntop(p->ai_family, ipv6, ipstr, sizeof(ipstr));
            strcpy(ipver, "ipv6");
        } else { // Other IP format
            continue;
        }
        printf("%d: %s\t:\t%s\n", count, ipstr, ipver);
        count++;
    }
}

And here's strin()

char *strin(FILE *stream) {
    char *str = NULL;
    size_t size = 0;

    int c;
    int i = 0;
    while (1) {
        c = getc(stream);
        if (stream != stdin) {
            if(c == EOF)
                break;
        } else {
            if(c == EOF || c =='\n')
                break;
        }
        size++;
        str = realloc(str, size * sizeof(char));
        *(str + i) = c;
        i++;
    }
    if (size == 0) {
        return NULL;
    }
    str[size] = '\0';
    return str;
}

Here's the problem (Finally...)

When I run server.c, getaddrinfo() returnes the *res structure, which is a pointer to a linked list of addresses, the output looks like this:

0: 2.0.31.144 : ipv4 1: a00:1f90:: : ipv6

I would have expected to get the localhost address, 127.0.0.1 for IPv4 and ::1 for IPv6 but apparently not, when I enter 0 to choose the ipv4 address:

'hints' filled successfully! getaddrinfo(): Success! 0: 2.0.31.144 : ipv4 1: a00:1f90:: : ipv6 Select an address to bind to: 0 Chosen address: 2.0.31.144 socket(): Success! bind(): Success! Socket listening on port 8080...

And then connect to that address when running client.c

Choose an address to connect to: 2.0.31.144 getaddrinfo(): Success! socket(): Success! connect(): Failure!

The connect function doesn't work...

Does anybody have any suggestions?

Thank you all in advance for your answers, I really appreciate it!

I tried asking artificial intelligence for a solution... Not the smartest move, I know... I'm getting pretty desperate

1

There are 1 best solutions below

2
selbie On BEST ANSWER

Instead of this:

inet_ntop(p->ai_family, ipv4, ipstr, sizeof(ipstr));

This:

inet_ntop(p->ai_family, &ipv4->sin_addr, ipstr, sizeof(ipstr));

Similar treatment for the ipv6 struct:

inet_ntop(p->ai_family, &ipv6->sin6_addr, ipstr, sizeof(ipstr));

This will print 0.0.0.0 and :: for your two addresses. Which I believe is to be expected for when using AI_PASSIVE. These are the "any" address which tells server code to bind to all adapters - which is usually what you want.

If you remove the AI_PASSIVE flag from your getaddrsinfo call, you'll likely get 127.0.0.1 and ::1 printed. These are the localhost addresses.