From 98da57ba74cbea0cb8f48724d7852462eb7d7ed7 Mon Sep 17 00:00:00 2001 From: frekky Date: Sat, 29 Aug 2015 20:08:46 +0800 Subject: [PATCH] More unsignedness and working DNS tunnelling --- src/server.c | 618 ++++++++++++++++++--------------------------------- src/server.h | 11 +- 2 files changed, 226 insertions(+), 403 deletions(-) diff --git a/src/server.c b/src/server.c index 55571e2..015b5cb 100644 --- a/src/server.c +++ b/src/server.c @@ -181,7 +181,8 @@ save_to_qmem_pingordata(int userid, struct query *q) + 1 char CMC; that last char is non-Base32. */ - char cmc[8]; + warnx("save_to_qmem_pingordata deprecated! use something else instead!"); + uint8_t cmc[8]; int i; if (q->name[0] == 'P' || q->name[0] == 'p') { @@ -195,15 +196,15 @@ save_to_qmem_pingordata(int userid, struct query *q) /* We already unpacked in handle_null_request(), but that's lost now... Note: b32 directly, we want no undotify here! */ - i = b32->decode(cmc, &cmcsize, q->name + 1, (cp - q->name) - 1); + i = b32->decode(cmc, &cmcsize, (uint8_t *)q->name + 1, (cp - q->name) - 1); if (i < 4) return; /* illegal ping; shouldn't happen */ - save_to_qmem(users[userid].qmemping_cmc, + /*save_to_qmem(users[userid].qmemping_cmc, users[userid].qmemping_type, QMEMPING_LEN, &users[userid].qmemping_lastfilled, - (void *) cmc, q->type); + (void *) cmc, q->type);*/ } else { /* Data packet, hopefully not illegal */ if (strlen(q->name) < 5) @@ -220,10 +221,10 @@ save_to_qmem_pingordata(int userid, struct query *q) else cmc[i] = q->name[i+1]; - save_to_qmem(users[userid].qmemdata_cmc, + /*save_to_qmem(users[userid].qmemdata_cmc, users[userid].qmemdata_type, QMEMDATA_LEN, &users[userid].qmemdata_lastfilled, - (void *) cmc, q->type); + (void *) cmc, q->type);*/ } } @@ -231,7 +232,8 @@ static inline int answer_from_qmem_data(int dns_fd, int userid, struct query *q) /* Quick helper function to keep handle_null_request() clean */ { - char cmc[4]; + warnx("answer_from_qmem_data deprecated! use something else"); + /*char cmc[4]; int i; for (i = 0; i < 4; i++) @@ -242,7 +244,8 @@ answer_from_qmem_data(int dns_fd, int userid, struct query *q) return answer_from_qmem(dns_fd, q, users[userid].qmemdata_cmc, users[userid].qmemdata_type, QMEMDATA_LEN, - (void *) cmc); + (void *) cmc);*/ + return 0; } /* END INLINE FUNCTION DEFINITIONS */ @@ -260,7 +263,7 @@ answer_from_qmem_data(int dns_fd, int userid, struct query *q) are prevented. Data-CMC is only 36 counts, so our cache length should not exceed 36/2=18 packets. (This quick rule assumes all packets are otherwise equal, which they arent: up/downstream seq, TCP/IP headers and - the actual data) TODO: adjust dns cache length + the actual data */ static void @@ -370,40 +373,8 @@ forward_query(int bind_fd, struct query *q) } } -void -send_ping_response(int dns_fd, int userid, struct query *q) -{ - static uint8_t pkt[10]; - size_t datalen = 5; - /* Build downstream data + ping header (see doc/proto_xxxxxxxx.txt) for details */ - pkt[0] = users[userid].outgoing->start_seq_id & 0xFF; - pkt[1] = users[userid].incoming->start_seq_id & 0xFF; - pkt[2] = (1 << 5); /* ping flag */ - pkt[3] = users[userid].outgoing->windowsize & 0xFF; - pkt[4] = users[userid].incoming->windowsize & 0xFF; - - write_dns(dns_fd, q, (char *)pkt, datalen, users[userid].downenc); - - if (q->id2 != 0) { /* rotate pending duplicate queries */ - q->id = q->id2; - q->fromlen = q->fromlen2; - memcpy(&(q->from), &(q->from2), q->fromlen2); - if (debug >= 1) - warnx("OUT again to last duplicate"); - write_dns(dns_fd, q, (char *)pkt, datalen, users[userid].downenc); - } - - save_to_qmem_pingordata(userid, q); - -#ifdef DNSCACHE_LEN - save_to_dnscache(userid, q, (char *)pkt, datalen + 2); -#endif - - q->id = 0; /* this query is used */ -} - static int -send_frag_or_dataless(int dns_fd, int userid, struct query *q) +send_frag_or_dataless(int dns_fd, int userid, struct query *q, int ping) /* Sends current fragment to user, or a ping if no data available. Does not update anything, except: - discards q always (query is used) @@ -413,55 +384,57 @@ send_frag_or_dataless(int dns_fd, int userid, struct query *q) 0 = don't call us again for now. */ { - static uint8_t pkt[MAX_FRAGSIZE + DOWNSTREAM_HDR]; - int ping = 0; - size_t datalen; + static uint8_t pkt[MAX_FRAGSIZE + DOWNSTREAM_PING_HDR]; + size_t datalen, headerlen; fragment *f; - struct frag_buffer *out; + struct frag_buffer *out, *in; + in = users[userid].incoming; out = users[userid].outgoing; - f = window_get_next_sending_fragment(out, users[userid].next_upstream_ack); - if (!f && user_sending(userid)) { - /* Need to tell client to sync stuff - send data header with ping stuff */ + f = window_get_next_sending_fragment(out, &users[userid].next_upstream_ack); + if (!f) { + /* No data, may as well send data/ping header (with extra info) */ ping = 1; - } /* TODO: If re-sent too many times, drop stuff */ - - /* Build downstream data header (see doc/proto_xxxxxxxx.txt) for details */ - pkt[0] = f->seqID & 0xFF; - pkt[1] = f->ack_other & 0xFF; - pkt[2] = ((ping & 1) << 5) | ((f->compressed & 1) << 4) | ((f->ack_other < 0 ? 0 : 1) << 3) - | (f->is_nack << 2) | (f->start << 1) | f->end; - - if (ping) { - pkt[3] = out->windowsize & 0xFF; - pkt[4] = users[userid].incoming->windowsize & 0xFF; - datalen = 5; + datalen = 0; + pkt[0] = 0; /* Pings don't need seq IDs unless they have data */ + pkt[1] = users[userid].next_upstream_ack & 0xFF; + pkt[2] = (1 << 5) | ((users[userid].next_upstream_ack < 0 ? 0 : 1) << 3); + /* TODO: resend ACKs in pings? */ + users[userid].next_upstream_ack = -1; } else { - datalen = DOWNSTREAM_HDR + f->len; - if (datalen > sizeof(pkt)) { - warnx("send_frag_or_dataless: fragment too large to send!"); - return 0; - } - memcpy(&pkt, f->data, f->len); - } - write_dns(dns_fd, q, (char *)pkt, datalen, users[userid].downenc); - - if (q->id2 != 0) { /* reply to any duplicates */ - q->id = q->id2; - q->fromlen = q->fromlen2; - memcpy(&(q->from), &(q->from2), q->fromlen2); - if (debug >= 1) - warnx("OUT again to last duplicate"); - write_dns(dns_fd, q, (char *)pkt, datalen, users[userid].downenc); + datalen = f->len; + pkt[0] = f->seqID & 0xFF; + pkt[1] = f->ack_other & 0xFF; + pkt[2] = ((f->compressed & 1) << 4) | ((f->ack_other < 0 ? 0 : 1) << 3) | + (f->is_nack << 2) | (f->start << 1) | f->end; + headerlen = DOWNSTREAM_HDR; } - save_to_qmem_pingordata(userid, q); + /* Build downstream data/ping header (see doc/proto_xxxxxxxx.txt) for details */ + + if (ping) { /* TODO: pings with downstream data */ + pkt[3] = out->windowsize & 0xFF; + pkt[4] = in->windowsize & 0xFF; + pkt[5] = out->start_seq_id & 0xFF; + pkt[6] = in->start_seq_id & 0xFF; + headerlen = DOWNSTREAM_PING_HDR; + } + if (datalen + headerlen > sizeof(pkt)) { + warnx("send_frag_or_dataless: fragment too large to send! (%lu)", datalen); + return 0; + } + if (f) memcpy(pkt + headerlen, f->data, datalen); + write_dns(dns_fd, q, (char *)pkt, datalen + headerlen, users[userid].downenc); + + /* TODO: reply to any duplicates (q.id2 etc) */ + +// save_to_qmem_pingordata(userid, q); #ifdef DNSCACHE_LEN save_to_dnscache(userid, q, (char *)pkt, datalen + 2); #endif - /* this query has been */ + /* this query has been used */ q->id = 0; window_tick(out); @@ -569,14 +542,8 @@ tunnel_tun(int tun_fd, struct dnsfd *dns_fds) window_add_outgoing_data(users[userid].outgoing, out, outlen, 1); - /* Start sending immediately if query is waiting */ - if (users[userid].q_sendrealsoon.id != 0) { - int dns_fd = get_dns_fd(dns_fds, &users[userid].q_sendrealsoon.from); - send_frag_or_dataless(dns_fd, userid, &users[userid].q_sendrealsoon); - } else if (users[userid].q.id != 0) { - int dns_fd = get_dns_fd(dns_fds, &users[userid].q.from); - send_frag_or_dataless(dns_fd, userid, &users[userid].q); - } + /* TODO: Start sending immediately if query is waiting + * Need to get incoming query handling done first. */ return outlen; } else { /* CONN_RAW_UDP */ @@ -598,8 +565,8 @@ tunnel_dns(int tun_fd, int dns_fd, struct dnsfd *dns_fds, int bind_fd) return 0; if (debug >= 2) { - fprintf(stderr, "RX: client %s, type %d, name %s\n", - format_addr(&q.from, q.fromlen), q.type, q.name); + fprintf(stderr, "RX: client %s ID %5d, type %d, name %s\n", + format_addr(&q.from, q.fromlen), q.id, q.type, q.name); } domain_len = strlen(q.name) - strlen(topdomain); @@ -653,7 +620,7 @@ tunnel_dns(int tun_fd, int dns_fd, struct dnsfd *dns_fds, int bind_fd) } else { /* Forward query to other port ? */ if (debug >= 3) { - fprintf(stderr, "Requested domain outside our topdomain."); + fprintf(stderr, "Requested domain outside our topdomain.\n"); } if (bind_fd) { forward_query(bind_fd, &q); @@ -673,26 +640,9 @@ server_tunnel(int tun_fd, struct dnsfd *dns_fds, int bind_fd, int max_idle_time) while (running) { int maxfd; - tv.tv_sec = 10; /* doesn't really matter */ + tv.tv_sec = 5; /* TODO: adjust time based on query timeouts (lazy mode) */ tv.tv_usec = 0; - /* Adjust timeout if there is anything to send realsoon. - Clients won't be sending new data until we send our ack, TODO: adjust stuff in this function - so don't keep them waiting long. This only triggers at - final upstream fragments, which is about once per eight - requests during heavy upstream traffic. - 20msec: ~8 packets every 1/50sec = ~400 DNSreq/sec, - or ~1200bytes every 1/50sec = ~0.5 Mbit/sec upstream */ - for (userid = 0; userid < created_users; userid++) { - if (user_active(userid)) { - users[userid].q_sendrealsoon_new = 0; - if (users[userid].q_sendrealsoon.id != 0) { - tv.tv_sec = 0; - tv.tv_usec = 20000; - } - } - } - FD_ZERO(&fds); maxfd = 0; @@ -754,14 +704,6 @@ server_tunnel(int tun_fd, struct dnsfd *dns_fds, int bind_fd, int max_idle_time) tunnel_bind(bind_fd, dns_fds); } } - - /* Send realsoon's if tun or dns didn't already */ - for (userid = 0; userid < created_users; userid++) - if (user_active(userid) && users[userid].q_sendrealsoon.id != 0 && - users[userid].conn == CONN_DNS_NULL && !users[userid].q_sendrealsoon_new) { - int dns_fd = get_dns_fd(dns_fds, &users[userid].q_sendrealsoon.from); - send_frag_or_dataless(dns_fd, userid, &users[userid].q_sendrealsoon); - } } return 0; @@ -770,36 +712,25 @@ server_tunnel(int tun_fd, struct dnsfd *dns_fds, int bind_fd, int max_idle_time) void handle_full_packet(int tun_fd, struct dnsfd *dns_fds, int userid, uint8_t *data, size_t len) { - unsigned long outlen; + size_t outlen; uint8_t out[64*1024]; + struct ip *hdr; int touser; - int ret; - + int ret; // TODO: optional upstream compression flag outlen = sizeof(out); if ((ret = uncompress(out, &outlen, data, len)) == Z_OK) { - struct ip *hdr; - hdr = (struct ip*) (out + 4); touser = find_user_by_ip(hdr->ip_dst.s_addr); - + if (debug >= 3) + fprintf(stderr, "FULL PKT: %lu bytes from user %d (touser %d)\n", len, userid, touser); if (touser == -1) { /* send the uncompressed packet to tun device */ write_tun(tun_fd, out, outlen); } else { - /* send the compressed(!) packet to other client */ + /* send the compressed (!) packet to other client */ if (users[touser].conn == CONN_DNS_NULL) { - if (window_buffer_available(users[touser].outgoing) * users[touser].outgoing->maxfraglen >= len) { - window_add_outgoing_data(users[touser].outgoing, data, len, 1); - - /* Start sending immediately if query is waiting */ - if (users[touser].q_sendrealsoon.id != 0) { - int dns_fd = get_dns_fd(dns_fds, &users[touser].q_sendrealsoon.from); - send_frag_or_dataless(dns_fd, touser, &users[touser].q_sendrealsoon); - } else if (users[touser].q.id != 0) { - int dns_fd = get_dns_fd(dns_fds, &users[touser].q.from); - send_frag_or_dataless(dns_fd, touser, &users[touser].q); - } - } + window_add_outgoing_data(users[touser].outgoing, data, len, 1); + /* TODO: send immediately if query waiting */ } else{ /* CONN_RAW_UDP */ int dns_fd = get_dns_fd(dns_fds, &users[touser].q.from); send_raw(dns_fd, data, len, touser, RAW_HDR_CMD_DATA, &users[touser].q); @@ -1002,13 +933,13 @@ read_dns(int fd, struct dnsfd *dns_fds, int tun_fd, struct query *q) } static size_t -write_dns_nameenc(char *buf, size_t buflen, char *data, int datalen, char downenc) +write_dns_nameenc(uint8_t *buf, size_t buflen, uint8_t *data, size_t datalen, char downenc) /* Returns #bytes of data that were encoded */ { static int td1 = 0; static int td2 = 0; size_t space; - char *b; + uint8_t *b; /* Make a rotating topdomain to prevent filtering */ td1+=3; @@ -1057,7 +988,7 @@ write_dns_nameenc(char *buf, size_t buflen, char *data, int datalen, char downen /* Add dot (if it wasn't there already) and topdomain */ b = buf; - b += strlen(buf) - 1; + b += strlen((char *)buf) - 1; if (*b != '.') *++b = '.'; b++; @@ -1072,7 +1003,7 @@ write_dns_nameenc(char *buf, size_t buflen, char *data, int datalen, char downen } void -write_dns(int fd, struct query *q, char *data, int datalen, char downenc) +write_dns(int fd, struct query *q, char *data, size_t datalen, char downenc) { char buf[64*1024]; int len = 0; @@ -1080,11 +1011,9 @@ write_dns(int fd, struct query *q, char *data, int datalen, char downenc) if (q->type == T_CNAME || q->type == T_A) { char cnamebuf[1024]; /* max 255 */ - write_dns_nameenc(cnamebuf, sizeof(cnamebuf), - data, datalen, downenc); + write_dns_nameenc((uint8_t *)cnamebuf, sizeof(cnamebuf), (uint8_t *)data, datalen, downenc); - len = dns_encode(buf, sizeof(buf), q, QR_ANSWER, cnamebuf, - sizeof(cnamebuf)); + len = dns_encode(buf, sizeof(buf), q, QR_ANSWER, cnamebuf, sizeof(cnamebuf)); } else if (q->type == T_MX || q->type == T_SRV) { char mxbuf[64*1024]; char *b = mxbuf; @@ -1092,9 +1021,8 @@ write_dns(int fd, struct query *q, char *data, int datalen, char downenc) int res; while (1) { - res = write_dns_nameenc(b, sizeof(mxbuf) - (b - mxbuf), - data + offset, - datalen - offset, downenc); + res = write_dns_nameenc((uint8_t *)b, sizeof(mxbuf) - (b - mxbuf), + (uint8_t *)data + offset, datalen - offset, downenc); if (res < 1) { /* nothing encoded */ b++; /* for final \0 */ @@ -1115,22 +1043,22 @@ write_dns(int fd, struct query *q, char *data, int datalen, char downenc) sizeof(mxbuf)); } else if (q->type == T_TXT) { /* TXT with base32 */ - char txtbuf[64*1024]; + uint8_t txtbuf[64*1024]; size_t space = sizeof(txtbuf) - 1;; memset(txtbuf, 0, sizeof(txtbuf)); if (downenc == 'S') { txtbuf[0] = 's'; /* plain base64(Sixty-four) */ - len = b64->encode(txtbuf+1, &space, data, datalen); + len = b64->encode(txtbuf+1, &space, (uint8_t *)data, datalen); } else if (downenc == 'U') { txtbuf[0] = 'u'; /* Base64 with Underscore */ - len = b64u->encode(txtbuf+1, &space, data, datalen); + len = b64u->encode(txtbuf+1, &space, (uint8_t *)data, datalen); } else if (downenc == 'V') { txtbuf[0] = 'v'; /* Base128 */ - len = b128->encode(txtbuf+1, &space, data, datalen); + len = b128->encode(txtbuf+1, &space, (uint8_t *)data, datalen); } else if (downenc == 'R') { txtbuf[0] = 'r'; /* Raw binary data */ @@ -1138,9 +1066,9 @@ write_dns(int fd, struct query *q, char *data, int datalen, char downenc) memcpy(txtbuf + 1, data, len); } else { txtbuf[0] = 't'; /* plain base32(Thirty-two) */ - len = b32->encode(txtbuf+1, &space, data, datalen); + len = b32->encode(txtbuf+1, &space, (uint8_t *)data, datalen); } - len = dns_encode(buf, sizeof(buf), q, QR_ANSWER, txtbuf, len+1); + len = dns_encode(buf, sizeof(buf), q, QR_ANSWER, (char *)txtbuf, len+1); } else { /* Normal NULL-record encode */ len = dns_encode(buf, sizeof(buf), q, QR_ANSWER, data, datalen); @@ -1152,22 +1080,47 @@ write_dns(int fd, struct query *q, char *data, int datalen, char downenc) } if (debug >= 2) { - fprintf(stderr, "TX: client %s, type %d, name %s, %d bytes data\n", - format_addr(&q->from, q->fromlen), q->type, q->name, datalen); + fprintf(stderr, "TX: client %s ID %5d, %lu bytes data, type %d, name '%10s'\n", + format_addr(&q->from, q->fromlen), q->id, datalen, q->type, q->name); } sendto(fd, buf, len, 0, (struct sockaddr*)&q->from, q->fromlen); } +void +send_data_or_ping_response(int tun_fd, int dns_fd, struct dnsfd *dns_fds, int userid, struct query *q, int ping) { + uint8_t unpacked[64*1024]; + size_t read; + + /* if waiting for an ACK to be sent back upstream (on incoming buffer) */ + if (users[userid].next_upstream_ack < 0) { + users[userid].next_upstream_ack = window_get_next_ack(users[userid].incoming); + } + window_tick(users[userid].outgoing); + + read = window_reassemble_data(users[userid].incoming, unpacked, sizeof(unpacked), NULL); + window_tick(users[userid].incoming); + + if (read > 0) { /* Data reassembled successfully + cleared out of buffer */ + handle_full_packet(tun_fd, dns_fds, userid, unpacked, read); + } + + send_frag_or_dataless(dns_fd, userid, q, ping); + + /* Save new query and time info */ + memcpy(&(users[userid].q), q, sizeof(struct query)); + users[userid].last_pkt = time(NULL); +} + void handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query *q, int domain_len) /* Handles a NULL DNS request. See doc/proto_XXXXXXXX.txt for details on iodine protocol. */ { struct in_addr tempip; - char in[512]; + uint8_t in[512]; char logindata[16]; - char out[64*1024]; - static char unpacked[64*1024]; + uint8_t out[64*1024]; + static uint8_t unpacked[64*1024]; char *tmp[2]; int userid; size_t read; @@ -1202,45 +1155,47 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query userid = find_available_user(); if (userid >= 0) { int i; - - users[userid].seed = rand(); + struct tun_user *u = &users[userid]; + u->seed = rand(); /* Store remote IP number */ - memcpy(&(users[userid].host), &(q->from), q->fromlen); - users[userid].hostlen = q->fromlen; + memcpy(&(u->host), &(q->from), q->fromlen); + u->hostlen = q->fromlen; - memcpy(&(users[userid].q), q, sizeof(struct query)); - users[userid].encoder = get_base32_encoder(); - users[userid].downenc = 'T'; - send_version_response(dns_fd, VERSION_ACK, users[userid].seed, userid, q); + memcpy(&(u->q), q, sizeof(struct query)); + u->encoder = get_base32_encoder(); + u->downenc = 'T'; + u->downenc_bits = 5; + send_version_response(dns_fd, VERSION_ACK, u->seed, userid, q); syslog(LOG_INFO, "accepted version for user #%d from %s", userid, format_addr(&q->from, q->fromlen)); - users[userid].q.id = 0; - users[userid].q.id2 = 0; - users[userid].q_sendrealsoon.id = 0; - users[userid].q_sendrealsoon.id2 = 0; - users[userid].q_sendrealsoon_new = 0; - users[userid].fragsize = 100; /* very safe */ - users[userid].conn = CONN_DNS_NULL; - users[userid].lazy = 0; + u->q.id = 0; + u->q.id2 = 0; + u->fragsize = 100; /* very safe */ + u->conn = CONN_DNS_NULL; + u->lazy = 0; // TODO: client specified window size - users[userid].incoming = window_buffer_init(128, 10, MAX_FRAGSIZE, WINDOW_RECVING); - users[userid].outgoing = window_buffer_init(16, 10, users[userid].fragsize, WINDOW_SENDING); - users[userid].next_upstream_ack = -1; + u->incoming = window_buffer_init(INFRAGBUF_LEN, 10, MAX_FRAGSIZE, WINDOW_RECVING); + u->outgoing = window_buffer_init(OUTFRAGBUF_LEN, 10, + u->encoder->get_raw_length(u->fragsize) - DOWNSTREAM_PING_HDR, WINDOW_SENDING); + u->next_upstream_ack = -1; #ifdef DNSCACHE_LEN { for (i = 0; i < DNSCACHE_LEN; i++) { - users[userid].dnscache_q[i].id = 0; - users[userid].dnscache_answerlen[i] = 0; + u->dnscache_q[i].id = 0; + u->dnscache_answerlen[i] = 0; } } - users[userid].dnscache_lastfilled = 0; + u->dnscache_lastfilled = 0; #endif - for (i = 0; i < QMEMPING_LEN; i++) - users[userid].qmemping_type[i] = T_UNSET; - users[userid].qmemping_lastfilled = 0; + /*for (i = 0; i < QMEMPING_LEN; i++) + u->qmemping_type[i] = T_UNSET; + u->qmemping_lastfilled = 0; for (i = 0; i < QMEMDATA_LEN; i++) - users[userid].qmemdata_type[i] = T_UNSET; - users[userid].qmemdata_lastfilled = 0; + u->qmemdata_type[i] = T_UNSET; + u->qmemdata_lastfilled = 0;*/ + if (debug >= 1) + fprintf(stderr, "User %d connected with correct version from %s.\n", + userid, format_addr(&q->from, q->fromlen)); } else { /* No space for another user */ send_version_response(dns_fd, VERSION_FULL, created_users, 0, q); @@ -1262,11 +1217,13 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query /* Login phase, handle auth */ userid = unpacked[0]; - + if (debug >= 3) + fprintf(stderr, "Received login request for user %d from %s.\n", + userid, format_addr(&q->from, q->fromlen)); if (check_user_and_ip(userid, q) != 0) { write_dns(dns_fd, q, "BADIP", 5, 'T'); - syslog(LOG_WARNING, "dropped login request from user #%d from unexpected source %s", - userid, format_addr(&q->from, q->fromlen)); + syslog(LOG_WARNING, "dropped login request from user #%d from %s; expected source %s", + userid, format_addr(&q->from, q->fromlen), format_addr(&users[userid].host, users[userid].hostlen)); return; } else { users[userid].last_pkt = time(NULL); @@ -1282,10 +1239,10 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query tempip.s_addr = users[userid].tun_ip; tmp[1] = strdup(inet_ntoa(tempip)); - read = snprintf(out, sizeof(out), "%s-%s-%d-%d", + read = snprintf((char *)out, sizeof(out), "%s-%s-%d-%d", tmp[0], tmp[1], my_mtu, netmask); - write_dns(dns_fd, q, out, read, users[userid].downenc); + write_dns(dns_fd, q, (char *)out, read, users[userid].downenc); q->id = 0; syslog(LOG_NOTICE, "accepted password from user #%d, given IP %s", userid, tmp[1]); @@ -1331,7 +1288,7 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query /* Reply with received hostname as data */ /* No userid here, reply with lowest-grade downenc */ - write_dns(dns_fd, q, in, domain_len, 'T'); + write_dns(dns_fd, q, (char *)in, domain_len, 'T'); return; } else if(in[0] == 'S' || in[0] == 's') { /* Switch upstream codec */ int codec; @@ -1389,31 +1346,37 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query return; /* illegal id */ } + int bits = 0; switch (in[2]) { case 'T': case 't': users[userid].downenc = 'T'; write_dns(dns_fd, q, "Base32", 6, users[userid].downenc); + bits = 5; break; case 'S': case 's': users[userid].downenc = 'S'; write_dns(dns_fd, q, "Base64", 6, users[userid].downenc); + bits = 6; break; case 'U': case 'u': users[userid].downenc = 'U'; write_dns(dns_fd, q, "Base64u", 7, users[userid].downenc); + bits = 6; break; case 'V': case 'v': users[userid].downenc = 'V'; write_dns(dns_fd, q, "Base128", 7, users[userid].downenc); + bits = 7; break; case 'R': case 'r': users[userid].downenc = 'R'; write_dns(dns_fd, q, "Raw", 3, users[userid].downenc); + bits = 8; break; case 'L': case 'l': @@ -1429,6 +1392,14 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query write_dns(dns_fd, q, "BADCODEC", 8, users[userid].downenc); break; } + if (bits) { + int f = users[userid].fragsize; + users[userid].outgoing->maxfraglen = (bits * f) / 8 - DOWNSTREAM_PING_HDR; + if (debug >= 1) + warnx("Setting max downstream data length to %u bytes for user %d; bits %d (%c)", + users[userid].outgoing->maxfraglen, userid, bits, users[userid].downenc); + users[userid].downenc_bits = bits; + } return; } else if(in[0] == 'Y' || in[0] == 'y') { /* Downstream codec check */ int i; @@ -1561,12 +1532,17 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query write_dns(dns_fd, q, "BADFRAG", 7, users[userid].downenc); } else { users[userid].fragsize = max_frag_size; - write_dns(dns_fd, q, &unpacked[1], 2, users[userid].downenc); + users[userid].outgoing->maxfraglen = (users[userid].downenc_bits * max_frag_size) / + 8 - DOWNSTREAM_PING_HDR; + write_dns(dns_fd, q, (char *) unpacked + 1, 2, users[userid].downenc); + + if (debug >= 1) + warnx("Setting max downstream data length to %u bytes for user %d; bits %d (%c)", + users[userid].outgoing->maxfraglen, userid, users[userid].downenc_bits, users[userid].downenc); } return; } else if(in[0] == 'P' || in[0] == 'p') { /* Ping request */ - int dn_seq, up_seq, dn_wins, up_wins; - int didsend = 0; + int dn_seq, up_seq, dn_wins, up_wins, dn_ack; int respond; /* We can't handle id=0, that's "no packet" to us. So drop @@ -1579,8 +1555,10 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query return; read = unpack_data(unpacked, sizeof(unpacked), in + 1, domain_len - 1, b32); - if (read < 7) + if (read < UPSTREAM_PING) { + if (debug >= 1) warnx("Invalid ping! Length %lu", read); return; + } /* Ping packet, store userid */ userid = unpacked[0]; @@ -1594,100 +1572,35 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query if (answer_from_dnscache(dns_fd, userid, q)) return; #endif - - /* Check if duplicate (and not in full dnscache any more) */ + /* TODO: incoming query handling for lazy mode */ + /* Check if duplicate (and not in full dnscache any more) * / if (answer_from_qmem(dns_fd, q, users[userid].qmemping_cmc, users[userid].qmemping_type, QMEMPING_LEN, (void *) unpacked)) - return; + return; */ - /* Check if duplicate of waiting queries; impatient DNS relays - like to re-try early and often (with _different_ .id!) */ - if (users[userid].q.id != 0 && - q->type == users[userid].q.type && - !strcmp(q->name, users[userid].q.name) && - users[userid].lazy) { - /* We have this ping already, and it's waiting to be - answered. Always keep the last duplicate, since the - relay may have forgotten its first version already. - Our answer will go to both. - (If we already sent an answer, qmem/cache will - have triggered.) */ - if (debug >= 2) { - fprintf(stderr, "PING pkt from user %d = dupe from impatient DNS server, remembering\n", - userid); - } - users[userid].q.id2 = q->id; - users[userid].q.fromlen2 = q->fromlen; - memcpy(&(users[userid].q.from2), &(q->from), q->fromlen); - return; + dn_ack = ((unpacked[6] >> 2) & 1) ? unpacked[1] : -1; + up_wins = unpacked[2]; + dn_wins = unpacked[3]; + dn_seq = unpacked[4]; + up_seq = unpacked[5]; + respond = unpacked[6] & 1; + + /* TODO: Use ping to re-sync window buffer */ + if (debug >= 2) { + fprintf(stderr, "PING pkt from user %d, down %d/%d, up %d/%d, ACK %d\n", userid, dn_seq, dn_wins, up_seq, up_wins, dn_ack); } - if (users[userid].q_sendrealsoon.id != 0 && - q->type == users[userid].q_sendrealsoon.type && - !strcmp(q->name, users[userid].q_sendrealsoon.name)) { - /* Outer select loop will send answer immediately, - to both queries. */ - if (debug >= 2) { - fprintf(stderr, "PING pkt from user %d = dupe from impatient DNS server, remembering\n", - userid); - } - users[userid].q_sendrealsoon.id2 = q->id; - users[userid].q_sendrealsoon.fromlen2 = q->fromlen; - memcpy(&(users[userid].q_sendrealsoon.from2), - &(q->from), q->fromlen); - return; - } + window_ack(users[userid].outgoing, dn_ack); - dn_seq = unpacked[1]; - up_seq = unpacked[2]; - up_wins = unpacked[3]; - dn_wins = unpacked[4]; - respond = unpacked[5] & 1; - - if (debug >= 1) { - fprintf(stderr, "PING pkt from user %d, down %d/%d, up %d/%d\n", userid, dn_seq, dn_wins, up_seq, up_wins); - } - - /* If there is a query that must be returned real soon, do it. - May contain new downstream data if the ping had a new ack. TODO: ping with downstream data - Otherwise, may also be re-sending old data. */ - if (users[userid].q_sendrealsoon.id != 0) { - if (respond) send_ping_response(dns_fd, userid, &users[userid].q_sendrealsoon); - else send_frag_or_dataless(dns_fd, userid, &users[userid].q_sendrealsoon); - } - - /* We need to store a new query, so if there still is an - earlier query waiting, always send a reply to finish it. - May contain new downstream data if the ping had a new ack. - Otherwise, may also be re-sending old data. - (This is duplicate data if we had q_sendrealsoon above.) */ - if (users[userid].q.id != 0) { - didsend = 1; - if (respond) - send_ping_response(dns_fd, userid, &users[userid].q); - else - if (send_frag_or_dataless(dns_fd, userid, &users[userid].q) == 1) - /* new packet from queue, send immediately */ - didsend = 0; - } - - /* Save new query and time info */ - memcpy(&(users[userid].q), q, sizeof(struct query)); - users[userid].last_pkt = time(NULL); - - /* If anything waiting and we didn't already send above, send - it now. And always send immediately if we're not lazy - (then above won't have sent at all). TODO only call send_frag if sending */ - if ((!didsend) || !users[userid].lazy) - send_frag_or_dataless(dns_fd, userid, &users[userid].q); + send_data_or_ping_response(tun_fd, dns_fd, dns_fds, userid, q, respond); } else if((in[0] >= '0' && in[0] <= '9') /* Upstream data packet */ || (in[0] >= 'a' && in[0] <= 'f') || (in[0] >= 'A' && in[0] <= 'F')) { - int didsend = 0; int code = -1; static fragment f; + size_t len; /* Need 6 char header + >=1 char data */ if (domain_len < 7) @@ -1699,8 +1612,10 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query different id, then all okay. Else client doesn't get our ack, and will retransmit in 1 second. */ - if (q->id == 0) + if (q->id == 0) { + warnx("Query with ID 0!"); return; + } if ((in[0] >= '0' && in[0] <= '9')) code = in[0] - '0'; @@ -1721,142 +1636,41 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query if (answer_from_dnscache(dns_fd, userid, q)) return; #endif + /* TODO: incoming query buffer/handling for lazy mode */ - /* Check if duplicate (and not in full dnscache any more) */ - if (answer_from_qmem_data(dns_fd, userid, q)) - return; - - /* Check if duplicate of waiting queries; impatient DNS relays - like to re-try early and often (with _different_ .id!) */ - if (users[userid].q.id != 0 && - q->type == users[userid].q.type && - !strcmp(q->name, users[userid].q.name) && - users[userid].lazy) { - /* We have this packet already, and it's waiting to be - answered. Always keep the last duplicate, since the - relay may have forgotten its first version already. - Our answer will go to both. - (If we already sent an answer, qmem/cache will - have triggered.) */ - if (debug >= 2) { - fprintf(stderr, "IN pkt from user %d = dupe from impatient DNS server, remembering\n", userid); - } - users[userid].q.id2 = q->id; - users[userid].q.fromlen2 = q->fromlen; - memcpy(&(users[userid].q.from2), &(q->from), q->fromlen); - return; - } - - if (users[userid].q_sendrealsoon.id != 0 && - q->type == users[userid].q_sendrealsoon.type && - !strcmp(q->name, users[userid].q_sendrealsoon.name)) { - /* Outer select loop will send answer immediately to both queries. */ - if (debug >= 2) { - fprintf(stderr, "IN pkt from user %d = dupe from impatient DNS server, remembering\n", userid); - } - users[userid].q_sendrealsoon.id2 = q->id; - users[userid].q_sendrealsoon.fromlen2 = q->fromlen; - memcpy(&(users[userid].q_sendrealsoon.from2), &(q->from), q->fromlen); - return; - } - + /* TODO: Check if duplicate of waiting queries (ping and data) */ /* Decode upstream data header - see docs/proto_XXXXXXXX.txt */ - /* First byte (after userid) = CMC (ignored?) */ - f.seqID = (b32_8to5(in[2]) << 2) | (b32_8to5(in[3]) >> 2); - f.ack_other = (b32_8to5(in[5]) & 8) ? ((b32_8to5(in[3]) & 3) << 6) - | (b32_8to5(in[4]) << 1) | (b32_8to5(in[5]) & 1) : -1; - f.is_nack = (b32_8to5(in[5]) >> 2) & 1; - f.start = (b32_8to5(in[5]) >> 1) & 1; - f.end = b32_8to5(in[5]) & 1; + /* First byte (after userid) = CMC (ignored) */ +// f.seqID = (b32_8to5(in[2]) << 2) | (b32_8to5(in[3]) >> 2); +// f.ack_other = (b32_8to5(in[5]) & 8) ? ((b32_8to5(in[3]) & 3) << 6) +// | (b32_8to5(in[4]) << 1) | ((b32_8to5(in[5]) >> 4) & 1) : -1; +// f.is_nack = (b32_8to5(in[5]) >> 2) & 1; +// f.start = (b32_8to5(in[5]) >> 1) & 1; +// f.end = b32_8to5(in[5]) & 1; + len = sizeof(unpacked); + read = b32->decode(unpacked, &len, in + 2, 5); + f.seqID = unpacked[0]; + unpacked[2] >>= 4; /* Lower 4 bits are unused */ + f.ack_other = ((unpacked[2] >> 3) & 1) ? unpacked[1] : -1; + f.is_nack = (unpacked[2] >> 2) & 1; + f.start = (unpacked[2] >> 1) & 1; + f.end = unpacked[2] & 1; /* Decode remainder of data with user encoding */ read = unpack_data(unpacked, sizeof(unpacked), in + UPSTREAM_HDR, domain_len - UPSTREAM_HDR, users[userid].encoder); + if (debug >= 4) warnx("++++ UNPACKED %d bytes into %lu using %s with header len %d", + domain_len, read, users[userid].encoder->name, UPSTREAM_HDR); f.len = MIN(read, MAX_FRAGSIZE); - memcpy(f.data, unpacked, read); + memcpy(f.data, unpacked, f.len); window_process_incoming_fragment(users[userid].incoming, &f); window_ack(users[userid].outgoing, f.ack_other); - /* if waiting for an ACK to be sent back upstream (on incoming buffer) */ - if (users[userid].next_upstream_ack < 0) { - users[userid].next_upstream_ack = window_get_next_ack(users[userid].incoming); - } - - read = window_reassemble_data(users[userid].incoming, (uint8_t *)unpacked, sizeof(unpacked), NULL); - - if (read > 0) { /* Data reassembled successfully + cleared out of buffer */ - handle_full_packet(tun_fd, dns_fds, userid, (uint8_t *)unpacked, read); - } - - window_tick(users[userid].incoming); - - /* If there is a query that must be returned real soon, do it. - Includes an ack of the just received upstream fragment, - may contain new data. */ - if (users[userid].q_sendrealsoon.id != 0) { - didsend = 1; - if (send_frag_or_dataless(dns_fd, userid, &users[userid].q_sendrealsoon) == 1) - /* new packet from queue, send immediately */ - didsend = 0; - } - - /* If we already have an earlier query waiting, we need to - get rid of it to store the new query. - - If we have new data waiting and not yet sent above, send immediately. - - If this wasn't the last upstream fragment, then we expect - more, so ack immediately if we didn't already. - - If we are in non-lazy mode, there should be no query - waiting, but if there is, send immediately. - - In all other cases (mostly the last-fragment cases), - we can afford to wait just a tiny little while for the - TCP ack to arrive from our tun. Note that this works best - when there is only one client. - */ -// if (users[userid].q.id != 0) { -// if ((users[userid].outpacket.len > 0 && !didsend) || -// (upstream_ok && !lastfrag && !didsend) || -// (!upstream_ok && !didsend) || -// !users[userid].lazy) { -// didsend = 1; -// if (send_frag_or_dataless(dns_fd, userid, &users[userid].q) == 1) -// /* new packet from queue, send immediately */ -// didsend = 0; -// } else { -// memcpy(&(users[userid].q_sendrealsoon), &(users[userid].q), -// sizeof(struct query)); -// users[userid].q_sendrealsoon_new = 1; -// users[userid].q.id = 0; /* used */ -// didsend = 1; -// } -// } - - /* Save new query and time info */ - memcpy(&(users[userid].q), q, sizeof(struct query)); - users[userid].last_pkt = time(NULL); - - /* If we still need to ack this upstream frag, do it to keep - upstream flowing. - - If we have new data waiting and not yet sent above, - send immediately. - - If this wasn't the last upstream fragment, then we expect - more, so ack immediately if we didn't already or are - in non-lazy mode. - - If this was the last fragment, and we didn't ack already - or are in non-lazy mode, send the ack after just a tiny - little while so that the TCP ack may have arrived from - our tun device. - - In all other cases, don't send anything now. */ - if (!didsend) // TODO: also check if sending - send_frag_or_dataless(dns_fd, userid, &users[userid].q); - else if (!didsend || !users[userid].lazy) { - memcpy(&(users[userid].q_sendrealsoon), &(users[userid].q), sizeof(struct query)); - users[userid].q_sendrealsoon_new = 1; - users[userid].q.id = 0; /* used */ - } + send_data_or_ping_response(tun_fd, dns_fd, dns_fds, userid, q, 0); } } @@ -1882,8 +1696,8 @@ handle_ns_request(int dns_fd, struct query *q) } if (debug >= 2) { - fprintf(stderr, "TX: client %s, type %d, name %s, %d bytes NS reply\n", - format_addr(&q->from, q->fromlen), q->type, q->name, len); + fprintf(stderr, "TX: client %s ID %5d, type %d, name %s, %d bytes NS reply\n", + format_addr(&q->from, q->fromlen), q->id, q->type, q->name, len); } if (sendto(dns_fd, buf, len, 0, (struct sockaddr*)&q->from, q->fromlen) <= 0) { warn("ns reply send error"); @@ -1916,8 +1730,8 @@ handle_a_request(int dns_fd, struct query *q, int fakeip) } if (debug >= 2) { - fprintf(stderr, "TX: client %s, type %d, name %s, %d bytes A reply\n", - format_addr(&q->from, q->fromlen), q->type, q->name, len); + fprintf(stderr, "TX: client %s ID %5d, type %d, name %s, %d bytes A reply\n", + format_addr(&q->from, q->fromlen), q->id, q->type, q->name, len); } if (sendto(dns_fd, buf, len, 0, (struct sockaddr*)&q->from, q->fromlen) <= 0) { warn("a reply send error"); diff --git a/src/server.h b/src/server.h index c50c131..7969576 100644 --- a/src/server.h +++ b/src/server.h @@ -47,6 +47,15 @@ #define QMEMDATA_LEN 15 /* Max advisable: 36/2 = 18. Total mem usage: QMEMDATA_LEN * USERS * 6 bytes */ +/* Number of fragments in outgoing buffer. + * Mem usage: USERS * (MAX_FRAGLEN * OUTFRAGBUF_LEN + sizeof(struct window_buffer) */ +#define OUTFRAGBUF_LEN 64 + +/* Number of fragments in incoming buffer + * Minimum recommended = ((max packet size or MTU) / (max up fragsize)) * 2 + * ie. (1200 / 100) * 2 = 24 */ +#define INFRAGBUF_LEN 32 + #define PASSWORD_ENV_VAR "IODINED_PASS" #if defined IP_RECVDSTADDR @@ -102,7 +111,7 @@ void server_stop(); int server_tunnel(int tun_fd, struct dnsfd *dns_fds, int bind_fd, int max_idle_time); int read_dns(int fd, struct dnsfd *dns_fds, int tun_fd, struct query *q); -void write_dns(int fd, struct query *q, char *data, int datalen, char downenc); +void write_dns(int fd, struct query *q, char *data, size_t datalen, char downenc); void handle_full_packet(int tun_fd, struct dnsfd *dns_fds, int userid, uint8_t *data, size_t len); void handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query *q, int domain_len); void handle_ns_request(int dns_fd, struct query *q);