Problemas ao comparar estruturas de endereço Internet [RESOLVIDO]

1. Problemas ao comparar estruturas de endereço Internet [RESOLVIDO]

Perfil removido
removido

(usa Nenhuma)

Enviado em 22/06/2021 - 18:42h

TL;DR
Eu estou tentando entender o porque do programa abaixo não comparar corretamente os endereços que são passados para a função check_addr().

Eis o programa em questão:

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

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

#define SERVICE "9009"
#define MAXCONN 3

static int make_server_socket(void) {

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

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

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

if ((ecode = getaddrinfo(NULL, SERVICE, &hints, &result)) != 0 ) {

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

exit(EXIT_FAILURE);
}

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

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

continue;
}

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

break;
}

close(sockfd);
}

if (rp == NULL) {

fprintf(stderr, "make_server_socket() -> socket()/bind() -> ERROR: %s\n", strerror(errno));

close(sockfd);

exit(EXIT_FAILURE);
}

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

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

close(sockfd);

exit(EXIT_FAILURE);
}

return sockfd;
}

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

int res = 0;

if (af == AF_INET) {

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

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

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

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

res = -1;
}
}

} else if (af == AF_INET6) {

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

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

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

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

res = -1;
}
}

} else {

res = -1;
}

return res;
}

static void show_addr(int af, unsigned char *buff_addr) {

char *ip = NULL;

if (af == AF_INET) {

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

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

if (inet_ntop(af, &addr4->sin_addr.s_addr, ip, INET_ADDRSTRLEN) < 0 ) {

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

} else {

printf("%s\n", ip);
}

} else {

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

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

if (inet_ntop(af, &addr6->sin6_addr.s6_addr, ip, INET6_ADDRSTRLEN) < 0 ) {

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

} else {

printf("%s\n", ip);
}
}

free(ip);
}

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

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

if (buff_addr_table == NULL) {

fprintf(stderr, "ERROR: Memory allocation for %zu bytes failed\n", count * sizeof(unsigned char *));
fprintf(stderr, "realloc_addr_table -> realloc() -> ERROR: %s\n", strerror(errno));
}

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

if (buff_addr_table == NULL) {

fprintf(stderr, "ERROR: Memory allocation for %zu bytes failed\n", count * sizeof(unsigned char *));
fprintf(stderr, "realloc_addr_table -> realloc() -> ERROR: %s\n", strerror(errno));
}

return buff_addr_table;
}

static void free_addr_table(unsigned 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(void) {

size_t i = 0;
int sockfd = make_server_socket();
unsigned char *buff_addr = NULL, **buff_addr_table = NULL;

const char msg_1[] = "Welcome!\n", msg_2[] = "Get out!\n";

while (i < MAXCONN) {

int new_sockfd;
struct sockaddr addr;
socklen_t addrlen = sizeof(addr);

if ((new_sockfd = accept(sockfd, &addr, &addrlen)) < 0 ) {

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

} else {

buff_addr = (unsigned char *)&addr;

if (check_addr(addr.sa_family, buff_addr, buff_addr_table, i) != -1 ) {

i++;

buff_addr_table = realloc_addr_table(buff_addr_table, i, addrlen);

memcpy(buff_addr_table[i-1], &addr, addrlen * sizeof(*buff_addr_table));

show_addr(addr.sa_family, buff_addr);

send(new_sockfd, msg_1, sizeof(msg_1), 0);

}else{

send(new_sockfd, msg_2, sizeof(msg_2), 0);
}

close(new_sockfd);
}
}

free_addr_table(buff_addr_table, i);

close(sockfd);
}

int main(void) {

search();

return EXIT_SUCCESS;
}


RUNTIME

Servidor

[[email protected] TCP]$ gcc -Wall server.c -o server
[[email protected] TCP]$ ./server
::d00:0:0:0


Cliente

[[email protected] TCP]$ nc 127.0.0.1 9009
Welcome!
^C
[[email protected] TCP]$ nc ::1 9009
Get out!
^C
[[email protected] TCP]$ nc 192.168.1.107 9009
Get out!
^C
[[email protected] TCP]$


Sendo que a saída do servidor deveria ter sido algo desse tipo:

::ffff:127.0.0.1
::1
::ffff:192.168.1.107



  


2. Re: Problemas ao comparar estruturas de endereço Internet [RESOLVIDO]

Perfil removido
removido

(usa Nenhuma)

Enviado em 22/06/2021 - 19:03h

Aproveitando a pergunta... Aquele último else em check_addr() faz sentido?


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

int res = 0;

if (af == AF_INET) {

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

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

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

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

res = -1;
}
}

} else if (af == AF_INET6) {

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

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

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

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

res = -1;
}
}

} else {

res = -1;
}

return res;
}


Ou eu deveria ter feito simplismente:


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

int res = 0;

if (af == AF_INET) {

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

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

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

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

res = -1;
}
}

} else {

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

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

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

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

res = -1;
}
}
}

return res;
}


???


3. Re: Problemas ao comparar estruturas de endereço Internet

Paulo
paulo1205

(usa Ubuntu)

Enviado em 22/06/2021 - 21:22h

Veja se o seguinte bloco lhe ajuda a ver o que está errado.
$ cat x.c
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <stdio.h>

int main(void){
printf("%zu\n", sizeof(struct sockaddr));
printf("%zu\n", sizeof(struct sockaddr_in));
printf("%zu\n", sizeof(struct sockaddr_in6));
printf("%zu\n", sizeof(struct sockaddr_un));
}
$ gcc x.c -o x
$ ./x
16
16
28
110


Se você quer receber uma conexão por IPv6 e armazenar seu endereço, não deve usar um mero struct sockaddr como destino do armazenamento, pois não vai caber. Tem de usar um buffer maior.

Sugestão: crie um tipo com union que lhe permita guardar qualquer endereço que a sua aplicação possa consumir.
union sockaddr_any {
int addr_family;
struct sockaddr generic;
struct sockaddr_in in;
struct sockaddr_in6 in6;
struct sockaddr_un un;
};


É provável que alguém já tenha feito esse tipo de coisa, mas eu nunca vi em nenhum sistema nem material didático, o que é uma pena.

Por que funciona? Uniões são uma forma de fazer com que vários dados ocupem o mesmo lugar na memória, supondo que, quando você escolher um determinado dado, não vai usar os demais. O tamanho total da união será o tamanho do maior tipo de dados usado na sua composição, e todos os possíveis dados ficarão alocados no mesmo endereço inicial, que também é o endereço da união como um todo (então, por exemplo, com um dado u do tipo union U, que tem os componentes alternativos a do tipo A e b do tipo B, as seguintes comparações são verdadeiras:
  • (void *)&u==(void *)&u.a (e também (intptr_t)&u==(intptr_t)&u.a, que é a comparação dos endereços convertidos para inteiros),
  • (void *)&u==(void *)&u.b,
  • (void *)&u.a==(void *)&u.b e
  • sizeof u==max(sizeof u.a, sizeof u.b)).

No caso particular da union sockaddr_any mostrada acima, temos quatro tipos de sockets e um inteiro. Todos os tipos de sockets começam com um campo inteiro que indica o tipo de família de endereços, então dá para trazer esse campo para fora das estruturas, colocando-o diretamente na união (essa característica comum entre eles é o que permitia as conversões de tipos de ponteiros que normalmente aparecem nas chamadas a bind(), connect() e accept(), mas o uso da união permite evitar essas conversões explícitas, bastando referenciar o campo interno que você quiser usar no momento).

Eis como você pode usar isso no seu programa.
union sockaddr_any client_addr;
sockaddr_len addrlen=sizeof client_addr;
/*
Note que, abaixo, eu economizo uma conversão de ponteiro passando o endereço
do campo que já tem o tipo que a função deseja, valendo-me do fato de que o
endereço da união como um todo é numericamente idêntico ao endereço de cada
um dos seus componentes.
*/
int cli_sock=accept(serv_sock, &client_addr.generic, &addrlen);

switch(client_addr.addr_family){
case AF_INET:
print_IPv4(client_addr.in.sin_addr.s_addr);
break;
case AF_INET:
print_IPv6(&client_addr.in6.sin6_addr);
break;
case AF_LOCAL:
printf("%s", client_addr.un.sun_path);
break;
default:
fprintf(stderr, "Unexpected socket type.\n";
}



... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)


4. Re: Problemas ao comparar estruturas de endereço Internet

Paulo
paulo1205

(usa Ubuntu)

Enviado em 22/06/2021 - 23:19h

Algumas outras sugestões:

  • Na função make_server_socket(), não esqueça de chamar freeaddrinfo().

  • Em check_addr(), considere usar a convenção normal do C de indicar o valor de retorno nulo para operação que falhou, e não-nulo para operação bem sucedida, pois isso facilita a construção de testes condicionais, deixando-as mais sucintas e mais legíveis. A prática de usar -1 como indicador de erros não é nativa do C, mas provém de sistemas UNIX e representa uma quantidade (de memória, de tamanho de arquivo, bytes transferidos, índice na tabela de descritores de arquivos do processo etc.) inválida.

  • Em vez de usar unsigned char * para passar endereços de tipos desconhecidos, possivelmente você pode usar void *, assim economizando algumas conversões de tipo explícitas no código.

  • Em realloc_addr_table(), você cometeu um erro muito comum, que é o de colocar como destino do valor retornado por realloc() o mesmo ponteiro que está sendo realocado. Isso deve ser evitado porque a função pode não conseguir alocar a memória, deixando a área anteriormente alocada intocada, mas como o valor retornado em caso de falha é NULL, você acaba perdendo a referência que tinha aos dados que a função teve o cuidado de preservar. A forma correta de fazer é usar um segundo ponteiro, e substituir o ponteiro original apenas depois de ter certeza de que a realocação foi bem sucedida.
void *new_ptr=realloc(buf_addr_table, count*sizeof *buf_addr_table);
if(new_buf)
buf_addr_table=new_ptr;

  • Ao chamar calloc() para criar espaço para buf_addr_table[count-1], acho que você deveria usar apenas addrlen bytes, não addrlen*sizeof *buf_addr_table.

  • Em show_addr(), sizeof *ip é igual a 1 por definição, uma vez que *ip tem o tipo char. Multiplicar por 1 é supérfluo, então pode remover isso das alocações.

  • Na mesma função, as próprias alocações (e a desaloção) também são possivelmente desnecessárias, a não ser que você tenha alguma restrição que o impeça de usar alocação automática. É muito mais simples usar, por exemplo, “char buffer[TAMANHO];” do que “char *buffer; buffer=malloc(TAMANHO); free(buffer);”.


... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)


5. Re: Problemas ao comparar estruturas de endereço Internet

Perfil removido
removido

(usa Nenhuma)

Enviado em 23/06/2021 - 15:00h


paulo1205 escreveu:

Em check_addr(), considere usar a convenção normal do C de indicar o valor de retorno nulo para operação que falhou, e não-nulo para operação bem sucedida, pois isso facilita a construção de testes condicionais, deixando-as mais sucintas e mais legíveis. A prática de usar -1 como indicador de erros não é nativa do C, mas provém de sistemas UNIX e representa uma quantidade (de memória, de tamanho de arquivo, bytes transferidos, índice na tabela de descritores de arquivos do processo etc.) inválida.


Assim parece bom?


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

unsigned char *res = buff_addr;

if (af == AF_INET) {

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

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

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

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

res = NULL;
}
}

} else {

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

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

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

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

res = NULL;
}
}
}

return res;
}



paulo1205 escreveu:

Em realloc_addr_table(), você cometeu um erro muito comum, que é o de colocar como destino do valor retornado por realloc() o mesmo ponteiro que está sendo realocado. Isso deve ser evitado porque a função pode não conseguir alocar a memória, deixando a área anteriormente alocada intocada, mas como o valor retornado em caso de falha é NULL, você acaba perdendo a referência que tinha aos dados que a função teve o cuidado de preservar. A forma correta de fazer é usar um segundo ponteiro, e substituir o ponteiro original apenas depois de ter certeza de que a realocação foi bem sucedida.


void *new_ptr=realloc(buf_addr_table, count*sizeof *buf_addr_table);
if(new_buf)
buf_addr_table=new_ptr;




Você está certo, mas acredito que o local que você indicou não é adequado para esse "ponteiro de backup". Eu acredito que o ideal é que esse ponteiro ficasse na função search():


/*(código... etc)*/

/*Mantém a implementação original da função realloc_addr_table()*/
static unsigned char **realloc_addr_table(unsigned char **buff_addr_table, size_t count, size_t addrlen) {

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

if (buff_addr_table == NULL) {

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

buff_addr_table[count-1] = calloc(1, addrlen);

if (buff_addr_table == NULL) {

fprintf(stderr, "ERROR: Memory allocation for %zu bytes failed\n", addrlen);
fprintf(stderr, "realloc_addr_table -> realloc() -> ERROR: %s\n", strerror(errno));
}

return buff_addr_table;
}

static void free_addr_table(unsigned 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(void) {

size_t i = 0;
int sockfd = make_server_socket();
unsigned char *buff_addr = NULL, **buff_addr_table = NULL;

const char msg_1[] = "Welcome!\n", msg_2[] = "Get out!\n";

while (i < MAXCONN) {

int new_sockfd;
struct sockaddr addr;
socklen_t addrlen = sizeof(addr);

if ((new_sockfd = accept(sockfd, &addr, &addrlen)) < 0 ) {

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

} else {

buff_addr = (unsigned char *)&addr;

if (check_addr(addr.sa_family, buff_addr, buff_addr_table, i) != NULL ) {

unsigned char **tmp = realloc_addr_table(buff_addr_table, i+1, addrlen); /*Faz o uso do ponteiro auxiliar dentro da função search()*/

if (tmp != NULL) {

i++;

buff_addr_table = tmp;

memcpy(buff_addr_table[i-1], &addr, addrlen);

show_addr(addr.sa_family, buff_addr);

send(new_sockfd, msg_1, sizeof(msg_1), 0);
}

}else{

send(new_sockfd, msg_2, sizeof(msg_2), 0);
}

close(new_sockfd);
}
}

free_addr_table(buff_addr_table, i);

close(sockfd);
}


O que você acha desse implementação?


paulo1205 escreveu:

Em show_addr(), sizeof *ip é igual a 1 por definição, uma vez que *ip tem o tipo char. Multiplicar por 1 é supérfluo, então pode remover isso das alocações.

Na mesma função, as próprias alocações (e a desaloção) também são possivelmente desnecessárias, a não ser que você tenha alguma restrição que o impeça de usar alocação automática. É muito mais simples usar, por exemplo, "char buffer[TAMANHO];" do que "char *buffer; buffer=malloc(TAMANHO); free(buffer);".


Yeap! Costume masoquista meu... Estou tentando deixar de lado essa mania estranha.. Sei que é errado!


static void show_addr(int af, const unsigned char *buff_addr) {

char ip[INET6_ADDRSTRLEN];

if (af == AF_INET) {

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

if (inet_ntop(af, &addr4->sin_addr.s_addr, ip, INET_ADDRSTRLEN) < 0 ) {

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

} else {

printf("%s\n", ip);
}

} else {

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

if (inet_ntop(af, &addr6->sin6_addr.s6_addr, ip, INET6_ADDRSTRLEN) < 0 ) {

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

} else {

printf("%s\n", ip);
}
}
}


Usar INET6_ADDRSTRLEN deve ser o suficiente para endereços IPv4, IPv6 e "IPv4 - IPv6 mapped address".

--------------------------

* OBS: Tive que remover o ítalico das suas quotes pelo fato de estarem bugadas. Tava deixando todo o meu texto em ítalico (incluindo os meus códigos).


6. Outros problemas relacionados a alocação que passaram despercebidos...

Perfil removido
removido

(usa Nenhuma)

Enviado em 23/06/2021 - 15:28h

Hey, Paulo! Além de todos os problemas que você indicou ainda há alguns que você não percebeu e só reparei agora pouco.

O primeiro erro está na própria função realloc_addr_table():


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

buff_addr_table = realloc(buff_addr_table, count * sizeof(*buff_addr_table)); //Isso tá ok

if (buff_addr_table == NULL) {

fprintf(stderr, "ERROR: Memory allocation for %zu bytes failed\n", count * sizeof(unsigned char *));
fprintf(stderr, "realloc_addr_table -> realloc() -> ERROR: %s\n", strerror(errno));
}

buff_addr_table[count-1] = calloc(count, addrlen * sizeof(*buff_addr_table)); /*count não deveria está dentro da função calloc, o certo seria alocar apenas o quantidade de bytes armazenados em addrlen*/

if (buff_addr_table == NULL) {

fprintf(stderr, "ERROR: Memory allocation for %zu bytes failed\n", count * sizeof(unsigned char *)); //Isso tá errado também, deveria mostrar apenas a quantidade de bytes armazenados em addrlen
fprintf(stderr, "realloc_addr_table -> realloc() -> ERROR: %s\n", strerror(errno));
}

return buff_addr_table;
}


Portanto, o correto deveria ter sido algo desse tipo:


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

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

if (buff_addr_table == NULL) {

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

buff_addr_table[count-1] = calloc(1, addrlen);

if (buff_addr_table == NULL) {

fprintf(stderr, "ERROR: Memory allocation for %zu bytes failed\n", addrlen);
fprintf(stderr, "realloc_addr_table -> realloc() -> ERROR: %s\n", strerror(errno));
}

return buff_addr_table;
}


Bom, além da função realloc_addr_table(), também temos uma linha sem sentido na função search():


} else {

buff_addr = (unsigned char *)&addr;

if (check_addr(addr.sa_family, buff_addr, buff_addr_table, i) != -1 ) {

i++;

buff_addr_table = realloc_addr_table(buff_addr_table, i, addrlen);

memcpy(buff_addr_table[i-1], &addr, addrlen * sizeof(*buff_addr_table)); //Esse sizeof não faz sentido, deveria ser apenas addrlen para informar o tamanho do buffer de destino

show_addr(addr.sa_family, buff_addr);

send(new_sockfd, msg_1, sizeof(msg_1), 0);

}else{

send(new_sockfd, msg_2, sizeof(msg_2), 0);
}

close(new_sockfd);
}


Fico me perguntando como diabos não deu falha de segmentação...

--------------------------

Ponteiros para ponteiros em C é um treco confuso...



7. Re: Problemas ao comparar estruturas de endereço Internet

Paulo
paulo1205

(usa Ubuntu)

Enviado em 23/06/2021 - 21:01h

PC300-ITS44 escreveu:

paulo1205 escreveu:

Em check_addr(), considere usar a convenção normal do C de indicar o valor de retorno nulo para operação que falhou, e não-nulo para operação bem sucedida, pois isso facilita a construção de testes condicionais, deixando-as mais sucintas e mais legíveis. A prática de usar -1 como indicador de erros não é nativa do C, mas provém de sistemas UNIX e representa uma quantidade (de memória, de tamanho de arquivo, bytes transferidos, índice na tabela de descritores de arquivos do processo etc.) inválida.


Assim parece bom?

static unsigned char *check_addr(int af, unsigned char *buff_addr, unsigned char **buff_addr_table, size_t count) {
unsigned char *res = buff_addr;
if (af == AF_INET) {
struct sockaddr_in *addr4 = (struct sockaddr_in*)buff_addr;
for (size_t i = 0; i < count; i++) {
struct sockaddr_in *addr4_from_table = (struct sockaddr_in*)buff_addr_table[i];
if (memcmp(&addr4->sin_addr.s_addr, &addr4_from_table->sin_addr.s_addr, sizeof(addr4->sin_addr.s_addr)) == 0 ) {
res = NULL;
}
}
} else {
struct sockaddr_in6 *addr6 = (struct sockaddr_in6*)buff_addr;
for(size_t i = 0; i < count; i++) {
struct sockaddr_in6 *addr6_from_table = (struct sockaddr_in6*)buff_addr_table[i];
if (memcmp(&addr6->sin6_addr.s6_addr, &addr6_from_table->sin6_addr.s6_addr, sizeof(addr6->sin6_addr.s6_addr)) == 0 ) {
res = NULL;
}
}
}
return res;
}


Eu acho que piorou. Minha sugestão era de que você retornasse 1 (verdadeiro) quando encontrasse o endereço no cache de endereços, e 0 (nulo, ou falso) quando não encontrasse, de modo a poder escrever, na hora de testar, algo como o seguinte.
if(check_addr(/* ... */)){
/* Algo a executar quando encontra. */
}
else{
/* Algo a executar quando não encontra. */
}


paulo1205 escreveu:

Em realloc_addr_table(), você cometeu um erro muito comum, que é o de colocar como destino do valor retornado por realloc() o mesmo ponteiro que está sendo realocado. Isso deve ser evitado porque a função pode não conseguir alocar a memória, deixando a área anteriormente alocada intocada, mas como o valor retornado em caso de falha é NULL, você acaba perdendo a referência que tinha aos dados que a função teve o cuidado de preservar. A forma correta de fazer é usar um segundo ponteiro, e substituir o ponteiro original apenas depois de ter certeza de que a realocação foi bem sucedida.

void *new_ptr=realloc(buf_addr_table, count*sizeof *buf_addr_table);
if(new_buf)
buf_addr_table=new_ptr;


Você está certo, mas acredito que o local que você indicou não é adequado para esse "ponteiro de backup". Eu acredito que o ideal é que esse ponteiro ficasse na função search():


O lugar onde se deve corrigir é o local onde o erro está. Ao manter realloc_addr_table() como estava, você simplesmente manteve o erro que eu apontei, que é o de que não se deve usar como destino do valor de retorno de realloc() o mesmo ponteiro que está sendo realocado.

O que eu não fiz, porque não achei que seria necessário, foi explorar desdobramentos sobre o restante do programa caso você percebesse que realloc() falhou, até porque essa decisão caberia a você. Dependendo da aplicação, você pode preferir prosseguir com a quantidade anterior de endereços, e avisar a quem chamou a função que não conseguiu incrementar o tamanho do cache (seja por meio do valor de retorno, seja através de algum argumento ponteiro), ou você pode querer interromper o programa, ou pode querer substituir algum endereço mais antigo.

Reiterando: eu me limitei a apontar um erro na forma de usar a função realloc(), sem entrar no mérito da lógica do seu programa. Se fosse para entrar nesse mérito, minha sugestão teria sido a de mudar a função completamente, trocando inclusive seu nome, passando a chamá-la de add_new_table_addr(), com mais ou menos o seguinte aspecto (código não testado, e note que eu uso void * em lugar de unsigned char *, que foi uma outra sugestão que eu fiz). [EDIT (2021/07/03): Corrigi um bug no primeiro argumento, que passava uma cópia do cache, em vez de uma referência para ele.]
// 1º argumento: ponteiro (referência) para o array dinâmico de cache de endereços.
// 2º argumento: ponteiro para tamanho do cache.
// 3º argumento: novo endereço, referenciado por ponteiro para uma “union sockaddr_any” que eu mostrei em outra mensagem.
bool add_new_table_addr(void ***cache, size_t *cache_size, const union sockaddr_any *addr){
if(!cache || !cache_size || !addr){
errno=EFAULT;
return false;
}
// Verifica quanto espaço de memória precisará ser alocado, e depois aloca.
size_t addr_len=
addr->addr_family==AF_INET? sizeof addr->in:
addr->addr_family==AF_INET6? sizeof addr->in6:
addr->addr_family==AF_UNIX? sizeof addr->un:
sizeof addr->generic
;
void *new_addr=malloc(addr_len);
if(!new_addr)
return false; // Retorna falso por causa do erro de alocação.

// Não copia os dados de *addr ainda. Primeiro vamos ver se a realocação do cache inteiro também vai funcionar.

void *new_ptr=realloc(*cache, (1+*cache_size)*sizeof **cache);
if(!new_ptr){
free(new_addr); // Por causa do erro de alocação, descarta o espaço já alocado para o novo endereço.
return false; // Retorna false, indicando erro.
}

// Neste ponto, todas as alocações já deram certo. Copia os dados e coloca o ponteiro para o novo endereço no cache.
memcpy(new_addr, addr, addr_len);
*cache=new_ptr;
(*cache)[*cache_size]=new_addr;
++*cache_size; // Incrementa o dado com o tamanho do cache, apontado pelo ponteiro.

return true; // Retorna true, indicando sucesso.
}


/* ... Código suprimido por brevidade. ... */ 


O que você acha desse implementação?


Não acho boa, pelo que já expliquei acima: não corrige um problema real, e tenta consertar algo que não estava errado.

paulo1205 escreveu:

Em show_addr(), sizeof *ip é igual a 1 por definição, uma vez que *ip tem o tipo char. Multiplicar por 1 é supérfluo, então pode remover isso das alocações.

Na mesma função, as próprias alocações (e a desalocação) também são possivelmente desnecessárias, a não ser que você tenha alguma restrição que o impeça de usar alocação automática. É muito mais simples usar, por exemplo, “char buffer[TAMANHO];” do que “char *buffer; buffer=malloc(TAMANHO); free(buffer);”.


Yeap! Costume masoquista meu... Estou tentando deixar de lado essa mania estranha.. Sei que é errado!


Não é errado, apenas mais trabalhoso. Mas às vezes pode ser necessário fazer com alocação dinâmica, em vez de alocação automática (por exemplo: quando você tem restrições muito grandes ao uso da pilha).

* OBS: Tive que remover o ítalico das suas quotes pelo fato de estarem bugadas. Tava deixando todo o meu texto em ítalico (incluindo os meus códigos).


Minha mensagem não tinha nenhum bug de formatação; foi a inclusão do seu código que provocou o problema. E a razão é que você usou a variável i como índice de arrays, mas o fórum do VOL usa [i] e [/i] como tags de início e fim de itálicos. Seus arrays indexados com “[i]” provocaram confusão por causa do desequilíbrio entre tags de início e de fim de itálicos.

A forma de de contornar esses problemas é meio suja, infelizmente. Por exemplo, no trecho de código que você colocou e que eu preservei nesta mensagem, eu tive de escrever “[i[i][/i]]” no meio do código, para não caracterizar desbalanceamento de tags de formatação.


... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)


8. Re: Problemas ao comparar estruturas de endereço Internet [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 23/06/2021 - 22:04h

PC300-ITS44 escreveu:

Hey, Paulo! Além de todos os problemas que você indicou ainda há alguns que você não percebeu e só reparei agora pouco.


Em nenhum momento eu disse que a lista era exaustiva, e algumas partes do código eu nem olhei (por exemplo, os lugares em que você imprimia mensagens de erro). E, como você já viu, eu cometo meus erros, também.

O primeiro erro está na própria função realloc_addr_table():

static unsigned char **realloc_addr_table(unsigned char **buff_addr_table, size_t count, size_t addrlen) {
buff_addr_table = realloc(buff_addr_table, count * sizeof(*buff_addr_table)); //Isso tá ok


Não, não está. Já falei várias vezes sobre essa linha acima. E, para você ter certeza de que não é blefe meu, veja os comentários sobre a forma de usar realloc() na seção de exemplos da manpage dessa função no NetBSD (https://man.netbsd.org/realloc.3).

        if (buff_addr_table == NULL) {
fprintf(stderr, "ERROR: Memory allocation for %zu bytes failed\n", count * sizeof(unsigned char *));
fprintf(stderr, "realloc_addr_table -> realloc() -> ERROR: %s\n", strerror(errno));
}
buff_addr_table[count-1] = calloc(count, addrlen * sizeof(*buff_addr_table)); /*count não deveria está dentro da função calloc, o certo seria alocar apenas o quantidade de bytes armazenados em addrlen*/


Realmente. O comentário que eu fiz sobre essa função passou batido sobre o primeiro argumento. Mas o certo mesmo, sendo o ponteiro alocado um ponteiro para unsigned char, seria fazer do seguinte modo.
buff_addr_table[count-1]=calloc(addrlen, 1);  // Porque sizeof(unsigned char)==1. 


        if (buff_addr_table == NULL) {
fprintf(stderr, "ERROR: Memory allocation for %zu bytes failed\n", count * sizeof(unsigned char *)); //Isso tá errado também, deveria mostrar apenas a quantidade de bytes armazenados em addrlen
fprintf(stderr, "realloc_addr_table -> realloc() -> ERROR: %s\n", strerror(errno));
}
return buff_addr_table;
}


Portanto, o correto deveria ter sido algo desse tipo:

static unsigned char **realloc_addr_table(unsigned char **buff_addr_table, size_t count, size_t addrlen) {
buff_addr_table = realloc(buff_addr_table, count * sizeof(*buff_addr_table));


Alguém já disse que a linha acima tem um bug? ;)

        if (buff_addr_table == NULL) {
fprintf(stderr, "ERROR: Memory allocation for %zu bytes failed\n", count * sizeof(*buff_addr_table));
fprintf(stderr, "realloc_addr_table -> realloc() -> ERROR: %s\n", strerror(errno));
}
buff_addr_table[count-1] = calloc(1, addrlen);


O efeito final é o mesmo, em termos de quantidade de memória alocada e inicializada com bytes nulos, mas a ordem dos argumentos deveria ser invertida, pois o primeiro é um contador de elementos do array, e o segundo é o tamanho de cada elemento.

        if (buff_addr_table == NULL) {
fprintf(stderr, "ERROR: Memory allocation for %zu bytes failed\n", addrlen);
fprintf(stderr, "realloc_addr_table -> realloc() -> ERROR: %s\n", strerror(errno));


Esta agora vai parecer nitpicking, mas veja que é verdade: se a primeira das chamadas a fprintf() acima falhar, a segunda pode acabar usando um valor de errno que já não se refere mais à falha de alocação, mas sim ao erro recebido na linha imediatamente acima.

Na hora de lidar com erros, quanto menos operações você realizar, melhor, e, sendo assim, talvez fosse interessante aglutinar as duas chamadas numa só. Mas não sem antes corrigir a mensagem de erro da segunda, que diz que realloc() falou, quando, na verdade, a falha teria sido em calloc().

        }
return buff_addr_table;
}


Bom, além da função realloc_addr_table(), também temos uma linha sem sentido na função search():

} else {
buff_addr = (unsigned char *)&addr;
if (check_addr(addr.sa_family, buff_addr, buff_addr_table, i) != -1 ) {
i++;
buff_addr_table = realloc_addr_table(buff_addr_table, i, addrlen);
memcpy(buff_addr_table[i-1], &addr, addrlen * sizeof(*buff_addr_table)); //Esse sizeof não faz sentido, deveria ser apenas addrlen para informar o tamanho do buffer de destino


Realmente está errado. Possivelmente só funcionou porque você estava alocando essa mesma quantidade excessiva de bytes na função de alocação.

                show_addr(addr.sa_family, buff_addr);
send(new_sockfd, msg_1, sizeof(msg_1), 0);
}else{
send(new_sockfd, msg_2, sizeof(msg_2), 0);
}
close(new_sockfd);
}


Ponteiros para ponteiros em C é um treco confuso...


Com o tempo a gente se acostuma. Mas nunca é bom mexer com isso quando se está com sono ou com baixa concentração.


... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)


9. Re: Problemas ao comparar estruturas de endereço Internet

Perfil removido
removido

(usa Nenhuma)

Enviado em 24/06/2021 - 20:29h


Eu acho que piorou. Minha sugestão era de que você retornasse 1 (verdadeiro) quando encontrasse o endereço no cache de endereços, e 0 (nulo, ou falso) quando não encontrasse, de modo a poder escrever, na hora de testar, algo como o seguinte.


if(check_addr(/* ... */)){
/* Algo a executar quando encontra. */
}
else{
/* Algo a executar quando não encontra. */
}



Eu não tinha entendido de primeira. Desculpa. É que eu sou meio "lesado" e levo tempo para processar qualquer tipo de informação, sou igual Windows...

Acredito que uma forma interessante seria retornar false (caso não encontrasse o endereço na tabela) ou true (caso encontrasse o endereço).


O lugar onde se deve corrigir é o local onde o erro está. Ao manter realloc_addr_table() como estava, você simplesmente manteve o erro que eu apontei, que é o de que não se deve usar como destino do valor de retorno de realloc() o mesmo ponteiro que está sendo realocado.


Realmente. Não tinha prestado muita atenção que tinha mantido esse erro tosco.


Reiterando: eu me limitei a apontar um erro na forma de usar a função realloc(), sem entrar no mérito da lógica do seu programa. Se fosse para entrar nesse mérito, minha sugestão teria sido a de mudar a função completamente, trocando inclusive seu nome, passando a chamá-la de add_new_table_addr(), com mais ou menos o seguinte aspecto (código não testado, e note que eu uso void * em lugar de unsigned char *, que foi uma outra sugestão que eu fiz).


Eu acho que eu vou tentar dessa forma no meu próximo "pathfinder".

Aproveitando: dessa forma que você indicou o consumo de processamento é menor comparado ao meu?


A forma de de contornar esses problemas é meio suja, infelizmente. Por exemplo, no trecho de código que você colocou e que eu preservei nesta mensagem, eu tive de escrever “[i]” no meio do código, para não caracterizar desbalanceamento de tags de formatação.


Li em algum canto que o dono do site faria uma grande reformulação do site. Espero que resolvam essa problema.


Em nenhum momento eu disse que a lista era exaustiva, e algumas partes do código eu nem olhei (por exemplo, os lugares em que você imprimia mensagens de erro). E, como você já viu, eu cometo meus erros, também.


Eu te entendo perfeitamente!
Eu entendo que todo mundo que frequenta qualquer forúm ou site de tecnologia, como o VOL, StackOverflow.com, linuxquestions.org... ou qualquer outro é simplismente um voluntário e tem coisas mais importantes a fazer (trabalho, lazer, família, etc).

Quando eu me cadastrei nesse site eu já tinha em mente que talvez ninguém respondesse ao meu tópico. Eu já tinha feito algumas perguntas bem formuladas no pt.stackoverflow.com sobre POSIX Sockets e ninguém havia respondido,
então foi aí que eu acabei vindo parar aqui antes de tentar um site gringo.

Eu realmente agradeço pelas respostas que você já deu, foi sorte ter encontrado um voluntário com o seu nível de conhecimento. Eu só apontei aqueles erros no meu código pelo fato de realmente estarem muito toscos.


Não, não está. Já falei várias vezes sobre essa linha acima. E, para você ter certeza de que não é blefe meu, veja os comentários sobre a forma de usar realloc() na seção de exemplos da manpage dessa função no NetBSD (https://man.netbsd.org/realloc.3).


Eu realmente peço desculpas se fiz parecer que sentia "blefe" por sua parte. O que a conteceu é o seguinte: eu sou meio "lesado" e não tinha prestado atenção naquele ponto que você havia indicado sobre realloc().

Dá próxima vez eu tento prestar mais atenção...


Alguém já disse que a linha acima tem um bug? ;)


Hehehehe... se essa chamada para realloc falhar ferra toda a execução do programa. Melhor eu colocar um ponteiro auxiliar aí.


Esta agora vai parecer nitpicking, mas veja que é verdade: se a primeira das chamadas a fprintf() acima falhar, a segunda pode acabar usando um valor de errno que já não se refere mais à falha de alocação, mas sim ao erro recebido na linha imediatamente acima.

Na hora de lidar com erros, quanto menos operações você realizar, melhor, e, sendo assim, talvez fosse interessante aglutinar as duas chamadas numa só. Mas não sem antes corrigir a mensagem de erro da segunda, que diz que realloc() falou, quando, na verdade, a falha teria sido em calloc().


Realmente não é picuinha. Eu nunca parei para pensar que fprintf() poderia falhar. Vou tentar lembrar disso daqui em diante.

Aproveitando: não existe algo como clearerr() para errno? Não que eu vá usar na situação acima, mas só por curiosidade hehehe.


Com o tempo a gente se acostuma. Mas nunca é bom mexer com isso quando se está com sono ou com baixa concentração.


Então eu estou ferrado! Sofro de hiperatividade desde criança. O tratamento não tem sido muito eficaz, mas eu dou o melhor que eu posso.

Na verade, sou estou estudando programação por curiosidade e hobby. Embora jovem, não tenho planos para atuar na área, não me sinto capaz de competir nesse mercado. Atualmente busco alternativas para o meu futuro no mercado de trabalho.



10. Sobre os parâmetros da função calloc()

Perfil removido
removido

(usa Nenhuma)

Enviado em 24/06/2021 - 21:14h


O efeito final é o mesmo, em termos de quantidade de memória alocada e inicializada com bytes nulos, mas a ordem dos argumentos deveria ser invertida, pois o primeiro é um contador de elementos do array, e o segundo é o tamanho de cada elemento.



void *calloc(size_t nmemb, size_t size);


nmemb -> Número de elementos a serem alocados.
size -> Tamanho de cada elemento.


//No meu programa
tmp[count-1] = calloc(1, addrlen); //Quantidade de elementos, tamanho do elemento


addrlen é o tamanho do elemento, não a quantidade de elementos.

Isso é realmente confuso. É um elemento do tipo struct sockaddr_xy vezes o tamanho desse elemento (1, addrlen). Tem certeza que não está correto dessa maneira?

https://www.cplusplus.com/reference/cstdlib/calloc/
https://koor.fr/C/cstdlib/calloc.wp
https://www.tutorialspoint.com/c_standard_library/c_function_calloc.htm
https://www.guru99.com/calloc-in-c-example.html




11. Código atualizado

Perfil removido
removido

(usa Nenhuma)

Enviado em 24/06/2021 - 21:20h

Atualizei o código (e adicionei algumas coisas novas também):


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

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

#define SNDTIMEO 5
#define MAXCONN 10

static bool set_sock_opts(int sockfd, bool only_ipv6) {

bool ok = false;
const int optval = 1;
struct timeval timeout = {SNDTIMEO, 0};

do {

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

break;
}

if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) != 0 ) {

break;
}

if (only_ipv6 == true) {

if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof(optval)) != 0 ) {

break;
}
}

ok = true;

} while (false);

return ok;
}

static int make_server_socket(int af, const char *service, bool only_ipv6, size_t somaxconn) {

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

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

hints.ai_flags = AI_PASSIVE;
hints.ai_family = af;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;

if ((ecode = getaddrinfo(NULL, service, &hints, &result)) != 0 ) {

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

exit(EXIT_FAILURE);
}

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

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

continue;

}

if (set_sock_opts(sockfd, only_ipv6) != true ) {

close(sockfd);

continue;
}

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

break;
}

close(sockfd);
}

if (rp == NULL) {

fprintf(stderr, "make_server_socket() -> socket()/bind() -> ERROR: %s\n", strerror(errno));

freeaddrinfo(rp);

close(sockfd);

exit(EXIT_FAILURE);
}

freeaddrinfo(rp);

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

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

close(sockfd);

exit(EXIT_FAILURE);
}

return sockfd;
}

static bool check_addr(int af, void *addr, void **addr_table, size_t count) {

bool found = false;

if (af == AF_LOCAL) {

struct sockaddr_un *local = addr;

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

struct sockaddr_un *local_from_table = addr_table[i];

if (memcmp(local->sun_path, local_from_table->sun_path, sizeof(local->sun_path)) == 0 ) {

found = true;
}
}

} else if (af == AF_INET) {

struct sockaddr_in *addr4 = addr;

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

struct sockaddr_in *addr4_from_table = addr_table[i];

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

found = true;
}
}

} else {

struct sockaddr_in6 *addr6 = addr;

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

struct sockaddr_in6 *addr6_from_table = addr_table[i];

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

found = true;
}
}
}

return found;
}

static void show_addr(int af, void *addr) {

char buff[INET6_ADDRSTRLEN];

if (af == AF_LOCAL) {

struct sockaddr_un *local = addr;

printf("%s\n", local->sun_path);

} else if (af == AF_INET) {

struct sockaddr_in *addr4 = addr;

if (inet_ntop(af, &addr4->sin_addr.s_addr, buff, INET_ADDRSTRLEN) < 0 ) {

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

} else {

printf("%s\n", buff);
}

} else {

struct sockaddr_in6 *addr6 = addr;

if (inet_ntop(af, &addr6->sin6_addr.s6_addr, buff, INET6_ADDRSTRLEN) < 0 ) {

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

} else {

printf("%s\n", buff);
}
}
}

static void **realloc_addr_table(void **addr_table, size_t count, size_t addrlen) {

void **tmp = realloc(addr_table, count * sizeof(*addr_table));

if (tmp == NULL) {

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

} else {

tmp[count-1] = calloc(1, addrlen);

if (tmp == NULL) {

fprintf(stderr, "ERROR: Memory allocation for %zu bytes failed\nrealloc_addr_table -> realloc() -> ERROR: %s\n",
addrlen, strerror(errno));
}
}

return tmp;
}

static void free_addr_table(void **addr_table, size_t count) {

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

free(addr_table[i]);
}

free(addr_table);
}

static void search(char **argv, size_t somaxconn, size_t maxconn) {

int af;
bool only_ipv6 = false;
socklen_t addrlen;

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

af = AF_LOCAL;

addrlen = sizeof(struct sockaddr_un);

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

af = AF_INET;

addrlen = sizeof(struct sockaddr_in);

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

af = AF_INET6;

only_ipv6 = true;

addrlen = sizeof(struct sockaddr_in6);

} else {

af = AF_INET6;

addrlen = sizeof(struct sockaddr_in6);
}

size_t i = 0;
int sockfd = make_server_socket(af, argv[2], only_ipv6, somaxconn);
void *addr = calloc(1, addrlen);
void **addr_table = NULL;

const char msg_1[] = "Welcome!\n", msg_2[] = "Get out!\n";

while (i < maxconn) {

int new_sockfd;

if ((new_sockfd = accept(sockfd, addr, &addrlen)) < 0 ) {

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

} else {

if (check_addr(af, addr, addr_table, i) == false ) {

void **tmp = realloc_addr_table(addr_table, i+1, addrlen);

if (tmp != NULL) {

i++;

addr_table = tmp;

memcpy(addr_table[i-1], addr, addrlen);

show_addr(af, addr);

send(new_sockfd, msg_1, sizeof(msg_1), 0);
}

}else{

send(new_sockfd, msg_2, sizeof(msg_2), 0);
}

close(new_sockfd);
}
}

free(addr);

free_addr_table(addr_table, i);

close(sockfd);
}

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

if (argc == 1) {

printf(" *** ERROR: No arguments! ***\n");
printf("Usage: %s [OPTION] [PORT/SERVICE] [SOMAXCONN] [MAXCONN]\n", argv[0]);
printf(" -4 Use IPv4\n");
printf(" -6 Use IPv6\n");
printf(" -M Use IPv4 and IPv6 (IPv4-Mapped IPv6 Adress)\n");
printf(" -h Show this page\n");

exit(EXIT_FAILURE);
}

int opt = getopt(argc, argv, "u46Mh");

if (strlen(argv[1]) > 2 ) {

printf("*** Invalid value for [OPTION]! ***\n");
printf("Usage: %s [OPTION] [PORT/SERVICE] [SOMAXCONN] [MAXCONN]\n", argv[0]);
printf(" -4 Use IPv4\n");
printf(" -6 Use IPv6\n");
printf(" -M Use IPv4 and IPv6 (IPv4-Mapped IPv6 Adress)\n");
printf(" -h Show this page\n");

} else if (opt == 'h' || opt == '?') {

printf("Usage: %s [OPTION] [PORT/SERVICE] [SOMAXCONN] [MAXCONN]\n", argv[0]);
printf(" -4 Use IPv4\n");
printf(" -6 Use IPv6\n");
printf(" -M Use IPv4 and IPv6 (IPv4-Mapped IPv6 Adress)\n");
printf(" -h Show this page\n");

} else if ((argc < 5) || (argc > 5)) {

printf("*** Invalid values! ***\n");
printf("Usage: %s [OPTION] [PORT/SERVICE] [SOMAXCONN] [MAXCONN]\n", argv[0]);
printf(" -4 Use IPv4\n");
printf(" -6 Use IPv6\n");
printf(" -M Use IPv4 and IPv6 (IPv4-Mapped IPv6 Adress)\n");
printf(" -h Show this page\n");

}else{

size_t somaxconn = strtoull(argv[3], NULL, 10);
size_t maxconn = strtoull(argv[4], NULL, 10);

if (somaxconn > SOMAXCONN) {

printf("*** Invalid value for [SOMAXCONN]. The value is too long! ***\n");
printf("The maximum allowed value is %u\n", SOMAXCONN);

} else if ( maxconn > MAXCONN ) {

printf("*** Invalid value for [MAXCONN]. The value is too long! ***\n");
printf("The maximum allowed value is %u\n", MAXCONN);

} else {

search(argv, somaxconn, maxconn);
}
}

return EXIT_SUCCESS;
}


RUNTIME


[[email protected] TCP]$ gcc -Wall server.c -o server
[[email protected] TCP]$ ./server -M 9009 128 3
::ffff:127.0.0.1
::1
::ffff:192.168.1.107
[[email protected] TCP]$