#75, update client code

This commit is contained in:
Erik Ekman 2009-09-20 15:11:14 +00:00
parent bd4a4eb24c
commit 83dfde0728
2 changed files with 318 additions and 40 deletions

View File

@ -14,6 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <ctype.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
@ -75,12 +76,20 @@ static char userid;
/* DNS id for next packet */
static uint16_t chunkid;
/* Base32 encoder used for non-data packets */
/* Base32 encoder used for non-data packets and replies */
static struct encoder *b32;
/* Base64 encoder for replies */
static struct encoder *b64;
/* The encoder used for data packets
* Defaults to Base32, can be changed after handshake */
static struct encoder *dataenc;
/* The encoder to use for downstream data */
static char downenc = ' ';
/* set query type to send */
static unsigned short do_qtype = T_NULL;
/* My connection mode */
static enum connection conn;
@ -95,10 +104,11 @@ client_init()
downstream_fragment = 0;
down_ack_seqno = 0;
down_ack_fragment = 0;
chunkid = 0;
chunkid = ((unsigned int) rand()) & 0xFFFF;
b32 = get_base32_encoder();
b64 = get_base64_encoder();
dataenc = get_base32_encoder();
rand_seed = rand();
rand_seed = ((unsigned int) rand()) & 0xFFFF;
conn = CONN_DNS_NULL;
}
@ -140,6 +150,32 @@ client_set_password(const char *cp)
password = cp;
}
void
set_qtype(char *qtype)
{
if (!strcasecmp(qtype, "NULL"))
do_qtype = T_NULL;
else if (!strcasecmp(qtype, "CNAME"))
do_qtype = T_CNAME;
else if (!strcasecmp(qtype, "A"))
do_qtype = T_A;
else if (!strcasecmp(qtype, "MX"))
do_qtype = T_MX;
else if (!strcasecmp(qtype, "TXT"))
do_qtype = T_TXT;
}
void
set_downenc(char *encoding)
{
if (!strcasecmp(encoding, "base32"))
downenc = 'T';
else if (!strcasecmp(encoding, "base64"))
downenc = 'S';
else if (!strcasecmp(encoding, "raw"))
downenc = 'R';
}
const char *
client_get_raw_addr()
{
@ -153,10 +189,19 @@ send_query(int fd, char *hostname)
struct query q;
size_t len;
q.id = ++chunkid;
q.type = T_NULL;
chunkid += 7727;
if (chunkid == 0)
/* 0 is used as "no-query" in iodined.c */
chunkid = 7727;
q.id = chunkid;
q.type = do_qtype;
len = dns_encode(packet, sizeof(packet), &q, QR_QUERY, hostname, strlen(hostname));
if (len < 1) {
warnx("dns_encode doesn't fit");
return;
}
sendto(fd, packet, len, 0, (struct sockaddr*)&nameserv, sizeof(nameserv));
}
@ -207,7 +252,7 @@ is_sending()
static void
send_chunk(int fd)
{
char hex[] = "0123456789ABCDEF";
char hex[] = "0123456789abcdef";
char buf[4096];
int avail;
int code;
@ -261,7 +306,7 @@ send_ping(int fd)
rand_seed++;
send_packet(fd, 'P', data, sizeof(data));
send_packet(fd, 'p', data, sizeof(data));
} else {
send_raw(fd, NULL, 0, userid, RAW_HDR_CMD_PING);
}
@ -288,6 +333,88 @@ read_dns(int dns_fd, int tun_fd, char *buf, int buflen) /* FIXME: tun_fd needed
rv = dns_decode(buf, buflen, &q, QR_ANSWER, data, r);
if ((q.type == T_CNAME || q.type == T_MX || q.type == T_TXT)
&& rv >= 1)
/* CNAME an also be returned from an A (or MX) question */
{
size_t space;
/*
* buf is a hostname or txt stream that we still need to
* decode to binary
*
* also update rv with the number of valid bytes
*
* data is unused here, and will certainly hold the smaller binary
*/
switch (buf[0]) {
case 'h': /* Hostname with base32 */
case 'H':
if (rv < 5) {
/* 1 byte H, 3 bytes ".xy", >=1 byte data */
rv = 0;
break;
}
rv -= 3; /* rv=strlen, strip ".xy" */
rv = unpack_data (data, sizeof(data), buf + 1, rv - 1, b32);
/* this also does undotify */
rv = MIN(rv, buflen);
memcpy(buf, data, rv);
break;
case 'i': /* Hostname++ with base64 */
case 'I':
if (rv < 5) {
/* 1 byte H, 3 bytes ".xy", >=1 byte data */
rv = 0;
break;
}
rv -= 3; /* rv=strlen, strip ".xy" */
rv = unpack_data (data, sizeof(data), buf + 1, rv - 1, b64);
/* this also does undotify */
rv = MIN(rv, buflen);
memcpy(buf, data, rv);
break;
case 't': /* plain base32(Thirty-two) from TXT */
case 'T':
if (rv < 2) {
rv = 0;
break;
}
space = sizeof(data);
rv = b32->decode (data, &space, buf + 1, rv - 1);
rv = MIN(rv, buflen);
memcpy(buf, data, rv);
break;
case 's': /* plain base64(Sixty-four) from TXT */
case 'S':
if (rv < 2) {
rv = 0;
break;
}
space = sizeof(data);
rv = b64->decode (data, &space, buf + 1, rv - 1);
rv = MIN(rv, buflen);
memcpy(buf, data, rv);
break;
case 'r': /* Raw binary from TXT */
case 'R':
rv--; /* rv>=1 already checked */
memmove(buf, buf+1, rv);
break;
default:
warnx("Received unsupported encoding");
rv = 0;
break;
}
}
/* decode the data header, update seqno and frag before next request */
if (rv >= 2) {
downstream_seqno = (buf[1] >> 5) & 7;
@ -311,7 +438,7 @@ read_dns(int dns_fd, int tun_fd, char *buf, int buflen) /* FIXME: tun_fd needed
downstream_seqno != down_ack_seqno ||
downstream_fragment != down_ack_fragment
)) {
send_ping(dns_fd);
}
} else {
@ -487,7 +614,7 @@ send_login(int fd, char *login, int len)
rand_seed++;
send_packet(fd, 'L', data, sizeof(data));
send_packet(fd, 'l', data, sizeof(data));
}
static void
@ -496,9 +623,12 @@ send_fragsize_probe(int fd, int fragsize)
char probedata[256];
char buf[4096];
/* build a large query domain which is random and maximum size */
memset(probedata, MIN(1, rand_seed & 0xff), sizeof(probedata));
probedata[1] = MIN(1, (rand_seed >> 8) & 0xff);
/*
* build a large query domain which is random and maximum size,
* will also take up maximal space in the return packet
*/
memset(probedata, MAX(1, rand_seed & 0xff), sizeof(probedata));
probedata[1] = MAX(1, (rand_seed >> 8) & 0xff);
rand_seed++;
build_hostname(buf + 4, sizeof(buf) - 4, probedata, sizeof(probedata), topdomain, dataenc);
@ -525,14 +655,14 @@ send_set_downstream_fragsize(int fd, int fragsize)
rand_seed++;
send_packet(fd, 'N', data, sizeof(data));
send_packet(fd, 'n', data, sizeof(data));
}
static void
send_version(int fd, uint32_t version)
{
char data[6];
data[0] = (version >> 24) & 0xff;
data[1] = (version >> 16) & 0xff;
data[2] = (version >> 8) & 0xff;
@ -585,7 +715,7 @@ send_case_check(int fd)
static void
send_codec_switch(int fd, int userid, int bits)
{
char buf[512] = "S_____.";
char buf[512] = "s_____.";
buf[1] = b32_5to8(userid);
buf[2] = b32_5to8(bits);
@ -597,6 +727,23 @@ send_codec_switch(int fd, int userid, int bits)
strncat(buf, topdomain, 512 - strlen(buf));
send_query(fd, buf);
}
static void
send_downenc_switch(int fd, int userid)
{
char buf[512] = "o_____.";
buf[1] = b32_5to8(userid);
buf[2] = tolower(downenc);
buf[3] = b32_5to8((rand_seed >> 10) & 0x1f);
buf[4] = b32_5to8((rand_seed >> 5) & 0x1f);
buf[5] = b32_5to8((rand_seed ) & 0x1f);
rand_seed++;
strncat(buf, topdomain, 512 - strlen(buf));
send_query(fd, buf);
}
static int
handshake_version(int dns_fd, int *seed)
@ -657,7 +804,7 @@ handshake_version(int dns_fd, int *seed)
fprintf(stderr, "Retrying version check...\n");
}
warnx("couldn't connect to server");
warnx("couldn't connect to server (maybe other -T options will work)");
return 1;
}
@ -891,7 +1038,7 @@ handshake_switch_codec(int dns_fd)
int read;
dataenc = get_base64_encoder();
fprintf(stderr, "Switching to %s codec\n", dataenc->name);
fprintf(stderr, "Switching upstream to %s codec\n", dataenc->name);
/* Send to server that this user will use base64 from now on */
for (i=0; running && i<5 ;i++) {
int bits;
@ -922,7 +1069,7 @@ handshake_switch_codec(int dns_fd)
goto codec_revert;
}
in[read] = 0; /* zero terminate */
fprintf(stderr, "Server switched to codec %s\n", in);
fprintf(stderr, "Server switched upstream to codec %s\n", in);
return;
}
}
@ -935,6 +1082,138 @@ codec_revert:
dataenc = get_base32_encoder();
}
static void
handshake_switch_downenc(int dns_fd)
{
struct timeval tv;
char in[4096];
fd_set fds;
int i;
int r;
int read;
char *dname;
dname = "Base32";
if (downenc == 'S')
dname = "Base64";
else if (downenc == 'R')
dname = "Raw";
fprintf(stderr, "Switching downstream to codec %s\n", dname);
for (i=0; running && i<5 ;i++) {
tv.tv_sec = i + 1;
tv.tv_usec = 0;
send_downenc_switch(dns_fd, userid);
FD_ZERO(&fds);
FD_SET(dns_fd, &fds);
r = select(dns_fd + 1, &fds, NULL, NULL, &tv);
if(r > 0) {
read = read_dns(dns_fd, 0, in, sizeof(in));
if (read > 0) {
if (strncmp("BADLEN", in, 6) == 0) {
fprintf(stderr, "Server got bad message length. ");
goto codec_revert;
} else if (strncmp("BADIP", in, 5) == 0) {
fprintf(stderr, "Server rejected sender IP address. ");
goto codec_revert;
} else if (strncmp("BADCODEC", in, 8) == 0) {
fprintf(stderr, "Server rejected the selected codec. ");
goto codec_revert;
}
in[read] = 0; /* zero terminate */
fprintf(stderr, "Server switched downstream to codec %s\n", in);
return;
}
}
fprintf(stderr, "Retrying codec switch...\n");
}
fprintf(stderr, "No reply from server on codec switch. ");
codec_revert:
fprintf(stderr, "Falling back to base32\n");
}
static int
fragsize_check(char *in, int read, int proposed_fragsize, int *max_fragsize)
/* Returns: 0: keep checking, 1: break loop (either okay or definitely wrong) */
{
int acked_fragsize = ((in[0] & 0xff) << 8) | (in[1] & 0xff);
static int nocheck_warned = 0;
if (read >= 5 && strncmp("BADIP", in, 5) == 0) {
fprintf(stderr, "got BADIP (Try iodined -c)..\n");
fflush(stderr);
return 0; /* maybe temporary error */
}
if (acked_fragsize != proposed_fragsize) {
/*
* got ack for wrong fragsize, maybe late response for
* earlier query, or ack corrupted
*/
return 0;
}
if (read != proposed_fragsize) {
/*
* correctly acked fragsize but read too little (or too
* much): this fragsize is definitely not reliable
*/
return 1;
}
/* here: read == proposed_fragsize == acked_fragsize */
/* test: */
/* in[123] = 123; */
/* Check for corruption */
if ((in[2] & 0xff) == 107) {
int okay = 1;
int i;
unsigned int v = in[3] & 0xff;
for (i = 3; i < read; i++, v += 107)
if ((in[i] & 0xff) != (v & 0xff)) {
okay = 0;
break;
}
if (okay) {
fprintf(stderr, "%d ok.. ", acked_fragsize);
fflush(stderr);
*max_fragsize = acked_fragsize;
return 1;
} else {
if (downenc != ' ' && downenc != 'T')
fprintf(stderr, "%d corrupted at %d.. (Try -O Base32)\n", acked_fragsize, i);
else
fprintf(stderr, "%d corrupted at %d.. ", acked_fragsize, i);
fflush(stderr);
return 1;
}
} /* always returns */
/* here when uncheckable, so assume correct */
if (read >= 3 && nocheck_warned == 0) {
fprintf(stderr, "(Old server version, cannot check for corruption)\n");
fflush(stderr);
nocheck_warned = 1;
}
fprintf(stderr, "%d ok.. ", acked_fragsize);
fflush(stderr);
*max_fragsize = acked_fragsize;
return 1;
}
static int
handshake_autoprobe_fragsize(int dns_fd)
{
@ -946,11 +1225,12 @@ handshake_autoprobe_fragsize(int dns_fd)
int read;
int proposed_fragsize = 768;
int range = 768;
int max_fragsize = 0;
int max_fragsize;
max_fragsize = 0;
fprintf(stderr, "Autoprobing max downstream fragment size... (skip with -m fragsize)\n");
while (running && range > 0 && (range >= 8 || !max_fragsize)) {
while (running && range > 0 && (range >= 8 || max_fragsize < 300)) {
/* stop the slow probing early when we have enough bytes anyway */
for (i=0; running && i<3 ;i++) {
tv.tv_sec = 1;
tv.tv_usec = 0;
@ -961,24 +1241,13 @@ handshake_autoprobe_fragsize(int dns_fd)
r = select(dns_fd + 1, &fds, NULL, NULL, &tv);
if(r > 0) {
if(r >= 2) {
read = read_dns(dns_fd, 0, in, sizeof(in));
if (read > 0) {
/* We got a reply */
int acked_fragsize = ((in[0] & 0xff) << 8) | (in[1] & 0xff);
if (acked_fragsize == proposed_fragsize) {
if (read == proposed_fragsize) {
fprintf(stderr, "%d ok.. ", acked_fragsize);
fflush(stderr);
max_fragsize = acked_fragsize;
}
}
if (strncmp("BADIP", in, 5) == 0) {
fprintf(stderr, "got BADIP.. ");
fflush(stderr);
}
break;
if (fragsize_check(in, read, proposed_fragsize, &max_fragsize) == 1)
break;
}
}
fprintf(stderr, ".");
@ -997,17 +1266,22 @@ handshake_autoprobe_fragsize(int dns_fd)
}
if (!running) {
fprintf(stderr, "\n");
warnx("stopped while autodetecting fragment size (Try probing manually with -m)");
warnx("stopped while autodetecting fragment size (Try setting manually with -m)");
return 0;
}
if (range == 0) {
if (max_fragsize <= 2) {
/* Tried all the way down to 2 and found no good size */
fprintf(stderr, "\n");
warnx("found no accepted fragment size. (Try probing manually with -m)");
warnx("found no accepted fragment size. (Try forcing with -m, or try other -T or -O options)");
return 0;
}
fprintf(stderr, "will use %d\n", max_fragsize);
return max_fragsize;
/* data header adds 2 bytes */
fprintf(stderr, "will use %d-2=%d\n", max_fragsize, max_fragsize - 2);
if (do_qtype != T_NULL && downenc == ' ')
fprintf(stderr, "(Maybe other -O options will increase throughput)\n");
return max_fragsize - 2;
}
static void
@ -1084,6 +1358,10 @@ client_handshake(int dns_fd, int raw_mode, int autodetect_frag_size, int fragsiz
handshake_switch_codec(dns_fd);
}
if (downenc != ' ') {
handshake_switch_downenc(dns_fd);
}
if (autodetect_frag_size) {
fragsize = handshake_autoprobe_fragsize(dns_fd);
if (!fragsize) {

View File

@ -29,7 +29,7 @@ get_resolvconf_addr()
rv = NULL;
if ((fp = fopen("/etc/resolv.conf", "r")) == NULL)
err(1, "/etc/resolve.conf");
err(1, "/etc/resolv.conf");
while (feof(fp) == 0) {
fgets(buf, sizeof(buf), fp);