Implemented new protocol for upstream data

This commit is contained in:
Erik Ekman 2008-12-11 22:39:06 +00:00 committed by Erik Ekman
parent 2c4c5ec1ba
commit 8d27febc7d
6 changed files with 161 additions and 81 deletions

View file

@ -38,8 +38,8 @@ Server replies:
Switch codec: Switch codec:
Client sends: Client sends:
First byte s or S First byte s or S
One byte ASCII digit, meaning userid 5 bits coded as Base32 char, meaning userid
One byte ASCII digit, with value 5 or 6, representing number of raw 5 bits coded as Base32 char, with value 5 or 6, representing number of raw
bits per encoded byte bits per encoded byte
Server sends: Server sends:
Name of codec if accepted. After this all upstream data packets must 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 BADCODEC if not accepted. Client must then revert to Base32
Data: Data:
Upstream data header (encoded as 4 bytes Base32): Upstream data header:
4321 0 432 10 43 210 4321 0 3210 432 10 43 210 4321 0
+----+-+---+--+--+---+----+-+ +----+---+--+--+---+----+-+
|UUUU|L|SSS|FF|FF|DDD|GGGG|C| |UUUU|SSS|FF|FF|DDD|GGGG|L|
+----+-+---+--+--+---+----+-+ +----+---+--+--+---+----+-+
Downstream data header: Downstream data header:
7 654 3210 765 4321 0 7 654 3210 765 4321 0
+-+---+----+---+----+-+ +-+---+----+---+----+-+
|L|SSS|FFFF|DDD|GGGG|C| |C|SSS|FFFF|DDD|GGGG|L|
+-+---+----+---+----+-+ +-+---+----+---+----+-+
UUUU = Userid UUUU = Userid
L = Last fragment in packet flag L = Last fragment in packet flag
SSS = Upstream packet sequence number SS = Upstream packet sequence number
FFFF = Upstream fragment number FFFF = Upstream fragment number
DDD = Downstream packet sequence number DDD = Downstream packet sequence number
GGGG = Downstream fragment 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 Upstream data packet starts with 1 byte ASCII hex coded user byte, then 3 bytes
the payload data, encoded with chosen codec. 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 Downstream data starts with 2 byte header. Then payload data, which may be
compressed. compressed.
@ -77,7 +77,10 @@ Ping:
Client sends: Client sends:
First byte p or P First byte p or P
Rest encoded with Base32: 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 CMC
The server response to Ping and Data packets is a DNS NULL type response: The server response to Ping and Data packets is a DNS NULL type response:

View file

@ -35,7 +35,7 @@ inline_dotify(char *buf, size_t buflen)
char *reader, *writer; char *reader, *writer;
total = strlen(buf); total = strlen(buf);
dots = total / 62; dots = total / 57;
writer = buf; writer = buf;
writer += total; writer += total;
@ -52,7 +52,7 @@ inline_dotify(char *buf, size_t buflen)
pos = (unsigned) (reader - buf) + 1; pos = (unsigned) (reader - buf) + 1;
while (dots) { while (dots) {
if (pos % 62 == 0) { if (pos % 57 == 0) {
*writer-- = '.'; *writer-- = '.';
dots--; dots--;
} }

View file

@ -124,10 +124,9 @@ build_hostname(char *buf, size_t buflen,
size_t space; size_t space;
char *b; char *b;
space = MIN(0xFF, buflen) - strlen(topdomain) - 5;
space = MIN(0xFF, buflen) - strlen(topdomain) - 2;
if (!encoder->places_dots()) if (!encoder->places_dots())
space -= (space / 62); /* space for dots */ space -= (space / 57); /* space for dots */
memset(buf, 0, buflen); memset(buf, 0, buflen);
@ -209,6 +208,8 @@ tunnel_tun(int tun_fd, int dns_fd)
packet.sentlen = 0; packet.sentlen = 0;
packet.offset = 0; packet.offset = 0;
packet.len = outlen; packet.len = outlen;
packet.seqno++;
packet.fragment = 0;
send_chunk(dns_fd); send_chunk(dns_fd);
@ -254,9 +255,11 @@ tunnel(int tun_fd, int dns_fd)
tv.tv_sec = 1; tv.tv_sec = 1;
tv.tv_usec = 0; tv.tv_usec = 0;
FD_ZERO(&fds); FD_ZERO(&fds);
if (!is_sending()) if (!is_sending()) {
FD_SET(tun_fd, &fds); FD_SET(tun_fd, &fds);
}
FD_SET(dns_fd, &fds); FD_SET(dns_fd, &fds);
i = select(MAX(tun_fd, dns_fd) + 1, &fds, NULL, NULL, &tv); i = select(MAX(tun_fd, dns_fd) + 1, &fds, NULL, NULL, &tv);
@ -297,15 +300,21 @@ send_chunk(int fd)
p += packet.offset; p += packet.offset;
avail = packet.len - 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) /* Build upstream data header (see doc/proto_xxxxxxxx.txt) */
code = 1;
else buf[0] = hex[userid & 15]; /* First byte is 4 bits userid */
code = 0;
code = ((packet.seqno & 7) << 2) | ((packet.fragment & 15) >> 2);
code |= (userid << 1); buf[1] = b32_5to8(code); /* Second byte is 3 bits seqno, 2 upper bits fragment count */
buf[0] = hex[code];
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); send_query(fd, buf);
} }
@ -330,7 +339,7 @@ send_login(int fd, char *login, int len)
static void static void
send_ping(int fd) send_ping(int fd)
{ {
char data[3]; char data[4];
if (is_sending()) { if (is_sending()) {
packet.sentlen = 0; packet.sentlen = 0;
@ -339,8 +348,9 @@ send_ping(int fd)
} }
data[0] = userid; data[0] = userid;
data[1] = (rand_seed >> 8) & 0xff; data[1] = 0;
data[2] = (rand_seed >> 0) & 0xff; data[2] = (rand_seed >> 8) & 0xff;
data[3] = (rand_seed >> 0) & 0xff;
rand_seed++; rand_seed++;
@ -380,13 +390,9 @@ send_case_check(int fd)
static void static void
send_codec_switch(int fd, int userid, int bits) send_codec_switch(int fd, int userid, int bits)
{ {
char buf[512] = "S00."; char buf[512] = "S__.";
if (userid >= 0 && userid < 9) { buf[1] = b32_5to8(userid);
buf[1] += userid; buf[2] = b32_5to8(bits);
}
if (bits >= 0 && bits < 9) {
buf[2] += bits;
}
strncat(buf, topdomain, 512 - strlen(buf)); strncat(buf, topdomain, 512 - strlen(buf));
send_query(fd, buf); send_query(fd, buf);
@ -705,6 +711,8 @@ main(int argc, char **argv)
device = NULL; device = NULL;
chunkid = 0; chunkid = 0;
packet.seqno = 0;
b32 = get_base32_encoder(); b32 = get_base32_encoder();
dataenc = get_base32_encoder(); dataenc = get_base32_encoder();

View file

@ -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)); 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 static void
handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len) 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; 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') { } else if(in[0] == 'Z' || in[0] == 'z') {
/* Check for case conservation and chars not allowed according to RFC */ /* 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; return;
} }
userid = in[1] & 0x7; userid = b32_8to5(in[1]);
if (ip_cmp(userid, q) != 0) { if (ip_cmp(userid, q) != 0) {
write_dns(dns_fd, q, "BADIP", 5); write_dns(dns_fd, q, "BADIP", 5);
return; /* illegal id */ return; /* illegal id */
} }
codec = in[2] & 0xF; codec = b32_8to5(in[2]);
switch (codec) { switch (codec) {
case 5: /* 5 bits per byte = base32 */ case 5: /* 5 bits per byte = base32 */
enc = get_base32_encoder(); enc = get_base32_encoder();
@ -288,6 +306,25 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len)
break; break;
} }
return; 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') } else if((in[0] >= '0' && in[0] <= '9')
|| (in[0] >= 'a' && in[0] <= 'f') || (in[0] >= 'a' && in[0] <= 'f')
|| (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')) if ((in[0] >= 'A' && in[0] <= 'F'))
code = in[0] - 'A' + 10; code = in[0] - 'A' + 10;
userid = code >> 1; userid = code;
if (userid < 0 || userid >= USERS) { if (userid < 0 || userid >= USERS) {
write_dns(dns_fd, q, "BADIP", 5); write_dns(dns_fd, q, "BADIP", 5);
return; /* illegal id */ 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) { if (check_ip && ip_cmp(userid, q) != 0) {
write_dns(dns_fd, q, "BADIP", 5); write_dns(dns_fd, q, "BADIP", 5);
} else { } else {
/* decode with this users encoding */ /* Decode data header */
read = unpack_data(unpacked, sizeof(unpacked), &(in[1]), domain_len - 1, int up_seq = (b32_8to5(in[1]) >> 2) & 7;
users[userid].encoder); 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); users[userid].last_pkt = time(NULL);
memcpy(&(users[userid].q), q, sizeof(struct query)); 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); memcpy(users[userid].inpacket.data + users[userid].inpacket.offset, unpacked, read);
users[userid].inpacket.len += read; users[userid].inpacket.len += read;
users[userid].inpacket.offset += 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); outlen = sizeof(out);
uncompress((uint8_t*)out, &outlen, ret = uncompress((uint8_t*)out, &outlen,
(uint8_t*)users[userid].inpacket.data, users[userid].inpacket.len); (uint8_t*)users[userid].inpacket.data, users[userid].inpacket.len);
hdr = (struct ip*) (out + 4); if (ret == Z_OK) {
touser = find_user_by_ip(hdr->ip_dst.s_addr); hdr = (struct ip*) (out + 4);
touser = find_user_by_ip(hdr->ip_dst.s_addr);
if (touser == -1) { if (touser == -1) {
/* send the uncompressed packet to tun device */ /* send the uncompressed packet to tun device */
write_tun(tun_fd, out, outlen); write_tun(tun_fd, out, outlen);
} else { } else {
/* send the compressed packet to other client /* send the compressed packet to other client
* if another packet is queued, throw away this one. TODO build queue */ * if another packet is queued, throw away this one. TODO build queue */
if (users[touser].outpacket.len == 0) { if (users[touser].outpacket.len == 0) {
memcpy(users[touser].outpacket.data, users[userid].inpacket.data, users[userid].inpacket.len); memcpy(users[touser].outpacket.data, users[userid].inpacket.data, users[userid].inpacket.len);
users[touser].outpacket.len = 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; 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 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); len = dns_encode_ns_response(buf, sizeof(buf), q, topdomain);
if (debug >= 1) { if (debug >= 2) {
struct sockaddr_in *tempin; struct sockaddr_in *tempin;
tempin = (struct sockaddr_in *) &(q->from); tempin = (struct sockaddr_in *) &(q->from);
printf("TX: client %s, type %d, name %s, %d bytes NS reply\n", 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)); memcpy(&(myaddr->sin_addr), &newaddr, sizeof(in_addr_t));
myaddr->sin_port = htons(bind_port); myaddr->sin_port = htons(bind_port);
if (debug >= 1) { if (debug >= 2) {
printf("TX: NS reply \n"); printf("TX: NS reply \n");
} }
@ -422,18 +483,18 @@ tunnel_bind(int bind_fd, int dns_fd)
id = dns_get_id(packet, r); id = dns_get_id(packet, r);
if (debug >= 1) { if (debug >= 2) {
printf("RX: Got response on query %u from DNS\n", (id & 0xFFFF)); printf("RX: Got response on query %u from DNS\n", (id & 0xFFFF));
} }
/* Get sockaddr from id */ /* Get sockaddr from id */
fw_query_get(id, &query); fw_query_get(id, &query);
if (!query && debug >= 1) { if (!query && debug >= 2) {
printf("Lost sender of id %u, dropping reply\n", (id & 0xFFFF)); printf("Lost sender of id %u, dropping reply\n", (id & 0xFFFF));
return 0; return 0;
} }
if (debug >= 1) { if (debug >= 2) {
struct sockaddr_in *in; struct sockaddr_in *in;
in = (struct sockaddr_in *) &(query->addr); in = (struct sockaddr_in *) &(query->addr);
printf("TX: client %s id %u, %d bytes\n", 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) if ((read = read_dns(dns_fd, &q)) <= 0)
return 0; return 0;
if (debug >= 1) { if (debug >= 2) {
struct sockaddr_in *tempin; struct sockaddr_in *tempin;
tempin = (struct sockaddr_in *) &(q.from); tempin = (struct sockaddr_in *) &(q.from);
printf("RX: client %s, type %d, name %s\n", 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; int j;
for (j = 0; j < USERS; j++) { for (j = 0; j < USERS; j++) {
if (users[j].q.id != 0) { 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); write_dns(dns_fd, &(users[j].q), users[j].outpacket.data, users[j].outpacket.len);
users[j].outpacket.len = 0; users[j].outpacket.len = 0;
users[j].q.id = 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); len = dns_encode(buf, sizeof(buf), q, QR_ANSWER, data, datalen);
if (debug >= 1) { if (debug >= 2) {
struct sockaddr_in *tempin; struct sockaddr_in *tempin;
tempin = (struct sockaddr_in *) &(q->from); tempin = (struct sockaddr_in *) &(q->from);
printf("TX: client %s, type %d, name %s, %d bytes data\n", printf("TX: client %s, type %d, name %s, %d bytes data\n",

View file

@ -50,6 +50,8 @@ init_users(in_addr_t my_ip)
users[i].inpacket.offset = 0; users[i].inpacket.offset = 0;
users[i].outpacket.len = 0; users[i].outpacket.len = 0;
users[i].q.id = 0; users[i].q.id = 0;
users[i].out_acked_seqno = 0;
users[i].out_acked_fragment = 0;
} }
} }

View file

@ -17,7 +17,7 @@
#ifndef __USER_H__ #ifndef __USER_H__
#define __USER_H__ #define __USER_H__
#define USERS 8 #define USERS 16
struct user { struct user {
char id; char id;
@ -30,6 +30,8 @@ struct user {
struct packet inpacket; struct packet inpacket;
struct packet outpacket; struct packet outpacket;
struct encoder *encoder; struct encoder *encoder;
int out_acked_seqno;
int out_acked_fragment;
}; };
extern struct user users[USERS]; extern struct user users[USERS];