diff --git a/src/algo_naive.c b/src/algo_naive.c index 2ae0af9..744aa35 100644 --- a/src/algo_naive.c +++ b/src/algo_naive.c @@ -19,6 +19,7 @@ int on_tcp_co(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { in_len = sizeof(addr); conn_sock1 = accept(fdinfo->fd, (struct sockaddr*)&addr, &in_len); + if (conn_sock1 == -1 && errno == EAGAIN) return 1; if (conn_sock1 == -1) goto co_error; conn_sock2 = dup(conn_sock1); if (conn_sock2 == -1) goto co_error; @@ -36,7 +37,7 @@ int on_tcp_co(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { sprintf(to_fdinfo.url, "tcp:write:127.0.0.1:%s", port); evt_core_add_fd (ctx, &to_fdinfo); - return 1; + return 0; co_error: perror("Failed to handle new connection"); diff --git a/src/algo_rr.c b/src/algo_rr.c index 65fc13f..3872012 100644 --- a/src/algo_rr.c +++ b/src/algo_rr.c @@ -47,6 +47,7 @@ int rr_on_tcp_co(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { in_len = sizeof(addr); conn_sock1 = accept(fdinfo->fd, (struct sockaddr*)&addr, &in_len); + if (conn_sock1 == -1 && errno == EAGAIN) return 1; if (conn_sock1 == -1) goto co_error; conn_sock2 = dup(conn_sock1); if (conn_sock2 == -1) goto co_error; @@ -64,7 +65,7 @@ int rr_on_tcp_co(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { sprintf(to_fdinfo.url, "tcp:write:127.0.0.1:%s", port); evt_core_add_fd (ctx, &to_fdinfo); - return 1; + return 0; co_error: perror("Failed to handle new connection"); diff --git a/src/donar_client.c b/src/donar_client.c index bc93276..3fd089c 100644 --- a/src/donar_client.c +++ b/src/donar_client.c @@ -5,121 +5,68 @@ void load_onion_services(struct donar_client_ctx* ctx, char* onion_file, int por tor_os_read (&(ctx->tos)); } -void init_tcp_client(struct donar_client_ctx* ctx, int i) { - struct evt_core_cat cat = {0}; - struct evt_core_fdinfo fdinfo = {0}; - char url_buf[255]; - int err; - - ctx->ports[i] = 7500 + i; - cat.name = "configure-socks5"; - fdinfo.cat = &cat; - fdinfo.url = url_buf; - - while (1) { - fdinfo.fd = create_tcp_client("127.0.0.1", "9050"); - if (fdinfo.fd < 0) goto failed_socks5; - ctx->client_sock[i].fd = fdinfo.fd; - ctx->client_sock[i].state = SOCKS5_STATE_NEW; - sprintf(url_buf, "socks5:dist:%d", i); - evt_core_add_fd (&(ctx->evts), &fdinfo); - //@FIXME: We suppose that we will be able to do the whole write at once which is wrong - err = socks5_handshake_syn(fdinfo.fd); - if (err) goto failed_socks5; - - break; -failed_socks5: - fprintf(stderr, "Failed connection for socket %d/%d. Sleeping 2 seconds...\n",i+1, CLIENT_PORT_SIZE); - close(fdinfo.fd); - sleep(2); - } -} - -int configure_tcp_clients(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { - int err, pos; - struct donar_client_ctx* app_ctx = (struct donar_client_ctx*) fdinfo->cat->app_ctx; - - pos = -1; - for (int i = 0; i < CLIENT_PORT_SIZE; i++) { - if (app_ctx->client_sock[i].fd == fdinfo->fd) { - pos = i; - } - } - - char target_host[255]; +void init_socks5_client(struct donar_client_ctx* app_ctx, int pos) { + char target_host[255]; if (strlen(app_ctx->tos.keys[pos].pub) > 254) { fprintf(stderr, "Domain name is too long\n"); exit(EXIT_FAILURE); } sprintf(target_host, "%s.onion", app_ctx->tos.keys[pos].pub); - switch (app_ctx->client_sock[pos].state) { - case SOCKS5_STATE_NEW: - //@FIXME: We suppose that we will be able to do the whole read at once which is wrong - err = socks5_handshake_ack (fdinfo->fd); - if (err < 0) goto on_socks5_err; - //@FIXME: We suppose that we will be able to do the whole write at once which is wrong too - err = socks5_connect_dns(fdinfo->fd, target_host, app_ctx->ports[pos]); - if (err < 0) goto on_socks5_err; - app_ctx->client_sock[pos].state = SOCKS5_STATE_ACK; - printf("Socket %d/%d %s:%d is connecting...\n", pos+1, CLIENT_PORT_SIZE, target_host, app_ctx->ports[pos]); - break; - case SOCKS5_STATE_ACK: - //@FIXME: We suppose that we will be able to do the whole read at once which is wrong too - err = socks5_reply (fdinfo->fd); - if (err == -SOCKS5_REP_GENERAL_FAILURE || err == -SOCKS5_REP_TTLEXP || err == -SOCKS5_REP_HOSTUNREACH) { - fprintf(stderr, "%s is not ready, received %s\n", fdinfo->url, socks5_rep(-err)); - goto on_socks5_err; - } else if (err < 0) { - fprintf(stderr, "An other error occured on %s\n", fdinfo->url); - exit(EXIT_FAILURE); - } - app_ctx->client_sock[pos].state = SOCKS5_STATE_RDY; - int sock1, sock2; - sock1 = dup(fdinfo->fd); - sock2 = dup(fdinfo->fd); - if (sock1 < 0 || sock2 < 0) { - exit(EXIT_FAILURE); - } - void* fdcat = evt_core_rm_fd (ctx, fdinfo->fd); - if (fdcat == NULL) { - exit(EXIT_FAILURE); - } - struct evt_core_fdinfo fdinfo = {0}; - struct evt_core_cat cat = {0}; - char url[1024]; - fdinfo.cat = &cat; - fdinfo.url = url; + app_ctx->ports[pos] = 7500 + pos; + socks5_create_dns_client (&app_ctx->evts, "127.0.0.1", "9050", target_host, app_ctx->ports[pos]); +} - fdinfo.cat->name = "tcp-write"; - fdinfo.fd = sock1; - sprintf(fdinfo.url, "tcp:write:127.0.0.1:%d", app_ctx->ports[pos]); - evt_core_add_fd (ctx, &fdinfo); +int on_socks5_success(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { + struct evt_core_fdinfo fdinfo_n = {0}; + struct evt_core_cat cat_n = {0}; + char url[1024]; + struct socks5_ctx* s5ctx = fdinfo->other; + fdinfo_n.cat = &cat_n; + fdinfo_n.url = url; - fdinfo.cat->name = "tcp-read"; - fdinfo.fd = sock2; - sprintf(fdinfo.url, "tcp:read:127.0.0.1:%d", app_ctx->ports[pos]); - evt_core_add_fd (ctx, &fdinfo); + fdinfo_n.fd = dup(fdinfo->fd); + fdinfo_n.cat->name = "tcp-write"; + sprintf(fdinfo_n.url, "tcp:write:127.0.0.1:%d", s5ctx->port); + evt_core_add_fd (ctx, &fdinfo_n); - printf("Socket %d/%d %s:%d has been added to the pool!\n", pos+1, CLIENT_PORT_SIZE, target_host, app_ctx->ports[pos]); - break; - case SOCKS5_STATE_RDY: - goto on_socks5_err; - break; - case SOCKS5_STATE_ERR: - goto on_socks5_err; - break; - } + fdinfo_n.fd = dup(fdinfo->fd); + fdinfo_n.cat->name = "tcp-read"; + sprintf(fdinfo_n.url, "tcp:read:127.0.0.1:%d", s5ctx->port); + evt_core_add_fd (ctx, &fdinfo_n); - return 1; -on_socks5_err: - //perror("An error occured while connecting to an Onion Service"); - app_ctx->client_sock[pos].state = SOCKS5_STATE_ERR; - evt_core_rm_fd (ctx, fdinfo->fd); - sleep(2); - init_tcp_client (app_ctx, pos); - return 1; + evt_core_rm_fd (ctx, fdinfo->fd); + return 1; + +failed: + fprintf(stderr, "Memory allocation failed\n"); + exit(EXIT_FAILURE); +} + +int on_socks5_failed(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { + struct donar_client_ctx* app_ctx = fdinfo->cat->app_ctx; + struct socks5_ctx* s5ctx = fdinfo->other; + int pos = 7500 - s5ctx->port; + + evt_core_rm_fd (ctx, fdinfo->fd); + init_socks5_client (app_ctx, pos); + return 1; +} + +void init_socks5_sinks(struct donar_client_ctx* app_ctx) { + struct evt_core_cat template = { 0 }; + + template.cb = on_socks5_success; + template.name = "socks5-success"; + template.flags = EPOLLET; + evt_core_add_cat(&app_ctx->evts, &template); + + template.cb = on_socks5_failed; + template.app_ctx = app_ctx; + template.name = "socks5-failed"; + template.flags = EPOLLET; + evt_core_add_cat(&app_ctx->evts, &template); } void donar_client(struct donar_client_ctx* ctx, char* algoname, @@ -128,16 +75,8 @@ void donar_client(struct donar_client_ctx* ctx, char* algoname, evt_core_init (&(ctx->evts)); init_algo(&ctx->evts, &algo, algoname); - struct evt_core_cat init_socks5 = { - .app_ctx = ctx, - .free_app_ctx = NULL, - .cb = configure_tcp_clients, - .err_cb = NULL, - .name = "configure-socks5", - .flags = EPOLLIN | EPOLLET, - .socklist = NULL - }; - evt_core_add_cat (&(ctx->evts), &init_socks5); + socks5_init (&ctx->evts); + init_socks5_sinks(ctx); evt_core_add_cat (&(ctx->evts), &(algo.on_tcp_co)); evt_core_add_cat (&(ctx->evts), &(algo.on_udp_read)); evt_core_add_cat (&(ctx->evts), &(algo.on_tcp_read)); @@ -149,7 +88,7 @@ void donar_client(struct donar_client_ctx* ctx, char* algoname, printf("--- Onion services loaded\n"); for (int i = 0; i < CLIENT_PORT_SIZE; i++) { - init_tcp_client(ctx, i); + init_socks5_client(ctx, i); } printf("--- TCP Clients Connected\n"); diff --git a/src/evt_core.c b/src/evt_core.c index ffe547a..1e7b728 100644 --- a/src/evt_core.c +++ b/src/evt_core.c @@ -64,7 +64,39 @@ void evt_core_add_cat(struct evt_core_ctx* ctx, struct evt_core_cat* cat) { g_hash_table_insert (ctx->catlist, dyn->name, dyn); } -void evt_core_add_fd(struct evt_core_ctx* ctx, struct evt_core_fdinfo* user_data) { +void evt_core_mv_fd(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo, struct evt_core_cat* to_cat) { + printf("Moving fd=%d from cat=%s to cat=%s\n",fdinfo->fd, fdinfo->cat->name, to_cat->name); + + // 1. Update old category + for (int i = 0; i < fdinfo->cat->socklist->len; i++) { + if (g_array_index(fdinfo->cat->socklist, struct evt_core_fdinfo*, i) == fdinfo) { + g_array_remove_index(fdinfo->cat->socklist, i); + } + } + + // 2. Set new cat for fdinfo + fdinfo->cat = to_cat; + + // 3. Update new category + g_array_append_val (fdinfo->cat->socklist, fdinfo); + + // 4. Update epoll flags + update_fd_epoll (ctx->epollfd, fdinfo->fd, fdinfo->cat->flags); + + // 5. Handle cases where data arrived before registering the file descriptor + fdinfo->cat->cb(ctx, fdinfo); +} + +void evt_core_mv_fd2(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo, char* to_cat) { + struct evt_core_cat* cat = evt_core_get_from_cat (ctx, to_cat); + if (cat == NULL) { + fprintf(stderr, "Category %s does not exist\n", to_cat); + exit(EXIT_FAILURE); + } + evt_core_mv_fd (ctx, fdinfo, cat); +} + +struct evt_core_fdinfo* evt_core_add_fd(struct evt_core_ctx* ctx, struct evt_core_fdinfo* user_data) { // 1. Fetch fd category struct evt_core_cat* cat = g_hash_table_lookup(ctx->catlist, user_data->cat->name); if (cat == NULL) { @@ -99,6 +131,11 @@ void evt_core_add_fd(struct evt_core_ctx* ctx, struct evt_core_fdinfo* user_data // 5. Add file descriptor to epoll add_fd_to_epoll(ctx->epollfd, user_data->fd, cat->flags); printf("Added fd=%d with url=%s in cat=%s\n", fdinfo->fd, fdinfo->url, fdinfo->cat->name); + + // 6. Ensure that events arrived before epoll registering are handled + fdinfo->cat->cb(ctx, fdinfo); + + return fdinfo; } struct evt_core_cat* evt_core_rm_fd(struct evt_core_ctx* ctx, int fd) { @@ -108,14 +145,11 @@ struct evt_core_cat* evt_core_rm_fd(struct evt_core_ctx* ctx, int fd) { struct evt_core_fdinfo* fdinfo = g_hash_table_lookup (ctx->socklist, &fd); if (fdinfo == NULL) return NULL; cat = fdinfo->cat; + printf("Closing fd=%d from cat=%s\n",fdinfo->fd, fdinfo->cat->name); // 2. Update category for (int i = 0; i < cat->socklist->len; i++) { if (g_array_index(cat->socklist, struct evt_core_fdinfo*, i) == fdinfo) { - if (fdinfo->fd != fd) { - fprintf(stderr, "Logic error in updating file descriptor list\n"); - exit(EXIT_FAILURE); - } g_array_remove_index(cat->socklist, i); } } diff --git a/src/evt_core.h b/src/evt_core.h index 2f6d96a..0b5b2ac 100644 --- a/src/evt_core.h +++ b/src/evt_core.h @@ -45,7 +45,9 @@ struct evt_core_fdinfo { void evt_core_init(struct evt_core_ctx* ctx); void evt_core_add_cat(struct evt_core_ctx* ctx, struct evt_core_cat* cat); -void evt_core_add_fd(struct evt_core_ctx* ctx, struct evt_core_fdinfo* user_data); +void evt_core_mv_fd(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo, struct evt_core_cat* to_cat); +void evt_core_mv_fd2(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo, char* to_cat); +struct evt_core_fdinfo* evt_core_add_fd(struct evt_core_ctx* ctx, struct evt_core_fdinfo* user_data); struct evt_core_cat* evt_core_rm_fd(struct evt_core_ctx* ctx, int fd); void evt_core_free(struct evt_core_ctx* ctx); void evt_core_loop(struct evt_core_ctx* ctx); diff --git a/src/meas_lat.c b/src/meas_lat.c index e65064f..ebf39be 100644 --- a/src/meas_lat.c +++ b/src/meas_lat.c @@ -8,6 +8,11 @@ #include "net_tools.h" #include "socks5.h" +struct measlat_ctx { + int count, size, interval; + char *host, *port, *transport; +}; + struct measure_conf { uint64_t max_measure; uint64_t payload_size; @@ -79,12 +84,19 @@ int on_udp(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { return 0; } +void free_timer_conf(void* v) { + struct measure_conf* mc = v; + free(mc->payload); + free(mc); +} + int on_timer(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { ssize_t s; uint64_t ticks; struct measure_conf* mc = fdinfo->other; s = read(fdinfo->fd, &ticks, sizeof(uint64_t)); + if (s == -1 && errno == EAGAIN) return 0; if (s != sizeof(uint64_t)) { perror("Read error"); exit(EXIT_FAILURE); @@ -113,112 +125,9 @@ int on_timer(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { perror("Send error"); //exit(EXIT_FAILURE); } - return 1; + return 0; } -void free_timer_conf(void* v) { - struct measure_conf* mc = v; - free(mc->payload); - free(mc); -} - -void register_categories(struct evt_core_ctx* evts) { - struct evt_core_cat tcp_read = { - .app_ctx = NULL, - .free_app_ctx = NULL, - .cb = on_udp, - .err_cb = on_udp_err, - .name = "tcp-read", - .flags = EPOLLIN | EPOLLET, - .socklist = NULL - }; - struct evt_core_cat udp_read = { - .app_ctx = NULL, - .free_app_ctx = NULL, - .cb = on_udp, - .err_cb = on_udp_err, - .name = "udp-read", - .flags = EPOLLIN | EPOLLET, - .socklist = NULL - }; - struct evt_core_cat timer = { - .app_ctx = NULL, - .free_app_ctx = NULL, - .cb = on_timer, - .err_cb = NULL, - .name = "timer", - .flags = EPOLLIN | EPOLLET, - .socklist = NULL - }; - - evt_core_init(evts); - evt_core_add_cat (evts, &udp_read); - evt_core_add_cat (evts, &tcp_read); - evt_core_add_cat(evts, &timer); - printf("--- Categories registered\n"); - -} - -int register_tor_socket(struct evt_core_ctx* evts, char* host, char* port, int count, int size) { - int socks5_sock = -1, err = 0; - for (int i = 120; i > 0; i--) { - sleep(2); - printf("Remaining try %d/120 to connect to %s:%s\n", i, host, port); - if (socks5_sock >= 0) { - close(socks5_sock); - socks5_sock = -1; - } - socks5_sock = create_tcp_client ("127.0.0.1", "9050"); - err = socks5_handshake_syn (socks5_sock); - if (err < 0) continue; - err = socks5_handshake_ack (socks5_sock); - if (err < 0) continue; - err = socks5_connect_dns (socks5_sock, host, atoi(port)); - if (err < 0) continue; - err = socks5_reply (socks5_sock); - if (err < 0) { - fprintf(stderr, "Socks5 error %s\n", socks5_rep(-err)); - continue; - } - else break; - } - if (socks5_sock < 0 || err < 0) { - exit(EXIT_FAILURE); - } - - char url[1024]; - struct evt_core_cat cat = {0}; - struct evt_core_fdinfo fdinfo = {0}; - fdinfo.cat = &cat; - fdinfo.url = url; - - fdinfo.fd = socks5_sock; - fdinfo.cat->name = "tcp-read"; - fdinfo.other = create_measure_conf (count, size); - fdinfo.free_other = free_mesure_conf; - sprintf(fdinfo.url, "tcp:read:%s:%s", host, port); - evt_core_add_fd (evts, &fdinfo); - printf("--- Tor socket registered\n"); - return fdinfo.fd; -} - -int register_udp_socket(struct evt_core_ctx* evts, char* host, char* port, int count, int size) { - int udp_sock = create_udp_client (host, port); - char url[1024]; - struct evt_core_cat cat = {0}; - struct evt_core_fdinfo fdinfo = {0}; - fdinfo.cat = &cat; - fdinfo.url = url; - - fdinfo.fd = udp_sock; - fdinfo.cat->name = "udp-read"; - fdinfo.other = create_measure_conf (count, size); - fdinfo.free_other = free_mesure_conf; - sprintf(fdinfo.url, "udp:read:%s:%s", host, port); - evt_core_add_fd (evts, &fdinfo); - printf("--- UDP socket registered\n"); - return fdinfo.fd; -} void register_timer(struct evt_core_ctx* evts, int udp, int interval, int count, int size) { struct timespec now; @@ -256,34 +165,144 @@ void register_timer(struct evt_core_ctx* evts, int udp, int interval, int count, printf("--- Timer registered\n"); } +int on_socks5_success_measlat(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { + char url[1024]; + struct evt_core_cat cat = {0}; + struct evt_core_fdinfo fdinfo_n = {0}; + struct socks5_ctx* s5ctx = fdinfo->other; + fdinfo_n.cat = &cat; + fdinfo_n.url = url; + + struct evt_core_cat* ucat = evt_core_get_from_cat (ctx, "tcp-read"); + if (ucat == NULL) { + fprintf(stderr, "Category udp-read not found\n"); + exit(EXIT_FAILURE); + } + struct measlat_ctx* mctx = ucat->app_ctx; + + fdinfo_n.fd = dup(fdinfo->fd); + fdinfo_n.cat->name = "tcp-read"; + fdinfo_n.other = create_measure_conf (mctx->count, mctx->size); + fdinfo_n.free_other = free_mesure_conf; + sprintf(fdinfo_n.url, "tcp:read:%s:%d", s5ctx->addr, s5ctx->port); + + evt_core_add_fd (ctx, &fdinfo_n); + printf("--- Tor socket registered\n"); + + register_timer(ctx, fdinfo->fd, mctx->interval, mctx->count, mctx->size); + return 1; +} + +void spawn_tor_socket(struct evt_core_ctx* evts) { + struct evt_core_cat* ucat = evt_core_get_from_cat (evts, "tcp-read"); + if (ucat == NULL) { + fprintf(stderr, "Category udp-read not found\n"); + exit(EXIT_FAILURE); + } + struct measlat_ctx* mctx = ucat->app_ctx; + + socks5_create_dns_client (evts, "127.0.0.1", "9050", mctx->host, atoi(mctx->port)); +} + +int on_socks5_failed_measlat(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { + evt_core_rm_fd (ctx, fdinfo->fd); + sleep(1); + spawn_tor_socket(ctx); + + return 1; +} + +void register_categories(struct evt_core_ctx* evts, struct measlat_ctx* mctx) { + struct evt_core_cat template = {0}; + template.app_ctx = mctx; + evt_core_init(evts); + + template.cb = on_timer; + template.name = "timer"; + template.flags = EPOLLIN | EPOLLET; + evt_core_add_cat(evts, &template); + + template.cb = on_udp; // intended but not elegant + template.err_cb = on_udp_err; // intended but not elegant + template.name = "tcp-read"; + template.flags = EPOLLIN | EPOLLET; + evt_core_add_cat(evts, &template); + + template.cb = on_udp; + template.err_cb = on_udp_err; + template.name = "udp-read"; + template.flags = EPOLLIN | EPOLLET; + evt_core_add_cat(evts, &template); + + template.cb = on_socks5_success_measlat; + template.err_cb = on_socks5_failed_measlat; + template.name = "socks5-success"; + template.flags = EPOLLET; + + template.cb = on_socks5_failed_measlat; + template.err_cb = on_socks5_failed_measlat; + template.name = "socks5-failed"; + template.flags = EPOLLET; + + socks5_init(evts); + printf("--- Categories registered\n"); + +} + +void spawn_udp_socket(struct evt_core_ctx* evts) { + struct evt_core_cat* ucat = evt_core_get_from_cat (evts, "udp-read"); + if (ucat == NULL) { + fprintf(stderr, "Category udp-read not found\n"); + exit(EXIT_FAILURE); + } + struct measlat_ctx* mctx = ucat->app_ctx; + + int udp_sock = create_udp_client (mctx->host, mctx->port); + char url[1024]; + struct evt_core_cat cat = {0}; + struct evt_core_fdinfo fdinfo = {0}; + fdinfo.cat = &cat; + fdinfo.url = url; + + fdinfo.fd = udp_sock; + fdinfo.cat->name = "udp-read"; + fdinfo.other = create_measure_conf (mctx->count, mctx->size); + fdinfo.free_other = free_mesure_conf; + sprintf(fdinfo.url, "udp:read:%s:%s", mctx->host, mctx->port); + evt_core_add_fd (evts, &fdinfo); + printf("--- UDP socket registered\n"); + + register_timer(evts, fdinfo.fd, mctx->interval, mctx->count, mctx->size); +} + int main(int argc, char** argv) { setvbuf(stdout, NULL, _IONBF, 0); printf("~ measlat ~\n"); - int opt, udp_fd, count = 0, size = 0, interval = 0; - char *host = NULL, *port = NULL, *transport = NULL; + int opt; + struct measlat_ctx mctx = {0}; struct evt_core_ctx evts = {0}; // 1. Parse parameters while ((opt = getopt(argc, argv, "h:p:c:s:i:t:")) != -1) { switch(opt) { case 'h': // host - host = optarg; + mctx.host = optarg; break; case 'p': // port - port = optarg; + mctx.port = optarg; break; case 't': // transport - transport = optarg; + mctx.transport = optarg; break; case 'c': // count - count = atoi(optarg); + mctx.count = atoi(optarg); break; case 's': // size - payload in bytes - size = atoi(optarg); + mctx.size = atoi(optarg); break; case 'i': // interval - every ms - interval = atoi(optarg); + mctx.interval = atoi(optarg); break; default: goto usage; @@ -292,17 +311,16 @@ int main(int argc, char** argv) { // 2. Check and fix parameters size_t header_size = sizeof(struct packet_header); - if (interval <= 0) interval = 1000; - if (count <= 0) count = 1; - if (size < header_size) size = header_size; - if (transport == NULL) transport = "udp"; - if (host == NULL || port == NULL) goto usage; + if (mctx.interval <= 0) mctx.interval = 1000; + if (mctx.count <= 0) mctx.count = 1; + if (mctx.size < header_size) mctx.size = header_size; + if (mctx.transport == NULL) mctx.transport = "udp"; + if (mctx.host == NULL || mctx.port == NULL) goto usage; // 3. Bind events - register_categories(&evts); - if (strcmp(transport, "udp") == 0) udp_fd = register_udp_socket(&evts, host, port, count, size); - else if (strcmp(transport, "tor") == 0) udp_fd = register_tor_socket(&evts, host, port, count, size); - register_timer(&evts, udp_fd, interval, count, size); + register_categories(&evts, &mctx); + if (strcmp(mctx.transport, "udp") == 0) spawn_udp_socket(&evts); + else if (strcmp(mctx.transport, "tor") == 0) spawn_tor_socket(&evts); // 4. Run main loop evt_core_loop(&evts); diff --git a/src/net_tools.c b/src/net_tools.c index dbee61d..cf1c419 100644 --- a/src/net_tools.c +++ b/src/net_tools.c @@ -165,3 +165,13 @@ void add_fd_to_epoll(int epollfd, int fd, uint32_t flags) { exit(EXIT_FAILURE); } } + +void update_fd_epoll(int epollfd, int fd, uint32_t flags) { + struct epoll_event current_event = {0}; + current_event.events = flags; + current_event.data.fd = fd; + if (epoll_ctl (epollfd, EPOLL_CTL_MOD, fd, ¤t_event) == -1) { + perror("Failed to update a file descriptor to epoll with epoll_ctl"); + exit(EXIT_FAILURE); + } +} diff --git a/src/net_tools.h b/src/net_tools.h index 9756c12..a847b9a 100644 --- a/src/net_tools.h +++ b/src/net_tools.h @@ -14,5 +14,6 @@ int create_tcp_server(char* host, char* service); int create_udp_server(char* host, char* service); int make_socket_non_blocking(int fd); void add_fd_to_epoll(int epollfd, int fd, uint32_t flags); +void update_fd_epoll(int epollfd, int fd, uint32_t flags); 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/socks5.c b/src/socks5.c index 1351cc4..a129cd4 100644 --- a/src/socks5.c +++ b/src/socks5.c @@ -1,105 +1,315 @@ #include "socks5.h" -int socks5_handshake_syn(int sock) { - //@TODO: Refactor the client handshake management - struct client_handshake ch = { - .ver = 0x05, - .nmethods = 0x01, - .methods = {0x00} - }; - - int size = sizeof(uint8_t) * (2 + ch.nmethods); - if (size != write(sock, &ch, size)) { - perror("write failed on tcp socket in socks5"); - return -1; - } - return 0; +void socks5_free_ctx(void* elem) { + struct socks5_ctx* ctx = elem; + free(ctx); } -int socks5_handshake_ack(int sock) { - //@TODO: Refactor the client handshake management - struct client_handshake ch = { - .ver = 0x05, - .nmethods = 0x01, - .methods = {0x00} - }; - struct server_handshake sh = {0}; - int err = read_entity(sock, &sh, sizeof(struct server_handshake)); - if (err == -1) { - perror("sock5 handshake failed read"); - return -1; - } - if (ch.ver != sh.ver || sh.method != ch.methods[0]) { - fprintf(stderr, "Protocol error: client asks for ver=%d, method=%d and server answers with ver=%d, method=%d\n", - ch.ver, ch.methods[0], sh.ver, sh.method); - return -1; - } - printf("[server_handshake] ver=%d, method=%d\n", sh.ver, sh.method); - return 0; +void socks5_create_dns_client(struct evt_core_ctx* ctx, char* proxy_host, char* proxy_port, char* addr, uint16_t port) { + struct evt_core_fdinfo fdinfo; + struct evt_core_cat cat; + struct socks5_ctx* s5ctx; + struct evt_core_fdinfo* reg_fdinfo; + char url[1024]; + + // 0. Compute domain length and enforce an upper bound on its size + size_t domainLength = strlen(addr); + if (domainLength > 255) { + fprintf(stderr, "domain is too long\n"); + exit(EXIT_FAILURE); + } + + // 1. Open connection + int sock = create_tcp_client (proxy_host, proxy_port); + if (sock < 0) { + fprintf(stderr, "Unable to connect to proxy %s:%s\n", proxy_host, proxy_port); + exit(EXIT_FAILURE); + } + + // 2. Create fdinfo + fdinfo.cat = &cat; + fdinfo.cat->name = "socks5-send-handshake"; + fdinfo.fd = sock; + fdinfo.other = malloc(sizeof(struct socks5_ctx)); + if (fdinfo.other == NULL) { + perror("malloc failed"); + exit(EXIT_FAILURE); + } + memset(fdinfo.other, 0, sizeof(struct socks5_ctx)); + fdinfo.free_other = socks5_free_ctx; + sprintf(url, "socks5:send-hs:%s:%d", addr, port); + fdinfo.url = strdup(url); + + // 3. Fill socks5_ctx structures + s5ctx = fdinfo.other; + s5ctx->port = port; + s5ctx->addr = strdup(addr); + + // 3.1 Client handshake to send + s5ctx->ch.ver = VER_SOCKS5; + s5ctx->ch.nmethods = 0x01; + s5ctx->ch.methods[0] = METHOD_NOAUTH; + s5ctx->ch_size = sizeof(uint8_t) * (2 + s5ctx->ch.nmethods); + + // 3.2 Client request to send + s5ctx->cr.ver = VER_SOCKS5; + s5ctx->cr.cmd = CMD_CONNECT; + s5ctx->cr.rsv = 0x00; + s5ctx->cr.atyp = ATYP_DOMAINNAME; + s5ctx->cr.dst_addr_len = domainLength; + s5ctx->cr.dst_addr = addr; + s5ctx->cr.port = htons(port); + + // 3.3 Generate client request buffer + s5ctx->cr_size = 0; + fill_buffer(&s5ctx->cr_size, s5ctx->cr_buffer, &s5ctx->cr, 5*sizeof(uint8_t)); + fill_buffer(&s5ctx->cr_size, s5ctx->cr_buffer, s5ctx->cr.dst_addr, s5ctx->cr.dst_addr_len*sizeof(char)); + fill_buffer(&s5ctx->cr_size, s5ctx->cr_buffer, &s5ctx->cr.port, sizeof(uint16_t)); + + reg_fdinfo = evt_core_add_fd (ctx, &fdinfo); } -int socks5_reply(int sock) { - int res; - struct server_reply sr = {0}; - res = read_entity(sock, &sr, sizeof(uint8_t) * 4); - if (res == -1) goto read_error; +int on_socks5_send_handshake(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { + struct socks5_ctx* s5ctx = fdinfo->other; - switch(sr.atyp) { - case ATYP_IPV4: - if (read_entity(sock, sr.bind_addr.ipv4, sizeof(uint8_t) * 4) == -1) - goto read_error; - break; - case ATYP_DOMAINNAME: - if (read_entity(sock, &sr.bind_addr.dns.len, sizeof(uint8_t) * 4) == -1) - goto read_error; - if (read_entity(sock, sr.bind_addr.dns.str, sizeof(char) * sr.bind_addr.dns.len) == -1) - goto read_error; - break; - case ATYP_IPV6: - if (read_entity(sock, sr.bind_addr.ipv6, sizeof(uint8_t) * 16) == -1) - goto read_error; - break; - default: - fprintf(stderr, "Unsupported ATYP in server reply\n"); - return -128; - } - res = read_entity(sock, &sr.port, sizeof(uint16_t)); - if (res == -1) { - perror("read_entity"); - exit(EXIT_FAILURE); - } + size_t written = write(fdinfo->fd, (char*)&s5ctx->ch + s5ctx->ch_cursor, s5ctx->ch_size - s5ctx->ch_cursor); + if (written == -1 && errno == EAGAIN) return 1; + if (written < 0) { + perror("write failed on tcp socket in socks5"); + evt_core_mv_fd2(ctx, fdinfo, "socks5-failed"); + return 1; + } + s5ctx->ch_cursor += written; + if (s5ctx->ch_cursor < s5ctx->ch_size) return 0; - if (sr.rep < 0 || sr.rep > 0x08) { - fprintf(stderr, "Invalid reply field\n"); - return -128; - } - - printf("[server_reply] ver=%d, rep=%s, atyp=%d, port=%d\n", sr.ver, rep_msg[sr.rep], sr.atyp, sr.port); - return -sr.rep; - -read_error: - fprintf(stderr, "Unable to read ATYP\n"); - return -128; + evt_core_mv_fd2(ctx, fdinfo, "socks5-recv-handshake"); + return 1; } -int socks5_connect_dns(int sock, char* addr, uint16_t port) { - char buffer[262]; - size_t domainLength = strlen(addr); - if (domainLength > 255) { - fprintf(stderr, "domain is too long\n"); - exit(EXIT_FAILURE); - } - struct client_request cr = { 0x05, CMD_CONNECT, 0x00, ATYP_DOMAINNAME, (uint8_t)domainLength, addr, htons(port) }; - size_t written = 0; - fill_buffer(&written, buffer, &cr, 5*sizeof(uint8_t)); - fill_buffer(&written, buffer, cr.dst_addr, cr.dst_addr_len*sizeof(char)); - fill_buffer(&written, buffer, &cr.port, sizeof(uint16_t)); +int on_socks5_recv_handshake(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { + struct socks5_ctx* s5ctx = fdinfo->other; + int readn = 0; - if (written != write(sock, buffer, written)) { - fprintf(stderr, "partial/failed write\n"); - return -1; + readn = read(fdinfo->fd, (char*)&s5ctx->sh + s5ctx->sh_cursor, sizeof(s5ctx->sh) - s5ctx->sh_cursor); + if (readn == -1 && errno == EAGAIN) return 1; + if (readn < 0) { + perror("sock5 handshake failed read"); + evt_core_mv_fd2(ctx, fdinfo, "socks5-failed"); + return 1; + } + + s5ctx->sh_cursor += readn; + if (s5ctx->sh_cursor < sizeof(s5ctx->sh)) return 0; + + if (s5ctx->ch.ver != s5ctx->sh.ver || s5ctx->sh.method != s5ctx->ch.methods[0]) { + fprintf(stderr, "Protocol error: client asks for ver=%d, method=%d and server answers with ver=%d, method=%d\n", + s5ctx->ch.ver, s5ctx->ch.methods[0], s5ctx->sh.ver, s5ctx->sh.method); + evt_core_mv_fd2(ctx, fdinfo, "socks5-failed"); + return 1; + } + printf("[socks5_server_handshake] fd=%d, ver=%d, method=%d\n", fdinfo->fd, s5ctx->sh.ver, s5ctx->sh.method); + evt_core_mv_fd2(ctx, fdinfo, "socks5-send-client-req"); + return 1; +} + +int on_socks5_send_client_req(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { + struct socks5_ctx* s5ctx = fdinfo->other; + int written = 0; + + written = write(fdinfo->fd, (char*)s5ctx->cr_buffer + s5ctx->cr_cursor, s5ctx->cr_size - s5ctx->cr_cursor); + if (written == -1 && errno == EAGAIN) return 1; + if (written < 0) { + fprintf(stderr, "socks5 send client request failed\n"); + evt_core_mv_fd2 (ctx, fdinfo, "socks5-failed"); + return 1; + } + s5ctx->cr_cursor += written; + if (s5ctx->cr_cursor < s5ctx->cr_size) return 0; + + evt_core_mv_fd2 (ctx, fdinfo, "socks5-recv-server-reply"); + return 1; +} + +int socks5_server_reply_atyp_ipv4(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { + struct socks5_ctx* s5ctx = fdinfo->other; + size_t fixed_headers_size = (char*)&s5ctx->sr.bind_addr.ipv4 - (char*)&s5ctx->sr; + size_t host_size = sizeof(s5ctx->sr.bind_addr.ipv4); + uint64_t relative_cursor = (s5ctx->sr_cursor - fixed_headers_size); + int nread = 0; + + nread = read(fdinfo->fd, + (char*)s5ctx->sr.bind_addr.ipv4 + relative_cursor, + host_size - relative_cursor); + + if (nread == -1 && errno == EAGAIN) return 1; + if (nread < 0) { + perror("write failed on tcp socket in socks5"); + evt_core_mv_fd2(ctx, fdinfo, "socks5-failed"); + return 1; + } + + s5ctx->sr_cursor += nread; + if (s5ctx->sr_cursor < fixed_headers_size + host_size) return 0; + + s5ctx->sr_host_read = 1; + return 0; +} + +int socks5_server_reply_atyp_ipv6(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { + struct socks5_ctx* s5ctx = fdinfo->other; + size_t fixed_headers_size = (char*)&s5ctx->sr.bind_addr.ipv6 - (char*)&s5ctx->sr; + size_t host_size = sizeof(s5ctx->sr.bind_addr.ipv6); + uint64_t relative_cursor = (s5ctx->sr_cursor - fixed_headers_size); + int nread = 0; + + nread = read(fdinfo->fd, + (char*)s5ctx->sr.bind_addr.ipv6 + relative_cursor, + host_size - relative_cursor); + + if (nread == -1 && errno == EAGAIN) return 1; + if (nread < 0) { + perror("write failed on tcp socket in socks5"); + evt_core_mv_fd2(ctx, fdinfo, "socks5-failed"); + return 1; + } + s5ctx->sr_cursor += nread; + if (s5ctx->sr_cursor < fixed_headers_size + host_size) return 0; + + s5ctx->sr_host_read = 1; + return 0; +} + +int socks5_server_reply_atyp_dn(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { + struct socks5_ctx* s5ctx = fdinfo->other; + size_t fixed_headers_size = (char*)&s5ctx->sr.bind_addr.ipv6 - (char*)&s5ctx->sr; + size_t dn_size_size = sizeof(s5ctx->sr.bind_addr.dns.len); + uint64_t relative_cursor = 0; + int nread = 0; + + if (s5ctx->sr_cursor < fixed_headers_size + dn_size_size) { + relative_cursor = (s5ctx->sr_cursor - fixed_headers_size); + nread = read(fdinfo->fd, (char*)&s5ctx->sr.bind_addr.dns.len + relative_cursor, dn_size_size - relative_cursor); + if (nread == -1 && errno == EAGAIN) return 1; + if (nread < 0) { + perror("write failed on tcp socket in socks5"); + evt_core_mv_fd2(ctx, fdinfo, "socks5-failed"); + return 1; } + s5ctx->sr_cursor += nread; return 0; + } + + relative_cursor = s5ctx->sr_cursor - fixed_headers_size - sizeof(s5ctx->sr.bind_addr.dns.len); + nread = read(fdinfo->fd, (char*)&s5ctx->sr.bind_addr.dns.str + relative_cursor, s5ctx->sr.bind_addr.dns.len - relative_cursor); + if (nread == -1 && errno == EAGAIN) return 1; + if (nread < 0) { + perror("write failed on tcp socket in socks5"); + evt_core_mv_fd2(ctx, fdinfo, "socks5-failed"); + return 1; + } + s5ctx->sr_cursor += nread; + if (s5ctx->sr_cursor < fixed_headers_size + dn_size_size + s5ctx->sr.bind_addr.dns.len) return 0; + + s5ctx->sr_host_read = 1; + return 0; +} + +size_t socks5_server_reply_size(struct server_reply* sr) { + size_t fixed_headers_size = (char*)&sr->bind_addr - (char*)sr; + size_t fixed_tail_size = (char*)(sr + 1) - (char*)&sr->port; + size_t host_size = 0; + + if (sr->atyp == ATYP_IPV4) { + host_size = sizeof(sr->bind_addr.ipv4); + } else if (sr->atyp == ATYP_IPV6) { + host_size = sizeof(sr->bind_addr.ipv6); + } else if (sr->atyp == ATYP_DOMAINNAME) { + host_size = sizeof(sr->bind_addr.dns.len) + sr->bind_addr.dns.len; + } else { + fprintf(stderr, "Unsupported ATYP for SOCK5\n"); + exit(EXIT_FAILURE); + } + + return fixed_headers_size + host_size + fixed_tail_size; +} + +int on_socks5_recv_server_reply(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { + struct socks5_ctx* s5ctx = fdinfo->other; + int readn = 0; + size_t fixed_headers_size = (char*)&s5ctx->sr.bind_addr - (char*)&s5ctx->sr; + + // Read headers + if (s5ctx->sr_cursor < fixed_headers_size) { + readn = read(fdinfo->fd, (char*)&s5ctx->sr + s5ctx->sr_cursor, fixed_headers_size - s5ctx->sr_cursor); + if (readn == -1 && errno == EAGAIN) return 1; + if (readn < 0) goto move_to_failed; + s5ctx->sr_cursor += readn; + return 0; // Needed as we might have not read enough bytes and free us from writing a loop + } + + // Read host + if (!s5ctx->sr_host_read) { + if (s5ctx->sr.atyp == ATYP_IPV4) return socks5_server_reply_atyp_ipv4(ctx, fdinfo); + else if (s5ctx->sr.atyp == ATYP_IPV6) return socks5_server_reply_atyp_ipv6(ctx, fdinfo); + else if (s5ctx->sr.atyp == ATYP_DOMAINNAME) return socks5_server_reply_atyp_dn(ctx, fdinfo); + else goto move_to_failed; + } + + // Read port + size_t final_size = socks5_server_reply_size(&s5ctx->sr); + if (s5ctx->sr_cursor < final_size) { + size_t relative_cursor = s5ctx->sr_cursor - (final_size - sizeof(s5ctx->sr.port)); + readn = read(fdinfo->fd, (char*)&s5ctx->sr.port + relative_cursor, sizeof(s5ctx->sr.port) - relative_cursor); + if (readn == -1 && errno == EAGAIN) return 1; + if (readn < 0) goto move_to_failed; + s5ctx->sr_cursor += readn; + return 0; // Needed as we might have not read enough bytes and free us from writing a loop + } + + // Do some checks + if (s5ctx->sr.rep > 0x08) goto move_to_failed; + printf("[socks5_server_reply] fd=%d, ver=%d, rep=%s, atyp=%d, port=%d\n", fdinfo->fd, s5ctx->sr.ver, rep_msg[s5ctx->sr.rep], s5ctx->sr.atyp, s5ctx->sr.port); + + if (s5ctx->sr.rep != SOCKS5_REP_SUCCESS) goto move_to_failed; + + evt_core_mv_fd2 (ctx, fdinfo, "socks5-success"); + return 1; +move_to_failed: + evt_core_mv_fd2 (ctx, fdinfo, "socks5-failed"); + return 1; +} + +int on_socks5_err(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { + evt_core_mv_fd2 (ctx, fdinfo, "socks5-failed"); + return 1; +} + +void socks5_init(struct evt_core_ctx* ctx) { + struct evt_core_cat template = {0}; + + template.cb = on_socks5_send_handshake; + template.err_cb = on_socks5_err; + template.name = "socks5-send-handshake"; + template.flags = EPOLLOUT | EPOLLET; + evt_core_add_cat (ctx, &template); + + template.cb = on_socks5_recv_handshake; + template.err_cb = on_socks5_err; + template.name = "socks5-recv-handshake"; + template.flags = EPOLLIN | EPOLLET; + evt_core_add_cat (ctx, &template); + + template.cb = on_socks5_send_client_req; + template.err_cb = on_socks5_err; + template.name = "socks5-send-client-req"; + template.flags = EPOLLOUT | EPOLLET; + evt_core_add_cat(ctx, &template); + + template.cb = on_socks5_recv_server_reply; + template.err_cb = on_socks5_err; + template.name = "socks5-recv-server-reply"; + template.flags = EPOLLIN | EPOLLET; + evt_core_add_cat(ctx, &template); } char* socks5_rep (enum socks5_rep rep) { diff --git a/src/socks5.h b/src/socks5.h index 742f663..1d84a14 100644 --- a/src/socks5.h +++ b/src/socks5.h @@ -7,6 +7,7 @@ #include #include #include "net_tools.h" +#include "evt_core.h" enum socks5_state { SOCKS5_STATE_NEW, @@ -27,10 +28,21 @@ enum atyp { ATYP_IPV6 = 0x04 }; +enum ver { + VER_SOCKS5 = 0x05 +}; + +enum methods { + METHOD_NOAUTH = 0x00, + METHOD_GSSAPI = 0x01, + METHOD_USERPASS = 0x02, + METHOD_NOACCEPT = 0xff +}; + union socks5_addr { struct { uint8_t len; - char* str; + char str[256]; } dns; uint8_t ipv4[4]; uint8_t ipv6[16]; @@ -95,8 +107,24 @@ struct server_reply { uint16_t port; }; -int socks5_handshake_syn(int sock); -int socks5_handshake_ack(int sock); -int socks5_connect_dns(int sock, char* addr, uint16_t port); -int socks5_reply(int sock); +struct socks5_ctx { + uint16_t port; + char* addr; + + struct client_handshake ch; + struct server_handshake sh; + struct client_request cr; + struct server_reply sr; + uint64_t ch_cursor; + uint64_t sh_cursor; + uint64_t cr_cursor; + uint64_t sr_cursor; + char cr_buffer[262]; + size_t ch_size; + size_t cr_size; + uint8_t sr_host_read; +}; + +void socks5_init(struct evt_core_ctx* ctx); +void socks5_create_dns_client(struct evt_core_ctx* ctx, char* proxy_host, char* proxy_port, char* addr, uint16_t port); char* socks5_rep (enum socks5_rep rep); diff --git a/src/tor_echo.c b/src/tor_echo.c index 6457576..2436b4c 100644 --- a/src/tor_echo.c +++ b/src/tor_echo.c @@ -37,6 +37,7 @@ int te_on_tcp_co(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { in_len = sizeof(addr); conn_sock1 = accept(fdinfo->fd, (struct sockaddr*)&addr, &in_len); + if (conn_sock1 == -1 && errno == EAGAIN) return 1; if (conn_sock1 == -1) goto co_error; url_get_port(port, fdinfo->url); @@ -46,7 +47,7 @@ int te_on_tcp_co(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { sprintf(to_fdinfo.url, "tcp:all:127.0.0.1:%s", port); evt_core_add_fd (ctx, &to_fdinfo); - return 1; + return 0; co_error: perror("Failed to handle new connection");