IPv4-mapped IPv6 addresses

1. IPv4-mapped IPv6 addresses

Osmund Saddler
Saddler

(usa Outra)

Enviado em 16/08/2019 - 11:23h

Seguinte, desenvolvi um pequeno servidor em C usando POSIX Sockets e gostaria de obter algumas opiniões e sugestões para melhoria de código.

Segue o(s) código(s) para análise:


server.c


#include <stdio.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

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

#define RCVTIMEO 3

static int set_socket_opts(int sockfd, bool use_only_ipv6){

    int rv;

    const int optval=1;
    socklen_t optlen=sizeof(optval);

    struct timeval rcv;
    socklen_t tvlen=sizeof(rcv);

    rcv.tv_sec=RCVTIMEO;
    rcv.tv_usec=0;

    do{

        if((rv=setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, optlen))!=0){

            break;
        }

        if((rv=setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &rcv, tvlen))!=0){

            break;
        }

        if(use_only_ipv6==true){

            rv=setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &optval, optlen);
        }

    }while(false);

    return rv;
}

static int make_server_socket(char *argv[]){

    bool use_only_ipv6;
    int af, ecode, sockfd;
    struct addrinfo hints, *rp=NULL, *res=NULL;

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

    if(strcmp(argv[1], "-4")==0){

        af=AF_INET;
        use_only_ipv6=false;

    }else if(strcmp(argv[1], "-M")==0){

        af=AF_INET6;
        use_only_ipv6=false;

    }else{

        af=AF_INET6;
        use_only_ipv6=true;
    }

    hints.ai_flags=AI_PASSIVE;
    hints.ai_family=af;
    hints.ai_socktype=SOCK_STREAM;
    hints.ai_protocol=IPPROTO_TCP;
    hints.ai_addr=NULL;
    hints.ai_canonname=NULL;
    hints.ai_next=NULL;

    if((ecode=getaddrinfo(NULL, argv[2], &hints, &res))!=0){

        fprintf(stderr, "* getaddrinfo() -> ERROR(ecode: %d): %s\n", ecode, gai_strerror(ecode));

        exit(EXIT_FAILURE);
    }

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

        sockfd=socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);

        if(sockfd<0){

            continue;

        }else{

            if(set_socket_opts(sockfd, use_only_ipv6)!=0){

                continue;
            }
        }

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

            break;
        }

        close(sockfd);
    }

    if(rp==NULL){

        fprintf(stderr, "* bind() -> ERROR(errno: %d): %s\n", errno, strerror(errno));

        exit(EXIT_FAILURE);

    }else{

        if(listen(sockfd, 10)!=0){

            close(sockfd);

            fprintf(stderr, "* listen() -> ERROR(errno: %d): %s\n", errno, strerror(errno));

            exit(EXIT_FAILURE);
        }
    }

    freeaddrinfo(res);

    res=NULL;
    rp=NULL;

    return sockfd;
}

static int check_addr(char **buff_addr_table, const char *buff_addr, int af, size_t count){

    int rv=0;

    size_t addrlen;

    if(af==AF_INET){

        struct sockaddr_in *addr4_from_table=NULL;
        struct sockaddr_in *addr4=(struct sockaddr_in*)buff_addr;

        addrlen=sizeof(addr4->sin_addr.s_addr);

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

            addr4_from_table=(struct sockaddr_in*)buff_addr_table[i];

            if(memcmp(&addr4_from_table->sin_addr.s_addr, &addr4->sin_addr.s_addr, addrlen)==0){

                rv=-1;

                break;
            }
        }

    }else if(af==AF_INET6){

        struct sockaddr_in6 *addr6_from_table=NULL;
        struct sockaddr_in6 *addr6=(struct sockaddr_in6*)buff_addr;

        addrlen=sizeof(addr6->sin6_addr.s6_addr);

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

            addr6_from_table=(struct sockaddr_in6*)buff_addr_table[i];

            if(memcmp(&addr6_from_table->sin6_addr.s6_addr, &addr6->sin6_addr.s6_addr, addrlen)==0){

                rv=-1;

                break;
            }
        }

    }else{

        rv=-2;
    }

    return rv;
}

static void show_addr(const struct sockaddr *addr){

    char *str_addr=NULL;

    if(addr->sa_family==AF_INET){

        str_addr=malloc(INET_ADDRSTRLEN*sizeof(*str_addr));

        if(str_addr==NULL){

            fprintf(stderr, "Memory allocation for %ld bytes failed\n", INET_ADDRSTRLEN*sizeof(*str_addr));
            fprintf(stderr, "malloc() -> ERROR(errno: %d): %s\n", errno, strerror(errno));

            exit(EXIT_FAILURE);
        }

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

        if(inet_ntop(AF_INET, &addr4->sin_addr.s_addr, str_addr, INET_ADDRSTRLEN)==NULL){

            fprintf(stderr, "* inet_ntop() -> ERROR(errno: %d): %s\n", errno, strerror(errno));

        }else{

            fprintf(stdout, "%s\n", str_addr);
        }

    }else{

        str_addr=malloc(INET6_ADDRSTRLEN*sizeof(*str_addr));

        if(str_addr==NULL){

            fprintf(stderr, "Memory allocation for %ld bytes failed\n", INET6_ADDRSTRLEN*sizeof(*str_addr));
            fprintf(stderr, "malloc() -> ERROR(errno: %d): %s\n", errno, strerror(errno));

            exit(EXIT_FAILURE);
        }

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

        if(inet_ntop(AF_INET6, addr6->sin6_addr.s6_addr, str_addr, INET6_ADDRSTRLEN)==NULL){

            fprintf(stderr, "* inet_ntop() -> ERROR(errno: %d): %s\n", errno, strerror(errno));

        }else{

            fprintf(stdout, "%s\n", str_addr);
        }
    }

    free(str_addr);
}

static char **realloc_addr_table(char **buff_addr_table, socklen_t addrlen, size_t count){

    buff_addr_table=realloc(buff_addr_table, count*sizeof(*buff_addr_table));

    if(buff_addr_table==NULL){

        fprintf(stderr, "Memory allocation for %ld bytes failed\n", count*sizeof(*buff_addr_table));
        fprintf(stderr, "realloc() -> ERROR(errno: %d): %s\n", errno, strerror(errno));

        exit(EXIT_FAILURE);
    }

    buff_addr_table[count-1]=malloc(addrlen*sizeof(**buff_addr_table));

    if(buff_addr_table[count-1]==NULL){

        fprintf(stderr, "Memory allocation for %ld bytes failed\n", addrlen*sizeof(**buff_addr_table));
        fprintf(stderr, "malloc() -> ERROR(errno: %d): %s\n", errno, strerror(errno));

        exit(EXIT_FAILURE);
    }

    return buff_addr_table;
}

static void free_addr_table(char **buff_addr_table, size_t count){

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

        free(buff_addr_table[i]);
    }

    free(buff_addr_table);
}

static void search(char *argv[]){

    socklen_t addrlen;
    char *buff_addr=NULL;
    char **buff_addr_table=NULL;
    struct sockaddr *addr=NULL;

    char *eptr;
    size_t i=0, max_connections=strtoll(argv[3], &eptr, 10);

    int sockfd, new_sockfd;

    char message[512];

    sockfd=make_server_socket(argv);

    if(strcmp(argv[1], "-4")==0){

        addrlen=sizeof(struct sockaddr_in);

    }else{

        addrlen=sizeof(struct sockaddr_in6);
    }

    buff_addr=calloc(addrlen, sizeof(*buff_addr));

    if(buff_addr==NULL){

        fprintf(stderr, "Memory allocation for %ld bytes failed!\n", addrlen*sizeof(*buff_addr));
        fprintf(stderr, "calloc() -> ERROR(errno: %d): %s", errno, strerror(errno));

        exit(EXIT_FAILURE);
    }

    addr=(struct sockaddr*)buff_addr;

    while(i<max_connections){

        new_sockfd=accept(sockfd, addr, &addrlen);

        if(new_sockfd!=-1){

            if(check_addr(buff_addr_table, buff_addr, addr->sa_family, i)==0){

                show_addr(addr);

                if(recv(new_sockfd, message, sizeof(message), 0)==-1){

                    fprintf(stderr, "recv() -> ERROR(errno: %d): %s\n", errno, strerror(errno));

                }else{

                    fprintf(stdout, "Client says: %s\n\n", message);
                }

                i++;

                buff_addr_table=realloc_addr_table(buff_addr_table, addrlen, i);
                memcpy(buff_addr_table[i-1], buff_addr, addrlen*sizeof(*buff_addr));
            }

            close(new_sockfd);
        }
    }

    free(buff_addr);
    free_addr_table(buff_addr_table, i);
}

int main(int argc, char *argv[]){

    char *eptr;
    long long int res;

    if(argc<4){

        fprintf(stdout, "Usage: %s [-option] [port/service] [max_connections]       \n", argv[0]);
        fprintf(stdout, "    -4     Use IPv4                                        \n");
        fprintf(stdout, "    -6     Use IPv6                                        \n");
        fprintf(stdout, "    -M     Use IPv4 and IPv6 (IPv4-mapped IPv6 address)    \n");
        fprintf(stdout, "    -h     Show this page                                  \n");

        exit(EXIT_SUCCESS);
    }

    res=strtoll(argv[3], &eptr, 10);

    if(res==0 || res==LONG_MAX || res==LONG_MIN){

        if(errno==EINVAL || errno==ERANGE){

            fprintf(stderr, "* strtoll() -> ERROR(errno: %d): %s\n", errno, strerror(errno));

        }else{

            fprintf(stderr, "* Invalid value for max_connections!\n");
        }

    }else{

        int opt=getopt(argc, argv, "46Mh");

        switch(opt){

            case '4':
            case '6':
            case 'M':

                search(argv);

                break;

            case '?':

                /*getopt() output*/

                break;

            case 'h':
            default:

                fprintf(stdout, "Usage: %s [-option] [port/service] [max_connections]       \n", argv[0]);
                fprintf(stdout, "    -4     Use IPv4                                        \n");
                fprintf(stdout, "    -6     Use IPv6                                        \n");
                fprintf(stdout, "    -M     Use IPv4 and IPv6 (IPv4-mapped IPv6 address)    \n");
                fprintf(stdout, "    -h     Show this page                                  \n");

                break;
        }
    }

    return EXIT_SUCCESS;
}
 


client.c

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

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

static int try_connect(char *argv[]){

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

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

    hints.ai_family=AF_UNSPEC;
    hints.ai_socktype=SOCK_STREAM;
    hints.ai_protocol=IPPROTO_TCP;
    hints.ai_addr=NULL;
    hints.ai_canonname=NULL;
    hints.ai_next=NULL;

    if((ecode=getaddrinfo(argv[1], argv[2], &hints, &res))!=0){

        fprintf(stderr, "* getaddrinfo() -> ERROR(ecode: %d): %s\n", ecode, gai_strerror(ecode));

        exit(EXIT_FAILURE);
    }

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

        if((sockfd=socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol))<0){

            continue;
        }

        if(connect(sockfd, rp->ai_addr, rp->ai_addrlen)==0){

            break;
        }

        close(sockfd);
    }

    if(rp==NULL){

        fprintf(stderr, "* socket()/conncet() -> ERROR(errno: %d): %s\n", errno, strerror(errno));

        exit(EXIT_FAILURE);
    }

    freeaddrinfo(res);

    return sockfd;
}

int main(int argc, char *argv[]){

    const char message[]="Real Muthaphuckkin G's";

    int sockfd=try_connect(argv);

    if(send(sockfd, message, sizeof(message), 0)!=sizeof(message)){

        fprintf(stderr, "* send() -> ERROR(errno: %d): %s\n", errno, message);

    }else{

        printf("send() -> OK\n");
    }

    return EXIT_SUCCESS;
}
 


./server

zherkezhi@zherkezhi:~/Documents$ gcc -Wall server.c -o server
zherkezhi@zherkezhi:~/Documents$ ./server -h
Usage: ./server [-option] [port/service] [max_connections]       
    -4     Use IPv4                                        
    -6     Use IPv6                                        
    -M     Use IPv4 and IPv6 (IPv4-mapped IPv6 address)    
    -h     Show this page                                  
zherkezhi@zherkezhi:~/Documents$ ./server -M 9009 2
::ffff:192.168.50.202
Client says: Real Muthaphuckkin G's

::ffff:127.0.0.1
Client says: Real Muthaphuckkin G's

zherkezhi@zherkezhi:~/Documents$ ./server -4 9009 2
127.0.0.1
Client says: Real Muthaphuckkin G's

192.168.50.202
Client says: Real Muthaphuckkin G's

zherkezhi@zherkezhi:~/Documents$ 
 


./client

zherkezhi@zherkezhi:~/Documents$ gcc -Wall client.c -o client
zherkezhi@zherkezhi:~/Documents$ ./client 192.168.50.202 9009
send() -> OK
zherkezhi@zherkezhi:~/Documents$ ./client 127.0.0.1 9009
send() -> OK
zherkezhi@zherkezhi:~/Documents$ ./client 127.0.0.1 9009
send() -> OK
zherkezhi@zherkezhi:~/Documents$ ./client 192.168.50.202 9009
send() -> OK
zherkezhi@zherkezhi:~/Documents$
 


OBS: Desenvolvi este código usando como base um tópico antigo meu presente aqui na comunidade C/C++: https://www.vivaolinux.com.br/topico/C-C++/-stack-smashing-detected-unknown-terminated
_________________________________________________________________________________

Leitura recomendada -> https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.hale001/ipv6d0031001726...




  






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts