diff --git a/src/donar_client.h b/src/donar_client.h index 2a60bc1..48f4823 100644 --- a/src/donar_client.h +++ b/src/donar_client.h @@ -1,3 +1,4 @@ +#pragma once #include #include diff --git a/src/donar_proxy.c b/src/donar_proxy.c index e18f237..d5495dc 100644 --- a/src/donar_proxy.c +++ b/src/donar_proxy.c @@ -29,8 +29,12 @@ int main(int argc, char** argv) { exit(EXIT_FAILURE); } - if (is_server) donar_server(); - else if (is_client) donar_client(); + if (is_server) { + struct donar_server_ctx ctx; + donar_server(&ctx); + } else if (is_client) { + donar_client(); + } return 0; /* diff --git a/src/donar_server.c b/src/donar_server.c index 911d6b2..31880db 100644 --- a/src/donar_server.c +++ b/src/donar_server.c @@ -1,6 +1,6 @@ #include "donar_server.h" -void create_onion_services(struct tor_os_str* tos, struct tor_ctl* tctl, int* ports, int ports_count) { +void create_onion_services(struct tor_os_str* tos, struct tor_ctl* tctl, uint16_t* ports, int ports_count) { tor_os_create (tos, "onion_services.pub", "onion_services.txt", ports_count); tor_os_read (tos); @@ -22,12 +22,136 @@ void destroy_resources(struct tor_os_str* tos, struct tor_ctl* tctl) { tor_os_free (tos); } -void donar_server() { - struct tor_os_str tos; - struct tor_ctl tctl = {}; - int ports[10] = { 7500, 7501, 7502, 7503, 7504, 7505, 7506, 7507, 7508, 7509}; - create_onion_services (&tos, &tctl, ports, 10); - printf("Onion services created\n"); +void init_tcp_servers(struct donar_server_ctx* ctx) { + char buffer[6]; + int err = 0; + for (int i = 0; i < ctx->connection_count; i++) { + sprintf (buffer, "%d", ctx->ports[i]); + ctx->tcp_socks[i] = create_tcp_server (buffer); + if (ctx->tcp_socks[i] < 0) goto socket_create_err; + // Might be needed only with EPOLLET - might not be needed at all + //err = make_socket_non_blocking (ctx->tcp_socks[i]); + //if (err != 0) goto socket_create_err; + err = listen(ctx->tcp_socks[i], SOMAXCONN); + if (err != 0) goto socket_create_err; + } + return; - destroy_resources (&tos, &tctl); +socket_create_err: + fprintf(stderr, "Unable to create a TCP socket\n"); + exit(EXIT_FAILURE); +} + +int is_listening_socket(struct donar_server_ctx* ctx, int sock) { + int i; + for (i = 0; i < ctx->connection_count && ctx->is_server; i++) { + if (sock == ctx->tcp_socks[i]) return 1; + } + return 0; +} + +void handle_new_tcp_client_connection(struct donar_server_ctx* ctx, struct epoll_event* evt) { + int conn_sock, err; + struct sockaddr in_addr; + socklen_t in_len; + struct epoll_event current_event; + + printf("Accepting new TCP client\n"); + in_len = sizeof(in_addr); + conn_sock = accept(evt->data.fd, &in_addr, &in_len); + if (conn_sock == -1) goto co_error; + printf("accept %d\n", conn_sock); + err = make_socket_non_blocking (conn_sock); + if (err == -1) goto co_error; + printf("make socket non blocking\n"); + + current_event.events = EPOLLIN | EPOLLET; + current_event.data.fd = conn_sock; + if (epoll_ctl(ctx->epollfd, EPOLL_CTL_ADD, conn_sock, ¤t_event) == -1) + goto co_error; + printf("add to epoll socket descriptor %d\n", conn_sock); + return; + +co_error: + perror("Failed to handle new connection"); + exit(EXIT_FAILURE); +} + +void handle_new_tcp_client_rw(struct donar_server_ctx* ctx, struct epoll_event* evt) { + char buffer[128]; + int nread, nwrite; + printf("Exchanging rw info with client for sock %d\n", evt->data.fd); + while(1) { + nread = read(evt->data.fd, buffer, sizeof(buffer)); + if (nread == 0) break; + if (nread == -1 && errno == EAGAIN) break; + if (nread == -1) goto co_error; + printf("successful read of %d", nread); + nwrite = write(evt->data.fd, buffer, nread); + if (nwrite == -1 && errno == EAGAIN) { + printf("Lost data EAGAIN\n"); + break; + } + if (nwrite == -1) goto co_error; + if (nwrite != nread) { + printf("Lost data not everything has been written\n"); + break; + } + } + return; + +co_error: + perror("Failed to handle read write"); + exit(EXIT_FAILURE); +} + +void donar_server(struct donar_server_ctx* ctx) { + ctx->is_server = 1; + ctx->connection_count = PORT_SIZE; + for (uint16_t i = 0; i < ctx->connection_count ; i++) { + ctx->ports[i] = 7500 + i; + } + create_onion_services (&(ctx->tos), &(ctx->tctl), ctx->ports, ctx->connection_count); + printf("--- Onion services created\n"); + init_tcp_servers(ctx); + + ctx->epollfd = epoll_create1(0); + if (ctx->epollfd == -1) { + perror("Failed to create epoll file descriptor epoll_create1"); + exit(EXIT_FAILURE); + } + + struct epoll_event current_event, events[MAX_EVENTS]; + + for (int i = 0; i < ctx->connection_count; i++) { + current_event.events = EPOLLIN; + current_event.data.fd = ctx->tcp_socks[i]; + if (epoll_ctl (ctx->epollfd, EPOLL_CTL_ADD, ctx->tcp_socks[i], ¤t_event) == -1) { + perror("Failed to add a file descriptor to epoll with epoll_ctl"); + exit(EXIT_FAILURE); + } + } + + printf("Start main loop\n"); + int num_fd, n = 0; + while(1) { + num_fd = epoll_wait(ctx->epollfd, events, MAX_EVENTS, -1); + if (num_fd == -1) { + perror("Failed to epoll_wait"); + exit(EXIT_FAILURE); + } + printf("Loop with %d elements\n", num_fd); + for (n = 0 ; n < num_fd; n++) { + if ((events[n].events & EPOLLERR) || (events[n].events & EPOLLHUP) || (!(events[n].events & EPOLLIN))) { + /* An error has occured on this fd, or the socket is not + ready for reading (why were we notified then?) */ + fprintf (stderr, "epoll error\n"); + close (events[n].data.fd); + continue; + } + else if (is_listening_socket (ctx, events[n].data.fd)) handle_new_tcp_client_connection (ctx, &(events[n])); + else handle_new_tcp_client_rw(ctx, &(events[n])); + } + } + destroy_resources (&(ctx->tos), &(ctx->tctl)); } diff --git a/src/donar_server.h b/src/donar_server.h index dd02172..169d7b0 100644 --- a/src/donar_server.h +++ b/src/donar_server.h @@ -1,7 +1,22 @@ +#pragma once #include #include +#include +#include #include "socks5.h" #include "tor_os.h" #include "tor_ctl.h" +#define PORT_SIZE 10 +#define MAX_EVENTS 10 -void donar_server(); +struct donar_server_ctx { + struct tor_os_str tos; + struct tor_ctl tctl; + int connection_count; + uint16_t ports[PORT_SIZE]; + int tcp_socks[PORT_SIZE]; + int epollfd; + int is_server; +}; + +void donar_server(struct donar_server_ctx* ctx); diff --git a/src/net_tools.c b/src/net_tools.c index 2c2fef1..a2991c9 100644 --- a/src/net_tools.c +++ b/src/net_tools.c @@ -1,5 +1,8 @@ #include "net_tools.h" +// some code is inspired by https://github.com/millken/c-example/blob/master/epoll-example.c +// (which seems to be copied from https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/epoll-example.c ) + int create_tcp_client(char* host, char* service) { int err, sock; struct addrinfo conf; @@ -34,6 +37,63 @@ int create_tcp_client(char* host, char* service) { return sock; } +int create_tcp_server(char* service) { + int err, sock; + struct addrinfo conf; + struct addrinfo *result, *cursor; + + memset(&conf, 0, sizeof(struct addrinfo)); + conf.ai_family = AF_UNSPEC; + conf.ai_socktype = SOCK_STREAM; + conf.ai_flags = 0; + conf.ai_protocol = 0; + + err = getaddrinfo(NULL, service, &conf, &result); + if (err != 0) { + fprintf(stderr, "Error with getaddrinfo()\n"); + exit(EXIT_FAILURE); + } + + for (cursor = result; cursor != NULL; cursor = cursor->ai_next) { + sock = socket(cursor->ai_family, cursor->ai_socktype, cursor->ai_protocol); + if (sock == -1) continue; + if (bind(sock, cursor->ai_addr, cursor->ai_addrlen) != -1) break; + close(sock); + } + + if (cursor == NULL) { + fprintf(stderr, "We failed to bind\n"); + exit(EXIT_FAILURE); + } + + freeaddrinfo (result); + + return sock; +} + +/* + * The idea is to get the file descriptor flags + * then we add to the bitmask O_NONBLOCK and set the + * new bitmask to the socket + */ +int make_socket_non_blocking(int fd) { + int err, flags; + flags = fcntl (fd, F_GETFL); + if (flags == -1) { + perror("Failed to get socket's flags via fcntl"); + return -1; + } + + flags |= O_NONBLOCK; + err = fcntl (fd, F_SETFL, flags); + if (err != 0) { + perror("Failed to set socket's flags via fcntl"); + return -1; + } + + return 0; +} + int read_entity(int fd, void* entity, int size) { int remaining = size; int total_read = 0; @@ -53,4 +113,3 @@ void fill_buffer(size_t* written, char* dest, void *src, size_t n) { memcpy(dest+*written, src, n); *written += n; } - diff --git a/src/net_tools.h b/src/net_tools.h index 1a6a2e7..63fa889 100644 --- a/src/net_tools.h +++ b/src/net_tools.h @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include #include @@ -7,5 +8,7 @@ #include int create_tcp_client(char* host, char* service); +int create_tcp_server(char* service); +int make_socket_non_blocking(int fd); int read_entity(int fd, void* entity, int size); void fill_buffer(size_t* written, char* dest, void *src, size_t n); diff --git a/src/tor_ctl.c b/src/tor_ctl.c index c056f9c..5abda2d 100644 --- a/src/tor_ctl.c +++ b/src/tor_ctl.c @@ -30,7 +30,7 @@ int tor_ctl_connect(struct tor_ctl* ctx, char* addr, char* service) { return 0; } -int tor_ctl_add_onion(struct tor_ctl* ctx, struct tor_os_str* tos, int* port) { +int tor_ctl_add_onion(struct tor_ctl* ctx, struct tor_os_str* tos, uint16_t* port) { int err = 0; char buffer1[1024] = {0}; char buffer2[1024] = {0}; diff --git a/src/tor_ctl.h b/src/tor_ctl.h index 6ab7109..720513b 100644 --- a/src/tor_ctl.h +++ b/src/tor_ctl.h @@ -15,5 +15,5 @@ struct tor_ctl { }; int tor_ctl_connect(struct tor_ctl* ctx, char* addr, char* service); -int tor_ctl_add_onion(struct tor_ctl* ctx, struct tor_os_str* tos, int* port); +int tor_ctl_add_onion(struct tor_ctl* ctx, struct tor_os_str* tos, uint16_t* port); void tor_ctl_close(struct tor_ctl* ctx);