netutils.c -- pequeñas utilidades

Clasicas funciones de utilidad para imprimir de forma humana direcciones
IP, y escribir de forma bloquante asegurando que cada byte fue escrito.
This commit is contained in:
Juan F. Codagnone 2018-03-15 13:02:19 -03:00 committed by Santiago Lo Coco
parent b9ce264b75
commit 07f01d932f
3 changed files with 235 additions and 0 deletions

101
src/netutils.c Normal file
View File

@ -0,0 +1,101 @@
#include <stdbool.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include "netutils.h"
#define N(x) (sizeof(x)/sizeof((x)[0]))
extern const char *
sockaddr_to_human(char *buff, const size_t buffsize,
const struct sockaddr *addr) {
if(addr == 0) {
strncpy(buff, "null", buffsize);
return buff;
}
in_port_t port;
void *p = 0x00;
bool handled = false;
switch(addr->sa_family) {
case AF_INET:
p = &((struct sockaddr_in *) addr)->sin_addr;
port = ((struct sockaddr_in *) addr)->sin_port;
handled = true;
break;
case AF_INET6:
p = &((struct sockaddr_in6 *) addr)->sin6_addr;
port = ((struct sockaddr_in6 *) addr)->sin6_port;
handled = true;
break;
}
if(handled) {
if (inet_ntop(addr->sa_family, p, buff, buffsize) == 0) {
strncpy(buff, "unknown ip", buffsize);
buff[buffsize - 1] = 0;
}
} else {
strncpy(buff, "unknown", buffsize);
}
strncat(buff, ":", buffsize);
buff[buffsize - 1] = 0;
const size_t len = strlen(buff);
if(handled) {
snprintf(buff + len, buffsize - len, "%d", ntohs(port));
}
buff[buffsize - 1] = 0;
return buff;
}
int
sock_blocking_write(const int fd, buffer *b) {
int ret = 0;
ssize_t nwritten;
size_t n;
uint8_t *ptr;
do {
ptr = buffer_read_ptr(b, &n);
nwritten = send(fd, ptr, n, MSG_NOSIGNAL);
if (nwritten > 0) {
buffer_read_adv(b, nwritten);
} else /* if (errno != EINTR) */ {
ret = errno;
break;
}
} while (buffer_can_read(b));
return ret;
}
int
sock_blocking_copy(const int source, const int dest) {
int ret = 0;
char buf[4096];
ssize_t nread;
while ((nread = recv(source, buf, N(buf), 0)) > 0) {
char* out_ptr = buf;
ssize_t nwritten;
do {
nwritten = send(dest, out_ptr, nread, MSG_NOSIGNAL);
if (nwritten > 0) {
nread -= nwritten;
out_ptr += nwritten;
} else /* if (errno != EINTR) */ {
ret = errno;
goto error;
}
} while (nread > 0);
}
error:
return ret;
}

43
src/netutils.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef NETUTILS_H_CTCyWGhkVt1pazNytqIRptmAi5U
#define NETUTILS_H_CTCyWGhkVt1pazNytqIRptmAi5U
#include <netinet/in.h>
#include "buffer.h"
#define SOCKADDR_TO_HUMAN_MIN (INET6_ADDRSTRLEN + 5 + 1)
/**
* Describe de forma humana un sockaddr:
*
* @param buff el buffer de escritura
* @param buffsize el tamaño del buffer de escritura
*
* @param af address family
* @param addr la dirección en si
* @param nport puerto en network byte order
*
*/
const char *
sockaddr_to_human(char *buff, const size_t buffsize,
const struct sockaddr *addr);
/**
* Escribe n bytes de buff en fd de forma bloqueante
*
* Retorna 0 si se realizó sin problema y errno si hubo problemas
*/
int
sock_blocking_write(const int fd, buffer *b);
/**
* copia todo el contenido de source a dest de forma bloqueante.
*
* Retorna 0 si se realizó sin problema y errno si hubo problemas
*/
int
sock_blocking_copy(const int source, const int dest);
#endif

91
src/netutils_test.c Normal file
View File

@ -0,0 +1,91 @@
#include <stdio.h>
#include <stdlib.h>
#include <check.h>
#include "netutils.h"
START_TEST (test_sockaddr_to_human_ipv4) {
char buff[50] = {0};
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_port = htons(9090),
};
addr.sin_addr.s_addr = htonl(0x01020304);
const struct sockaddr *x = (const struct sockaddr *) &addr;
ck_assert_str_eq(sockaddr_to_human(buff, sizeof(buff)/sizeof(buff[0]), x),
"1.2.3.4:9090");
ck_assert_str_eq(sockaddr_to_human(buff, 5, x), "unkn");
ck_assert_str_eq(sockaddr_to_human(buff, 8, x), "1.2.3.4");
ck_assert_str_eq(sockaddr_to_human(buff, 9, x), "1.2.3.4:");
ck_assert_str_eq(sockaddr_to_human(buff, 10, x), "1.2.3.4:9");
ck_assert_str_eq(sockaddr_to_human(buff, 11, x), "1.2.3.4:90");
ck_assert_str_eq(sockaddr_to_human(buff, 12, x), "1.2.3.4:909");
ck_assert_str_eq(sockaddr_to_human(buff, 13, x), "1.2.3.4:9090");
}
END_TEST
START_TEST (test_sockaddr_to_human_ipv6) {
char buff[50] = {0};
struct sockaddr_in6 addr = {
.sin6_family = AF_INET6,
.sin6_port = htons(9090),
};
uint8_t *d = ((uint8_t *)&addr.sin6_addr);
for(int i = 0; i < 16; i++) {
d[i] = 0xFF;
}
const struct sockaddr *x = (const struct sockaddr *) &addr;
ck_assert_str_eq(sockaddr_to_human(buff, 10, x), "unknown i");
ck_assert_str_eq(sockaddr_to_human(buff, 39, x), "unknown ip:9090");
ck_assert_str_eq(sockaddr_to_human(buff, 40, x),
"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
ck_assert_str_eq(sockaddr_to_human(buff, 41, x),
"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:");
ck_assert_str_eq(sockaddr_to_human(buff, 42, x),
"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:9");
ck_assert_str_eq(sockaddr_to_human(buff, 43, x),
"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:90");
ck_assert_str_eq(sockaddr_to_human(buff, 44, x),
"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:909");
ck_assert_str_eq(sockaddr_to_human(buff, 45, x),
"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:9090");
}
END_TEST
Suite *
hello_suite(void) {
Suite *s;
TCase *tc;
s = suite_create("socks");
/* Core test case */
tc = tcase_create("netutils");
tcase_add_test(tc, test_sockaddr_to_human_ipv4);
tcase_add_test(tc, test_sockaddr_to_human_ipv6);
suite_add_tcase(s, tc);
return s;
}
int
main(void) {
int number_failed;
Suite *s;
SRunner *sr;
s = hello_suite();
sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
number_failed = srunner_ntests_failed(sr);
srunner_free(sr);
return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}