#include #include #include #include #include #include #include #include "evt_core.h" #include "net_tools.h" #include "socks5.h" #include "utils.h" #include "measure.h" #include "url.h" #include "tor_os.h" #include "tor_ctl.h" struct measlat_ctx { struct sockaddr_in addr; socklen_t addrlen; int verbose, connectionless, tor_flags; char *host, *port, *transport, *tor_port; enum { MEASLAT_CLIENT = 0, MEASLAT_SERVER = 1 } role; struct measure_params mp; }; void free_ms(void* obj) { measure_state_free (obj); free(obj); } struct measure_state ms_transi = {0}; int streq(char* s1, char* s2) { return strcmp(s1, s2) == 0; } struct evt_core_fdinfo* register_timer(struct evt_core_ctx* evts, struct measlat_ctx* mctx, struct measure_state* ms, struct timespec* next_tick) { struct timespec now; struct itimerspec timer_config; char url[1024]; struct evt_core_cat cat = {0}; struct evt_core_fdinfo fdinfo = {0}; struct measure_state* msheap = malloc(sizeof(struct measure_state)); if (ms == NULL) { perror("unable to malloc struct timer_measure"); exit(EXIT_FAILURE); } memcpy(msheap, ms, sizeof(struct measure_state)); fdinfo.cat = &cat; fdinfo.url = url; fdinfo.other = msheap; fdinfo.free_other = free_ms; if (clock_gettime(CLOCK_MONOTONIC, &now) == -1) { perror("clock_gettime"); exit(EXIT_FAILURE); } uint64_t micro_sec = mctx->mp.interval; timer_config.it_value.tv_sec = next_tick == NULL ? now.tv_sec + 1 : next_tick->tv_sec; timer_config.it_value.tv_nsec = next_tick == NULL ? now.tv_nsec : next_tick->tv_nsec; timer_config.it_interval.tv_sec = micro_sec / 1000; timer_config.it_interval.tv_nsec = micro_sec % 1000 * 1000000; printf("timer_config: sec=%ld nsec=%ld \n", (uint64_t) timer_config.it_value.tv_sec, (uint64_t) timer_config.it_value.tv_nsec); fdinfo.fd = timerfd_create(CLOCK_MONOTONIC, 0); if (fdinfo.fd == -1) { perror("Unable to timerfd_create"); exit(EXIT_FAILURE); } if (timerfd_settime (fdinfo.fd, TFD_TIMER_ABSTIME, &timer_config, NULL) == -1) { perror("Unable to timerfd_time"); exit(EXIT_FAILURE); } fdinfo.cat->name = "timer"; sprintf(fdinfo.url, "timer:%d", msheap->fd); struct evt_core_fdinfo *new_fdinfo = evt_core_add_fd (evts, &fdinfo); printf("--- Timer registered\n"); printf("[states] measurement %d+%d started\n", msheap->fd, fdinfo.fd); return new_fdinfo; } int on_receive_measure_packet_err(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { struct measlat_ctx* mctx = fdinfo->cat->app_ctx; printf("An error occured with socket %d\n", fdinfo->fd); char url[255]; sprintf(url, "timer:%d", fdinfo->fd); struct evt_core_fdinfo* assoc_timer = evt_core_get_from_url (ctx, url); if (assoc_timer != NULL && !mctx->mp.probe && !mctx->connectionless) { if (mctx->role == MEASLAT_CLIENT && !mctx->mp.probe) measure_summary (&mctx->mp, assoc_timer->other); evt_core_rm_fd (ctx, assoc_timer->fd); printf("Deleted associated timer %s\n", url); } else { printf("No associated timer %s\n", url); } if (mctx->role == MEASLAT_CLIENT && !mctx->mp.probe) exit(EXIT_FAILURE); if (mctx->connectionless) return 1; // keep the NET FD else return 0; // delete the NET fd } void measlat_stop( struct evt_core_ctx* ctx, struct measlat_ctx* mctx, struct measure_state* ms, int net_fd, int timer_fd) { if (ms->mp_in->counter < mctx->mp.max_measure) return; if (ms->mp_out->counter < mctx->mp.max_measure) return; printf("[states] measurement %d+%d terminated\n", net_fd, timer_fd); if (mctx->role == MEASLAT_CLIENT && !mctx->mp.probe) measure_summary (&(mctx->mp), ms); evt_core_rm_fd(ctx, timer_fd); if (!(mctx->connectionless && mctx->role == MEASLAT_SERVER)) evt_core_rm_fd(ctx, net_fd); if (mctx->role == MEASLAT_CLIENT) exit(EXIT_SUCCESS); } int on_receive_measure_packet(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { struct measlat_ctx* mctx = fdinfo->cat->app_ctx; struct measure_state* ms; ssize_t nread; char url[255]; // 1. Get our measurement state sprintf(url, "timer:%d", fdinfo->fd); struct evt_core_fdinfo* assoc_timer = evt_core_get_from_url (ctx, url); if (assoc_timer) { // Measurement state exists ms = assoc_timer->other; } else { // Does not exist yet, we use a tmp stub. ms = &ms_transi; ms->fd = fdinfo->fd; ms->mp_nin = 0; } // 2. Read data in our measurement object nread = mctx->connectionless && mctx->role == MEASLAT_SERVER ? recvfrom(fdinfo->fd, ms->mp_in, mctx->mp.payload_size, MSG_TRUNC, (struct sockaddr*)&mctx->addr, &mctx->addrlen) : recv(fdinfo->fd, ((char*) ms->mp_in) + ms->mp_nin, mctx->mp.payload_size - ms->mp_nin, 0); if (nread <= 0) return 1; ms->mp_nin += nread; if (ms->mp_nin < mctx->mp.payload_size) return 0; // 3. Process data in our measurement object measure_parse (&mctx->mp, ms, mctx->verbose); // 4. Detect if it is a probe if (ms->mp_in->probe && mctx->role == MEASLAT_SERVER) { // Allow for probing without registering a timer int s = mctx->connectionless && mctx->role == MEASLAT_SERVER ? sendto(ms->fd, ms->mp_in, mctx->mp.payload_size, 0, (struct sockaddr*)&mctx->addr, mctx->addrlen) : send(ms->fd, ms->mp_in, mctx->mp.payload_size, 0); if (!mctx->connectionless) evt_core_rm_fd (ctx, fdinfo->fd); return 1; } else if (ms->mp_in->probe && mctx->role == MEASLAT_CLIENT) { if (ms->mp_out->probe) exit(EXIT_SUCCESS); else { fprintf(stderr, "lost probe, ignoring\n"); return 0; } } // 5. Persist our measurement object if needed // It includes starting a timer. if (ms == &ms_transi) { if (ms->mp_in->counter != 1) { // Guard against rando scanning the IPv4 range if (!(mctx->connectionless && mctx->role == MEASLAT_SERVER)) evt_core_rm_fd (ctx, fdinfo->fd); return 1; } struct timespec next_tick = {0}; struct measure_state ms_new = {0}; measure_state_init(&mctx->mp, &ms_new); ms_new.fd = fdinfo->fd; ms_new.mp_nin = ms->mp_nin; memcpy(ms_new.mp_in, ms->mp_in, mctx->mp.payload_size); measure_next_tick(&mctx->mp, &ms_new, &next_tick); assoc_timer = register_timer (ctx, mctx, &ms_new, &next_tick); } // 6. Check if our measurements are done if (ms->mp_in->counter >= mctx->mp.max_measure && ms->mp_out->counter >= mctx->mp.max_measure) { measlat_stop(ctx, mctx, ms, fdinfo->fd, assoc_timer->fd); return 1; } return 0; } int on_tcp_co(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { int conn_sock1, conn_sock2; struct sockaddr_in addr; socklen_t in_len; char url[1024], port[6]; struct evt_core_cat local_cat = {0}; struct evt_core_fdinfo to_fdinfo = {0}; to_fdinfo.cat = &local_cat; to_fdinfo.url = url; 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); to_fdinfo.fd = conn_sock1; to_fdinfo.cat->name = "tcp-read"; sprintf(to_fdinfo.url, "tcp:read:127.0.0.1:%s", port); evt_core_add_fd (ctx, &to_fdinfo); uint16_t myPort = ntohs(addr.sin_port); printf("client port is %u\n", myPort); return 0; co_error: perror("Failed to handle new connection"); exit(EXIT_FAILURE); } int on_timer(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { ssize_t s; uint64_t ticks = 0; struct measlat_ctx* mctx = fdinfo->cat->app_ctx; struct measure_state* ms = fdinfo->other; s = read(fdinfo->fd, &ticks, sizeof(uint64_t)); if (s == -1 && errno == EAGAIN) return 1; if (s != sizeof(uint64_t)) { perror("Read error"); exit(EXIT_FAILURE); } if (ticks != 1) { fprintf(stderr, "Has ticked %lu times, expected 1 time. This is a bug\n", ticks); } struct measure_packet* head = measure_generate(&mctx->mp, ms); //printf("send(id=%ld,is_echo=%d)\n", head->counter, head->is_echo); s = mctx->connectionless && mctx->role == MEASLAT_SERVER ? sendto(ms->fd, head, mctx->mp.payload_size, 0, (struct sockaddr*)&mctx->addr, mctx->addrlen) : send(ms->fd, head, mctx->mp.payload_size, 0); // Too bad, we will drop this packet if (s == -1 && errno == EAGAIN) return 1; if (s < 0 || s != mctx->mp.payload_size) { perror("Send error"); exit(EXIT_FAILURE); } if (ms->mp_in->counter >= mctx->mp.max_measure && ms->mp_out->counter >= mctx->mp.max_measure) { measlat_stop(ctx, mctx, ms, ms->fd, fdinfo->fd); return 1; } return 0; } 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"; sprintf(fdinfo_n.url, "tcp:read:%s:%d", s5ctx->addr, s5ctx->port); struct evt_core_fdinfo* reg_fdinfo = evt_core_add_fd (ctx, &fdinfo_n); printf("--- Tor socket registered\n"); struct measure_state ms = {0}; measure_state_init (&mctx->mp, &ms); ms.fd = fdinfo_n.fd; register_timer (ctx, mctx, &ms, NULL); return 1; } void spawn_tor_client(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", mctx->tor_port, mctx->host, atoi(mctx->port)); printf("--- Tor client SOCKS started\n"); } int on_socks5_failed_measlat(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) { evt_core_rm_fd (ctx, fdinfo->fd); spawn_tor_client(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, mctx->verbose <= 0 ? 0 : mctx->verbose - 1); template.cb = on_timer; template.name = "timer"; template.flags = EPOLLIN | EPOLLET; evt_core_add_cat(evts, &template); template.cb = on_receive_measure_packet; template.err_cb = on_receive_measure_packet_err; template.name = "tcp-read"; template.flags = EPOLLIN | EPOLLET; evt_core_add_cat(evts, &template); template.cb = on_receive_measure_packet; template.err_cb = on_receive_measure_packet_err; template.name = "udp-read"; template.flags = EPOLLIN | EPOLLET; evt_core_add_cat(evts, &template); template.cb = on_tcp_co; template.err_cb = NULL; template.name = "tcp-co"; 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; evt_core_add_cat(evts, &template); template.cb = on_socks5_failed_measlat; template.err_cb = on_socks5_failed_measlat; template.name = "socks5-failed"; template.flags = EPOLLET; evt_core_add_cat(evts, &template); socks5_init(evts); printf("--- Categories registered\n"); } void spawn_udp_client(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"; sprintf(fdinfo.url, "udp:read:%s:%s", mctx->host, mctx->port); struct evt_core_fdinfo* reg_fdinfo = evt_core_add_fd (evts, &fdinfo); printf("--- UDP client registered\n"); struct sockaddr_in my_addr; socklen_t my_addr_len = sizeof(my_addr); getsockname(udp_sock, (struct sockaddr *) &my_addr, &my_addr_len); uint16_t myPort = ntohs(my_addr.sin_port); printf("client port is %u\n", myPort); struct measure_state ms = {0}; measure_state_init (&mctx->mp, &ms); ms.fd = udp_sock; register_timer (evts, mctx, &ms, NULL); } void spawn_udp_server(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_server (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; sprintf(url, "udp:rw:127.0.0.1:%s", mctx->port); fdinfo.cat->name = "udp-read"; evt_core_add_fd(evts, &fdinfo); printf("--- UDP server is listening\n"); } void spawn_tcp_client(struct evt_core_ctx* evts) { struct evt_core_cat* ucat = evt_core_get_from_cat (evts, "tcp-read"); if (ucat == NULL) { fprintf(stderr, "Category tcp-read not found\n"); exit(EXIT_FAILURE); } struct measlat_ctx* mctx = ucat->app_ctx; int tcp_sock = create_tcp_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 = tcp_sock; fdinfo.cat->name = "tcp-read"; sprintf(fdinfo.url, "tcp:read:%s:%s", mctx->host, mctx->port); struct evt_core_fdinfo* reg_fdinfo = evt_core_add_fd (evts, &fdinfo); printf("--- TCP client registered\n"); struct sockaddr_in my_addr; socklen_t my_addr_len = sizeof(my_addr); getsockname(tcp_sock, (struct sockaddr *) &my_addr, &my_addr_len); uint16_t myPort = ntohs(my_addr.sin_port); printf("client port is %u\n", myPort); struct measure_state ms = {0}; measure_state_init (&mctx->mp, &ms); ms.fd = tcp_sock; register_timer (evts, mctx, &ms, NULL); } void spawn_tcp_server(struct evt_core_ctx* evts, uint16_t port) { char buffer[1024]; int tcp_serv_sock, err; sprintf(buffer, "%d", port); tcp_serv_sock = create_tcp_server ("0.0.0.0", buffer); err = listen(tcp_serv_sock, SOMAXCONN); struct evt_core_cat cat = {0}; struct evt_core_fdinfo fdinfo = {0}; fdinfo.cat = &cat; fdinfo.url = buffer; fdinfo.fd = tcp_serv_sock; sprintf(buffer, "tcp:co:127.0.0.1:%d", port); fdinfo.cat->name = "tcp-co"; evt_core_add_fd(evts, &fdinfo); printf("--- TCP server is listening\n"); } void measlat_create_onion_services(struct tor_os_str* tos, struct tor_ctl* tctl, uint16_t port, char* tor_port, enum TOR_ONION_FLAGS tof) { char fnpub[64] = {0}, fnpriv[64] = {0}; sprintf(fnpub, "os_%u.pub", port); sprintf(fnpriv, "os_%u.priv", port); tor_os_create (tos, fnpub, fnpriv, 1); tor_os_read (tos); int err = 0; err = tor_ctl_connect (tctl, "127.0.0.1", tor_port); if (err < 0) { fprintf(stderr, "Unable to open Tor Socket\n"); exit(EXIT_FAILURE); } err = tor_ctl_add_onion (tctl, tos, &port, 1, tof); if (err != 0) { fprintf(stderr, "Unable to create Onion Services (error: %d)\n", err); exit(EXIT_FAILURE); } printf("--- Onion services created\n"); } void spawn_onion_server(struct evt_core_ctx* evts, struct measlat_ctx* mctx, struct tor_os_str* tos, struct tor_ctl* tctl) { spawn_tcp_server(evts, atoi(mctx->port)); measlat_create_onion_services (tos, tctl, atoi(mctx->port), mctx->tor_port, mctx->tor_flags); } int main(int argc, char** argv) { setvbuf(stdout, NULL, _IONBF, 0); printf("~ measlat ~\n"); int opt; struct measlat_ctx mctx = {0}; mctx.mp.tag = "undefined"; struct evt_core_ctx evts = {0}; struct tor_os_str tos = {0}; struct tor_ctl tctl = {0}; tctl.os_endpoint = "127.0.0.1"; measure_params_init (&mctx.mp); // 1. Parse parameters while ((opt = getopt(argc, argv, "vq:h:p:c:s:i:t:lnm:b")) != -1) { switch(opt) { case 'b': mctx.mp.probe = 1; case 'v': mctx.verbose++; break; case 'h': // host mctx.host = optarg; break; case 'p': // port mctx.port = optarg; break; case 'q': mctx.tor_port = optarg; break; case 'l': mctx.role = MEASLAT_SERVER; break; case 't': // transport mctx.transport = optarg; break; case 'c': // count mctx.mp.max_measure = atoi(optarg); break; case 's': // size - payload in bytes measure_params_setpl(&mctx.mp, atoi(optarg)); break; case 'n': mctx.tor_flags |= TOR_ONION_FLAG_NON_ANONYMOUS; break; case 'i': // interval - every ms mctx.mp.interval = atoi(optarg); break; case 'm': mctx.mp.tag = optarg; break; default: goto usage; } } // 2. Check and fix parameters measure_state_init (&mctx.mp, &ms_transi); mctx.addrlen = sizeof(mctx.addr); if (mctx.mp.probe) mctx.mp.max_measure = 1; if (mctx.transport == NULL) mctx.transport = "udp"; if (strcmp(mctx.transport, "udp") == 0) mctx.connectionless = 1; if (mctx.host == NULL) mctx.host = "127.0.0.1"; if (mctx.port == NULL) mctx.port = mctx.connectionless ? "9000" : "7500"; if (mctx.tor_port == NULL) mctx.tor_port = "9050"; printf("[measlat_conf] host=%s, port=%s, listen=%d, transport=%s, count=%ld, size=%ld, interval=%ld, tor_port=%s\n", mctx.host, mctx.port, mctx.role, mctx.transport, mctx.mp.max_measure, mctx.mp.payload_size, mctx.mp.interval, mctx.tor_port); // 3. Create event structure register_categories(&evts, &mctx); // 4. Register services if (mctx.role == MEASLAT_SERVER) { if (streq(mctx.transport, "udp")) spawn_udp_server (&evts); else if (streq(mctx.transport, "tcp")) spawn_tcp_server(&evts, atoi(mctx.port)); else if (streq(mctx.transport, "tor")) spawn_onion_server (&evts, &mctx, &tos, &tctl); } else if (mctx.role == MEASLAT_CLIENT) { if (streq(mctx.transport, "udp")) spawn_udp_client(&evts); else if (streq(mctx.transport, "tor")) spawn_tor_client(&evts); else if (streq(mctx.transport, "tcp")) spawn_tcp_client(&evts); } else exit(EXIT_FAILURE); // 5. Run main loop evt_core_loop(&evts); return 0; usage: fprintf(stderr, "Usage: %s [-h ] [-p ] [-l] [-r] [-t ] [-c ] [-i ] [-s ]\n", argv[0]); exit(EXIT_FAILURE); }