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.
#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;
}
#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;
}
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$
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$
Enviar mensagem ao usuário trabalhando com as opções do php.ini
Meu Fork do Plugin de Integração do CVS para o KDevelop
Compartilhando a tela do Computador no Celular via Deskreen
Como Configurar um Túnel SSH Reverso para Acessar Sua Máquina Local a Partir de uma Máquina Remota
Configuração para desligamento automatizado de Computadores em um Ambiente Comercial
Criando uma VPC na AWS via CLI
Multifuncional HP imprime mas não digitaliza
Dica básica para escrever um Artigo.
Como Exibir Imagens Aleatórias no Neofetch para Personalizar seu Terminal