Ipv6 server accepting Ipv4 connections

Asked

Viewed 94 times

2

I wrote a server in C using the API socket UNIX for some tests and I ended up encountering a "weird" problem: The server is cutting connections Ipv4 being that it was programmed to accept only connections Ipv6.

Follow the code and a few details below for a better understanding of the problem:

server. c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include <netdb.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>

int make_server_socket(void){

    int sockfd;
    struct addrinfo *res=NULL, hints;

    memset(&hints, 0, sizeof(struct addrinfo));

    hints.ai_flags=AI_PASSIVE;
    hints.ai_family=AF_INET6;
    hints.ai_socktype=SOCK_STREAM;
    hints.ai_protocol=IPPROTO_TCP;

    if(getaddrinfo(NULL, "9009", &hints, &res)!=0){

        printf("\n* getaddrinfo() -> failed\n");
        exit(1);

    }else{

        struct addrinfo *it;

        for(it=res; it!=NULL; it=it->ai_next){

            if(it->ai_family==AF_INET || it->ai_family==AF_INET6){

                sockfd=socket(it->ai_family, SOCK_STREAM, IPPROTO_TCP);

                if(sockfd<0){

                    continue;

                }else{

                    if(bind(sockfd, it->ai_addr, it->ai_addrlen)==0){

                        break;
                    }
                }

                close(sockfd);
            }
        }

        freeaddrinfo(res);
    }

    return sockfd;
}

void show_addr(const struct sockaddr *addr){

    char strAddr[64];

    if(addr->sa_family==AF_INET){

        struct sockaddr_in *addr4=(struct sockaddr_in*)addr;

        inet_ntop(AF_INET, &addr4->sin_addr.s_addr,
                  strAddr, INET_ADDRSTRLEN);

        printf("[ipv4 addr] -> %s\n", strAddr);

    }else{

        struct sockaddr_in6 *addr6=(struct sockaddr_in6*)addr;

        inet_ntop(AF_INET6, addr6->sin6_addr.s6_addr,
                  strAddr, INET6_ADDRSTRLEN);

        printf("[ipv6 addr] -> %s\n", strAddr);
    }
}

int check_addr(const char *buffAddr, char *buffAddrTable[], size_t n){

    int rv=0;

    struct sockaddr *addr=(struct sockaddr*)buffAddr;
    struct sockaddr *addrs=(struct sockaddr*)buffAddrTable;

    if(addr->sa_family==AF_INET){

        struct sockaddr_in *aux4=(struct sockaddr_in*)buffAddr;

        for(size_t i=0; i<n; i++){

            struct sockaddr_in *addr4=(struct sockaddr_in*)&addrs[i];

            if(addr4->sin_addr.s_addr==aux4->sin_addr.s_addr){

                rv=-1;

                break;

            }else{

                rv=0;
            }
        }

    }else if(addr->sa_family==AF_INET6){

        struct sockaddr_in6 *aux6=(struct sockaddr_in6*)buffAddr;

        for(size_t i=0; i<n; i++){

            struct sockaddr_in6 *addr6=(struct sockaddr_in6*)&addrs[i];

            int res=memcmp(addr6->sin6_addr.s6_addr,
                           aux6->sin6_addr.s6_addr, 16);

            if(res==0){

                rv=-1;

                break;

            }else{

                rv=0;
            }
        }

    }else{

        rv=-2;
    }

    return rv;
}

int main(void){

    int sockfd=make_server_socket();

    if(sockfd<0){

        printf("\n* make_server_socket() -> failed\n");
        exit(1);
    }

    if(listen(sockfd, 10)<0){

        printf("\n* listen() -> failed\n");
        exit(1);
    }

    size_t max;

    printf("\nEnter the maximum number of connections >");
    scanf("%ld", &max);

    printf("\n");

    int csockfd;
    socklen_t socklen;
    char buffAddr[1024];
    char *buffAddrTable[max];

    for(size_t i=0; i<max;){

        socklen=sizeof(buffAddr);

        csockfd=accept(sockfd, (struct sockaddr*)buffAddr, &socklen);

        if(csockfd!=-1){

            if(check_addr(buffAddr, buffAddrTable, i)==0){

                memcpy(buffAddrTable, buffAddr, socklen);

                show_addr((struct sockaddr*)buffAddr);

                send(csockfd, "Bonjour!\0", 9, 0);

                i++;
            }

            close(csockfd);
        }
    }

    printf("\n");

    close(csockfd);

    return 0;
}

Why did I use char type to handle network addresses?


Client (netcat):

zherkezhi@zherkezhi:~/Documents/C$ nc 127.0.0.1 9009
zherkezhi@zherkezhi:~/Documents/C$ nc ::1 9009

(em outra maquina ligada a rede local)

erika@localhost:~/Desktop$ nc 192.168.50.202 9009

Server:

zherkezhi@zherkezhi:~/Documents/C$ gcc -Wall server.c -o server
zherkezhi@zherkezhi:~/Documents/C$ ./server

Enter the maximum number of connections >3

[ipv6 addr] -> ::ffff:127.0.0.1
[ipv6 addr] -> ::1
[ipv6 addr] -> ::ffff:192.168.50.18

zherkezhi@zherkezhi:~/Documents/C$ #WTF??? 

So what can I do to prevent someone from connecting to my server using an address Ipv4 when it is configuring to use Ipv6?


Curiosity: ffff converted to decimal is equal to 65535. 65535 is number of doors TCP’s existing at the moment (in fact they are 65536, but said 65535 because I didn’t consider the 0). So there is some relationship between that value and my problem?

1 answer

0


Addresses of the type ::ffff:127.0.0.1 are called Ipv4-Mapped Ipv6 Addresses. Behold here and here a small explanation about them. They are used in systems with TCP/IP implementation dual stack, that is, they work at the same time with Ipv4 and Ipv6.

As to force the use of Ipv6 only, you need to use the IPV6_V6ONLY option after creating the socket. The Wikipedia reference above talks about it, and this Linux documentation page also quotes this option.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.