summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client.c165
-rw-r--r--src/server.c10
2 files changed, 101 insertions, 74 deletions
diff --git a/src/client.c b/src/client.c
index 00f7a89..10ba78a 100644
--- a/src/client.c
+++ b/src/client.c
@@ -27,7 +27,6 @@ struct o_c_rsock {
struct o_c_sock *o_socks_by_lport;
struct c_data *c_data;
ev_io io_w;
- ev_timer tm_w;
UT_hash_handle hh;
int fd;
socklen_t r_addrlen;
@@ -43,8 +42,8 @@ struct o_c_sock {
UT_hash_handle hh_ca;
uint16_t seq_num;
in_port_t l_port;
- in_port_t r_port;
uint8_t status;
+ int8_t syn_retries;
};
#define PORTS_IN_INT sizeof(int) * CHAR_BIT
@@ -58,10 +57,13 @@ struct c_data {
socklen_t s_addrlen;
unsigned int used_ports[32768 / PORTS_IN_INT];
int s_sock;
+ int i_sock;
};
static struct c_data *global_c_data;
+static const uint8_t tcp_syn_retry_timeouts[] = { 3, 6, 12, 24, 0 };
+
/* reserve a local TCP port (local addr, remote addr, remote port are usually
* fixed in the tuple) */
static uint16_t reserve_port(unsigned int *used_ports) {
@@ -102,42 +104,68 @@ static void free_port(unsigned int *used_ports, uint16_t port_num) {
used_ports[port_num / PORTS_IN_INT] ^= 1 << (port_num % PORTS_IN_INT);
}
-static void c_tm_cb(EV_P_ ev_timer *w, int revents __attribute__((unused))) {
- struct o_c_sock *sock = w->data;
- DBG("timing out socket @ %p", sock);
- struct tcphdr buf = {
- .th_sport = sock->l_port,
- .th_dport = sock->r_port,
- .th_seq = htonl(sock->seq_num),
- .th_off = 5,
- .th_flags = TH_FIN
- };
- ssize_t sz = send(sock->rsock->c_data->s_sock, &buf, sizeof(buf), 0);
- if (sz < 0) {
- perror("send");
- ev_break(EV_A_ EVBREAK_ONE);
- return;
- } else if ((size_t)sz != sizeof(buf)) {
- fprintf(stderr, "send %s our packet: tried %lu, sent %zd\n", (size_t)sz > sizeof(buf) ? "expanded" : "truncated", sizeof(buf), sz);
+static void c_sock_cleanup(EV_P_ struct o_c_sock *sock, int stopping) {
+ if (sock->status != TCP_SYN_SENT) {
+ struct tcphdr buf = {
+ .th_sport = sock->l_port,
+ .th_dport = ((struct sockaddr_in *)sock->rsock->r_addr)->sin_port,
+ .th_seq = htonl(sock->seq_num),
+ .th_off = 5,
+ .th_flags = sock->status == TCP_ESTABLISHED ? TH_FIN : TH_RST
+ };
+
+ ssize_t sz = send(sock->rsock->c_data->s_sock, &buf, sizeof(buf), 0);
+ if (sz < 0) {
+ perror("send");
+ ev_break(EV_A_ EVBREAK_ONE);
+ return;
+ } else if ((size_t)sz != sizeof(buf)) {
+ fprintf(stderr, "send %s our packet: tried %lu, sent %zd\n", (size_t)sz > sizeof(buf) ? "expanded" : "truncated", sizeof(buf), sz);
+ }
}
- free_port(sock->rsock->c_data->used_ports, sock->l_port);
- ev_timer_stop(EV_A_ w);
- HASH_DELETE(hh_lp, sock->rsock->o_socks_by_lport, sock);
+ if (!stopping) {
+ free_port(sock->rsock->c_data->used_ports, sock->l_port);
+ ev_timer_stop(EV_A_ &sock->tm_w);
+
+ HASH_DELETE(hh_lp, sock->rsock->o_socks_by_lport, sock);
+
+ if (!sock->rsock->o_socks_by_lport) {
+ close(sock->rsock->fd);
+
+ ev_io_stop(EV_A_ &sock->rsock->io_w);
+
+ HASH_DEL(sock->rsock->c_data->o_rsocks, sock->rsock);
- if (!sock->rsock->o_socks_by_lport) {
- close(sock->rsock->fd);
+ free(sock->rsock->r_addr);
+ free(sock->rsock);
+ }
- ev_io_stop(EV_A_ &sock->rsock->io_w);
- ev_timer_stop(EV_A_ &sock->rsock->tm_w);
+ free(sock);
+ }
+}
- HASH_DEL(sock->rsock->c_data->o_rsocks, sock->rsock);
+static void c_tm_cb(EV_P_ ev_timer *w, int revents __attribute__((unused))) {
+ DBG("timing out socket %p", w->data);
+ c_sock_cleanup(EV_A_ w->data, 0);
+}
- free(sock->rsock->r_addr);
- free(sock->rsock);
+static int c_adv_syn_tm(EV_P_ struct o_c_sock *sock) {
+ uint8_t next_retr = tcp_syn_retry_timeouts[sock->syn_retries++];
+ if (next_retr) {
+ ev_timer_set(&sock->tm_w, next_retr, 0.);
+ ev_timer_start(EV_A_ &sock->tm_w);
}
+ return !!next_retr;
+}
- free(sock);
+static void c_syn_tm_cb(EV_P_ ev_timer *w, int revents __attribute__((unused))) {
+ if (c_adv_syn_tm(EV_A_ w->data)) {
+ // resend SYN
+ } else {
+ DBG("connection timed out");
+ c_sock_cleanup(EV_A_ w->data, 0);
+ }
}
static void cc_cb(struct ev_loop *loop __attribute__((unused)), ev_io *w, int revents __attribute__((unused))) {
@@ -180,7 +208,7 @@ static void cc_cb(struct ev_loop *loop __attribute__((unused)), ev_io *w, int re
struct tcphdr shdr = {
.th_sport = sock->l_port,
- .th_dport = sock->r_port,
+ .th_dport = ((struct sockaddr_in *)sock->rsock->r_addr)->sin_port,
.th_seq = htonl(sock->seq_num),
.th_ack = rhdr->th_seq,
.th_win = 65535,
@@ -214,6 +242,14 @@ static void cc_cb(struct ev_loop *loop __attribute__((unused)), ev_io *w, int re
}
free(sock->pending_data);
+
+ ev_timer_stop(EV_A_ &sock->tm_w);
+ // 10 minutes. this is not very important because UDP packets will not
+ // be lost (any more), only delayed until a new connection is established.
+ // however, it is probably a good idea to set this higher than the UDP
+ // ping delay if you are using one.
+ ev_timer_init(&sock->tm_w, c_tm_cb, 10. * 60., 10. * 60.);
+ ev_timer_start(EV_A_ &sock->tm_w);
}
should_ssz = rsz - ntohs(rhdr->th_off) * 32 / CHAR_BIT;
@@ -278,53 +314,47 @@ static void cs_cb(EV_P_ ev_io *w, int revents __attribute__((unused))) {
sock->c_address = malloc(addresslen);
memcpy(sock->c_address, &c_data->pkt_addr, addresslen);
- struct o_c_rsock *rsock;
-
- HASH_FIND(hh, c_data->o_rsocks, res->ai_addr, res->ai_addrlen, rsock);
+ HASH_FIND(hh, c_data->o_rsocks, res->ai_addr, res->ai_addrlen, sock->rsock);
- if (!rsock) {
+ if (!sock->rsock) {
DBG("could not locate remote socket to host, initializing new raw socket");
- rsock = malloc(sizeof(*rsock));
- rsock->r_addr = malloc(res->ai_addrlen);
+ sock->rsock = malloc(sizeof(*sock->rsock));
+ sock->rsock->r_addr = malloc(res->ai_addrlen);
- memcpy(rsock->r_addr, res->ai_addr, res->ai_addrlen);
- rsock->r_addrlen = res->ai_addrlen;
+ memcpy(sock->rsock->r_addr, res->ai_addr, res->ai_addrlen);
+ sock->rsock->r_addrlen = res->ai_addrlen;
freeaddrinfo(res);
- rsock->o_socks_by_lport = NULL;
- rsock->c_data = c_data;
+ sock->rsock->o_socks_by_lport = NULL;
+ sock->rsock->c_data = c_data;
- rsock->fd = socket(rsock->r_addr->sa_family, SOCK_RAW, IPPROTO_TCP);
- if (!rsock->fd) {
+ sock->rsock->fd = socket(sock->rsock->r_addr->sa_family, SOCK_RAW, IPPROTO_TCP);
+ if (!sock->rsock->fd) {
perror("socket");
ev_break(EV_A_ EVBREAK_ONE);
return;
}
- if (connect(rsock->fd, rsock->r_addr, rsock->r_addrlen) == -1) {
+ if (connect(sock->rsock->fd, sock->rsock->r_addr, sock->rsock->r_addrlen) == -1) {
perror("connect");
ev_break(EV_A_ EVBREAK_ONE);
return;
}
- ev_io_init(&rsock->io_w, cc_cb, rsock->fd, EV_READ);
- rsock->io_w.data = rsock;
- ev_io_start(EV_A_ &rsock->io_w);
+ ev_io_init(&sock->rsock->io_w, cc_cb, sock->rsock->fd, EV_READ);
+ sock->rsock->io_w.data = sock->rsock;
+ ev_io_start(EV_A_ &sock->rsock->io_w);
- HASH_ADD_KEYPTR(hh, c_data->o_rsocks, rsock->r_addr, rsock->r_addrlen, rsock);
+ HASH_ADD_KEYPTR(hh, c_data->o_rsocks, sock->rsock->r_addr, sock->rsock->r_addrlen, sock->rsock);
}
- sock->r_port = ((struct sockaddr_in *)rsock->r_addr)->sin_port;
-
HASH_ADD_KEYPTR(hh_ca, c_data->o_socks_by_caddr, sock->c_address, addresslen, sock);
- HASH_ADD(hh_lp, rsock->o_socks_by_lport, l_port, sizeof(in_port_t), sock);
-
- sock->rsock = rsock;
+ HASH_ADD(hh_lp, sock->rsock->o_socks_by_lport, l_port, sizeof(in_port_t), sock);
sock->seq_num = random();
struct tcphdr buf = {
.th_sport = sock->l_port,
- .th_dport = sock->r_port,
+ .th_dport = ((struct sockaddr_in *)sock->rsock->r_addr)->sin_port,
.th_seq = htonl(sock->seq_num++),
.th_flags = TH_SYN,
.th_off = 5
@@ -335,7 +365,7 @@ static void cs_cb(EV_P_ ev_io *w, int revents __attribute__((unused))) {
sock->pending_data_size = sz;
DBG("sending SYN to remote");
- sz = send(rsock->fd, &buf, sizeof(buf), 0);
+ sz = send(sock->rsock->fd, &buf, sizeof(buf), 0);
if (sz < 0) {
perror("send");
ev_break(EV_A_ EVBREAK_ONE);
@@ -346,9 +376,10 @@ static void cs_cb(EV_P_ ev_io *w, int revents __attribute__((unused))) {
// resend SYN
- //ev_timer_init(&sock->tm_w, c_tm_cb, 0., 60. * 60. * 3.);
- //sock->tm_w.data = sock;
- //ev_timer_start(EV_A_ &sock->tm_w);
+ ev_timer_init(&sock->tm_w, c_syn_tm_cb, 0., tcp_syn_retry_timeouts[0]);
+ sock->tm_w.data = sock;
+ sock->syn_retries = 0;
+ c_adv_syn_tm(EV_A_ sock);
sock->status = TCP_SYN_SENT;
@@ -357,7 +388,7 @@ static void cs_cb(EV_P_ ev_io *w, int revents __attribute__((unused))) {
struct tcphdr tcp_hdr = {
.th_sport = sock->l_port,
- .th_dport = sock->r_port,
+ .th_dport = ((struct sockaddr_in *)sock->rsock->r_addr)->sin_port,
.th_seq = htonl(sock->seq_num),
.th_off = 5,
.th_win = 65535,
@@ -399,14 +430,10 @@ static void c_cleanup() {
struct o_c_sock *sock;
for (sock = global_c_data->o_socks_by_caddr; sock != NULL; sock = sock->hh_ca.next) {
switch (sock->status) {
- case TCP_ESTABLISHED:
- // send TCP FIN
- break;
case TCP_CLOSE:
break;
default:
- ;
- // send TCP RST
+ c_sock_cleanup(EV_DEFAULT, sock, 1);
}
// don't bother freeing anything because we're about to exit anyways
}
@@ -422,7 +449,11 @@ int start_client(const char *s_host, const char *s_port, const char *r_host, con
return 3;
}
- struct c_data c_data = { 0 };
+ struct c_data c_data = {
+ .s_addrlen = res->ai_addrlen,
+ .r_host = r_host,
+ .r_port = r_port
+ };
c_data.s_sock = socket(res->ai_family, SOCK_DGRAM, 0);
if (c_data.s_sock == -1) {
@@ -435,10 +466,6 @@ int start_client(const char *s_host, const char *s_port, const char *r_host, con
return 2;
}
- c_data.s_addrlen = res->ai_addrlen;
- c_data.r_host = r_host;
- c_data.r_port = r_port;
-
freeaddrinfo(res);
global_c_data = &c_data;
diff --git a/src/server.c b/src/server.c
index d5332bc..f6714a2 100644
--- a/src/server.c
+++ b/src/server.c
@@ -43,7 +43,7 @@ static inline void s_prep_c_addr(struct o_s_sock *sock, struct tcphdr *hdr) {
hdr->th_off = 5;
}
-static void s_s_cleanup(EV_P_ struct o_s_sock *sock) {
+static void s_sock_cleanup(EV_P_ struct o_s_sock *sock) {
DBG("cleaning up socket %p", sock);
if (sock->status == TCP_ESTABLISHED) {
@@ -75,7 +75,7 @@ static void s_s_cleanup(EV_P_ struct o_s_sock *sock) {
static void s_tm_cb(EV_P_ ev_timer *w, int revents __attribute__((unused))) {
DBG("timing out socket %p", w->data);
- s_s_cleanup(EV_A_ w->data);
+ s_sock_cleanup(EV_A_ w->data);
}
static void sc_cb(EV_P_ ev_io *w, int revents __attribute__((unused))) {
@@ -146,7 +146,7 @@ static void ss_cb(EV_P_ ev_io *w, int revents __attribute__((unused))) {
#ifdef DEBUG
char hbuf[NI_MAXHOST];
- r = getnameinfo(&s_data->pkt_addr, c_addrlen, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST);
+ r = getnameinfo((struct sockaddr *)&s_data->pkt_addr, c_addrlen, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST);
if (r) {
fprintf(stderr, "getnameinfo: %s\n", gai_strerror(r));
ev_break(EV_A_ EVBREAK_ONE);
@@ -203,7 +203,7 @@ static void ss_cb(EV_P_ ev_io *w, int revents __attribute__((unused))) {
((struct sockaddr_in *)&s_data->pkt_addr)->sin_port = htons(0);
DBG("sending SYN/ACK");
- if ((sz = sendto(w->fd, &buf, sizeof(buf), 0, &s_data->pkt_addr, s_data->s_addrlen)) == -1) {
+ if ((sz = sendto(w->fd, &buf, sizeof(buf), 0, (struct sockaddr *)&s_data->pkt_addr, s_data->s_addrlen)) == -1) {
perror("sendto");
ev_break(EV_A_ EVBREAK_ONE);
return;
@@ -230,7 +230,7 @@ static void ss_cb(EV_P_ ev_io *w, int revents __attribute__((unused))) {
if (th_flags == TH_RST) {
DBG("RST received, cleaning up socket");
sock->status = TCP_CLOSE;
- s_s_cleanup(EV_A_ sock);
+ s_sock_cleanup(EV_A_ sock);
}
if (th_flags & ~(TH_PUSH | TH_ACK)) {