Implemented new protocol for upstream data

This commit is contained in:
Erik Ekman 2008-12-11 22:39:06 +00:00
parent 458b5af003
commit 89fdbc104b
6 changed files with 161 additions and 81 deletions

View file

@ -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:

View file

@ -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--;
}

View file

@ -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();

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));
}
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",

View file

@ -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;
}
}

View file

@ -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];