mirror of
https://github.com/yarrick/iodine.git
synced 2024-12-24 00:28:19 +00:00
Implemented new protocol for upstream data
This commit is contained in:
parent
458b5af003
commit
89fdbc104b
@ -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:
|
||||
|
@ -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--;
|
||||
}
|
||||
|
52
src/iodine.c
52
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();
|
||||
|
||||
|
151
src/iodined.c
151
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",
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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];
|
||||
|
Loading…
x
Reference in New Issue
Block a user