346 lines
9.6 KiB
C
346 lines
9.6 KiB
C
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <sys/timerfd.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include "evt_core.h"
|
|
#include "net_tools.h"
|
|
#include "socks5.h"
|
|
#include "utils.h"
|
|
|
|
struct measlat_ctx {
|
|
int count, size, interval, verbose;
|
|
char *host, *port, *transport;
|
|
};
|
|
|
|
struct measure_conf {
|
|
uint64_t max_measure;
|
|
uint64_t payload_size;
|
|
char* payload;
|
|
uint64_t counter;
|
|
};
|
|
|
|
struct packet_header {
|
|
uint64_t counter;
|
|
struct timespec emit_time;
|
|
};
|
|
|
|
struct measure_conf* create_measure_conf(int max_mes, int plsize) {
|
|
struct measure_conf* mc = malloc(sizeof(struct measure_conf));
|
|
if (mc == NULL) {
|
|
perror("Malloc failed");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
mc->counter = 0;
|
|
mc->max_measure = max_mes;
|
|
mc->payload_size = plsize;
|
|
mc->payload = malloc(mc->payload_size);
|
|
if (mc->payload == NULL) {
|
|
perror("malloc failed");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
return mc;
|
|
}
|
|
|
|
void free_mesure_conf(void* v) {
|
|
struct measure_conf* mc = (struct measure_conf*)v;
|
|
free(mc->payload);
|
|
free(mc);
|
|
}
|
|
|
|
int on_udp_err(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) {
|
|
return 1;
|
|
}
|
|
|
|
int on_udp(struct evt_core_ctx* ctx, struct evt_core_fdinfo* fdinfo) {
|
|
ssize_t res;
|
|
uint64_t micro_sec;
|
|
struct timespec curr;
|
|
struct measure_conf* mc = fdinfo->other;
|
|
res = read(fdinfo->fd, mc->payload, mc->payload_size);
|
|
if (res == -1 && errno == EAGAIN) return 1;
|
|
if (res != mc->payload_size) {
|
|
perror("read error");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
struct packet_header* head = (struct packet_header*) mc->payload;
|
|
if (clock_gettime(CLOCK_MONOTONIC, &curr) == -1){
|
|
perror("clock_gettime error");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
time_t now;
|
|
time(&now);
|
|
char* ctime_no_newline = strtok(ctime(&now), "\n");
|
|
|
|
micro_sec = elapsed_micros (&head->emit_time, &curr);
|
|
printf("[%s] Packet %llu latency %luµs\n", ctime_no_newline, (unsigned long long)head->counter, micro_sec);
|
|
|
|
if (head->counter >= mc->max_measure) {
|
|
printf("Measurement done\n");
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
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 = 0;
|
|
struct measure_conf* mc = 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);
|
|
}
|
|
mc->counter++;
|
|
|
|
memset(mc->payload, 0, mc->payload_size);
|
|
struct packet_header* head = (struct packet_header*)mc->payload;
|
|
head->counter = mc->counter;
|
|
if (clock_gettime(CLOCK_MONOTONIC, &head->emit_time) == -1) {
|
|
perror("clock_gettime error");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
char *my_msg = "Tu n'es pas tout a fait la misere,\nCar les levres les plus pauvres te denoncent\nPar un sourire.";
|
|
size_t msg_len = strlen(my_msg);
|
|
size_t cursor_msg = 0;
|
|
for (size_t i = sizeof(struct packet_header); i < mc->payload_size; i++) {
|
|
mc->payload[i] = my_msg[cursor_msg];
|
|
cursor_msg = (cursor_msg + 1) % msg_len;
|
|
}
|
|
|
|
struct evt_core_fdinfo* tgtinfo = evt_core_get_first_from_cat (ctx, "udp-read");
|
|
if (tgtinfo == NULL) tgtinfo = evt_core_get_first_from_cat (ctx, "tcp-read");
|
|
if (tgtinfo == NULL) {
|
|
printf("No connection yet\n");
|
|
return 1;
|
|
}
|
|
s = send(tgtinfo->fd, mc->payload, mc->payload_size, 0);
|
|
if (s < 0) {
|
|
perror("Send error");
|
|
//exit(EXIT_FAILURE);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
void register_timer(struct evt_core_ctx* evts, int udp, int interval, int count, int size) {
|
|
struct timespec now;
|
|
struct itimerspec timer_config;
|
|
char url[1024];
|
|
struct evt_core_cat cat = {0};
|
|
struct evt_core_fdinfo fdinfo = {0};
|
|
fdinfo.cat = &cat;
|
|
fdinfo.url = url;
|
|
|
|
if (clock_gettime(CLOCK_REALTIME, &now) == -1) {
|
|
perror("clock_gettime");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
uint64_t micro_sec = interval;
|
|
timer_config.it_value.tv_sec = now.tv_sec + 1;
|
|
timer_config.it_value.tv_nsec = now.tv_nsec;
|
|
timer_config.it_interval.tv_sec = micro_sec / 1000;
|
|
timer_config.it_interval.tv_nsec = micro_sec % 1000 * 1000000;
|
|
|
|
fdinfo.fd = timerfd_create(CLOCK_REALTIME, 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";
|
|
fdinfo.other = create_measure_conf(count, size);
|
|
fdinfo.free_other = free_timer_conf;
|
|
sprintf(fdinfo.url, "timer:%d:%d", interval, count);
|
|
evt_core_add_fd (evts, &fdinfo);
|
|
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);
|
|
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, mctx->verbose);
|
|
|
|
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;
|
|
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_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;
|
|
struct measlat_ctx mctx = {0};
|
|
struct evt_core_ctx evts = {0};
|
|
|
|
// 1. Parse parameters
|
|
while ((opt = getopt(argc, argv, "vh:p:c:s:i:t:")) != -1) {
|
|
switch(opt) {
|
|
case 'v':
|
|
mctx.verbose++;
|
|
break;
|
|
case 'h': // host
|
|
mctx.host = optarg;
|
|
break;
|
|
case 'p': // port
|
|
mctx.port = optarg;
|
|
break;
|
|
case 't': // transport
|
|
mctx.transport = optarg;
|
|
break;
|
|
case 'c': // count
|
|
mctx.count = atoi(optarg);
|
|
break;
|
|
case 's': // size - payload in bytes
|
|
mctx.size = atoi(optarg);
|
|
break;
|
|
case 'i': // interval - every ms
|
|
mctx.interval = atoi(optarg);
|
|
break;
|
|
default:
|
|
goto usage;
|
|
}
|
|
}
|
|
|
|
// 2. Check and fix parameters
|
|
size_t header_size = sizeof(struct packet_header);
|
|
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, &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);
|
|
|
|
return 0;
|
|
usage:
|
|
fprintf(stderr, "Usage: %s -h <host> -p <port> [-t <udp|tor>] [-c <count>] [-i <ms>] [-s <bytes>]\n", argv[0]);
|
|
exit(EXIT_FAILURE);
|
|
}
|