diff --git a/.gitignore b/.gitignore index 957b4bb..e1a2391 100644 --- a/.gitignore +++ b/.gitignore @@ -6,10 +6,16 @@ .viminfo .vscode/ +## Folders patches/ +test/ ## Output -server -client +socks5d *.o a.out + +## Tests +PVS-Studio.log +report.tasks +strace_out diff --git a/Makefile b/Makefile index c41f5ac..00c8f58 100644 --- a/Makefile +++ b/Makefile @@ -1,32 +1,30 @@ -CC = gcc -# CC = clang -CCFLAGS = -std=c11 -Wall -Wextra -Werror -Wno-unused-parameter -Wno-implicit-fallthrough -pedantic -pedantic-errors -fsanitize=address -g -D_POSIX_C_SOURCE=200809L +CCFLAGS = -std=c11 -Wall -Wextra -Wno-unused-parameter -Wno-implicit-fallthrough -pedantic -pedantic-errors -fsanitize=address -g -D_POSIX_C_SOURCE=200809L +LDFLAGS = -lpthread -SERVER_SOURCES=src/server.c src/args.c -CLIENT_SOURCES=src/client.c -SERVER_LIBS=include/server.h include/args.h -CLIENT_LIBS=include/client.h -SERVER_OBJ=server -CLIENT_OBJ=client +SERVER_SOURCES=src/args.c src/selector.c src/socks5nio.c src/stm.c src/hello.c src/request.c src/buffer.c src/server.c +SERVER_LIBS=include/args.h include/selector.h include/socks5nio.h include/stm.h include/hello.h include/request.h include/buffer.h include/server.h +SERVER_OBJECTS=$(SERVER_SOURCES:.c=.o) +SERVER_TARGET=socks5d -all: $(CLIENT_OBJ) $(SERVER_OBJ) +all: $(SERVER_OBJECTS) $(SERVER_TARGET) -$(SERVER_OBJ): $(SERVER_SOURCES) $(SERVER_LIBS) - $(CC) $(CCFLAGS) -I ./include -o $(SERVER_OBJ) $(SERVER_SOURCES) +%.o : %.c + $(CC) $(CCFLAGS) $(LDFLAGS) -I ./include -c $< -o $@ -$(CLIENT_OBJ): $(CLIENT_SOURCES) $(CLIENT_LIBS) - $(CC) $(CCFLAGS) -I ./include -o $(CLIENT_OBJ) $(CLIENT_SOURCES) +$(SERVER_TARGET): $(SERVER_OBJECTS) + $(CC) $(CCFLAGS) $(LDFLAGS) -I ./include -o $@ $^ clean: - rm -f $(CLIENT_OBJ) $(SERVER_OBJ) + rm -f $(SERVER_OBJECTS) $(SERVER_TARGET) -test: +test: clean pvs-studio-analyzer trace -- make pvs-studio-analyzer analyze plog-converter -a '64:1,2,3;GA:1,2,3;OP:1,2,3' -t tasklist -o report.tasks PVS-Studio.log - cppcheck --quiet --enable=all --force --inconclusive . + rm -f PVS-Studio.log + cppcheck --quiet --enable=all --force --inconclusive -I include . cleanTest: - rm -f PVS-Studio.log report.tasks strace_out + rm -f report.tasks strace_out .PHONY: all clean test cleanTest diff --git a/include/args.h b/include/args.h index 75597f5..9b5ea85 100644 --- a/include/args.h +++ b/include/args.h @@ -6,23 +6,22 @@ #define MAX_USERS 500 struct users { - char *name; - char *pass; + char * name; + char * pass; }; struct socks5args { - char *socks_addr; - unsigned short socks_port; + char * socks_addr; + unsigned short socks_port; + char * mng_addr; + unsigned short mng_port; - char * mng_addr; - unsigned short mng_port; + bool disectors_enabled; - bool disectors_enabled; - - struct users users[MAX_USERS]; + struct users users[MAX_USERS]; }; -void parse_args(const int argc, char **argv, struct socks5args *args); +void parse_args(const int argc, char ** argv, struct socks5args * args); #endif diff --git a/include/buffer.h b/include/buffer.h index d587928..d319527 100644 --- a/include/buffer.h +++ b/include/buffer.h @@ -83,67 +83,55 @@ */ typedef struct buffer buffer; struct buffer { - uint8_t *data; + uint8_t * data; /** límite superior del buffer. inmutable */ - uint8_t *limit; + uint8_t * limit; /** puntero de lectura */ - uint8_t *read; + uint8_t * read; /** puntero de escritura */ - uint8_t *write; + uint8_t * write; }; /** * inicializa el buffer sin utilizar el heap */ -void -buffer_init(buffer *b, const size_t n, uint8_t *data); +void buffer_init(buffer * b, const size_t n, uint8_t * data); /** * Retorna un puntero donde se pueden escribir hasta `*nbytes`. * Se debe notificar mediante la función `buffer_write_adv' */ -uint8_t * -buffer_write_ptr(buffer *b, size_t *nbyte); -void -buffer_write_adv(buffer *b, const ssize_t bytes); +uint8_t * buffer_write_ptr(buffer * b, size_t * nbyte); +void buffer_write_adv(buffer * b, const ssize_t bytes); -uint8_t * -buffer_read_ptr(buffer *b, size_t *nbyte); -void -buffer_read_adv(buffer *b, const ssize_t bytes); +uint8_t * buffer_read_ptr(buffer * b, size_t * nbyte); +void buffer_read_adv(buffer *b, const ssize_t bytes); /** * obtiene un byte */ -uint8_t -buffer_read(buffer *b); +uint8_t buffer_read(buffer * b); /** escribe un byte */ -void -buffer_write(buffer *b, uint8_t c); +void buffer_write(buffer * b, uint8_t c); /** * compacta el buffer */ -void -buffer_compact(buffer *b); +void buffer_compact(buffer * b); /** * Reinicia todos los punteros */ -void -buffer_reset(buffer *b); +void buffer_reset(buffer * b); /** retorna true si hay bytes para leer del buffer */ -bool -buffer_can_read(buffer *b); +bool buffer_can_read(buffer * b); /** retorna true si se pueden escribir bytes en el buffer */ -bool -buffer_can_write(buffer *b); - +bool buffer_can_write(buffer *b); #endif diff --git a/include/client.h b/include/client.h index 1fd929a..fd0a885 100644 --- a/include/client.h +++ b/include/client.h @@ -1,6 +1,4 @@ #ifndef CLIENT_H #define CLIENT_H -#include - #endif diff --git a/include/hello.h b/include/hello.h new file mode 100644 index 0000000..a433e82 --- /dev/null +++ b/include/hello.h @@ -0,0 +1,40 @@ +#ifndef HELLO_H +#define HELLO_H + +#include +#include +#include "buffer.h" + +static const uint8_t METHOD_NO_AUTHENTICATION_REQURED = 0x00; +static const uint8_t METHOD_NO_ACCEPTABLE_METHODS = 0xFF; + +enum hello_state { + hello_version, + hello_nmethods, + hello_methods, + hello_done, + hello_error_unsupported_version, +}; + +struct hello_parser { + void (* on_authentication_method) (struct hello_parser * parser, const uint8_t method); + void * data; + enum hello_state state; + uint8_t remaining; +}; + +void hello_parser_init(struct hello_parser *p); + +enum hello_state hello_parser_feed(struct hello_parser * p, uint8_t b); + +enum hello_state hello_consume(buffer * b, struct hello_parser * p, bool * errored); + +bool hello_is_done(const enum hello_state state, bool * errored); + +extern const char * hello_error(const struct hello_parser *p); + +void hello_parser_close(struct hello_parser *p); + +extern int hello_marshall(buffer * b, const uint8_t method); + +#endif diff --git a/include/netutils.h b/include/netutils.h index 02af91d..e4d9ce4 100644 --- a/include/netutils.h +++ b/include/netutils.h @@ -21,23 +21,4 @@ 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 diff --git a/include/parser.h b/include/parser.h deleted file mode 100644 index 3b66f64..0000000 --- a/include/parser.h +++ /dev/null @@ -1,93 +0,0 @@ -#ifndef PARSER_H -#define PARSER_H - -/** - * parser.c -- pequeño motor para parsers/lexers. - * - * El usuario describe estados y transiciones. - * Las transiciones contienen una condición, un estado destino y acciones. - * - * El usuario provee al parser con bytes y éste retona eventos que pueden - * servir para delimitar tokens o accionar directamente. - */ -#include -#include - -/** - * Evento que retorna el parser. - * Cada tipo de evento tendrá sus reglas en relación a data. - */ -struct parser_event { - /** tipo de evento */ - unsigned type; - /** caracteres asociados al evento */ - uint8_t data[3]; - /** cantidad de datos en el buffer `data' */ - uint8_t n; - - /** lista de eventos: si es diferente de null ocurrieron varios eventos */ - struct parser_event *next; -}; - -/** describe una transición entre estados */ -struct parser_state_transition { - /* condición: un caracter o una clase de caracter. Por ej: '\r' */ - int when; - /** descriptor del estado destino cuando se cumple la condición */ - unsigned dest; - /** acción 1 que se ejecuta cuando la condición es verdadera. requerida. */ - void (*act1)(struct parser_event *ret, const uint8_t c); - /** otra acción opcional */ - void (*act2)(struct parser_event *ret, const uint8_t c); -}; - -/** predicado para utilizar en `when' que retorna siempre true */ -static const unsigned ANY = 1 << 9; - -/** declaración completa de una máquina de estados */ -struct parser_definition { - /** cantidad de estados */ - const unsigned states_count; - /** por cada estado, sus transiciones */ - const struct parser_state_transition **states; - /** cantidad de estados por transición */ - const size_t *states_n; - - /** estado inicial */ - const unsigned start_state; -}; - -/** - * inicializa el parser. - * - * `classes`: caracterización de cada caracter (256 elementos) - */ -struct parser * -parser_init (const unsigned *classes, - const struct parser_definition *def); - -/** destruye el parser */ -void -parser_destroy (struct parser *p); - -/** permite resetear el parser al estado inicial */ -void -parser_reset (struct parser *p); - -/** - * el usuario alimenta el parser con un caracter, y el parser retorna un evento - * de parsing. Los eventos son reusado entre llamadas por lo que si se desea - * capturar los datos se debe clonar. - */ -const struct parser_event * -parser_feed (struct parser *p, const uint8_t c); - -/** - * En caso de la aplicacion no necesite clases caracteres, se - * provee dicho arreglo para ser usando en `parser_init' - */ -const unsigned * -parser_no_classes(void); - - -#endif diff --git a/include/parser_utils.h b/include/parser_utils.h deleted file mode 100644 index 93459c5..0000000 --- a/include/parser_utils.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef PARSER_UTILS_H -#define PARSER_UTILS_H - -/* - * parser_utils.c -- factory de ciertos parsers típicos - * - * Provee parsers reusables, como por ejemplo para verificar que - * un string es igual a otro de forma case insensitive. - */ -#include "parser.h" - -enum string_cmp_event_types { - STRING_CMP_MAYEQ, - /** hay posibilidades de que el string sea igual */ - STRING_CMP_EQ, - /** NO hay posibilidades de que el string sea igual */ - STRING_CMP_NEQ, -}; - -const char * -parser_utils_strcmpi_event(const enum string_cmp_event_types type); - - -/* - * Crea un parser que verifica que los caracteres recibidos forment el texto - * descripto por `s'. - * - * Si se recibe el evento `STRING_CMP_NEQ' el texto entrado no matchea. - */ -struct parser_definition -parser_utils_strcmpi(const char *s); - -/** - * libera recursos asociado a una llamada de `parser_utils_strcmpi' - */ -void -parser_utils_strcmpi_destroy(const struct parser_definition *p); - -#endif diff --git a/include/request.h b/include/request.h new file mode 100644 index 0000000..7915083 --- /dev/null +++ b/include/request.h @@ -0,0 +1,85 @@ +#ifndef REQUEST_H +#define REQUEST_H + +#include +#include +#include +#include +#include + +#include "buffer.h" + +enum socks_req_cmd { + socks_req_cmd_connect = 0x01, + socks_req_cmd_bind = 0x02, + socks_req_cmd_associate = 0x03, +}; + +enum socks_addr_type { + socks_req_addr_ipv4 = 0x01, + socks_req_addr_domain = 0x03, + socks_req_addr_ipv6 = 0x04, +}; + +union socks_addr { + char fqdn[0xff]; + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; +}; + +struct request { + enum socks_req_cmd cmd; + enum socks_addr_type dest_addr_type; + union socks_addr dest_addr; + in_port_t dest_port; +}; + +enum request_state { + request_version, + request_cmd, + request_rsv, + request_atyp, + request_dstaddr_fqdn, + request_dstaddr, + request_dstport, + request_done, + request_error, + request_error_unsupported_version, + request_error_unsupported_atyp, +}; + +struct request_parser { + struct request * request; + enum request_state state; + + uint8_t n; + uint8_t i; +}; + +enum socks_response_status { + status_succeeded = 0x00, + status_general_SOCKS_server_failure = 0x01, + status_connectino_not_allowed_by_ruleset = 0x02, + status_network_unreachable = 0x03, + status_host_unreachable = 0x04, + status_connection_refused = 0x05, + status_ttl_expired = 0x06, + status_command_not_supported = 0x07, + status_address_type_not_supported = 0x08, +}; + +void request_parser_init(struct request_parser * p); + +enum request_state request_consume(buffer * b, struct request_parser * p, bool * errored); + +bool request_is_done(const enum request_state state, bool * errored); + +void request_close(struct request_parser *p); + +extern int request_marshall(buffer * b, const enum socks_response_status status); + +enum socks_response_status errno_to_socks(int e); + +enum socks_response_status cmd_resolve(struct request * request, struct sockaddr ** originaddr, socklen_t * origin_len, int * domain); + +#endif diff --git a/include/selector.h b/include/selector.h index 5aabdad..5179441 100644 --- a/include/selector.h +++ b/include/selector.h @@ -101,7 +101,7 @@ typedef enum { OP_NOOP = 0, OP_READ = 1 << 0, OP_WRITE = 1 << 2, -} fd_interest ; +} fd_interest; /** * Quita un interés de una lista de intereses diff --git a/include/server.h b/include/server.h index a5b9767..d861c89 100644 --- a/include/server.h +++ b/include/server.h @@ -1,8 +1,4 @@ #ifndef SERVER_H #define SERVER_H -#include -#include -#include "args.h" - #endif diff --git a/include/socks5nio.h b/include/socks5nio.h new file mode 100644 index 0000000..b4f5542 --- /dev/null +++ b/include/socks5nio.h @@ -0,0 +1,7 @@ +#ifndef SOCKS5NIO_H +#define SOCKS5NIO_H + +void socksv5_passive_accept(struct selector_key * key); +void socksv5_pool_destroy(void); + +#endif diff --git a/include/stm.h b/include/stm.h index 2b3bb44..4e26b9a 100644 --- a/include/stm.h +++ b/include/stm.h @@ -34,7 +34,8 @@ struct state_machine { const struct state_definition *current; }; -struct selector_key *key; +// struct selector_key *key; +#include "selector.h" /** * definición de un estado de la máquina de estados diff --git a/src/args.c b/src/args.c index 1412986..63e6c98 100644 --- a/src/args.c +++ b/src/args.c @@ -1,3 +1,5 @@ +// This is a personal academic project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com #include #include #include @@ -5,7 +7,7 @@ #include #include -#include "../include/args.h" +#include "args.h" static unsigned short port(const char *s) { char *end = 0; @@ -89,7 +91,7 @@ void parse_args(const int argc, char **argv, struct socks5args *args) { args->mng_port = port(optarg); break; case 'u': - if(nusers >= MAX_USERS) { + if (nusers >= MAX_USERS) { fprintf(stderr, "Maximun number of command line users reached: %d.\n", MAX_USERS); exit(1); } else { diff --git a/src/buffer.c b/src/buffer.c index 6dabdf3..bdfe827 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -1,7 +1,5 @@ -/** - * buffer.c - buffer con acceso directo (útil para I/O) que mantiene - * mantiene puntero de lectura y de escritura. - */ +// This is a personal academic project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com #include #include #include @@ -48,7 +46,7 @@ buffer_read_ptr(buffer *b, size_t *nbyte) { inline void buffer_write_adv(buffer *b, const ssize_t bytes) { - if(bytes > -1) { + if (bytes > -1) { b->write += (size_t) bytes; assert(b->write <= b->limit); } @@ -56,11 +54,11 @@ buffer_write_adv(buffer *b, const ssize_t bytes) { inline void buffer_read_adv(buffer *b, const ssize_t bytes) { - if(bytes > -1) { + if (bytes > -1) { b->read += (size_t) bytes; assert(b->read <= b->write); - if(b->read == b->write) { + if (b->read == b->write) { // compactacion poco costosa buffer_compact(b); } @@ -70,7 +68,7 @@ buffer_read_adv(buffer *b, const ssize_t bytes) { inline uint8_t buffer_read(buffer *b) { uint8_t ret; - if(buffer_can_read(b)) { + if (buffer_can_read(b)) { ret = *b->read; buffer_read_adv(b, 1); } else { @@ -81,7 +79,7 @@ buffer_read(buffer *b) { inline void buffer_write(buffer *b, uint8_t c) { - if(buffer_can_write(b)) { + if (buffer_can_write(b)) { *b->write = c; buffer_write_adv(b, 1); } @@ -89,9 +87,9 @@ buffer_write(buffer *b, uint8_t c) { void buffer_compact(buffer *b) { - if(b->data == b->read) { + if (b->data == b->read) { // nada por hacer - } else if(b->read == b->write) { + } else if (b->read == b->write) { b->read = b->data; b->write = b->data; } else { diff --git a/src/client.c b/src/client.c index 06cb899..914e76b 100644 --- a/src/client.c +++ b/src/client.c @@ -1,6 +1,6 @@ // This is a personal academic project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -#include "client.h" +#include int main(int argc, char *argv[]) { diff --git a/src/hello.c b/src/hello.c new file mode 100644 index 0000000..9f1d859 --- /dev/null +++ b/src/hello.c @@ -0,0 +1,112 @@ +// This is a personal academic project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +#include +#include + +#include "hello.h" + +void hello_parser_init(struct hello_parser *p) { + p->state = hello_version; + p->remaining = 0; +} + +enum hello_state hello_parser_feed(struct hello_parser * p, uint8_t b) { + switch (p->state) { + case hello_version: + if (0x05 == b) { + p->state = hello_nmethods; + } else { + p->state = hello_error_unsupported_version; + } + break; + case hello_nmethods: + p->remaining = b; + p->state = hello_methods; + + if (p->remaining == 0) { + p->state = hello_done; + } + break; + case hello_methods: + if (NULL != p->on_authentication_method) { + p->on_authentication_method(p, b); + } + p->remaining--; + if (p->remaining == 0) { + p->state = hello_done; + } + break; + case hello_done: + case hello_error_unsupported_version: + break; + default: + fprintf(stderr, "unknown state %d\n", p->state); + abort(); + } + + return p->state; +} + +extern bool hello_is_done(const enum hello_state state, bool * errored) { + bool ret = true; + + switch (state) { + case hello_error_unsupported_version: + if (0 != errored) { + *errored = true; + } + break; + case hello_done: + break; + default: + ret = false; + break; + } + + return ret; +} + +extern const char * hello_error(const struct hello_parser *p) { + char * ret; + switch (p->state) { + case hello_error_unsupported_version: + ret = "unsupported version"; + break; + default: + ret = ""; + break; + } + + return ret; +} + +extern void hello_parser_close(struct hello_parser * p) {} + + +extern enum hello_state hello_consume(buffer * b, struct hello_parser *p, bool * errored) { + enum hello_state st = p->state; + + while (buffer_can_read(b)) { + const uint8_t c = buffer_read(b); + st = hello_parser_feed(p, c); + if (hello_is_done(st, errored)) { + break; + } + } + + return st; +} + +extern int hello_marshall(buffer * b, const uint8_t method) { + size_t n; + uint8_t * buff = buffer_write_ptr(b, &n); + if (n < 2) { + return -1; + } + + buff[0] = 0x05; + buff[1] = method; + + buffer_write_adv(b, 2); + return 2; +} diff --git a/src/netutils.c b/src/netutils.c index 6f4786e..1ef9ff6 100644 --- a/src/netutils.c +++ b/src/netutils.c @@ -1,3 +1,5 @@ +// This is a personal academic project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com #include #include #include @@ -13,7 +15,7 @@ extern const char * sockaddr_to_human(char *buff, const size_t buffsize, const struct sockaddr *addr) { - if(addr == 0) { + if (addr == 0) { strncpy(buff, "null", buffsize); return buff; } @@ -33,7 +35,7 @@ sockaddr_to_human(char *buff, const size_t buffsize, handled = true; break; } - if(handled) { + if (handled) { if (inet_ntop(addr->sa_family, p, buff, buffsize) == 0) { strncpy(buff, "unknown ip", buffsize); buff[buffsize - 1] = 0; @@ -46,56 +48,10 @@ sockaddr_to_human(char *buff, const size_t buffsize, buff[buffsize - 1] = 0; const size_t len = strlen(buff); - if(handled) { + 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; -} - diff --git a/src/parser.c b/src/parser.c deleted file mode 100644 index ac2e541..0000000 --- a/src/parser.c +++ /dev/null @@ -1,91 +0,0 @@ -#include -#include -#include -#include - -#include "parser.h" - -/* CDT del parser */ -struct parser { - /** tipificación para cada caracter */ - const unsigned *classes; - /** definición de estados */ - const struct parser_definition *def; - - /* estado actual */ - unsigned state; - - /* evento que se retorna */ - struct parser_event e1; - /* evento que se retorna */ - struct parser_event e2; -}; - -void -parser_destroy(struct parser *p) { - if(p != NULL) { - free(p); - } -} - -struct parser * -parser_init(const unsigned *classes, - const struct parser_definition *def) { - struct parser *ret = malloc(sizeof(*ret)); - if(ret != NULL) { - memset(ret, 0, sizeof(*ret)); - ret->classes = classes; - ret->def = def; - ret->state = def->start_state; - } - return ret; -} - -void -parser_reset(struct parser *p) { - p->state = p->def->start_state; -} - -const struct parser_event * -parser_feed(struct parser *p, const uint8_t c) { - const unsigned type = p->classes[c]; - - p->e1.next = p->e2.next = 0; - - const struct parser_state_transition *state = p->def->states[p->state]; - const size_t n = p->def->states_n[p->state]; - bool matched = false; - - for(unsigned i = 0; i < n ; i++) { - const int when = state[i].when; - if (state[i].when <= 0xFF) { - matched = (c == when); - } else if(state[i].when == ANY) { - matched = true; - } else if(state[i].when > 0xFF) { - matched = (type & when); - } else { - matched = false; - } - - if(matched) { - state[i].act1(&p->e1, c); - if(state[i].act2 != NULL) { - p->e1.next = &p->e2; - state[i].act2(&p->e2, c); - } - p->state = state[i].dest; - break; - } - } - return &p->e1; -} - - -static const unsigned classes[0xFF] = {0x00}; - -const unsigned * -parser_no_classes(void) { - return classes; -} - diff --git a/src/parser_utils.c b/src/parser_utils.c deleted file mode 100644 index f54f9ca..0000000 --- a/src/parser_utils.c +++ /dev/null @@ -1,144 +0,0 @@ -#include -#include -#include - -#include "parser_utils.h" - -const char * -parser_utils_strcmpi_event(const enum string_cmp_event_types type) { - const char *ret; - - switch(type) { - case STRING_CMP_MAYEQ: - ret = "wait(c)"; - break; - case STRING_CMP_EQ: - ret = "eq(c)"; - break; - case STRING_CMP_NEQ: - ret = "neq(c)"; - break; - } - return ret; -} - -static void -may_eq(struct parser_event *ret, const uint8_t c) { - ret->type = STRING_CMP_MAYEQ; - ret->n = 1; - ret->data[0] = c; -} - -static void -eq(struct parser_event *ret, const uint8_t c) { - ret->type = STRING_CMP_EQ; - ret->n = 1; - ret->data[0] = c; -} - -static void -neq(struct parser_event *ret, const uint8_t c) { - ret->type = STRING_CMP_NEQ; - ret->n = 1; - ret->data[0] = c; -} - -/* - * para comparar "foo" (length 3) necesitamos 3 + 2 estados. - * Los útimos dos, son el sumidero de comparación fallida, y - * el estado donde se llegó a la comparación completa. - * - * static const struct parser_state_transition ST_0 [] = { - * {.when = 'F', .dest = 1, .action1 = may_eq, }, - * {.when = 'f', .dest = 1, .action1 = may_eq, }, - * {.when = ANY, .dest = NEQ, .action1 = neq,}, - * }; - * static const struct parser_state_transition ST_1 [] = { - * {.when = 'O', .dest = 2, .action1 = may_eq, }, - * {.when = 'o', .dest = 2, .action1 = may_eq, }, - * {.when = ANY, .dest = NEQ, .action1 = neq,}, - * }; - * static const struct parser_state_transition ST_2 [] = { - * {.when = 'O', .dest = EQ, .action1 = eq, }, - * {.when = 'o', .dest = EQ, .action1 = eq, }, - * {.when = ANY, .dest = NEQ, .action1 = neq,}, - * }; - * static const struct parser_state_transition ST_EQ (3) [] = { - * {.when = ANY, .dest = NEQ, .action1 = neq,}, - * }; - * static const struct parser_state_transition ST_NEQ (4) [] = { - * {.when = ANY, .dest = NEQ, .action1 = neq,}, - * }; - * - */ -struct parser_definition -parser_utils_strcmpi(const char *s) { - const size_t n = strlen(s); - - struct parser_state_transition **states = calloc(n + 2, sizeof(*states)); - size_t *nstates = calloc(n + 2, sizeof(*nstates)); - struct parser_state_transition *transitions= calloc(3 *(n + 2), - sizeof(*transitions)); - if(states == NULL || nstates == NULL || transitions == NULL) { - free(states); - free(nstates); - free(transitions); - - struct parser_definition def = { - .start_state = 0, - .states_count = 0, - .states = NULL, - .states_n = NULL, - }; - return def; - } - - // estados fijos - const size_t st_eq = n; - const size_t st_neq = n + 1; - - for(size_t i = 0; i < n; i++) { - const size_t dest = (i + 1 == n) ? st_eq : i + 1; - - transitions[i * 3 + 0].when = tolower(s[i]); - transitions[i * 3 + 0].dest = dest; - transitions[i * 3 + 0].act1 = i + 1 == n ? eq : may_eq; - transitions[i * 3 + 1].when = toupper(s[i]); - transitions[i * 3 + 1].dest = dest; - transitions[i * 3 + 1].act1 = i + 1 == n ? eq : may_eq; - transitions[i * 3 + 2].when = ANY; - transitions[i * 3 + 2].dest = st_neq; - transitions[i * 3 + 2].act1 = neq; - states [i] = transitions + (i * 3 + 0); - nstates [i] = 3; - } - // EQ - transitions[(n + 0) * 3].when = ANY; - transitions[(n + 0) * 3].dest = st_neq; - transitions[(n + 0) * 3].act1 = neq; - states [(n + 0)] = transitions + ((n + 0) * 3 + 0); - nstates [(n + 0)] = 1; - // NEQ - transitions[(n + 1) * 3].when = ANY; - transitions[(n + 1) * 3].dest = st_neq; - transitions[(n + 1) * 3].act1 = neq; - states [(n + 1)] = transitions + ((n + 1) * 3 + 0); - nstates [(n + 1)] = 1; - - - struct parser_definition def = { - .start_state = 0, - .states_count = n + 2, - .states = (const struct parser_state_transition **) states, - .states_n = (const size_t *) nstates, - }; - - return def; -} - -void -parser_utils_strcmpi_destroy(const struct parser_definition *p) { - free((void *)p->states[0]); - free((void *)p->states); - free((void *)p->states_n); -} diff --git a/src/request.c b/src/request.c new file mode 100644 index 0000000..63b57af --- /dev/null +++ b/src/request.c @@ -0,0 +1,279 @@ +// This is a personal academic project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +#include +#include +#include + +#include "request.h" + +static void remaining_set(struct request_parser * p, const int n) { + p->i = 0; + p->n = n; +} + +static int remaining_is_done(struct request_parser * p) { + return p->i >= p->n; +} + +static enum request_state version(const uint8_t c, struct request_parser * p) { + enum request_state next; + switch (c) { + case 0x05: + next = request_cmd; + break; + default: + next = request_error_unsupported_version; + break; + } + + return next; +} + +static enum request_state cmd(const uint8_t c, struct request_parser * p) { + p->request->cmd = c; + return request_rsv; +} + +static enum request_state rsv(const uint8_t c, struct request_parser * p) { + return request_atyp; +} + +static enum request_state atyp(const uint8_t c, struct request_parser * p) { + enum request_state next; + + p->request->dest_addr_type = c; + switch (p->request->dest_addr_type) { + case socks_req_addr_ipv4: + remaining_set(p, 4); + memset(&(p->request->dest_addr.ipv4), 0, sizeof(p->request->dest_addr.ipv4)); + p->request->dest_addr.ipv4.sin_family = AF_INET; + next = request_dstaddr; + break; + case socks_req_addr_ipv6: + remaining_set(p, 16); + memset(&(p->request->dest_addr.ipv6), 0, sizeof(p->request->dest_addr.ipv6)); + p->request->dest_addr.ipv4.sin_family = AF_INET6; + next = request_dstaddr; + break; + case socks_req_addr_domain: + next = request_dstaddr_fqdn; + break; + default: + next = request_error_unsupported_atyp; + break; + } + + return next; +} + +static enum request_state dstaddr_fqdn(const uint8_t c, struct request_parser * p) { + remaining_set(p, c); + p->request->dest_addr.fqdn[p->n - 1] = 0; + + return request_dstaddr; +} + +static enum request_state dstaddr(const uint8_t c, struct request_parser * p) { + enum request_state next; + + switch (p->request->dest_addr_type) { + case socks_req_addr_ipv4: + ((uint8_t *)&(p->request->dest_addr.ipv4.sin_addr))[p->i++] = c; + break; + case socks_req_addr_ipv6: + ((uint8_t *)&(p->request->dest_addr.ipv6.sin6_addr))[p->i++] = c; + break; + case socks_req_addr_domain: + p->request->dest_addr.fqdn[p->i++] = c; + break; + } + if (remaining_is_done(p)) { + remaining_set(p, 2); + p->request->dest_port = 0; + next = request_dstport; + } else { + next = request_dstaddr; + } + + return next; +} +#include + +static enum request_state dstport(const uint8_t c, struct request_parser * p) { + enum request_state next; + *(((uint8_t *) &(p->request->dest_port)) + p->i) = c; + p->i++; + next = request_dstport; + if (p->i >= p->n) { + next = request_done; + } + return next; +} + +void request_parser_init(struct request_parser * p) { + p->state = request_version; + memset(p->request, 0, sizeof(*(p->request))); +} + +static enum request_state request_parser_feed(struct request_parser * p, const uint8_t c) { + enum request_state next; + + switch(p->state) { + case request_version: + next = version(c, p); + break; + case request_cmd: + next = cmd(c, p); + break; + case request_rsv: + next = rsv(c, p); + break; + case request_atyp: + next = atyp(c, p); + break; + case request_dstaddr_fqdn: + next = dstaddr_fqdn(c, p); + break; + case request_dstaddr: + next = dstaddr(c, p); + break; + case request_dstport: + next = dstport(c, p); + break; + case request_done: + case request_error: + case request_error_unsupported_version: + case request_error_unsupported_atyp: + next = p->state; + break; + default: + next = request_error; + break; + } + + return p->state = next; +} + +bool request_is_done(const enum request_state state, bool * errored) { + bool ret = true; + + switch (state) { + case request_error: + case request_error_unsupported_version: + case request_error_unsupported_atyp: + if (0 != errored) { + *errored = true; + } + break; + case request_done: + break; + default: + ret = false; + break; + } + + return ret; +} + +void request_close(struct request_parser *p) {} + +extern enum request_state request_consume(buffer * b, struct request_parser *p, bool * errored) { + enum request_state st = p->state; + + while (buffer_can_read(b)) { + const uint8_t c = buffer_read(b); + st = request_parser_feed(p, c); + if (request_is_done(st, errored)) { + break; + } + } + + return st; +} + +extern int request_marshall(buffer * b, const enum socks_response_status status) { + size_t n; + uint8_t * buff = buffer_write_ptr(b, &n); + if (n < 10) { + return -1; + } + + buff[0] = 0x05; + buff[1] = status; + buff[2] = 0x00; + buff[3] = socks_req_addr_ipv4; + buff[4] = 0x00; + buff[5] = 0x00; + buff[6] = 0x00; + buff[7] = 0x00; + buff[8] = 0x00; + buff[9] = 0x00; + + buffer_write_adv(b, 10); + return 10; +} + +// TODO REPLACE CON sockaddr_storage +enum socks_response_status cmd_resolve(struct request * request, struct sockaddr ** originaddr, socklen_t * origin_len, int * domain) { + enum socks_response_status ret = status_general_SOCKS_server_failure; + *domain = AF_INET; + struct sockaddr * addr = 0x00; + socklen_t addrlen = 0; + + switch (request->dest_addr_type) { + case socks_req_addr_domain: { + // TODO: SOPORTAR DOMAIN QUE VIENE CON IPV6 + struct hostent * hp = gethostbyname(request->dest_addr.fqdn); + if (hp == 0) { + memset(&request->dest_addr, 0x00, sizeof(request->dest_addr)); + break; + } + request->dest_addr.ipv4.sin_family = hp->h_addrtype; + memcpy((char *) &request->dest_addr.ipv4.sin_addr, *hp->h_addr_list, hp->h_length); + } + case socks_req_addr_ipv4: + // *domain = AF_INET; + addr = (struct sockaddr *)&(request->dest_addr.ipv4); + addrlen = sizeof(request->dest_addr.ipv4); + request->dest_addr.ipv4.sin_port = request->dest_port; + break; + case socks_req_addr_ipv6: + *domain = AF_INET6; + addr = (struct sockaddr *) &(request->dest_addr.ipv6); + addrlen = sizeof(request->dest_addr.ipv6); + request->dest_addr.ipv6.sin6_port = request->dest_port; + break; + default: + return status_address_type_not_supported; + } + + *originaddr = addr; + *origin_len = addrlen; + + return ret; +} + +enum socks_response_status errno_to_socks(int e) { + enum socks_response_status ret = status_general_SOCKS_server_failure; + switch (e) { + case 0: + ret = status_succeeded; + break; + case ECONNREFUSED: + ret = status_connection_refused; + break; + case EHOSTUNREACH: + ret = status_host_unreachable; + break; + case ENETUNREACH: + ret = status_network_unreachable; + break; + case ETIMEDOUT: + ret = status_ttl_expired; + break; + default: + break; + } + return ret; +} + diff --git a/src/selector.c b/src/selector.c index 861d1db..ca15ebf 100644 --- a/src/selector.c +++ b/src/selector.c @@ -1,14 +1,13 @@ -/** - * selector.c - un muliplexor de entrada salida - */ -#include // perror -#include // malloc -#include // memset -#include // :) -#include // :) +// This is a personal academic project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +#include +#include +#include +#include +#include #include -#include // SIZE_MAX +#include #include #include #include @@ -58,7 +57,7 @@ struct selector_init conf; static sigset_t emptyset, blockset; selector_status -selector_init(const struct selector_init *c) { +selector_init(const struct selector_init *c) { memcpy(&conf, c, sizeof(conf)); // inicializamos el sistema de comunicación entre threads y el selector @@ -74,7 +73,7 @@ selector_init(const struct selector_init *c) { // select sigemptyset(&blockset); sigaddset (&blockset, conf.signal); - if(-1 == sigprocmask(SIG_BLOCK, &blockset, NULL)) { + if (-1 == sigprocmask(SIG_BLOCK, &blockset, NULL)) { ret = SELECTOR_IO; goto finally; } @@ -153,7 +152,7 @@ struct fdselector { /** protege el acceso a resolutions jobs */ pthread_mutex_t resolution_mutex; /** - * lista de trabajos blockeantes que finalizaron y que pueden ser + * lista de trabajos bloqueantes que finalizaron y que pueden ser * notificados. */ struct blocking_job *resolution_jobs; @@ -179,7 +178,7 @@ size_t next_capacity(const size_t n) { tmp = 1UL << bits; assert(tmp >= n); - if(tmp > ITEMS_MAX_SIZE) { + if (tmp > ITEMS_MAX_SIZE) { tmp = ITEMS_MAX_SIZE; } @@ -211,8 +210,8 @@ items_max_fd(fd_selector s) { int max = 0; for(int i = 0; i <= s->max_fd; i++) { struct item *item = s->fds + i; - if(ITEM_USED(item)) { - if(item->fd > max) { + if (ITEM_USED(item)) { + if (item->fd > max) { max = item->fd; } } @@ -225,39 +224,38 @@ items_update_fdset_for_fd(fd_selector s, const struct item * item) { FD_CLR(item->fd, &s->master_r); FD_CLR(item->fd, &s->master_w); - if(ITEM_USED(item)) { - if(item->interest & OP_READ) { + if (ITEM_USED(item)) { + if (item->interest & OP_READ) { FD_SET(item->fd, &(s->master_r)); } - if(item->interest & OP_WRITE) { + if (item->interest & OP_WRITE) { FD_SET(item->fd, &(s->master_w)); } } } /** - * garantizar cierta cantidad de elemenos en `fds'. - * Se asegura de que `n' sea un número que la plataforma donde corremos lo + * garantizar cierta cantidad de elemenos en 'fds'. + * Se asegura de que 'n' sea un número que la plataforma donde corremos lo * soporta */ -static selector_status -ensure_capacity(fd_selector s, const size_t n) { +static selector_status ensure_capacity(fd_selector s, const size_t n) { selector_status ret = SELECTOR_SUCCESS; const size_t element_size = sizeof(*s->fds); - if(n < s->fd_size) { + if (n < s->fd_size) { // nada para hacer, entra... ret = SELECTOR_SUCCESS; - } else if(n > ITEMS_MAX_SIZE) { + } else if (n > ITEMS_MAX_SIZE) { // me estás pidiendo más de lo que se puede. ret = SELECTOR_MAXFD; - } else if(NULL == s->fds) { + } else if (NULL == s->fds) { // primera vez.. alocamos const size_t new_size = next_capacity(n); s->fds = calloc(new_size, element_size); - if(NULL == s->fds) { + if (NULL == s->fds) { ret = SELECTOR_ENOMEM; } else { s->fd_size = new_size; @@ -266,14 +264,14 @@ ensure_capacity(fd_selector s, const size_t n) { } else { // hay que agrandar... const size_t new_size = next_capacity(n); - if (new_size > SIZE_MAX/element_size) { // ver MEM07-C + if (new_size > (SIZE_MAX / element_size)) { // ver MEM07-C ret = SELECTOR_ENOMEM; } else { struct item *tmp = realloc(s->fds, new_size * element_size); - if(NULL == tmp) { + if (NULL == tmp) { ret = SELECTOR_ENOMEM; } else { - s->fds = tmp; + s->fds = tmp; const size_t old_size = s->fd_size; s->fd_size = new_size; @@ -289,14 +287,14 @@ fd_selector selector_new(const size_t initial_elements) { size_t size = sizeof(struct fdselector); fd_selector ret = malloc(size); - if(ret != NULL) { + if (ret != NULL) { memset(ret, 0x00, size); ret->master_t.tv_sec = conf.select_timeout.tv_sec; ret->master_t.tv_nsec = conf.select_timeout.tv_nsec; assert(ret->max_fd == 0); ret->resolution_jobs = 0; pthread_mutex_init(&ret->resolution_mutex, 0); - if(0 != ensure_capacity(ret, initial_elements)) { + if (0 != ensure_capacity(ret, initial_elements)) { selector_destroy(ret); ret = NULL; } @@ -307,20 +305,21 @@ selector_new(const size_t initial_elements) { void selector_destroy(fd_selector s) { // lean ya que se llama desde los casos fallidos de _new. - if(s != NULL) { - if(s->fds != NULL) { + if (s != NULL) { + if (s->fds != NULL) { for(size_t i = 0; i < s->fd_size ; i++) { - if(ITEM_USED(s->fds + i)) { + if (ITEM_USED(s->fds + i)) { selector_unregister_fd(s, i); } } pthread_mutex_destroy(&s->resolution_mutex); - for(struct blocking_job *j = s->resolution_jobs; j != NULL; - j = j->next) { + for(struct blocking_job *next, *j = s->resolution_jobs; j != NULL; j = j->next) { + next = j->next; free(j); + j = next; } free(s->fds); - s->fds = NULL; + s->fds = NULL; s->fd_size = 0; } free(s); @@ -330,29 +329,25 @@ selector_destroy(fd_selector s) { #define INVALID_FD(fd) ((fd) < 0 || (fd) >= ITEMS_MAX_SIZE) selector_status -selector_register(fd_selector s, - const int fd, - const fd_handler *handler, - const fd_interest interest, - void *data) { +selector_register(fd_selector s, const int fd, const fd_handler *handler, const fd_interest interest, void *data) { selector_status ret = SELECTOR_SUCCESS; // 0. validación de argumentos - if(s == NULL || INVALID_FD(fd) || handler == NULL) { + if (s == NULL || INVALID_FD(fd) || handler == NULL) { ret = SELECTOR_IARGS; goto finally; } // 1. tenemos espacio? size_t ufd = (size_t)fd; - if(ufd > s->fd_size) { + if (ufd >= s->fd_size) { ret = ensure_capacity(s, ufd); - if(SELECTOR_SUCCESS != ret) { + if (SELECTOR_SUCCESS != ret) { goto finally; } } // 2. registración struct item * item = s->fds + ufd; - if(ITEM_USED(item)) { + if (ITEM_USED(item)) { ret = SELECTOR_FDINUSE; goto finally; } else { @@ -362,7 +357,7 @@ selector_register(fd_selector s, item->data = data; // actualizo colaterales - if(fd > s->max_fd) { + if (fd > s->max_fd) { s->max_fd = fd; } items_update_fdset_for_fd(s, item); @@ -377,18 +372,18 @@ selector_unregister_fd(fd_selector s, const int fd) { selector_status ret = SELECTOR_SUCCESS; - if(NULL == s || INVALID_FD(fd)) { + if (NULL == s || INVALID_FD(fd)) { ret = SELECTOR_IARGS; goto finally; } struct item *item = s->fds + fd; - if(!ITEM_USED(item)) { + if (!ITEM_USED(item)) { ret = SELECTOR_IARGS; goto finally; } - if(item->handler->handle_close != NULL) { + if (item->handler->handle_close != NULL) { struct selector_key key = { .s = s, .fd = item->fd, @@ -412,12 +407,12 @@ selector_status selector_set_interest(fd_selector s, int fd, fd_interest i) { selector_status ret = SELECTOR_SUCCESS; - if(NULL == s || INVALID_FD(fd)) { + if (NULL == s || INVALID_FD(fd)) { ret = SELECTOR_IARGS; goto finally; } struct item *item = s->fds + fd; - if(!ITEM_USED(item)) { + if (!ITEM_USED(item)) { ret = SELECTOR_IARGS; goto finally; } @@ -431,7 +426,7 @@ selector_status selector_set_interest_key(struct selector_key *key, fd_interest i) { selector_status ret; - if(NULL == key || NULL == key->s || INVALID_FD(key->fd)) { + if (NULL == key || NULL == key->s || INVALID_FD(key->fd)) { ret = SELECTOR_IARGS; } else { ret = selector_set_interest(key->s, key->fd, i); @@ -453,21 +448,21 @@ handle_iteration(fd_selector s) { for (int i = 0; i <= n; i++) { struct item *item = s->fds + i; - if(ITEM_USED(item)) { + if (ITEM_USED(item)) { key.fd = item->fd; key.data = item->data; - if(FD_ISSET(item->fd, &s->slave_r)) { - if(OP_READ & item->interest) { - if(0 == item->handler->handle_read) { + if (FD_ISSET(item->fd, &s->slave_r)) { + if (OP_READ & item->interest) { + if (0 == item->handler->handle_read) { assert(("OP_READ arrived but no handler. bug!" == 0)); } else { item->handler->handle_read(&key); } } } - if(FD_ISSET(i, &s->slave_w)) { - if(OP_WRITE & item->interest) { - if(0 == item->handler->handle_write) { + if (FD_ISSET(i, &s->slave_w)) { + if (OP_WRITE & item->interest) { + if (0 == item->handler->handle_write) { assert(("OP_WRITE arrived but no handler. bug!" == 0)); } else { item->handler->handle_write(&key); @@ -484,18 +479,20 @@ handle_block_notifications(fd_selector s) { .s = s, }; pthread_mutex_lock(&s->resolution_mutex); - for(struct blocking_job *j = s->resolution_jobs; + for(struct blocking_job *next, *j = s->resolution_jobs; j != NULL ; j = j->next) { struct item *item = s->fds + j->fd; - if(ITEM_USED(item)) { - key.fd = item->fd; + if (ITEM_USED(item)) { + key.fd = item->fd; key.data = item->data; item->handler->handle_block(&key); } + next = j->next; free(j); + j = next; } s->resolution_jobs = 0; pthread_mutex_unlock(&s->resolution_mutex); @@ -509,7 +506,7 @@ selector_notify_block(fd_selector s, // TODO(juan): usar un pool struct blocking_job *job = malloc(sizeof(*job)); - if(job == NULL) { + if (job == NULL) { ret = SELECTOR_ENOMEM; goto finally; } @@ -541,7 +538,7 @@ selector_select(fd_selector s) { int fds = pselect(s->max_fd + 1, &s->slave_r, &s->slave_w, 0, &s->slave_t, &emptyset); - if(-1 == fds) { + if (-1 == fds) { switch(errno) { case EAGAIN: case EINTR: @@ -551,8 +548,8 @@ selector_select(fd_selector s) { // ayuda a encontrar casos donde se cierran los fd pero no // se desregistraron for(int i = 0 ; i < s->max_fd; i++) { - if(FD_ISSET(i, &s->master_r)|| FD_ISSET(i, &s->master_w)) { - if(-1 == fcntl(i, F_GETFD, 0)) { + if (FD_ISSET(i, &s->master_r)|| FD_ISSET(i, &s->master_w)) { + if (-1 == fcntl(i, F_GETFD, 0)) { fprintf(stderr, "Bad descriptor detected: %d\n", i); } } @@ -567,7 +564,7 @@ selector_select(fd_selector s) { } else { handle_iteration(s); } - if(ret == SELECTOR_SUCCESS) { + if (ret == SELECTOR_SUCCESS) { handle_block_notifications(s); } finally: @@ -578,10 +575,10 @@ int selector_fd_set_nio(const int fd) { int ret = 0; int flags = fcntl(fd, F_GETFD, 0); - if(flags == -1) { + if (flags == -1) { ret = -1; } else { - if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { ret = -1; } } diff --git a/src/server.c b/src/server.c index 11ffdec..3caa177 100644 --- a/src/server.c +++ b/src/server.c @@ -1,8 +1,133 @@ // This is a personal academic project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -#include "server.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "args.h" +#include "selector.h" +#include "socks5nio.h" + +static bool done = false; + +static void sigterm_handler(const int signal) { + printf("Signal %d, Cleaning up and exiting\n", signal); + done = true; +} + +int main(int argc, char **argv) { + unsigned port = 1080; -int main(int argc, char *argv[]) { struct socks5args * args = malloc(sizeof(struct socks5args)); parse_args(argc, argv, args); + free(args); + + close(STDIN_FILENO); + + const char *err_msg = NULL; + selector_status ss = SELECTOR_SUCCESS; + fd_selector selector = NULL; + + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(port); + + const int server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (server < 0) { + err_msg = "Unable to create socket"; + goto finally; + } + + fprintf(stdout, "Listening on TCP port %u\n", port); + + setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)); + + if (bind(server, (struct sockaddr*) &addr, sizeof(addr)) < 0) { + err_msg = "Unable to bind socket"; + goto finally; + } + + if (listen(server, 20) < 0) { + err_msg = "Unable to listen"; + goto finally; + } + + signal(SIGTERM, sigterm_handler); + signal(SIGINT, sigterm_handler); + + if (selector_fd_set_nio(server) == -1) { + err_msg = "Getting server socket flags"; + goto finally; + } + const struct selector_init conf = { + .signal = SIGALRM, + .select_timeout = { + .tv_sec = 10, + .tv_nsec = 0, + }, + }; + if (0 != selector_init(&conf)) { + err_msg = "Initializing selector"; + goto finally; + } + + selector = selector_new(1024); + if (selector == NULL) { + err_msg = "Unable to create selector"; + goto finally; + } + + const struct fd_handler socksv5 = { + .handle_read = socksv5_passive_accept, + .handle_write = NULL, + .handle_close = NULL, + }; + ss = selector_register(selector, server, &socksv5, OP_READ, NULL); + if (ss != SELECTOR_SUCCESS) { + err_msg = "Registering fd"; + goto finally; + } + for(;!done;) { + err_msg = NULL; + ss = selector_select(selector); + if (ss != SELECTOR_SUCCESS) { + err_msg = "Serving"; + goto finally; + } + } + if (err_msg == NULL) { + err_msg = "Closing"; + } + + int ret = 0; +finally: + if (ss != SELECTOR_SUCCESS) { + fprintf(stderr, "%s: %s\n", (err_msg == NULL) ? "": err_msg, ss == SELECTOR_IO ? strerror(errno) : selector_error(ss)); + ret = 2; + } else if (err_msg) { + perror(err_msg); + ret = 1; + } + if (selector != NULL) { + selector_destroy(selector); + } + selector_close(); + + socksv5_pool_destroy(); + + if (server >= 0) { + close(server); + } + return ret; } diff --git a/src/socks5nio.c b/src/socks5nio.c new file mode 100644 index 0000000..1006de7 --- /dev/null +++ b/src/socks5nio.c @@ -0,0 +1,845 @@ +// This is a personal academic project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "hello.h" +#include "request.h" +#include "buffer.h" +#include "selector.h" + +#include "stm.h" +#include "socks5nio.h" +#include "netutils.h" + +#define N(x) (sizeof(x)/sizeof((x)[0])) +#define BUFF_SIZE 2048 // TODO: decidir tamaño del buffer + +/** maquina de estados general */ +enum socks_v5state { + HELLO_READ, + HELLO_WRITE, + +// AUTH_READ, +// AUTH_WRITE, + + REQUEST_READ, + REQUEST_RESOLV, + REQUEST_CONNECTING, + REQUEST_WRITE, + + COPY, + + DONE, + ERROR, +}; + +//////////////////////////////////////////////////////////////////// +// Definición de variables para cada estado + +/** usado por HELLO_READ, HELLO_WRITE */ +struct hello_st { + /** buffer utilizado para I/O */ + buffer *rb, *wb; + struct hello_parser parser; + /** el método de autenticación seleccionado */ + uint8_t method; +}; + +struct request_st { + buffer *rb, *wb; + + struct request request; + struct request_parser parser; + + enum socks_response_status status; + + struct sockaddr_storage * origin_addr; + socklen_t * origin_addr_len; + int * origin_domain; + + const int * client_fd; + int * origin_fd; +}; + +struct connecting { + buffer * wb; + const int * client_fd; + int * origin_fd; + enum socks_response_status * status; +}; + +struct copy { + int * fd; + buffer * rb, * wb; + fd_interest duplex; + + struct copy * other; +}; + +/* + * Si bien cada estado tiene su propio struct que le da un alcance + * acotado, disponemos de la siguiente estructura para hacer una única + * alocación cuando recibimos la conexión. + * + * Se utiliza un contador de referencias (references) para saber cuando debemos + * liberarlo finalmente, y un pool para reusar alocaciones previas. + */ +struct socks5 { + struct sockaddr_storage client_addr; + socklen_t client_addr_len; + int client_fd; + + struct addrinfo * origin_resolution; + struct addrinfo * origin_resolution_current; + + struct sockaddr_storage origin_addr; + socklen_t origin_addr_len; + int origin_domain; + int origin_fd; + + /** maquinas de estados */ + struct state_machine stm; + + /** estados para el client_fd */ + union { + struct hello_st hello; + struct request_st request; + struct copy copy; + } client; + /** estados para el origin_fd */ + union { + struct connecting conn; + struct copy copy; + } orig; + + // TODO: decidir tamaño del buffer + uint8_t raw_buff_a[BUFF_SIZE], raw_buff_b[BUFF_SIZE]; + buffer read_buffer, write_buffer; + + unsigned references; + + struct socks5 * next; +}; + +static const unsigned max_pool = 50; +static unsigned pool_size = 0; +static struct socks5 * pool = 0; + +static const struct state_definition * socks5_describe_states(void); + +static struct socks5 * socks5_new(int client_fd) { + struct socks5 * ret; + + if (pool == NULL) { + ret = malloc(sizeof(*ret)); + } else { + ret = pool; + pool = pool->next; + ret->next = 0; + } + + if (ret == NULL) { + goto finally; + } + memset(ret, 0x00, sizeof(*ret)); + + ret->origin_fd = -1; + ret->client_fd = client_fd; + ret->client_addr_len = sizeof(ret->client_addr); + + ret->stm.initial = HELLO_READ; + ret->stm.max_state = ERROR; + ret->stm.states = socks5_describe_states(); + stm_init(&ret->stm); + + buffer_init(&ret->read_buffer, N(ret->raw_buff_a), ret->raw_buff_a); + buffer_init(&ret->write_buffer, N(ret->raw_buff_b), ret->raw_buff_b); + + ret->references = 1; +finally: + return ret; +} + + +/** realmente destruye */ +static void +socks5_destroy_(struct socks5* s) { + if(s->origin_resolution != NULL) { + freeaddrinfo(s->origin_resolution); + s->origin_resolution = 0; + } + free(s); +} + +/** + * destruye un `struct socks5', tiene en cuenta las referencias + * y el pool de objetos. + */ +static void +socks5_destroy(struct socks5 *s) { + if (s != NULL) { + if (s->references == 1) { + if(pool_size < max_pool) { + s->next = pool; + pool = s; + pool_size++; + } else { + socks5_destroy_(s); + } + } else { + s->references -= 1; + } + } +} + +void +socksv5_pool_destroy(void) { + struct socks5 *next, *s; + for(s = pool; s != NULL ; s = next) { + next = s->next; + free(s); + } +} + +/** obtiene el struct (socks5 *) desde la llave de selección */ +#define ATTACHMENT(key) ( (struct socks5 *)(key)->data) + +/* declaración forward de los handlers de selección de una conexión + * establecida entre un cliente y el proxy. + */ +static void socksv5_read (struct selector_key *key); +static void socksv5_write (struct selector_key *key); +static void socksv5_block (struct selector_key *key); +static void socksv5_close (struct selector_key *key); +static const struct fd_handler socks5_handler = { + .handle_read = socksv5_read, + .handle_write = socksv5_write, + .handle_close = socksv5_close, + .handle_block = socksv5_block, +}; + +/** Intenta aceptar la nueva conexión entrante*/ +void +socksv5_passive_accept(struct selector_key *key) { + struct sockaddr_storage client_addr; + socklen_t client_addr_len = sizeof(client_addr); + struct socks5 *state = NULL; + + const int client = accept(key->fd, (struct sockaddr*) &client_addr, + &client_addr_len); + if(client == -1) { + goto fail; + } + if(selector_fd_set_nio(client) == -1) { + goto fail; + } + state = socks5_new(client); + if(state == NULL) { + // sin un estado, nos es imposible manejaro. + // tal vez deberiamos apagar accept() hasta que detectemos + // que se liberó alguna conexión. + goto fail; + } + memcpy(&state->client_addr, &client_addr, client_addr_len); + state->client_addr_len = client_addr_len; + + if(SELECTOR_SUCCESS != selector_register(key->s, client, &socks5_handler, OP_READ, state)) { + goto fail; + } + return ; +fail: + if(client != -1) { + close(client); + } + socks5_destroy(state); +} + +//////////////////////////////////////////////////////////////////////////////// +// HELLO +//////////////////////////////////////////////////////////////////////////////// + +/** callback del parser utilizado en `read_hello' */ +static void on_hello_method(struct hello_parser * p, const uint8_t method) { + uint8_t *selected = p->data; + + if (METHOD_NO_AUTHENTICATION_REQURED == method) { + *selected = method; + } +} + +/** inicializa las variables de los estados HELLO_… */ +static void hello_read_init(const unsigned state, struct selector_key * key) { + struct hello_st *d = &ATTACHMENT(key)->client.hello; + + d->rb = &(ATTACHMENT(key)->read_buffer); + d->wb = &(ATTACHMENT(key)->write_buffer); + d->parser.data = &d->method; + d->parser.on_authentication_method = on_hello_method, hello_parser_init( + &d->parser); +} + +static unsigned +hello_process(const struct hello_st* d); + +/** lee todos los bytes del mensaje de tipo `hello' y inicia su proceso */ +static unsigned hello_read(struct selector_key * key) { + struct hello_st *d = &ATTACHMENT(key)->client.hello; + unsigned ret = HELLO_READ; + bool error = false; + uint8_t *ptr; + size_t count; + ssize_t n; + + ptr = buffer_write_ptr(d->rb, &count); + n = recv(key->fd, ptr, count, 0); + if(n > 0) { + buffer_write_adv(d->rb, n); + const enum hello_state st = hello_consume(d->rb, &d->parser, &error); + + if(hello_is_done(st, 0)) { + if(SELECTOR_SUCCESS == selector_set_interest_key(key, OP_WRITE)) { + ret = hello_process(d); + } else { + ret = ERROR; + } + } + } else { + ret = ERROR; + } + + return error ? ERROR : ret; +} + +/** procesamiento del mensaje `hello' */ +static unsigned +hello_process(const struct hello_st* d) { + unsigned ret = HELLO_WRITE; + + uint8_t m = d->method; + const uint8_t r = (m == METHOD_NO_ACCEPTABLE_METHODS) ? 0xFF : 0x00; + if (hello_marshall(d->wb, (enum socks_response_status) r) == -1) { + ret = ERROR; + } + if (METHOD_NO_ACCEPTABLE_METHODS == m) { + ret = ERROR; + } + return ret; +} + +static void hello_read_close(const unsigned state, struct selector_key * key) { + struct hello_st * d = &ATTACHMENT(key)->client.hello; + + hello_parser_close(&d->parser); +} + +static unsigned hello_write(struct selector_key * key) { + struct hello_st * d = &ATTACHMENT(key)->client.hello; + + unsigned ret = HELLO_WRITE; + uint8_t * ptr; + size_t count; + ssize_t n; + + ptr = buffer_read_ptr(d->wb, &count); + n = send(key->fd, ptr, count, MSG_NOSIGNAL); + if (n == -1) { + ret = ERROR; + } else { + buffer_read_adv(d->wb, n); + if (!buffer_can_read(d->wb)) { + if (SELECTOR_SUCCESS == selector_set_interest_key(key, OP_READ)) { + ret = REQUEST_READ; + } else { + ret = ERROR; + } + } + } + + return ret; +} + +static void request_init(const unsigned state, struct selector_key * key) { + struct request_st * d = &ATTACHMENT(key)->client.request; + + d->rb = &(ATTACHMENT(key)->read_buffer); + d->wb = &(ATTACHMENT(key)->write_buffer); + d->parser.request = &d->request; + d->status = status_general_SOCKS_server_failure; + request_parser_init(&d->parser); + d->client_fd = &ATTACHMENT(key)->client_fd; + d->origin_fd = &ATTACHMENT(key)->origin_fd; + + d->origin_addr = &ATTACHMENT(key)->origin_addr; + d->origin_addr_len = &ATTACHMENT(key)->origin_addr_len; + d->origin_domain = &ATTACHMENT(key)->origin_domain; +} + +static unsigned request_process(struct selector_key * key, struct request_st * d); + +static unsigned request_read(struct selector_key * key) { + struct request_st * d = &ATTACHMENT(key)->client.request; + + buffer *b = d->rb; + unsigned ret = REQUEST_READ; + bool error = false; + uint8_t * ptr; + size_t count; + ssize_t n; + + ptr = buffer_write_ptr(b, &count); + n = recv(key->fd, ptr, count, 0); + if (n > 0) { + buffer_write_adv(b, n); + int st = request_consume(b, &d->parser, &error); + if (request_is_done(st, 0)) { + ret = request_process(key, d); + } + } else { + ret = ERROR; + } + + return error ? ERROR : ret; +} + +static unsigned request_connect(struct selector_key * key, struct request_st * d); +static void * request_resolv_blocking(void * data); + +static unsigned request_process(struct selector_key * key, struct request_st * d) { + unsigned ret; + pthread_t tid; + + switch (d->request.cmd) { + case socks_req_cmd_connect: + switch (d->request.dest_addr_type) { + case socks_req_addr_ipv4: { + ATTACHMENT(key)->origin_domain = AF_INET; + d->request.dest_addr.ipv4.sin_port = d->request.dest_port; + ATTACHMENT(key)->origin_addr_len = sizeof(d->request.dest_addr.ipv4); + // TODO: CAMBIAR, DA SUBDESBORDAMIENTO DE BÚFER :] + memcpy(&ATTACHMENT(key)->origin_addr, &d->request.dest_addr, sizeof(d->request.dest_addr.ipv4)); + ret = request_connect(key, d); + break; + } case socks_req_addr_ipv6: { + ATTACHMENT(key)->origin_domain = AF_INET6; + d->request.dest_addr.ipv6.sin6_port = d->request.dest_port; + ATTACHMENT(key)->origin_addr_len = sizeof(d->request.dest_addr.ipv6); + // TODO: CAMBIAR, DA SUBDESBORDAMIENTO DE BÚFER :] + memcpy(&ATTACHMENT(key)->origin_addr, &d->request.dest_addr, sizeof(d->request.dest_addr.ipv6)); + ret = request_connect(key, d); + break; + } case socks_req_addr_domain: { + struct selector_key * k = malloc(sizeof(*key)); + if (k == NULL) { + ret = REQUEST_WRITE; + d->status = status_general_SOCKS_server_failure; + selector_set_interest_key(key, OP_WRITE); + } else { + memcpy(k, key, sizeof(*k)); + if (-1 == pthread_create(&tid, 0, request_resolv_blocking, k)) { + ret = REQUEST_WRITE; + d->status = status_general_SOCKS_server_failure; + selector_set_interest_key(key, OP_WRITE); + } else { + ret = REQUEST_RESOLV; + selector_set_interest_key(key, OP_NOOP); + } + } + break; + } default: { + ret = REQUEST_WRITE; + d->status = status_address_type_not_supported; + selector_set_interest_key(key, OP_WRITE); + } + } + break; + case socks_req_cmd_bind: + case socks_req_cmd_associate: + default: + d->status = status_command_not_supported; + ret = REQUEST_WRITE; + break; + } + + return ret; +} + +static void * request_resolv_blocking(void * data) { + struct selector_key * key = (struct selector_key *) data; + struct socks5 * s = ATTACHMENT(key); + + pthread_detach(pthread_self()); + s->origin_resolution = 0; + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_STREAM, + .ai_flags = AI_PASSIVE, + .ai_protocol = 0, + .ai_canonname = NULL, + .ai_addr = NULL, + .ai_next = NULL, + }; + + char buff[7]; + snprintf(buff, sizeof(buff), "%d", ntohs(s->client.request.request.dest_port)); + + getaddrinfo(s->client.request.request.dest_addr.fqdn, buff, &hints, &s->origin_resolution); + +// TODO:manejar el error de getaddrinfo + + selector_notify_block(key->s, key->fd); + free(data); + return 0; +} + +static unsigned request_resolv_done(struct selector_key * key) { + struct request_st * d = &ATTACHMENT(key)->client.request; + struct socks5 * s = ATTACHMENT(key); + + if (s->origin_resolution == 0) { + d->status = status_general_SOCKS_server_failure; + } else { + s->origin_domain = s->origin_resolution->ai_family; + s->origin_addr_len = s->origin_resolution->ai_addrlen; + memcpy(&s->origin_addr, s->origin_resolution->ai_addr, s->origin_resolution->ai_addrlen); + freeaddrinfo(s->origin_resolution); + s->origin_resolution = 0; + } + + return request_connect(key, d); +} + +static unsigned request_connect(struct selector_key * key, struct request_st * d) { + bool error = false; + enum socks_response_status status = d->status; + int *fd = d->origin_fd; + + *fd = socket(ATTACHMENT(key)->origin_domain, SOCK_STREAM, 0); + if (*fd == -1) { + error = true; + goto finally; + } + if (selector_fd_set_nio(*fd) == -1) { + goto finally; + } + + if (-1 == connect(*fd, (const struct sockaddr *) &ATTACHMENT(key)->origin_addr, ATTACHMENT(key)->origin_addr_len)) { + if (errno == EINPROGRESS) { + selector_status st = selector_set_interest_key(key, OP_NOOP); + if (SELECTOR_SUCCESS != st) { + error = true; + goto finally; + } + + st = selector_register(key->s, *fd, &socks5_handler, OP_WRITE, key->data); + if (SELECTOR_SUCCESS != st) { + error = true; + goto finally; + } + ATTACHMENT(key)->references += 1; + } else { + status = errno_to_socks(errno); + error = true; + goto finally; + } + } else { + abort(); + } + +finally: + if (error) { + if (*fd != -1) { + close(*fd); + *fd = -1; + } + } + d->status = status; + + return REQUEST_CONNECTING; +} + +static void request_read_close(const unsigned state, struct selector_key * key) { + struct request_st * d = &ATTACHMENT(key)->client.request; + + request_close(&d->parser); +} + +static void request_connecting_init(const unsigned state, struct selector_key *key){ + struct connecting * d = &ATTACHMENT(key)->orig.conn; + + d->client_fd = &ATTACHMENT(key)->client_fd; + d->origin_fd = &ATTACHMENT(key)->origin_fd; + d->status = &ATTACHMENT(key)->client.request.status; + d->wb = &ATTACHMENT(key)->write_buffer; +} + +static unsigned request_connecting(struct selector_key * key) { + int error; + socklen_t len = sizeof(error); + struct connecting * d = &ATTACHMENT(key)->orig.conn; + + if (getsockopt(key->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + *d->status = status_general_SOCKS_server_failure; + } else { + if (error == 0) { + *d->status = status_succeeded; + *d->origin_fd = key->fd; + } else { + *d->status = errno_to_socks(error); + } + } + + if (-1 == request_marshall(d->wb, *d->status)) { + *d->status = status_general_SOCKS_server_failure; + abort(); + } + selector_status s = 0; + s |= selector_set_interest(key->s, *d->client_fd, OP_WRITE); + s |= selector_set_interest_key(key, OP_NOOP); + return SELECTOR_SUCCESS == s ? REQUEST_WRITE : ERROR; +} + +// void log_request(enum socks_response_status status, const struct sockaddr * clientaddr, const struct sockaddr * originaddr); + +static unsigned request_write(struct selector_key * key) { + struct request_st * d = &ATTACHMENT(key)->client.request; + + unsigned ret = REQUEST_WRITE; + buffer * b = d->wb; + uint8_t * ptr; + size_t count; + ssize_t n; + + ptr = buffer_read_ptr(b, &count); + n = send(key->fd, ptr, count, MSG_NOSIGNAL); + if (-1 == n){ + ret = ERROR; + } else { + buffer_read_adv(b, n); + + if (!buffer_can_read(b)){ + if (d->status == status_succeeded) { + ret = COPY; + selector_set_interest(key->s, *d->client_fd, OP_READ); + selector_set_interest(key->s, *d->origin_fd, OP_READ); + } else { + ret = DONE; + selector_set_interest(key->s, *d->client_fd, OP_NOOP); + if (-1 == *d->origin_fd) { + selector_set_interest(key->s, *d->origin_fd, OP_NOOP); + } + } + } + } + + // log_request(d->status, (const struct sockaddr *) &ATTACHMENT(key)->client_addr, (const struct sockaddr *) &ATTACHMENT(key)->origin_addr); + return ret; +} + +static void copy_init(const unsigned state, struct selector_key * key) { + struct copy * d = &ATTACHMENT(key)->client.copy; + + d->fd = &ATTACHMENT(key)->client_fd; + d->rb = &ATTACHMENT(key)->read_buffer; + d->wb = &ATTACHMENT(key)->write_buffer; + d->duplex = OP_READ | OP_WRITE; + d->other = &ATTACHMENT(key)->orig.copy; + + d = &ATTACHMENT(key)->orig.copy; + d->fd = &ATTACHMENT(key)->origin_fd; + d->rb = &ATTACHMENT(key)->write_buffer; + d->wb = &ATTACHMENT(key)->read_buffer; + d->duplex = OP_READ | OP_WRITE; + d->other = &ATTACHMENT(key)->client.copy; +} + +static unsigned copy_compute_interests(fd_selector s, struct copy * d) { + // ... TODO ... + + fd_interest ret = OP_NOOP; + if ((d->duplex & OP_READ) && buffer_can_write(d->rb)) { + ret |= OP_READ; + } + if ((d->duplex & OP_WRITE) && buffer_can_read(d->wb)) { + ret |= OP_WRITE; + } + + if (SELECTOR_SUCCESS != selector_set_interest(s, *d->fd, ret)) { + abort(); + } + + return ret; +} + +static struct copy * copy_ptr(struct selector_key * key) { + struct copy * d = &ATTACHMENT(key)->client.copy; + if (*d->fd != key->fd) { + d = d->other; + } + return d; +} + +static unsigned copy_r(struct selector_key * key) { + struct copy *d = copy_ptr(key); + + assert(*d->fd == key->fd); + + size_t size; + ssize_t n; + buffer * b = d->rb; + unsigned ret = COPY; + + uint8_t * ptr = buffer_write_ptr(b, &size); + n = recv(key->fd, ptr, size, 0); + if (n <= 0) { + shutdown(*d->fd, SHUT_RD); + d->duplex &= ~OP_READ; + if (*d->other->fd != -1) { + shutdown(*d->other->fd, SHUT_WR); + d->other->duplex &= ~OP_WRITE; + } + } else { + buffer_write_adv(b, n); + } + copy_compute_interests(key->s, d); + copy_compute_interests(key->s, d->other); + if (d->duplex == OP_NOOP) { + ret = DONE; + } + return ret; +} + +static unsigned copy_w(struct selector_key * key) { + struct copy * d = copy_ptr(key); + + assert(*d->fd == key->fd); + size_t size; + ssize_t n; + buffer * b = d->wb; + unsigned ret = COPY; + + uint8_t * ptr = buffer_read_ptr(b, &size); + n = send(key->fd, ptr, size, MSG_NOSIGNAL); + if (n == -1) { + shutdown(*d->fd, SHUT_WR); + d->duplex &= ~OP_WRITE; + if (*d->other->fd != -1) { + shutdown(*d->other->fd, SHUT_RD); + d->other->duplex &= ~OP_READ; + } + } else { + buffer_read_adv(b, n); + } + copy_compute_interests(key->s, d); + copy_compute_interests(key->s, d->other); + if (d->duplex == OP_NOOP) { + ret = DONE; + } + return ret; +} + +/** definición de handlers para cada estado */ +static const struct state_definition client_statbl[] = { + { + .state = HELLO_READ, + .on_arrival = hello_read_init, + .on_departure = hello_read_close, + .on_read_ready = hello_read, + }, { + .state = HELLO_WRITE, + .on_write_ready = hello_write, + }, { + .state = REQUEST_READ, + .on_arrival = request_init, + .on_departure = request_read_close, + .on_read_ready = request_read, + }, { + .state = REQUEST_RESOLV, + .on_block_ready = request_resolv_done, + }, { + .state = REQUEST_CONNECTING, + .on_arrival = request_connecting_init, + .on_write_ready = request_connecting, + }, { + .state = REQUEST_WRITE, + .on_write_ready = request_write, + }, { + .state = COPY, + .on_arrival = copy_init, + .on_read_ready = copy_r, + .on_write_ready = copy_w, + }, { + .state = DONE, + }, { + .state = ERROR, + } +}; + +static const struct state_definition * socks5_describe_states(void) { + return client_statbl; +} + +/////////////////////////////////////////////////////////////////////////////// +// Handlers top level de la conexión pasiva. +// son los que emiten los eventos a la maquina de estados. +static void +socksv5_done(struct selector_key* key); + +static void +socksv5_read(struct selector_key *key) { + struct state_machine *stm = &ATTACHMENT(key)->stm; + const enum socks_v5state st = stm_handler_read(stm, key); + + if(ERROR == st || DONE == st) { + socksv5_done(key); + } +} + +static void +socksv5_write(struct selector_key *key) { + struct state_machine *stm = &ATTACHMENT(key)->stm; + const enum socks_v5state st = stm_handler_write(stm, key); + + if(ERROR == st || DONE == st) { + socksv5_done(key); + } +} + +static void +socksv5_block(struct selector_key *key) { + struct state_machine *stm = &ATTACHMENT(key)->stm; + const enum socks_v5state st = stm_handler_block(stm, key); + + if(ERROR == st || DONE == st) { + socksv5_done(key); + } +} + +static void +socksv5_close(struct selector_key *key) { + socks5_destroy(ATTACHMENT(key)); +} + +static void +socksv5_done(struct selector_key* key) { + const int fds[] = { + ATTACHMENT(key)->client_fd, + ATTACHMENT(key)->origin_fd, + }; + for(unsigned i = 0; i < N(fds); i++) { + if(fds[i] != -1) { + if(SELECTOR_SUCCESS != selector_unregister_fd(key->s, fds[i])) { + abort(); + } + close(fds[i]); + } + } +} diff --git a/src/stm.c b/src/stm.c index 081f55a..58a2b09 100644 --- a/src/stm.c +++ b/src/stm.c @@ -1,7 +1,5 @@ -/** - * stm.c - pequeño motor de maquina de estados donde los eventos son los - * del selector.c - */ +// This is a personal academic project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com #include #include "stm.h" @@ -11,12 +9,12 @@ void stm_init(struct state_machine *stm) { // verificamos que los estados son correlativos, y que están bien asignados. for(unsigned i = 0 ; i <= stm->max_state; i++) { - if(i != stm->states[i].state) { + if (i != stm->states[i].state) { abort(); } } - if(stm->initial < stm->max_state) { + if (stm->initial < stm->max_state) { stm->current = NULL; } else { abort(); @@ -25,9 +23,9 @@ stm_init(struct state_machine *stm) { inline static void handle_first(struct state_machine *stm, struct selector_key *key) { - if(stm->current == NULL) { + if (stm->current == NULL) { stm->current = stm->states + stm->initial; - if(NULL != stm->current->on_arrival) { + if (NULL != stm->current->on_arrival) { stm->current->on_arrival(stm->current->state, key); } } @@ -35,16 +33,16 @@ handle_first(struct state_machine *stm, struct selector_key *key) { inline static void jump(struct state_machine *stm, unsigned next, struct selector_key *key) { - if(next > stm->max_state) { + if (next > stm->max_state) { abort(); } - if(stm->current != stm->states + next) { - if(stm->current != NULL && stm->current->on_departure != NULL) { + if (stm->current != stm->states + next) { + if (stm->current != NULL && stm->current->on_departure != NULL) { stm->current->on_departure(stm->current->state, key); } stm->current = stm->states + next; - if(NULL != stm->current->on_arrival) { + if (NULL != stm->current->on_arrival) { stm->current->on_arrival(stm->current->state, key); } } @@ -53,7 +51,7 @@ void jump(struct state_machine *stm, unsigned next, struct selector_key *key) { unsigned stm_handler_read(struct state_machine *stm, struct selector_key *key) { handle_first(stm, key); - if(stm->current->on_read_ready == 0) { + if (stm->current->on_read_ready == 0) { abort(); } const unsigned int ret = stm->current->on_read_ready(key); @@ -65,7 +63,7 @@ stm_handler_read(struct state_machine *stm, struct selector_key *key) { unsigned stm_handler_write(struct state_machine *stm, struct selector_key *key) { handle_first(stm, key); - if(stm->current->on_write_ready == 0) { + if (stm->current->on_write_ready == 0) { abort(); } const unsigned int ret = stm->current->on_write_ready(key); @@ -77,7 +75,7 @@ stm_handler_write(struct state_machine *stm, struct selector_key *key) { unsigned stm_handler_block(struct state_machine *stm, struct selector_key *key) { handle_first(stm, key); - if(stm->current->on_block_ready == 0) { + if (stm->current->on_block_ready == 0) { abort(); } const unsigned int ret = stm->current->on_block_ready(key); @@ -88,7 +86,7 @@ stm_handler_block(struct state_machine *stm, struct selector_key *key) { void stm_handler_close(struct state_machine *stm, struct selector_key *key) { - if(stm->current != NULL && stm->current->on_departure != NULL) { + if (stm->current != NULL && stm->current->on_departure != NULL) { stm->current->on_departure(stm->current->state, key); } } @@ -96,7 +94,7 @@ stm_handler_close(struct state_machine *stm, struct selector_key *key) { unsigned stm_state(struct state_machine *stm) { unsigned ret = stm->initial; - if(stm->current != NULL) { + if (stm->current != NULL) { ret= stm->current->state; } return ret; diff --git a/test/buffer_test.c b/test/buffer_test.c deleted file mode 100644 index a86ad83..0000000 --- a/test/buffer_test.c +++ /dev/null @@ -1,116 +0,0 @@ -#include -#include - -// asi se puede probar las funciones internas -#include "buffer.c" - -#define N(x) (sizeof(x)/sizeof((x)[0])) - - -START_TEST (test_buffer_misc) { - struct buffer buf; - buffer *b = &buf; - uint8_t direct_buff[6]; - buffer_init(&buf, N(direct_buff), direct_buff); - ck_assert_ptr_eq(&buf, b); - - ck_assert_int_eq(true, buffer_can_write(b)); - ck_assert_int_eq(false, buffer_can_read(b)); - - size_t wbytes = 0, rbytes = 0; - uint8_t *ptr = buffer_write_ptr(b, &wbytes); - ck_assert_uint_eq(6, wbytes); - // escribo 4 bytes - uint8_t first_write [] = { - 'H', 'O', 'L', 'A', - }; - memcpy(ptr, first_write, sizeof(first_write)); - buffer_write_adv(b, sizeof(first_write)); - - // quedan 2 libres para escribir - buffer_write_ptr(b, &wbytes); - ck_assert_uint_eq(2, wbytes); - - // tengo por leer - buffer_read_ptr(b, &rbytes); - ck_assert_uint_eq(4, rbytes); - - // leo 3 del buffer - ck_assert_uint_eq('H', buffer_read(b)); - ck_assert_uint_eq('O', buffer_read(b)); - ck_assert_uint_eq('L', buffer_read(b)); - - // queda 1 por leer - buffer_read_ptr(b, &rbytes); - ck_assert_uint_eq(1, rbytes); - - // quiero escribir..tendria que seguir habiendo 2 libres - ptr = buffer_write_ptr(b, &wbytes); - ck_assert_uint_eq(2, wbytes); - - uint8_t second_write [] = { - ' ', 'M', - }; - memcpy(ptr, second_write, sizeof(second_write)); - buffer_write_adv(b, sizeof(second_write)); - - ck_assert_int_eq(false, buffer_can_write(b)); - buffer_write_ptr(b, &wbytes); - ck_assert_uint_eq(0, wbytes); - - // tiene que haber 2 + 1 para leer - ptr = buffer_read_ptr(b, &rbytes); - ck_assert_uint_eq(3, rbytes); - ck_assert_ptr_ne(ptr, b->data); - - buffer_compact(b); - ck_assert_ptr_eq(b->data, buffer_read_ptr(b, &rbytes)); - ck_assert_uint_eq(3, rbytes); - ck_assert_ptr_eq(b->data + 3, buffer_write_ptr(b, &wbytes)); - ck_assert_uint_eq(3, wbytes); - - uint8_t third_write [] = { - 'U', 'N', 'D', - }; - memcpy(ptr, third_write, sizeof(third_write)); - buffer_write_adv(b, sizeof(third_write)); - - buffer_write_ptr(b, &wbytes); - ck_assert_uint_eq(0, wbytes); - ck_assert_ptr_eq(b->data, buffer_read_ptr(b, &rbytes)); - buffer_read_adv(b, rbytes); - buffer_read_ptr(b, &rbytes); - ck_assert_uint_eq(0, rbytes); - ck_assert_ptr_eq(b->data, buffer_write_ptr(b, &wbytes)); - ck_assert_uint_eq(6, wbytes); - - buffer_compact(b); - buffer_read_ptr(b, &rbytes); - ck_assert_uint_eq(0, rbytes); - buffer_write_ptr(b, &wbytes); - ck_assert_uint_eq(N(direct_buff), wbytes); - -} -END_TEST - -Suite * -suite(void) { - Suite *s = suite_create("buffer"); - TCase *tc = tcase_create("buffer"); - - tcase_add_test(tc, test_buffer_misc); - suite_add_tcase(s, tc); - - return s; -} - -int -main(void) { - SRunner *sr = srunner_create(suite()); - int number_failed; - - srunner_run_all(sr, CK_NORMAL); - number_failed = srunner_ntests_failed(sr); - srunner_free(sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} diff --git a/test/netutils_test.c b/test/netutils_test.c deleted file mode 100644 index c21e66a..0000000 --- a/test/netutils_test.c +++ /dev/null @@ -1,91 +0,0 @@ -#include -#include -#include - -#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; -} - diff --git a/test/parser_test.c b/test/parser_test.c deleted file mode 100644 index c662c96..0000000 --- a/test/parser_test.c +++ /dev/null @@ -1,115 +0,0 @@ -#include -#include -#include - -#include "parser.h" - -// definición de maquina - -enum states { - S0, - S1 -}; - -enum event_type { - FOO, - BAR, -}; - -static void -foo(struct parser_event *ret, const uint8_t c) { - ret->type = FOO; - ret->n = 1; - ret->data[0] = c; -} - -static void -bar(struct parser_event *ret, const uint8_t c) { - ret->type = BAR; - ret->n = 1; - ret->data[0] = c; -} - -static const struct parser_state_transition ST_S0 [] = { - {.when = 'F', .dest = S0, .act1 = foo,}, - {.when = 'f', .dest = S0, .act1 = foo,}, - {.when = ANY, .dest = S1, .act1 = bar,}, -}; -static const struct parser_state_transition ST_S1 [] = { - {.when = 'F', .dest = S0, .act1 = foo,}, - {.when = 'f', .dest = S0, .act1 = foo,}, - {.when = ANY, .dest = S1, .act1 = bar,}, -}; - -static const struct parser_state_transition *states [] = { - ST_S0, - ST_S1, -}; - -#define N(x) (sizeof(x)/sizeof((x)[0])) - -static const size_t states_n [] = { - N(ST_S0), - N(ST_S1), -}; - -static struct parser_definition definition = { - .states_count = N(states), - .states = states, - .states_n = states_n, - .start_state = S0, -}; - -//// TEST - -static void -assert_eq(const unsigned type, const int c, const struct parser_event *e) { - ck_assert_ptr_eq (0, e->next); - ck_assert_uint_eq(1, e->n); - ck_assert_uint_eq(type, e->type); - ck_assert_uint_eq(c, e->data[0]); - -} - -START_TEST (test_basic) { - struct parser *parser = parser_init(parser_no_classes(), &definition); - assert_eq(FOO, 'f', parser_feed(parser, 'f')); - assert_eq(FOO, 'F', parser_feed(parser, 'F')); - assert_eq(BAR, 'B', parser_feed(parser, 'B')); - assert_eq(BAR, 'b', parser_feed(parser, 'b')); - - parser_destroy(parser); -} -END_TEST - -Suite * -suite(void) { - Suite *s; - TCase *tc; - - s = suite_create("parser_utils"); - - /* Core test case */ - tc = tcase_create("parser_utils"); - - tcase_add_test(tc, test_basic); - suite_add_tcase(s, tc); - - return s; -} - -int -main(void) { - int number_failed; - Suite *s; - SRunner *sr; - - s = 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; -} - diff --git a/test/parser_utils_test.c b/test/parser_utils_test.c deleted file mode 100644 index 86ecab9..0000000 --- a/test/parser_utils_test.c +++ /dev/null @@ -1,63 +0,0 @@ -#include -#include -#include - -#include "parser_utils.h" - -static void -assert_eq(const unsigned type, const int c, const struct parser_event *e) { - ck_assert_ptr_eq (0, e->next); - ck_assert_uint_eq(1, e->n); - ck_assert_uint_eq(type, e->type); - ck_assert_uint_eq(c, e->data[0]); - -} - -START_TEST (test_eq) { - const struct parser_definition d = parser_utils_strcmpi("foo"); - - struct parser *parser = parser_init(parser_no_classes(), &d); - assert_eq(STRING_CMP_MAYEQ, 'f', parser_feed(parser, 'f')); - assert_eq(STRING_CMP_MAYEQ, 'O', parser_feed(parser, 'O')); - assert_eq(STRING_CMP_EQ, 'o', parser_feed(parser, 'o')); - assert_eq(STRING_CMP_NEQ, 'X', parser_feed(parser, 'X')); - assert_eq(STRING_CMP_NEQ, 'y', parser_feed(parser, 'y')); - - parser_destroy(parser); - parser_utils_strcmpi_destroy(&d); -} -END_TEST - -Suite * -suite(void) { - Suite *s; - TCase *tc; - - s = suite_create("parser_utils"); - - /* Core test case */ - tc = tcase_create("parser_utils"); - - tcase_add_test(tc, test_eq); - suite_add_tcase(s, tc); - - return s; -} - - - -int -main(void) { - int number_failed; - Suite *s; - SRunner *sr; - - s = 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; -} - diff --git a/test/selector_test.c b/test/selector_test.c deleted file mode 100644 index 6ae7745..0000000 --- a/test/selector_test.c +++ /dev/null @@ -1,179 +0,0 @@ -#include -#include - -#define INITIAL_SIZE ((size_t) 1024) - -// para poder testear las funciones estaticas -#include "selector.c" - -START_TEST (test_selector_error) { - const selector_status data[] = { - SELECTOR_SUCCESS, - SELECTOR_ENOMEM, - SELECTOR_MAXFD, - SELECTOR_IARGS, - SELECTOR_IO, - }; - // verifica que `selector_error' tiene mensajes especificos - for(unsigned i = 0 ; i < N(data); i++) { - ck_assert_str_ne(ERROR_DEFAULT_MSG, selector_error(data[i])); - } -} -END_TEST - -START_TEST (test_next_capacity) { - const size_t data[] = { - 0, 1, - 1, 2, - 2, 4, - 3, 4, - 4, 8, - 7, 8, - 8, 16, - 15, 16, - 31, 32, - 16, 32, - ITEMS_MAX_SIZE, ITEMS_MAX_SIZE, - ITEMS_MAX_SIZE + 1, ITEMS_MAX_SIZE, - }; - for(unsigned i = 0; i < N(data) / 2; i++ ) { - ck_assert_uint_eq(data[i * 2 + 1] + 1, next_capacity(data[i*2])); - } -} -END_TEST - -START_TEST (test_ensure_capacity) { - fd_selector s = selector_new(0); - for(size_t i = 0; i < s->fd_size; i++) { - ck_assert_int_eq(FD_UNUSED, s->fds[i].fd); - } - - size_t n = 1; - ck_assert_int_eq(SELECTOR_SUCCESS, ensure_capacity(s, n)); - ck_assert_uint_ge(s->fd_size, n); - - n = 10; - ck_assert_int_eq(SELECTOR_SUCCESS, ensure_capacity(s, n)); - ck_assert_uint_ge(s->fd_size, n); - - const size_t last_size = s->fd_size; - n = ITEMS_MAX_SIZE + 1; - ck_assert_int_eq(SELECTOR_MAXFD, ensure_capacity(s, n)); - ck_assert_uint_eq(last_size, s->fd_size); - - for(size_t i = 0; i < s->fd_size; i++) { - ck_assert_int_eq(FD_UNUSED, s->fds[i].fd); - } - - selector_destroy(s); - - ck_assert_ptr_null(selector_new(ITEMS_MAX_SIZE + 1)); -} -END_TEST - -// callbacks de prueba -static void *data_mark = (void *)0x0FF1CE; -static unsigned destroy_count = 0; -static void -destroy_callback(struct selector_key *key) { - ck_assert_ptr_nonnull(key->s); - ck_assert_int_ge(key->fd, 0); - ck_assert_int_lt(key->fd, ITEMS_MAX_SIZE); - - ck_assert_ptr_eq(data_mark, key->data); - destroy_count++; -} - -START_TEST (test_selector_register_fd) { - destroy_count = 0; - fd_selector s = selector_new(INITIAL_SIZE); - ck_assert_ptr_nonnull(s); - - ck_assert_uint_eq(SELECTOR_IARGS, selector_register(0, -1, 0, 0, data_mark)); - - const struct fd_handler h = { - .handle_read = NULL, - .handle_write = NULL, - .handle_close = destroy_callback, - }; - int fd = ITEMS_MAX_SIZE - 1; - ck_assert_uint_eq(SELECTOR_SUCCESS, - selector_register(s, fd, &h, 0, data_mark)); - const struct item *item = s->fds + fd; - ck_assert_int_eq (fd, s->max_fd); - ck_assert_int_eq (fd, item->fd); - ck_assert_ptr_eq (&h, item->handler); - ck_assert_uint_eq(0, item->interest); - ck_assert_ptr_eq (data_mark, item->data); - - selector_destroy(s); - // destroy desregistró? - ck_assert_uint_eq(1, destroy_count); - -} -END_TEST - -START_TEST (test_selector_register_unregister_register) { - destroy_count = 0; - fd_selector s = selector_new(INITIAL_SIZE); - ck_assert_ptr_nonnull(s); - - const struct fd_handler h = { - .handle_read = NULL, - .handle_write = NULL, - .handle_close = destroy_callback, - }; - int fd = ITEMS_MAX_SIZE - 1; - ck_assert_uint_eq(SELECTOR_SUCCESS, - selector_register(s, fd, &h, 0, data_mark)); - ck_assert_uint_eq(SELECTOR_SUCCESS, - selector_unregister_fd(s, fd)); - - const struct item *item = s->fds + fd; - ck_assert_int_eq (0, s->max_fd); - ck_assert_int_eq (FD_UNUSED, item->fd); - ck_assert_ptr_eq (0x00, item->handler); - ck_assert_uint_eq(0, item->interest); - ck_assert_ptr_eq (0x00, item->data); - - ck_assert_uint_eq(SELECTOR_SUCCESS, - selector_register(s, fd, &h, 0, data_mark)); - item = s->fds + fd; - ck_assert_int_eq (fd, s->max_fd); - ck_assert_int_eq (fd, item->fd); - ck_assert_ptr_eq (&h, item->handler); - ck_assert_uint_eq(0, item->interest); - ck_assert_ptr_eq (data_mark, item->data); - - selector_destroy(s); - ck_assert_uint_eq(2, destroy_count); - -} -END_TEST - -Suite * -suite(void) { - Suite *s = suite_create("nio"); - TCase *tc = tcase_create("nio"); - - tcase_add_test(tc, test_next_capacity); - tcase_add_test(tc, test_selector_error); - tcase_add_test(tc, test_ensure_capacity); - tcase_add_test(tc, test_selector_register_fd); - tcase_add_test(tc, test_selector_register_unregister_register); - suite_add_tcase(s, tc); - - return s; -} - -int -main(void) { - int number_failed; - SRunner *sr = srunner_create(suite()); - - srunner_run_all(sr, CK_NORMAL); - number_failed = srunner_ntests_failed(sr); - srunner_free(sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} - diff --git a/test/stm_test.c b/test/stm_test.c deleted file mode 100644 index 6b937d9..0000000 --- a/test/stm_test.c +++ /dev/null @@ -1,143 +0,0 @@ -#include -#include -#include -#include "selector.h" -#include "stm.h" - -enum test_states { - A, - B, - C, -}; - -struct data { - bool arrived [3]; - bool departed[3]; - unsigned i; -}; - -static void -on_arrival(const unsigned state, struct selector_key *key) { - struct data *d = (struct data *)key->data; - d->arrived[state] = true; -} - -static void -on_departure(const unsigned state,struct selector_key *key) { - struct data *d = (struct data *)key->data; - d->departed[state] = true; -} - -static unsigned -on_read_ready(struct selector_key *key) { - struct data *d = (struct data *)key->data; - unsigned ret; - - if(d->i < C) { - ret = ++d->i; - } else { - ret = C; - } - return ret; -} - -static unsigned -on_write_ready(struct selector_key *key) { - return on_read_ready(key); -} - -static const struct state_definition statbl[] = { - { - .state = A, - .on_arrival = on_arrival, - .on_departure = on_departure, - .on_read_ready = on_read_ready, - .on_write_ready = on_write_ready, - },{ - .state = B, - .on_arrival = on_arrival, - .on_departure = on_departure, - .on_read_ready = on_read_ready, - .on_write_ready = on_write_ready, - },{ - .state = C, - .on_arrival = on_arrival, - .on_departure = on_departure, - .on_read_ready = on_read_ready, - .on_write_ready = on_write_ready, - } -}; - -//static bool init = false; - -START_TEST (test_buffer_misc) { - struct state_machine stm = { - .initial = A, - .max_state = C, - .states = statbl, - }; - struct data data = { - .i = 0, - }; - struct selector_key key = { - .data = &data, - }; - stm_init(&stm); - ck_assert_uint_eq(A, stm_state(&stm)); - ck_assert_uint_eq(false, data.arrived[A]); - ck_assert_uint_eq(false, data.arrived[B]); - ck_assert_uint_eq(false, data.arrived[C]); - ck_assert_ptr_null(stm.current); - - stm_handler_read(&stm, &key); - ck_assert_uint_eq(B, stm_state(&stm)); - ck_assert_uint_eq(true, data.arrived[A]); - ck_assert_uint_eq(true, data.arrived[B]); - ck_assert_uint_eq(false, data.arrived[C]); - ck_assert_uint_eq(true, data.departed[A]); - ck_assert_uint_eq(false, data.departed[B]); - ck_assert_uint_eq(false, data.departed[C]); - - stm_handler_write(&stm, &key); - ck_assert_uint_eq(C, stm_state(&stm)); - ck_assert_uint_eq(true, data.arrived[A]); - ck_assert_uint_eq(true, data.arrived[B]); - ck_assert_uint_eq(true, data.arrived[C]); - ck_assert_uint_eq(true, data.departed[A]); - ck_assert_uint_eq(true, data.departed[B]); - ck_assert_uint_eq(false, data.departed[C]); - - stm_handler_read(&stm, &key); - ck_assert_uint_eq(C, stm_state(&stm)); - ck_assert_uint_eq(true, data.arrived[A]); - ck_assert_uint_eq(true, data.arrived[B]); - ck_assert_uint_eq(true, data.arrived[C]); - ck_assert_uint_eq(true, data.departed[A]); - ck_assert_uint_eq(true, data.departed[B]); - ck_assert_uint_eq(false, data.departed[C]); - - stm_handler_close(&stm, &key); -} -END_TEST - -Suite * -suite(void) { - Suite *s = suite_create("nio_stm"); - TCase *tc = tcase_create("nio_stm"); - - tcase_add_test(tc, test_buffer_misc); - suite_add_tcase(s, tc); - - return s; -} - -int -main(void) { - SRunner *sr = srunner_create(suite()); - int number_failed; - - srunner_run_all(sr, CK_NORMAL); - number_failed = srunner_ntests_failed(sr); - srunner_free(sr); - return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -}