diff --git a/doc/proto_00000403.txt b/doc/proto_00000403.txt index c392079..cf05d94 100644 --- a/doc/proto_00000403.txt +++ b/doc/proto_00000403.txt @@ -38,8 +38,8 @@ Server replies: Switch codec: Client sends: First byte s or S - One byte ASCII digit, meaning userid - One byte ASCII digit, with value 5 or 6, representing number of raw + 5 bits coded as Base32 char, meaning userid + 5 bits coded as Base32 char, with value 5 or 6, representing number of raw bits per encoded byte Server sends: Name of codec if accepted. After this all upstream data packets must @@ -47,28 +47,28 @@ Server sends: BADCODEC if not accepted. Client must then revert to Base32 Data: -Upstream data header (encoded as 4 bytes Base32): - 4321 0 432 10 43 210 4321 0 - +----+-+---+--+--+---+----+-+ - |UUUU|L|SSS|FF|FF|DDD|GGGG|C| - +----+-+---+--+--+---+----+-+ +Upstream data header: + 3210 432 10 43 210 4321 0 + +----+---+--+--+---+----+-+ + |UUUU|SSS|FF|FF|DDD|GGGG|L| + +----+---+--+--+---+----+-+ Downstream data header: 7 654 3210 765 4321 0 +-+---+----+---+----+-+ - |L|SSS|FFFF|DDD|GGGG|C| + |C|SSS|FFFF|DDD|GGGG|L| +-+---+----+---+----+-+ UUUU = Userid L = Last fragment in packet flag -SSS = Upstream packet sequence number +SS = Upstream packet sequence number FFFF = Upstream fragment number DDD = Downstream packet sequence number GGGG = Downstream fragment number -C = Compression enabled for this packet +C = Compression enabled for downstream packet -Upstream data packet starts with 4 bytes Base32 encoded header, then comes -the payload data, encoded with chosen codec. +Upstream data packet starts with 1 byte ASCII hex coded user byte, then 3 bytes +Base32 encoded header, then comes the payload data, encoded with chosen codec. Downstream data starts with 2 byte header. Then payload data, which may be compressed. @@ -77,7 +77,10 @@ Ping: Client sends: First byte p or P Rest encoded with Base32: - 1 byte userid + 1 byte with 4 bits userid + 1 byte with: + 3 bits downstream seqno + 4 bits downstream fragment CMC The server response to Ping and Data packets is a DNS NULL type response: diff --git a/src/encoding.c b/src/encoding.c index d581587..c3c94fb 100644 --- a/src/encoding.c +++ b/src/encoding.c @@ -35,7 +35,7 @@ inline_dotify(char *buf, size_t buflen) char *reader, *writer; total = strlen(buf); - dots = total / 62; + dots = total / 57; writer = buf; writer += total; @@ -52,7 +52,7 @@ inline_dotify(char *buf, size_t buflen) pos = (unsigned) (reader - buf) + 1; while (dots) { - if (pos % 62 == 0) { + if (pos % 57 == 0) { *writer-- = '.'; dots--; } diff --git a/src/iodine.c b/src/iodine.c index 6905f26..ac52e04 100644 --- a/src/iodine.c +++ b/src/iodine.c @@ -124,10 +124,9 @@ build_hostname(char *buf, size_t buflen, size_t space; char *b; - - space = MIN(0xFF, buflen) - strlen(topdomain) - 2; + space = MIN(0xFF, buflen) - strlen(topdomain) - 5; if (!encoder->places_dots()) - space -= (space / 62); /* space for dots */ + space -= (space / 57); /* space for dots */ memset(buf, 0, buflen); @@ -209,6 +208,8 @@ tunnel_tun(int tun_fd, int dns_fd) packet.sentlen = 0; packet.offset = 0; packet.len = outlen; + packet.seqno++; + packet.fragment = 0; send_chunk(dns_fd); @@ -254,9 +255,11 @@ tunnel(int tun_fd, int dns_fd) tv.tv_sec = 1; tv.tv_usec = 0; + FD_ZERO(&fds); - if (!is_sending()) + if (!is_sending()) { FD_SET(tun_fd, &fds); + } FD_SET(dns_fd, &fds); i = select(MAX(tun_fd, dns_fd) + 1, &fds, NULL, NULL, &tv); @@ -297,15 +300,21 @@ send_chunk(int fd) p += packet.offset; avail = packet.len - packet.offset; - packet.sentlen = build_hostname(buf + 1, sizeof(buf) - 1, p, avail, topdomain, dataenc); + packet.sentlen = build_hostname(buf + 4, sizeof(buf) - 4, p, avail, topdomain, dataenc); + packet.fragment++; - if (packet.sentlen == avail) - code = 1; - else - code = 0; - - code |= (userid << 1); - buf[0] = hex[code]; + /* Build upstream data header (see doc/proto_xxxxxxxx.txt) */ + + buf[0] = hex[userid & 15]; /* First byte is 4 bits userid */ + + code = ((packet.seqno & 7) << 2) | ((packet.fragment & 15) >> 2); + buf[1] = b32_5to8(code); /* Second byte is 3 bits seqno, 2 upper bits fragment count */ + + code = ((packet.fragment & 3) << 3) | (0); + buf[2] = b32_5to8(code); /* Third byte is 2 bits lower fragment count, 3 bits downstream packet seqno */ + + code = (0 << 1) | (packet.sentlen == avail); + buf[3] = b32_5to8(code); /* Fourth byte is 4 bits downstream fragment count, 1 bit compression flag */ send_query(fd, buf); } @@ -330,7 +339,7 @@ send_login(int fd, char *login, int len) static void send_ping(int fd) { - char data[3]; + char data[4]; if (is_sending()) { packet.sentlen = 0; @@ -339,8 +348,9 @@ send_ping(int fd) } data[0] = userid; - data[1] = (rand_seed >> 8) & 0xff; - data[2] = (rand_seed >> 0) & 0xff; + data[1] = 0; + data[2] = (rand_seed >> 8) & 0xff; + data[3] = (rand_seed >> 0) & 0xff; rand_seed++; @@ -380,13 +390,9 @@ send_case_check(int fd) static void send_codec_switch(int fd, int userid, int bits) { - char buf[512] = "S00."; - if (userid >= 0 && userid < 9) { - buf[1] += userid; - } - if (bits >= 0 && bits < 9) { - buf[2] += bits; - } + char buf[512] = "S__."; + buf[1] = b32_5to8(userid); + buf[2] = b32_5to8(bits); strncat(buf, topdomain, 512 - strlen(buf)); send_query(fd, buf); @@ -705,6 +711,8 @@ main(int argc, char **argv) device = NULL; chunkid = 0; + packet.seqno = 0; + b32 = get_base32_encoder(); dataenc = get_base32_encoder(); diff --git a/src/iodined.c b/src/iodined.c index 3ddb752..2750877 100644 --- a/src/iodined.c +++ b/src/iodined.c @@ -151,6 +151,33 @@ send_version_response(int fd, version_ack_t ack, uint32_t payload, int userid, s write_dns(fd, q, out, sizeof(out)); } +static void +update_downstream_seqno(int dns_fd, int userid, int down_seq, int down_frag) +{ + /* update outgoing seqno/frag */ + if (down_seq != users[userid].out_acked_seqno) { + /* First ack on new outgoing packet */ + users[userid].out_acked_seqno = down_seq; + users[userid].out_acked_fragment = down_frag; + } else { + if (down_frag > users[userid].out_acked_fragment) { + /* Ack on later fragment */ + users[userid].out_acked_fragment = down_frag; + } + } + + /* Send reply if waiting */ + if (users[userid].outpacket.len > 0) { + if (debug >= 1) { + printf("OUT pkt seq# %d, frag %d (last=%d), fragsize %d of total %d, to user %d\n", + 0, 0, 1, users[userid].outpacket.len, users[userid].outpacket.len, userid); + } + write_dns(dns_fd, &users[userid].q, users[userid].outpacket.data, users[userid].outpacket.len); + users[userid].outpacket.len = 0; + users[userid].q.id = 0; + } +} + static void handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) { @@ -240,16 +267,6 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) } } return; - } else if(in[0] == 'P' || in[0] == 'p') { - read = unpack_data(unpacked, sizeof(unpacked), &(in[1]), domain_len - 1, b32); - /* Ping packet, store userid */ - userid = unpacked[0]; - if (userid < 0 || userid >= USERS || ip_cmp(userid, q) != 0) { - write_dns(dns_fd, q, "BADIP", 5); - return; /* illegal id */ - } - memcpy(&(users[userid].q), q, sizeof(struct query)); - users[userid].last_pkt = time(NULL); } else if(in[0] == 'Z' || in[0] == 'z') { /* Check for case conservation and chars not allowed according to RFC */ @@ -264,14 +281,15 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) return; } - userid = in[1] & 0x7; + userid = b32_8to5(in[1]); if (ip_cmp(userid, q) != 0) { write_dns(dns_fd, q, "BADIP", 5); return; /* illegal id */ } - codec = in[2] & 0xF; + codec = b32_8to5(in[2]); + switch (codec) { case 5: /* 5 bits per byte = base32 */ enc = get_base32_encoder(); @@ -288,6 +306,25 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) break; } return; + } else if(in[0] == 'P' || in[0] == 'p') { + int dn_seq; + int dn_frag; + + read = unpack_data(unpacked, sizeof(unpacked), &(in[1]), domain_len - 1, b32); + /* Ping packet, store userid */ + userid = unpacked[0]; + if (userid < 0 || userid >= USERS || ip_cmp(userid, q) != 0) { + write_dns(dns_fd, q, "BADIP", 5); + return; /* illegal id */ + } + + dn_seq = unpacked[1] >> 4; + dn_frag = unpacked[1] & 15; + memcpy(&(users[userid].q), q, sizeof(struct query)); + users[userid].last_pkt = time(NULL); + + /* Update seqno and maybe send immediate response packet */ + update_downstream_seqno(dns_fd, userid, dn_seq, dn_frag); } else if((in[0] >= '0' && in[0] <= '9') || (in[0] >= 'a' && in[0] <= 'f') || (in[0] >= 'A' && in[0] <= 'F')) { @@ -298,7 +335,7 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) if ((in[0] >= 'A' && in[0] <= 'F')) code = in[0] - 'A' + 10; - userid = code >> 1; + userid = code; if (userid < 0 || userid >= USERS) { write_dns(dns_fd, q, "BADIP", 5); return; /* illegal id */ @@ -308,45 +345,69 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) if (check_ip && ip_cmp(userid, q) != 0) { write_dns(dns_fd, q, "BADIP", 5); } else { - /* decode with this users encoding */ - read = unpack_data(unpacked, sizeof(unpacked), &(in[1]), domain_len - 1, - users[userid].encoder); + /* Decode data header */ + int up_seq = (b32_8to5(in[1]) >> 2) & 7; + int up_frag = ((b32_8to5(in[1]) & 3) << 2) | ((b32_8to5(in[2]) >> 3) & 3); + int dn_seq = (b32_8to5(in[2]) & 7); + int dn_frag = b32_8to5(in[3]) >> 1; + int lastfrag = b32_8to5(in[3]) & 1; + /* Update query and time info for user */ users[userid].last_pkt = time(NULL); memcpy(&(users[userid].q), q, sizeof(struct query)); + + if (up_seq != users[userid].inpacket.seqno) { + /* New packet has arrived */ + users[userid].inpacket.seqno = up_seq; + users[userid].inpacket.len = 0; + users[userid].inpacket.offset = 0; + } + users[userid].inpacket.fragment = up_frag; + + /* decode with this users encoding */ + read = unpack_data(unpacked, sizeof(unpacked), &(in[4]), domain_len - 4, + users[userid].encoder); + + /* copy to packet buffer, update length */ memcpy(users[userid].inpacket.data + users[userid].inpacket.offset, unpacked, read); users[userid].inpacket.len += read; users[userid].inpacket.offset += read; - if (code & 1) { + if (debug >= 1) { + printf("IN pkt seq# %d, frag %d (last=%d), fragsize %d, total %d, from user %d\n", + up_seq, up_frag, lastfrag, read, users[userid].inpacket.len, userid); + } + + if (lastfrag & 1) { /* packet is complete */ + int ret; outlen = sizeof(out); - uncompress((uint8_t*)out, &outlen, - (uint8_t*)users[userid].inpacket.data, users[userid].inpacket.len); + ret = uncompress((uint8_t*)out, &outlen, + (uint8_t*)users[userid].inpacket.data, users[userid].inpacket.len); - hdr = (struct ip*) (out + 4); - touser = find_user_by_ip(hdr->ip_dst.s_addr); + if (ret == Z_OK) { + hdr = (struct ip*) (out + 4); + touser = find_user_by_ip(hdr->ip_dst.s_addr); - if (touser == -1) { - /* send the uncompressed packet to tun device */ - write_tun(tun_fd, out, outlen); - } else { - /* send the compressed packet to other client - * if another packet is queued, throw away this one. TODO build queue */ - if (users[touser].outpacket.len == 0) { - memcpy(users[touser].outpacket.data, users[userid].inpacket.data, users[userid].inpacket.len); - users[touser].outpacket.len = users[userid].inpacket.len; + if (touser == -1) { + /* send the uncompressed packet to tun device */ + write_tun(tun_fd, out, outlen); + } else { + /* send the compressed packet to other client + * if another packet is queued, throw away this one. TODO build queue */ + if (users[touser].outpacket.len == 0) { + memcpy(users[touser].outpacket.data, users[userid].inpacket.data, users[userid].inpacket.len); + users[touser].outpacket.len = users[userid].inpacket.len; + } } + } else { + printf("Discarded data, uncompress() result: %d\n", ret); } users[userid].inpacket.len = users[userid].inpacket.offset = 0; } + /* Update seqno and maybe send immediate response packet */ + update_downstream_seqno(dns_fd, userid, dn_seq, dn_frag); } } - /* userid must be set for a reply to be sent */ - if (userid >= 0 && userid < USERS && ip_cmp(userid, q) == 0 && users[userid].outpacket.len > 0) { - write_dns(dns_fd, q, users[userid].outpacket.data, users[userid].outpacket.len); - users[userid].outpacket.len = 0; - users[userid].q.id = 0; - } } static void @@ -361,7 +422,7 @@ handle_ns_request(int dns_fd, struct query *q) len = dns_encode_ns_response(buf, sizeof(buf), q, topdomain); - if (debug >= 1) { + if (debug >= 2) { struct sockaddr_in *tempin; tempin = (struct sockaddr_in *) &(q->from); printf("TX: client %s, type %d, name %s, %d bytes NS reply\n", @@ -394,7 +455,7 @@ forward_query(int bind_fd, struct query *q) memcpy(&(myaddr->sin_addr), &newaddr, sizeof(in_addr_t)); myaddr->sin_port = htons(bind_port); - if (debug >= 1) { + if (debug >= 2) { printf("TX: NS reply \n"); } @@ -422,18 +483,18 @@ tunnel_bind(int bind_fd, int dns_fd) id = dns_get_id(packet, r); - if (debug >= 1) { + if (debug >= 2) { printf("RX: Got response on query %u from DNS\n", (id & 0xFFFF)); } /* Get sockaddr from id */ fw_query_get(id, &query); - if (!query && debug >= 1) { + if (!query && debug >= 2) { printf("Lost sender of id %u, dropping reply\n", (id & 0xFFFF)); return 0; } - if (debug >= 1) { + if (debug >= 2) { struct sockaddr_in *in; in = (struct sockaddr_in *) &(query->addr); printf("TX: client %s id %u, %d bytes\n", @@ -460,7 +521,7 @@ tunnel_dns(int tun_fd, int dns_fd, int bind_fd) if ((read = read_dns(dns_fd, &q)) <= 0) return 0; - if (debug >= 1) { + if (debug >= 2) { struct sockaddr_in *tempin; tempin = (struct sockaddr_in *) &(q.from); printf("RX: client %s, type %d, name %s\n", @@ -543,6 +604,10 @@ tunnel(int tun_fd, int dns_fd, int bind_fd) int j; for (j = 0; j < USERS; j++) { if (users[j].q.id != 0) { + if (debug >= 1) { + printf("OUT pkt seq# %d, frag %d (last=%d), fragsize %d of total %d, to user %d\n", + 0, 0, 1, users[j].outpacket.len, users[j].outpacket.len, j); + } write_dns(dns_fd, &(users[j].q), users[j].outpacket.data, users[j].outpacket.len); users[j].outpacket.len = 0; users[j].q.id = 0; @@ -626,7 +691,7 @@ write_dns(int fd, struct query *q, char *data, int datalen) len = dns_encode(buf, sizeof(buf), q, QR_ANSWER, data, datalen); - if (debug >= 1) { + if (debug >= 2) { struct sockaddr_in *tempin; tempin = (struct sockaddr_in *) &(q->from); printf("TX: client %s, type %d, name %s, %d bytes data\n", diff --git a/src/user.c b/src/user.c index c23d9c7..cc6a883 100644 --- a/src/user.c +++ b/src/user.c @@ -50,6 +50,8 @@ init_users(in_addr_t my_ip) users[i].inpacket.offset = 0; users[i].outpacket.len = 0; users[i].q.id = 0; + users[i].out_acked_seqno = 0; + users[i].out_acked_fragment = 0; } } diff --git a/src/user.h b/src/user.h index 8f1250c..0dc7cc2 100644 --- a/src/user.h +++ b/src/user.h @@ -17,7 +17,7 @@ #ifndef __USER_H__ #define __USER_H__ -#define USERS 8 +#define USERS 16 struct user { char id; @@ -30,6 +30,8 @@ struct user { struct packet inpacket; struct packet outpacket; struct encoder *encoder; + int out_acked_seqno; + int out_acked_fragment; }; extern struct user users[USERS];