From 7eb7c02e5f56d725c6a3999a26f6a7e27e922af6 Mon Sep 17 00:00:00 2001
From: Erik Ekman <yarrick@kryo.se>
Date: Sat, 6 Dec 2008 19:08:14 +0000
Subject: [PATCH] Updated encoding tests

---
 src/common.h   |  19 ++++++++
 src/iodine.c   | 124 ++++++++++++++++++++++++++++++-------------------
 tests/base32.c |  10 +++-
 tests/base64.c |  10 +++-
 4 files changed, 111 insertions(+), 52 deletions(-)

diff --git a/src/common.h b/src/common.h
index c87722c..be318c7 100644
--- a/src/common.h
+++ b/src/common.h
@@ -30,6 +30,23 @@
 
 #define QUERY_NAME_SIZE 256
 
+#define MAKE_HEADER(c,u) ((((c) & 0xf) << 4) | ((u) & 0xf))
+#define HEADER_GET_COMMAND(h) (((h) & 0xf0) >> 4)
+#define HEADER_GET_USERID(h) ((h) & 0x0f)
+
+enum header_command {
+	CMD_VERSION = 0,
+	CMD_LOGIN,
+	CMD_CASE_CHECK,
+	CMD_CODEC_SWITCH,
+	CMD_DATA,
+	CMD_PING
+};
+
+#define DATAHEADER_TOTLEN 3
+#define MAKE_DATAHEADER(last,compressed,fragno) \
+	((((last) & 0x1) << 7) | (((compressed) & 0x1) << 6) | ((fragno) & 0x3f))
+
 #if defined IP_RECVDSTADDR 
 # define DSTADDR_SOCKOPT IP_RECVDSTADDR 
 # define dstaddr(x) (CMSG_DATA(x)) 
@@ -44,6 +61,8 @@ struct packet
 	int sentlen;		/* Length of chunk currently transmitted */
 	int offset;		/* Current offset */
 	char data[64*1024];	/* The data */
+	char seqno;		/* The packet sequence number */
+	char fragment;		/* Fragment index */
 };
 
 struct query {
diff --git a/src/iodine.c b/src/iodine.c
index a9ca24c..3910c16 100644
--- a/src/iodine.c
+++ b/src/iodine.c
@@ -69,10 +69,7 @@ static char userid;
 /* DNS id for next packet */
 static uint16_t chunkid;
 
-/* Base32 encoder used for non-data packets */
-static struct encoder *b32;
-
-/* The encoder used for data packets
+/* The encoder used for packets
  * Defaults to Base32, can be changed after handshake */
 static struct encoder *dataenc;
 
@@ -105,13 +102,11 @@ send_query(int fd, char *hostname)
 }
 
 static void
-send_packet(int fd, char cmd, const char *data, const size_t datalen)
+send_packet(int fd, const char *data, const size_t datalen)
 {
 	char buf[4096];
-
-	buf[0] = cmd;
-	
-	build_hostname(buf + 1, sizeof(buf) - 1, data, datalen, topdomain, b32);
+	/* Encode the data and add the topdomain */
+	build_hostname(buf, sizeof(buf), data, datalen, topdomain, dataenc);
 	send_query(fd, buf);
 }
 
@@ -120,7 +115,6 @@ build_hostname(char *buf, size_t buflen,
 		const char *data, const size_t datalen, 
 		const char *topdomain, struct encoder *encoder)
 {
-	int encsize;
 	size_t space;
 	char *b;
 
@@ -131,7 +125,7 @@ build_hostname(char *buf, size_t buflen,
 
 	memset(buf, 0, buflen);
 	
-	encsize = encoder->encode(buf, &space, data, datalen);
+	encoder->encode(buf, &space, data, datalen);
 
 	if (!encoder->places_dots())
 		inline_dotify(buf, buflen);
@@ -209,6 +203,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);
 
@@ -287,26 +283,51 @@ tunnel(int tun_fd, int dns_fd)
 static void
 send_chunk(int fd)
 {
-	char hex[] = "0123456789ABCDEF";
-	char buf[4096];
+	char buf[2048];
+	char header[64];
+	char headerenc[64];
 	int avail;
-	int code;
 	char *p;
+	int rawblock;
+	int headerblocks;
+	int pkt_encoffset;
+	int buf_encoffset;
+	int sentlen;
+	int i;
+	size_t enclen;
 
+	rawblock = dataenc->blocksize_raw();
+
+	headerblocks = 1;
+	while (headerblocks * rawblock < DATAHEADER_TOTLEN) {
+		headerblocks++;
+	}
+	rawblock *= headerblocks;
+
+	pkt_encoffset = rawblock - DATAHEADER_TOTLEN;
+	buf_encoffset = dataenc->blocksize_encoded() * headerblocks;
+
+	/* Encode all of the message except the first headerblocks block */
 	p = packet.data;
 	p += packet.offset;
-	avail = packet.len - packet.offset;
+	avail = packet.len - packet.offset + pkt_encoffset;
+	sentlen = build_hostname(buf + buf_encoffset, sizeof(buf) - buf_encoffset, 
+		&p[pkt_encoffset], avail, topdomain, dataenc);
 
-	packet.sentlen = build_hostname(buf + 1, sizeof(buf) - 1, p, avail, topdomain, dataenc);
-
-	if (packet.sentlen == avail)
-		code = 1;
-	else
-		code = 0;
-		
-	code |= (userid << 1);
-	buf[0] = hex[code];
+	packet.sentlen = sentlen + pkt_encoffset;
 
+	/* Then encode the header and maybe some pkt data */
+	header[0] = MAKE_HEADER(CMD_DATA, userid);
+	header[1] = packet.seqno;
+	header[2] = MAKE_DATAHEADER(avail == sentlen, 1, packet.fragment++);
+	for (i = 0; i < pkt_encoffset; i++) {
+		header[i + DATAHEADER_TOTLEN] = p[i];
+	}
+	/* Encode the first part, copy it into the buffer */
+	dataenc->encode(headerenc, &enclen, header, DATAHEADER_TOTLEN + pkt_encoffset);
+	for (i = 0; i < enclen; i++) {
+		buf[i] = headerenc[i];
+	}
 	send_query(fd, buf);
 }
 
@@ -316,7 +337,7 @@ send_login(int fd, char *login, int len)
 	char data[19];
 
 	memset(data, 0, sizeof(data));
-	data[0] = userid;
+	data[0] = MAKE_HEADER(CMD_LOGIN, userid);
 	memcpy(&data[1], login, MIN(len, 16));
 
 	data[17] = (rand_seed >> 8) & 0xff;
@@ -324,7 +345,7 @@ send_login(int fd, char *login, int len)
 	
 	rand_seed++;
 
-	send_packet(fd, 'L', data, sizeof(data));
+	send_packet(fd, data, sizeof(data));
 }
 
 static void
@@ -338,31 +359,32 @@ send_ping(int fd)
 		packet.len = 0;
 	}
 
-	data[0] = userid;
+	data[0] = MAKE_HEADER(CMD_PING, userid);
 	data[1] = (rand_seed >> 8) & 0xff;
 	data[2] = (rand_seed >> 0) & 0xff;
 	
 	rand_seed++;
 
-	send_packet(fd, 'P', data, sizeof(data));
+	send_packet(fd, data, sizeof(data));
 }
 
 static void 
 send_version(int fd, uint32_t version)
 {
-	char data[6];
+	char data[7];
 
-	data[0] = (version >> 24) & 0xff;
-	data[1] = (version >> 16) & 0xff;
-	data[2] = (version >> 8) & 0xff;
-	data[3] = (version >> 0) & 0xff;
+	data[0] = MAKE_HEADER(CMD_VERSION, 0xf);
+	data[1] = (version >> 24) & 0xff;
+	data[2] = (version >> 16) & 0xff;
+	data[3] = (version >> 8) & 0xff;
+	data[4] = (version >> 0) & 0xff;
 
-	data[4] = (rand_seed >> 8) & 0xff;
-	data[5] = (rand_seed >> 0) & 0xff;
+	data[5] = (rand_seed >> 8) & 0xff;
+	data[6] = (rand_seed >> 0) & 0xff;
 	
 	rand_seed++;
 
-	send_packet(fd, 'V', data, sizeof(data));
+	send_packet(fd, data, sizeof(data));
 }
 
 static void
@@ -371,7 +393,15 @@ send_case_check(int fd)
 	/* The '+' plus character is not allowed according to RFC. 
 	 * Expect to get SERVFAIL or similar if it is rejected.
 	 */
-	char buf[512] = "zZ+-aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyY1234.";
+	char buf[512] = "__zZ+-aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyY1234.";
+	char header = MAKE_HEADER(CMD_CASE_CHECK, userid);
+	char enc[5];
+	unsigned enclen;
+
+	dataenc->encode(enc, &enclen, &header, 1);
+	/* Encode header into start of message */
+	buf[0] = enc[0];
+	buf[1] = enc[1];
 
 	strncat(buf, topdomain, 512 - strlen(buf));
 	send_query(fd, buf);
@@ -380,16 +410,12 @@ 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;
-	}
-	
-	strncat(buf, topdomain, 512 - strlen(buf));
-	send_query(fd, buf);
+	char data[2];
+
+	data[0] = MAKE_HEADER(CMD_VERSION, 0xf);
+	data[1] = bits & 0xff;
+
+	send_packet(fd, data, sizeof(data));
 }
 
 static int
@@ -705,8 +731,10 @@ main(int argc, char **argv)
 	device = NULL;
 	chunkid = 0;
 
-	b32 = get_base32_encoder();
 	dataenc = get_base32_encoder();
+
+	packet.seqno = 0;
+	packet.fragment = 0;
 	
 #if !defined(BSD) && !defined(__GLIBC__)
 	__progname = strrchr(argv[0], '/');
diff --git a/tests/base32.c b/tests/base32.c
index 849790c..723490b 100644
--- a/tests/base32.c
+++ b/tests/base32.c
@@ -38,12 +38,15 @@ START_TEST(test_base32_encode)
 {
 	size_t len;
 	char buf[4096];
+	struct encoder *b32;
 	int val;
 	int i;
 
+	b32 = get_base32_encoder();
+
 	for (i = 0; testpairs[i].a != NULL; i++) {
 		len = sizeof(buf);
-		val = base32_encode(buf, &len, testpairs[i].a, strlen(testpairs[i].a));
+		val = b32->encode(buf, &len, testpairs[i].a, strlen(testpairs[i].a));
 
 		fail_unless(val > 0, strerror(errno));
 		fail_unless(strcmp(buf, testpairs[i].b) == 0,
@@ -56,12 +59,15 @@ START_TEST(test_base32_decode)
 {
 	size_t len;
 	char buf[4096];
+	struct encoder *b32;
 	int val;
 	int i;
+	
+	b32 = get_base32_encoder();
 
 	for (i = 0; testpairs[i].a != NULL; i++) {
 		len = sizeof(buf);
-		val = base32_decode(buf, &len, testpairs[i].b, strlen(testpairs[i].b));
+		val = b32->decode(buf, &len, testpairs[i].b, strlen(testpairs[i].b));
 
 		fail_unless(val > 0, strerror(errno));
 		fail_unless(buf != NULL, "buf == NULL");
diff --git a/tests/base64.c b/tests/base64.c
index 8e8ca0f..99f629c 100644
--- a/tests/base64.c
+++ b/tests/base64.c
@@ -66,12 +66,15 @@ START_TEST(test_base64_encode)
 {
 	size_t len;
 	char buf[4096];
+	struct encoder *b64;
 	int val;
 	int i;
 
+	b64 = get_base64_encoder();
+
 	for (i = 0; testpairs[i].a != NULL; i++) {
 		len = sizeof(buf);
-		val = base64_encode(buf, &len, testpairs[i].a, strlen(testpairs[i].a));
+		val = b64->encode(buf, &len, testpairs[i].a, strlen(testpairs[i].a));
 
 		fail_unless(val > 0, strerror(errno));
 		fail_unless(strcmp(buf, testpairs[i].b) == 0,
@@ -84,12 +87,15 @@ START_TEST(test_base64_decode)
 {
 	size_t len;
 	char buf[4096];
+	struct encoder *b64;
 	int val;
 	int i;
 
+	b64 = get_base64_encoder();
+
 	for (i = 0; testpairs[i].a != NULL; i++) {
 		len = sizeof(buf);
-		val = base64_decode(buf, &len, testpairs[i].b, strlen(testpairs[i].b));
+		val = b64->decode(buf, &len, testpairs[i].b, strlen(testpairs[i].b));
 
 		fail_unless(val > 0, strerror(errno));
 		fail_unless(buf != NULL, "buf == NULL");