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

[email protected]:~/Documents$ gcc -Wall server.c -o server
[email protected]:~/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
[email protected]:~/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

[email protected]:~/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

[email protected]:~/Documents$


./client

[email protected]:~/Documents$ gcc -Wall client.c -o client
[email protected]:~/Documents$ ./client 192.168.50.202 9009
send() -> OK
[email protected]:~/Documents$ ./client 127.0.0.1 9009
send() -> OK
[email protected]:~/Documents$ ./client 127.0.0.1 9009
send() -> OK
[email protected]:~/Documents$ ./client 192.168.50.202 9009
send() -> OK
[email protected]:~/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...