IPv6 server accepting IPv4 connections

2

I wrote a server in C using the UNIX sockets API for some tests and I ended up with a "weird" problem: The server is blocking connections > IPv4 and it was programmed to only accept IPv6 connections.

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!
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
", 9, 0); i++; } close(csockfd); } } printf("\n"); close(csockfd); return 0; }

Why I used the char type to handle network addresses ?

Client (netcat):

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??? 

Server:

#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!
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
", 9, 0); i++; } close(csockfd); } } printf("\n"); close(csockfd); return 0; }

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

Curiosity : ffff converted to decimal equals 65535 . 65535 is the number of TCP ports currently in existence (actually they are 65536 , but said 65535 because I did not consider 0 ). So, is there any relationship between this value and my problem?

    
asked by anonymous 10.11.2018 / 18:11

1 answer

0

Addresses of type ::ffff:127.0.0.1 are called IPv4-mapped IPv6 Addresses . See here and here a little explanation about them. They are used in systems with TCP / IP dual stack implementation, that is, they work at the same time with IPv4 and IPv6.

As for forcing the use of IPv6 only, you must use the IPV6_V6ONLY option after the socket is created. The Wikipedia reference above talks about this, and this Linux documentation page also cites this option.

    
10.11.2018 / 19:23