Re-implemented working DNS tunnelling (with windows!)

This commit is contained in:
frekky 2015-08-29 20:11:21 +08:00
parent 9f6033c4b8
commit 0449c465e7
2 changed files with 192 additions and 186 deletions

View File

@ -61,6 +61,8 @@
static void handshake_lazyoff(int dns_fd); static void handshake_lazyoff(int dns_fd);
int debug;
static int running; static int running;
static const char *password; static const char *password;
@ -238,7 +240,7 @@ client_set_hostname_maxlen(size_t i)
{ {
if (i <= 0xFF && i != hostname_maxlen) { if (i <= 0xFF && i != hostname_maxlen) {
hostname_maxlen = i; hostname_maxlen = i;
outbuf->maxfraglen = get_raw_length(hostname_maxlen, dataenc, topdomain); outbuf->maxfraglen = get_raw_length_from_dns(hostname_maxlen - UPSTREAM_HDR, dataenc, topdomain);
} }
} }
@ -257,12 +259,15 @@ client_rotate_nameserver()
} }
static void static void
send_query(int fd, char *hostname) send_query(int fd, uint8_t *hostname)
{ {
char packet[4096]; uint8_t packet[4096];
struct query q; struct query q;
size_t len; size_t len;
if (debug >= 2)
fprintf(stderr, "TX: pkt len %lu: hostname '%s'\n", strlen((char *)hostname), hostname);
chunkid += 7727; chunkid += 7727;
if (chunkid == 0) if (chunkid == 0)
/* 0 is used as "no-query" in iodined.c */ /* 0 is used as "no-query" in iodined.c */
@ -271,15 +276,14 @@ send_query(int fd, char *hostname)
q.id = chunkid; q.id = chunkid;
q.type = do_qtype; q.type = do_qtype;
len = dns_encode(packet, sizeof(packet), &q, QR_QUERY, hostname, strlen(hostname)); len = dns_encode((char *)packet, sizeof(packet), &q, QR_QUERY, (char *)hostname, strlen((char *)hostname));
if (len < 1) { if (len < 1) {
warnx("dns_encode doesn't fit"); warnx("dns_encode doesn't fit");
return; return;
} }
#if 0 if (debug >= 3)
fprintf(stderr, " Sendquery: id %5d name[0] '%c'\n", q.id, hostname[0]); fprintf(stderr, " Sendquery: id %5d name[0] '%c'\n", q.id, hostname[0]);
#endif
sendto(fd, packet, len, 0, (struct sockaddr*) &nameserv_addrs[current_nameserver], sendto(fd, packet, len, 0, (struct sockaddr*) &nameserv_addrs[current_nameserver],
sizeof(struct sockaddr_storage)); sizeof(struct sockaddr_storage));
@ -347,14 +351,15 @@ send_raw_data(int dns_fd) // TODO: fix send_raw
static void static void
send_packet(int fd, char cmd, const char *data, const size_t datalen) send_packet(int fd, char cmd, const uint8_t *data, const size_t datalen)
/* Base32 encodes data and sends as single DNS query */ /* Base32 encodes data and sends as single DNS query */
{ {
char buf[4096]; uint8_t buf[4096];
buf[0] = cmd; buf[0] = cmd;
build_hostname(buf, sizeof(buf), data, datalen, topdomain, b32, hostname_maxlen, 1); build_hostname(buf, sizeof(buf), data, datalen, topdomain, b32, hostname_maxlen, 1);
send_query(fd, buf); send_query(fd, buf);
} }
@ -364,21 +369,28 @@ is_sending()
return (outbuf->numitems > 0); return (outbuf->numitems > 0);
} }
static inline int
is_recving()
{
return inbuf->numitems > 0;
}
static void static void
send_ping(int fd, int ping_response) // TODO: setup window sync stuff in ping send_ping(int fd, int ping_response, int ack) // TODO: setup window sync stuff in ping
{ {
if (conn == CONN_DNS_NULL) { if (conn == CONN_DNS_NULL) {
char data[4]; uint8_t data[9];
data[0] = userid; data[0] = userid;
data[1] = inbuf->start_seq_id & 0xff; data[1] = ack & 0xFF;
data[2] = outbuf->start_seq_id & 0xff; data[2] = outbuf->windowsize & 0xff;
data[3] = outbuf->windowsize & 0xff; data[3] = inbuf->windowsize & 0xff;
data[4] = inbuf->windowsize & 0xff; data[4] = inbuf->start_seq_id & 0xff; /* Downstream window start */
data[5] = ping_response & 1; data[5] = outbuf->start_seq_id & 0xff; /* Upstream window start */
data[6] = (rand_seed >> 8) & 0xff; data[6] = ((ack < 0 ? 0 : 1) << 2) | (ping_response & 1);
data[7] = (rand_seed >> 0) & 0xff; data[7] = (rand_seed >> 8) & 0xff;
rand_seed += 7; data[8] = (rand_seed >> 0) & 0xff;
rand_seed += 263;
send_packet(fd, 'p', data, sizeof(data)); send_packet(fd, 'p', data, sizeof(data));
} else { } else {
@ -390,49 +402,54 @@ static void
send_next_frag(int fd) send_next_frag(int fd)
/* Sends next available fragment of data from the outgoing window buffer */ /* Sends next available fragment of data from the outgoing window buffer */
{ {
static uint8_t buf[MAX_FRAGSIZE]; static uint8_t buf[MAX_FRAGSIZE], hdr[5];
int code; int code;
static int datacmc = 0; static int datacmc = 0;
static char *datacmcchars = "abcdefghijklmnopqrstuvwxyz0123456789"; static char *datacmcchars = "abcdefghijklmnopqrstuvwxyz0123456789";
fragment *f; fragment *f;
size_t buflen, len;
/* Get next fragment to send */ /* Get next fragment to send */
f = window_get_next_sending_fragment(outbuf, next_downstream_ack); f = window_get_next_sending_fragment(outbuf, &next_downstream_ack);
if (f == NULL) { if (!f) {
if (is_sending()) { if (is_sending()) {
/* There is stuff to send but we're out of sync, so send a ping /* There is stuff to send but we're out of sync, so send a ping
* to get things back in order and keep the packets flowing */ * to get things back in order and keep the packets flowing */
send_ping(fd, 1); send_ping(fd, 1, next_downstream_ack);
next_downstream_ack = -1;
window_tick(outbuf);
} }
return; /* nothing to send - why was this called? */ return; /* nothing to send - why was this called? */
} }
/* Note: must be same, or smaller than send_fragsize_probe() */
build_hostname((char *)buf, sizeof(buf), (char *)f->data, f->len, topdomain,
dataenc, hostname_maxlen, 6);
/* Build upstream data header (see doc/proto_xxxxxxxx.txt) */ /* Build upstream data header (see doc/proto_xxxxxxxx.txt) */
buf[0] = userid_char; /* First byte is hex userid */ buf[0] = userid_char; /* First byte is hex userid */
buf[1] = datacmcchars[datacmc]; /* Second byte is data-CMC */ buf[1] = datacmcchars[datacmc]; /* Second byte is data-CMC */
code = (f->seqID & 0xF8) >> 3; /* Next 3 bytes is seq ID, downstream ACK and flags */
buf[2] = b32_5to8(code); /* Second byte is 5 bits seqno */ code = ((f->ack_other < 0 ? 0 : 1) << 3) | (f->is_nack << 2)
| (f->start << 1) | f->end;
code = ((f->seqID & 7) << 2) | ((f->ack_other & 0xC0) >> 6); hdr[0] = f->seqID & 0xFF;
buf[3] = b32_5to8(code); /* Third byte is 3 bits seqno, 2 bits downstream ACK */ hdr[1] = f->ack_other & 0xFF;
hdr[2] = code << 4; /* Flags are in upper 4 bits - lower 4 unused */
code = ((f->ack_other & 0x3E) >> 1); buflen = sizeof(buf) - 1;
buf[4] = b32_5to8(code); /* Fourth byte is 5 bits downstream ACK */ /* Encode 3 bytes data into 2 bytes after buf */
len = b32->encode(buf + 2, &buflen, hdr, 3);
if (len != 5)
warnx("mismatch in encoded upstream header length! expected 5, got %lu", len);
code = (f->ack_other & 1) << 4 | ((f->ack_other < 0 ? 0 : 1) << 3) | (f->is_nack << 2) | (f->start << 1) | f->end; /* Encode data into buf after header (6 = user + CMC + 4 bytes header) */
buf[5] = b32_5to8(code); /* Fifth byte is 1 bit downstream ACK, bit flags isACK, isNACK, first, last */ build_hostname(buf, sizeof(buf), f->data, f->len, topdomain,
dataenc, hostname_maxlen, 6);
datacmc++; datacmc++;
if (datacmc >= 36) if (datacmc >= 36)
datacmc = 0; datacmc = 0;
send_query(fd, (char *)buf); send_query(fd, buf);
window_tick(outbuf); window_tick(outbuf);
} }
@ -477,8 +494,8 @@ write_dns_error(struct query *q, int ignore_some_errors)
} }
} }
static int static size_t
dns_namedec(char *outdata, int outdatalen, char *buf, int buflen) dns_namedec(uint8_t *outdata, size_t outdatalen, uint8_t *buf, size_t buflen)
/* Decodes *buf to *outdata. /* Decodes *buf to *outdata.
* *buf WILL be changed by undotify. * *buf WILL be changed by undotify.
* Note: buflen must be _exactly_ strlen(buf) before undotifying. * Note: buflen must be _exactly_ strlen(buf) before undotifying.
@ -496,8 +513,7 @@ dns_namedec(char *outdata, int outdatalen, char *buf, int buflen)
return 0; return 0;
/* this also does undotify */ /* this also does undotify */
return unpack_data(outdata, outdatalen, buf + 1, buflen - 4, return unpack_data(outdata, outdatalen, buf + 1, buflen - 4, b32);
b32);
case 'i': /* Hostname++ with base64 */ case 'i': /* Hostname++ with base64 */
case 'I': case 'I':
@ -506,8 +522,7 @@ dns_namedec(char *outdata, int outdatalen, char *buf, int buflen)
return 0; return 0;
/* this also does undotify */ /* this also does undotify */
return unpack_data(outdata, outdatalen, buf + 1, buflen - 4, return unpack_data(outdata, outdatalen, buf + 1, buflen - 4, b64);
b64);
case 'j': /* Hostname++ with base64u */ case 'j': /* Hostname++ with base64u */
case 'J': case 'J':
@ -516,8 +531,7 @@ dns_namedec(char *outdata, int outdatalen, char *buf, int buflen)
return 0; return 0;
/* this also does undotify */ /* this also does undotify */
return unpack_data(outdata, outdatalen, buf + 1, buflen - 4, return unpack_data(outdata, outdatalen, buf + 1, buflen - 4, b64u);
b64u);
case 'k': /* Hostname++ with base128 */ case 'k': /* Hostname++ with base128 */
case 'K': case 'K':
@ -526,8 +540,7 @@ dns_namedec(char *outdata, int outdatalen, char *buf, int buflen)
return 0; return 0;
/* this also does undotify */ /* this also does undotify */
return unpack_data(outdata, outdatalen, buf + 1, buflen - 4, return unpack_data(outdata, outdatalen, buf + 1, buflen - 4, b128);
b128);
case 't': /* plain base32(Thirty-two) from TXT */ case 't': /* plain base32(Thirty-two) from TXT */
case 'T': case 'T':
@ -575,7 +588,7 @@ dns_namedec(char *outdata, int outdatalen, char *buf, int buflen)
} }
static int static int
read_dns_withq(int dns_fd, int tun_fd, char *buf, int buflen, struct query *q) read_dns_withq(int dns_fd, int tun_fd, uint8_t *buf, size_t buflen, struct query *q)
/* Returns -1 on receive error or decode error, including DNS error replies. /* Returns -1 on receive error or decode error, including DNS error replies.
Returns 0 on replies that could be correct but are useless, and are not Returns 0 on replies that could be correct but are useless, and are not
DNS error replies. DNS error replies.
@ -583,7 +596,7 @@ read_dns_withq(int dns_fd, int tun_fd, char *buf, int buflen, struct query *q)
*/ */
{ {
struct sockaddr_storage from; struct sockaddr_storage from;
char data[64*1024]; uint8_t data[64*1024];
socklen_t addrlen; socklen_t addrlen;
int r; int r;
@ -600,7 +613,7 @@ read_dns_withq(int dns_fd, int tun_fd, char *buf, int buflen, struct query *q)
/* useless packet */ /* useless packet */
return 0; return 0;
rv = dns_decode(buf, buflen, q, QR_ANSWER, data, r); rv = dns_decode((char *)buf, buflen, q, QR_ANSWER, (char *)data, r);
if (rv <= 0) if (rv <= 0)
return rv; return rv;
@ -630,7 +643,7 @@ read_dns_withq(int dns_fd, int tun_fd, char *buf, int buflen, struct query *q)
int thispartlen, dataspace, datanew; int thispartlen, dataspace, datanew;
while (1) { while (1) {
thispartlen = strlen(buf); thispartlen = strlen((char *)buf);
thispartlen = MIN(thispartlen, buftotal-bufoffset); thispartlen = MIN(thispartlen, buftotal-bufoffset);
dataspace = sizeof(data) - dataoffset; dataspace = sizeof(data) - dataoffset;
if (thispartlen <= 0 || dataspace <= 0) if (thispartlen <= 0 || dataspace <= 0)
@ -681,7 +694,7 @@ read_dns_withq(int dns_fd, int tun_fd, char *buf, int buflen, struct query *q)
} }
static int static int
handshake_waitdns(int dns_fd, char *buf, int buflen, char c1, char c2, int timeout) handshake_waitdns(int dns_fd, char *buf, size_t buflen, char c1, char c2, int timeout)
/* Wait for DNS reply fitting to our latest query and returns it. /* Wait for DNS reply fitting to our latest query and returns it.
Returns length of reply = #bytes used in buf. Returns length of reply = #bytes used in buf.
Returns 0 if fitting reply happens to be useless. Returns 0 if fitting reply happens to be useless.
@ -713,12 +726,11 @@ handshake_waitdns(int dns_fd, char *buf, int buflen, char c1, char c2, int timeo
q.id = 0; q.id = 0;
q.name[0] = '\0'; q.name[0] = '\0';
rv = read_dns_withq(dns_fd, 0, buf, buflen, &q); rv = read_dns_withq(dns_fd, 0, (uint8_t *)buf, buflen, &q);
if (q.id != chunkid || (q.name[0] != c1 && q.name[0] != c2)) { if (q.id != chunkid || (q.name[0] != c1 && q.name[0] != c2)) {
#if 0 if (debug >= 1)
fprintf(stderr, "Ignoring unfitting reply id %d starting with '%c'\n", q.id, q.name[0]); fprintf(stderr, "Ignoring unfitting reply id %d starting with '%c'\n", q.id, q.name[0]);
#endif
continue; continue;
} }
@ -766,34 +778,37 @@ handshake_waitdns(int dns_fd, char *buf, int buflen, char c1, char c2, int timeo
static int static int
parse_data(uint8_t *data, size_t len, fragment *f) parse_data(uint8_t *data, size_t len, fragment *f)
{ {
size_t headerlen = DOWNSTREAM_HDR;
memset(f, 0, sizeof(fragment)); memset(f, 0, sizeof(fragment));
int ping = (data[3] >> 5) & 1; int ping = (data[2] >> 5) & 1;
if (!ping) { f->compressed = (data[2] >> 4) & 1;
f->ack_other = (data[2] >> 3) & 1 ? data[1] : -1;
f->is_nack = (data[2] >> 2) & 1;
f->start = (data[2] >> 1) & 1;
f->end = data[2] & 1;
f->seqID = data[0]; f->seqID = data[0];
f->start = data[3] & 1; if (ping) { /* Handle ping stuff */
f->end = (data[3] >> 1) & 1; headerlen = DOWNSTREAM_PING_HDR;
f->is_nack = (data[3] >> 2) & 1; if (len < headerlen) return -1; /* invalid packet - continue */
f->ack_other = (data[3] >> 3) & 1 ? data[1] : -1; /* Parse data/ping header */
f->compressed = (data[3] >> 4) & 1; static unsigned dn_start_seq, up_start_seq, dn_wsize, up_wsize;
f->len = len - 3; dn_wsize = data[3]; /* TODO: do something with wsize/start params in ping */
memcpy(f->data, data + 3, MIN(f->len, sizeof(f->data))); up_wsize = data[4];
} else { /* Handle ping stuff */ dn_start_seq = data[5];
if (len != 5) return 1; /* invalid packet - continue */ up_start_seq = data[6];
// static unsigned in_start_seq, out_start_seq, in_wsize, out_wsize; if (debug >= 3)
// out_start_seq = data[0]; fprintf(stderr, "PING pkt data=%lu WS: up=%u, dn=%u; Start: up=%u, dn=%u\n",
// in_start_seq = data[1]; len - headerlen, up_wsize, dn_wsize, up_start_seq, dn_start_seq);
// in_wsize = data[3];
// out_wsize = data[4];
warnx("Pingy thingy received.");
// TODO: handle pings
} }
f->len = len - headerlen;
if (f->len > 0) memcpy(f->data, data + headerlen, MIN(f->len, sizeof(f->data)));
return ping; /* return ping flag (if corresponding query was a ping) */ return ping; /* return ping flag (if corresponding query was a ping) */
} }
static int static int
tunnel_tun(int tun_fd, int dns_fd) tunnel_tun(int tun_fd, int dns_fd)
{ {
size_t outlen, inlen; size_t outlen;
uint8_t out[64*1024]; uint8_t out[64*1024];
uint8_t in[64*1024]; uint8_t in[64*1024];
ssize_t read; ssize_t read;
@ -801,20 +816,26 @@ tunnel_tun(int tun_fd, int dns_fd)
if ((read = read_tun(tun_fd, in, sizeof(in))) <= 0) if ((read = read_tun(tun_fd, in, sizeof(in))) <= 0)
return -1; return -1;
/* Give outgoing buffer all new data if it can hold it */ /* Check if outgoing buffer can hold data */
if (window_buffer_available(outbuf) > read / MAX_FRAGSIZE) if (window_buffer_available(outbuf) < (read / MAX_FRAGSIZE) + 1) {
if (debug >= 3)
fprintf(stderr, " Outgoing buffer full (%lu/%lu), not adding data!\n", outbuf->numitems, outbuf->length);
return -1; return -1;
}
if (debug >= 2)
fprintf(stderr, " IN: %lu bytes on tunnel\n", read);
outlen = sizeof(out); outlen = sizeof(out);
inlen = read; compress2(out, &outlen, in, read, 9);
compress2(out, &outlen, in, inlen, 9);
window_add_outgoing_data(outbuf, out, outlen, 1); window_add_outgoing_data(outbuf, out, outlen, 1);
if (conn == CONN_DNS_NULL) { if (conn == CONN_DNS_NULL) {
send_next_frag(dns_fd); send_next_frag(dns_fd);
send_ping_soon = 0; // TODO: adjust min send interval based on DNS server droppiness
send_ping_soon = is_sending() ? 2 : 0;
} else { } else {
send_raw_data(dns_fd); send_raw_data(dns_fd);
} }
@ -829,7 +850,7 @@ tunnel_dns(int tun_fd, int dns_fd)
static long packrecv_oos = 0; static long packrecv_oos = 0;
static long packrecv_servfail = 0; static long packrecv_servfail = 0;
static struct query q; static struct query q;
size_t datalen; size_t datalen, buflen;
static uint8_t buf[64*1024], cbuf[64*1024]; static uint8_t buf[64*1024], cbuf[64*1024];
static fragment f; static fragment f;
int read, compressed, res; int read, compressed, res;
@ -838,15 +859,13 @@ tunnel_dns(int tun_fd, int dns_fd)
memset(&q, 0, sizeof(q)); memset(&q, 0, sizeof(q));
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
memset(cbuf, 0, sizeof(cbuf)); memset(cbuf, 0, sizeof(cbuf));
read = read_dns_withq(dns_fd, tun_fd, (char *)cbuf, sizeof(cbuf), &q); read = read_dns_withq(dns_fd, tun_fd, cbuf, sizeof(cbuf), &q);
if (conn != CONN_DNS_NULL) if (conn != CONN_DNS_NULL)
return 1; /* everything already done */ return 1; /* everything already done */
#if 0 if (debug >= 2)
fprintf(stderr, " Recv: id %5d name[0]='%c'\n", fprintf(stderr, "RX: id %5d name[0]='%c'\n", q.id, q.name[0]);
q.id, q.name[0]);
#endif
/* Don't process anything that isn't data for us; usually error /* Don't process anything that isn't data for us; usually error
replies from fragsize probes etc. However a sequence of those, replies from fragsize probes etc. However a sequence of those,
@ -859,7 +878,7 @@ tunnel_dns(int tun_fd, int dns_fd)
return -1; /* nothing done */ return -1; /* nothing done */
} }
if (read < 3) { if (read < DOWNSTREAM_HDR) {
/* Maybe SERVFAIL etc. Send ping to get things back in order, /* Maybe SERVFAIL etc. Send ping to get things back in order,
but wait a bit to prevent fast ping-pong loops. */ but wait a bit to prevent fast ping-pong loops. */
@ -883,14 +902,6 @@ tunnel_dns(int tun_fd, int dns_fd)
} }
} }
/* read==1 happens with "QMEM" illegal replies, caused by
heavy reordering, or after short disconnections when
data-CMC has looped around into the "duplicate" values.
All these cases are helped by faster pinging. */
#if 0
if (read == 1)
fprintf(stderr, " q=%c id %5d 1-byte illegal \"QMEM\" reply\n", q.name[0], q.id);
#endif
send_ping_soon = 900; send_ping_soon = 900;
return -1; /* nothing done */ return -1; /* nothing done */
} }
@ -905,13 +916,17 @@ tunnel_dns(int tun_fd, int dns_fd)
send_ping_soon = 0; send_ping_soon = 0;
} }
/* Okay, we have a recent downstream packet */
lastdownstreamtime = time(NULL);
if (!(packrecv & 0x1000000))
packrecv++;
send_query_recvcnt++; /* overflow doesn't matter */
/* Decode the downstream data header and fragment-ify ready for processing */ /* Decode the downstream data header and fragment-ify ready for processing */
res = parse_data(cbuf, read, &f); res = parse_data(cbuf, read, &f);
if ((debug >= 3 && res) || (debug >= 2 && !res))
/* if this response was a reverse ping/response to a ping, we need to do something */ fprintf(stderr, " RX %s frag id %u, ACK %d, nack %d, datalen %lu, s%d e%d\n",
if (res) { res ? "PING" : "DATA", f.seqID, f.ack_other, f.is_nack, f.len, f.start, f.end);
goto skip_recv;
}
window_ack(outbuf, f.ack_other); window_ack(outbuf, f.ack_other);
@ -919,19 +934,16 @@ tunnel_dns(int tun_fd, int dns_fd)
query, only during heavy data transfer. Since this means the server query, only during heavy data transfer. Since this means the server
doesn't have any packets to send, send one relatively fast (but not doesn't have any packets to send, send one relatively fast (but not
too fast, to avoid runaway ping-pong loops..) */ too fast, to avoid runaway ping-pong loops..) */
/* Don't send anything too soon; no data waiting from server */
if (f.len == 0) { if (f.len == 0) {
if (!res && debug >= 1)
fprintf(stderr, "Received downstream data with 0 length and NOT a ping!");
send_ping_soon = 700; send_ping_soon = 700;
return -1; return -1;
} }
/* Get next ACK if nothing already pending */ /* Send something quickly to see if any more data needs to come through */
if (next_downstream_ack < 0) { send_ping_soon = 5;
if ((next_downstream_ack = window_get_next_ack(inbuf)) < 0) {
next_downstream_ack = -1;
} else {
send_something_now = 1;
}
}
/* Downstream data traffic */ /* Downstream data traffic */
if (!window_process_incoming_fragment(inbuf, &f)) { if (!window_process_incoming_fragment(inbuf, &f)) {
@ -940,40 +952,38 @@ tunnel_dns(int tun_fd, int dns_fd)
return -1; /* nothing done */ return -1; /* nothing done */
} }
// TODO: check OOS packet count and send ping to update params /* Get next ACK if nothing already pending: only do this if we are sending */
if (next_downstream_ack < 0) {
if ((next_downstream_ack = window_get_next_ack(inbuf)) >= 0) {
send_something_now = 1;
}
}
/* Okay, we have a recent downstream packet */ // TODO: check OOS packet count and send ping to update params
lastdownstreamtime = time(NULL);
if (!(packrecv & 0x1000000))
packrecv++;
send_query_recvcnt++; /* overflow doesn't matter */
datalen = window_reassemble_data(inbuf, cbuf, sizeof(cbuf), &compressed); datalen = window_reassemble_data(inbuf, cbuf, sizeof(cbuf), &compressed);
if (datalen != 0) { if (datalen != 0) {
if (compressed) { if (compressed) {
if ((res = uncompress(buf, &datalen, cbuf, datalen)) == Z_OK) { buflen = sizeof(buf);
write_tun(tun_fd, buf, datalen); if ((res = uncompress(buf, &buflen, cbuf, datalen)) != Z_OK) {
} else { if (debug >= 1)
warnx("Uncompress failed (%d): reassembled data corrupted or incomplete!", res); warnx("Uncompress failed (%d): reassembled data corrupted or incomplete!", res);
} }
send_ping_soon = 5; datalen = buflen;
} }
write_tun(tun_fd, buf, datalen);
/* ACK frag quickly */
send_ping_soon = 2;
} else { } else {
/* Send anything to ack the received seqno/frag, and get more */ /* ACK received frag and let server send any more data */
/* was last frag; wait just a trifle because our
tun will probably return TCP-ack immediately.
5msec = 200 DNSreq/sec */
/* server certainly has more data */
send_something_now = 1; send_something_now = 1;
} }
skip_recv:
/* Move window along after doing all data processing */ /* Move window along after doing all data processing */
window_tick(inbuf); window_tick(inbuf);
/* NOTE: buf[] was overwritten when down-packet complete */
/* Upstream data traffic */ /* Upstream data traffic */
if (is_sending()) { if (is_sending() || is_recving()) {
/* More to send - next fragment*/ /* More to send - next fragment*/
send_next_frag(dns_fd); send_next_frag(dns_fd);
send_ping_soon = 2; send_ping_soon = 2;
@ -982,7 +992,8 @@ tunnel_dns(int tun_fd, int dns_fd)
/* Send ping if we didn't send anything yet */ /* Send ping if we didn't send anything yet */
if (send_something_now) { if (send_something_now) {
send_ping(dns_fd, 0); send_ping(dns_fd, 0, next_downstream_ack);
next_downstream_ack = -1;
send_ping_soon = 0; send_ping_soon = 0;
} }
@ -1006,9 +1017,15 @@ client_tunnel(int tun_fd, int dns_fd)
tv.tv_usec = 0; tv.tv_usec = 0;
if (is_sending()) { if (is_sending()) {
/* fast timeout for retransmits */ tv.tv_sec = 0;
tv.tv_sec = 1; tv.tv_usec = 2000;
tv.tv_usec = 0; send_ping_soon = 0;
}
if (is_recving()) {
/* get next thingy soon ish */
tv.tv_sec = 0;
tv.tv_usec = 5000;
} }
if (send_ping_soon) { if (send_ping_soon) {
@ -1017,10 +1034,9 @@ client_tunnel(int tun_fd, int dns_fd)
} }
FD_ZERO(&fds); FD_ZERO(&fds);
if (window_buffer_available(outbuf) > 5 || 1) { if (window_buffer_available(outbuf) > 16) {
/* Fill up outgoing buffer with available data /* Fill up outgoing buffer with available data
* The windowing protocol manages data retransmits, timeouts etc. * The windowing protocol manages data retransmits, timeouts etc. */
* TODO: is this even necessary? tunnel_tun seems to be more useful */
FD_SET(tun_fd, &fds); FD_SET(tun_fd, &fds);
} }
FD_SET(dns_fd, &fds); FD_SET(dns_fd, &fds);
@ -1045,10 +1061,10 @@ client_tunnel(int tun_fd, int dns_fd)
send_next_frag(dns_fd); send_next_frag(dns_fd);
} else { } else {
outbuf->resends = 0; outbuf->resends = 0;
send_ping(dns_fd, 1); send_ping(dns_fd, 1, -1);
} }
} else { } else {
send_ping(dns_fd, 0); send_ping(dns_fd, 0, -1);
} }
send_ping_soon = 0; send_ping_soon = 0;
@ -1074,7 +1090,7 @@ client_tunnel(int tun_fd, int dns_fd)
static void static void
send_login(int fd, char *login, int len) send_login(int fd, char *login, int len)
{ {
char data[19]; uint8_t data[19];
memset(data, 0, sizeof(data)); memset(data, 0, sizeof(data));
data[0] = userid; data[0] = userid;
@ -1091,8 +1107,8 @@ send_login(int fd, char *login, int len)
static void static void
send_fragsize_probe(int fd, int fragsize) send_fragsize_probe(int fd, int fragsize)
{ {
char probedata[256]; uint8_t probedata[256];
char buf[4096]; uint8_t buf[MAX_FRAGSIZE];
/* /*
* build a large query domain which is random and maximum size, * build a large query domain which is random and maximum size,
@ -1120,7 +1136,7 @@ send_fragsize_probe(int fd, int fragsize)
static void static void
send_set_downstream_fragsize(int fd, int fragsize) send_set_downstream_fragsize(int fd, int fragsize)
{ {
char data[5]; uint8_t data[5];
data[0] = userid; data[0] = userid;
data[1] = (fragsize & 0xff00) >> 8; data[1] = (fragsize & 0xff00) >> 8;
@ -1136,7 +1152,7 @@ send_set_downstream_fragsize(int fd, int fragsize)
static void static void
send_version(int fd, uint32_t version) send_version(int fd, uint32_t version)
{ {
char data[6]; uint8_t data[6];
data[0] = (version >> 24) & 0xff; data[0] = (version >> 24) & 0xff;
data[1] = (version >> 16) & 0xff; data[1] = (version >> 16) & 0xff;
@ -1154,7 +1170,7 @@ send_version(int fd, uint32_t version)
static void static void
send_ip_request(int fd, int userid) send_ip_request(int fd, int userid)
{ {
char buf[512] = "i____."; uint8_t buf[512] = "i____.";
buf[1] = b32_5to8(userid); buf[1] = b32_5to8(userid);
buf[2] = b32_5to8((rand_seed >> 10) & 0x1f); buf[2] = b32_5to8((rand_seed >> 10) & 0x1f);
@ -1162,7 +1178,7 @@ send_ip_request(int fd, int userid)
buf[4] = b32_5to8((rand_seed ) & 0x1f); buf[4] = b32_5to8((rand_seed ) & 0x1f);
rand_seed++; rand_seed++;
strncat(buf, topdomain, 512 - strlen(buf)); strncat((char *)buf, topdomain, 512 - strlen((char *)buf));
send_query(fd, buf); send_query(fd, buf);
} }
@ -1189,7 +1205,7 @@ send_upenctest(int fd, char *s)
strncat(buf, s, 512); strncat(buf, s, 512);
strncat(buf, ".", 512); strncat(buf, ".", 512);
strncat(buf, topdomain, 512 - strlen(buf)); strncat(buf, topdomain, 512 - strlen(buf));
send_query(fd, buf); send_query(fd, (uint8_t *)buf);
} }
static void static void
@ -1207,7 +1223,7 @@ send_downenctest(int fd, char downenc, int variant, char *s, int slen)
rand_seed++; rand_seed++;
strncat(buf, topdomain, 512 - strlen(buf)); strncat(buf, topdomain, 512 - strlen(buf));
send_query(fd, buf); send_query(fd, (uint8_t *)buf);
} }
static void static void
@ -1223,7 +1239,7 @@ send_codec_switch(int fd, int userid, int bits)
rand_seed++; rand_seed++;
strncat(buf, topdomain, 512 - strlen(buf)); strncat(buf, topdomain, 512 - strlen(buf));
send_query(fd, buf); send_query(fd, (uint8_t *)buf);
} }
@ -1240,7 +1256,7 @@ send_downenc_switch(int fd, int userid)
rand_seed++; rand_seed++;
strncat(buf, topdomain, 512 - strlen(buf)); strncat(buf, topdomain, 512 - strlen(buf));
send_query(fd, buf); send_query(fd, (uint8_t *)buf);
} }
static void static void
@ -1260,7 +1276,7 @@ send_lazy_switch(int fd, int userid)
rand_seed++; rand_seed++;
strncat(buf, topdomain, 512 - strlen(buf)); strncat(buf, topdomain, 512 - strlen(buf));
send_query(fd, buf); send_query(fd, (uint8_t *)buf);
} }
static int static int
@ -1285,7 +1301,7 @@ handshake_version(int dns_fd, int *seed)
((in[6] & 0xff) << 8) | ((in[6] & 0xff) << 8) |
((in[7] & 0xff))); ((in[7] & 0xff)));
if (strncmp("VACK", in, 4) == 0) { if (strncmp("VACK", (char *)in, 4) == 0) {
*seed = payload; *seed = payload;
userid = in[8]; userid = in[8];
userid_char = hex[userid & 15]; userid_char = hex[userid & 15];
@ -1294,11 +1310,11 @@ handshake_version(int dns_fd, int *seed)
fprintf(stderr, "Version ok, both using protocol v 0x%08x. You are user #%d\n", fprintf(stderr, "Version ok, both using protocol v 0x%08x. You are user #%d\n",
PROTOCOL_VERSION, userid); PROTOCOL_VERSION, userid);
return 0; return 0;
} else if (strncmp("VNAK", in, 4) == 0) { } else if (strncmp("VNAK", (char *)in, 4) == 0) {
warnx("You use protocol v 0x%08x, server uses v 0x%08x. Giving up", warnx("You use protocol v 0x%08x, server uses v 0x%08x. Giving up",
PROTOCOL_VERSION, payload); PROTOCOL_VERSION, payload);
return 1; return 1;
} else if (strncmp("VFUL", in, 4) == 0) { } else if (strncmp("VFUL", (char *)in, 4) == 0) {
warnx("Server full, all %d slots are taken. Try again later", payload); warnx("Server full, all %d slots are taken. Try again later", payload);
return 1; return 1;
} }
@ -1483,13 +1499,6 @@ handshake_upenctest(int dns_fd, char *s)
if (read > 0) { if (read > 0) {
int k; int k;
#if 0
/* in[56] = '@'; */
/* in[56] = '_'; */
/* if (in[29] == '\344') in[29] = 'a'; */
in[read] = '\0';
fprintf(stderr, "BounceReply: >%s<\n", in);
#endif
/* quick check if case swapped, to give informative error msg */ /* quick check if case swapped, to give informative error msg */
if (in[4] == 'A') { if (in[4] == 'A') {
fprintf(stderr, "DNS queries get changed to uppercase, keeping upstream codec Base32\n"); fprintf(stderr, "DNS queries get changed to uppercase, keeping upstream codec Base32\n");
@ -1808,10 +1817,8 @@ handshake_qtype_autodetect(int dns_fd)
if (handshake_qtypetest(dns_fd, timeout)) { if (handshake_qtypetest(dns_fd, timeout)) {
/* okay */ /* okay */
highestworking = qtypenum; highestworking = qtypenum;
#if 0 if (debug >= 1)
fprintf(stderr, " Type %s timeout %d works\n", fprintf(stderr, " Type %s timeout %d works\n", client_get_qtype(), timeout);
client_get_qtype(), timeout);
#endif
break; break;
/* try others with longer timeout */ /* try others with longer timeout */
} }
@ -1922,13 +1929,13 @@ handshake_switch_codec(int dns_fd, int bits)
if (read > 0) { if (read > 0) {
if (strncmp("BADLEN", in, 6) == 0) { if (strncmp("BADLEN", in, 6) == 0) {
fprintf(stderr, "Server got bad message length. "); fprintf(stderr, "Server got bad message length.\n");
goto codec_revert; goto codec_revert;
} else if (strncmp("BADIP", in, 5) == 0) { } else if (strncmp("BADIP", in, 5) == 0) {
fprintf(stderr, "Server rejected sender IP address. "); fprintf(stderr, "Server rejected sender IP address.\n");
goto codec_revert; goto codec_revert;
} else if (strncmp("BADCODEC", in, 8) == 0) { } else if (strncmp("BADCODEC", in, 8) == 0) {
fprintf(stderr, "Server rejected the selected codec. "); fprintf(stderr, "Server rejected the selected codec.\n");
goto codec_revert; goto codec_revert;
} }
in[read] = 0; /* zero terminate */ in[read] = 0; /* zero terminate */
@ -1936,7 +1943,7 @@ handshake_switch_codec(int dns_fd, int bits)
dataenc = tempenc; dataenc = tempenc;
/* Update outgoing buffer max (decoded) fragsize */ /* Update outgoing buffer max (decoded) fragsize */
outbuf->maxfraglen = get_raw_length(hostname_maxlen, dataenc, topdomain); outbuf->maxfraglen = get_raw_length_from_dns(hostname_maxlen - UPSTREAM_HDR, dataenc, topdomain);
return; return;
} }
@ -1945,7 +1952,7 @@ handshake_switch_codec(int dns_fd, int bits)
if (!running) if (!running)
return; return;
fprintf(stderr, "No reply from server on codec switch. "); fprintf(stderr, "No reply from server on codec switch.\n");
codec_revert: codec_revert:
fprintf(stderr, "Falling back to upstream codec %s\n", dataenc->name); fprintf(stderr, "Falling back to upstream codec %s\n", dataenc->name);
@ -1978,13 +1985,13 @@ handshake_switch_downenc(int dns_fd)
if (read > 0) { if (read > 0) {
if (strncmp("BADLEN", in, 6) == 0) { if (strncmp("BADLEN", in, 6) == 0) {
fprintf(stderr, "Server got bad message length. "); fprintf(stderr, "Server got bad message length.\n");
goto codec_revert; goto codec_revert;
} else if (strncmp("BADIP", in, 5) == 0) { } else if (strncmp("BADIP", in, 5) == 0) {
fprintf(stderr, "Server rejected sender IP address. "); fprintf(stderr, "Server rejected sender IP address.\n");
goto codec_revert; goto codec_revert;
} else if (strncmp("BADCODEC", in, 8) == 0) { } else if (strncmp("BADCODEC", in, 8) == 0) {
fprintf(stderr, "Server rejected the selected codec. "); fprintf(stderr, "Server rejected the selected codec.\n");
goto codec_revert; goto codec_revert;
} }
in[read] = 0; /* zero terminate */ in[read] = 0; /* zero terminate */
@ -1997,7 +2004,7 @@ handshake_switch_downenc(int dns_fd)
if (!running) if (!running)
return; return;
fprintf(stderr, "No reply from server on codec switch. "); fprintf(stderr, "No reply from server on codec switch.\n");
codec_revert: codec_revert:
fprintf(stderr, "Falling back to downstream codec Base32\n"); fprintf(stderr, "Falling back to downstream codec Base32\n");
@ -2019,13 +2026,13 @@ handshake_try_lazy(int dns_fd)
if (read > 0) { if (read > 0) {
if (strncmp("BADLEN", in, 6) == 0) { if (strncmp("BADLEN", in, 6) == 0) {
fprintf(stderr, "Server got bad message length. "); fprintf(stderr, "Server got bad message length.\n");
goto codec_revert; goto codec_revert;
} else if (strncmp("BADIP", in, 5) == 0) { } else if (strncmp("BADIP", in, 5) == 0) {
fprintf(stderr, "Server rejected sender IP address. "); fprintf(stderr, "Server rejected sender IP address.\n");
goto codec_revert; goto codec_revert;
} else if (strncmp("BADCODEC", in, 8) == 0) { } else if (strncmp("BADCODEC", in, 8) == 0) {
fprintf(stderr, "Server rejected lazy mode. "); fprintf(stderr, "Server rejected lazy mode.\n");
goto codec_revert; goto codec_revert;
} else if (strncmp("Lazy", in, 4) == 0) { } else if (strncmp("Lazy", in, 4) == 0) {
fprintf(stderr, "Server switched to lazy mode\n"); fprintf(stderr, "Server switched to lazy mode\n");
@ -2039,7 +2046,7 @@ handshake_try_lazy(int dns_fd)
if (!running) if (!running)
return; return;
fprintf(stderr, "No reply from server on lazy switch. "); fprintf(stderr, "No reply from server on lazy switch.\n");
codec_revert: codec_revert:
fprintf(stderr, "Falling back to legacy mode\n"); fprintf(stderr, "Falling back to legacy mode\n");
@ -2062,7 +2069,7 @@ handshake_lazyoff(int dns_fd)
read = handshake_waitdns(dns_fd, in, sizeof(in), 'o', 'O', 1); read = handshake_waitdns(dns_fd, in, sizeof(in), 'o', 'O', 1);
if (read == 9 && strncmp("Immediate", in, 9) == 0) { if (read == 9 && strncmp("Immediate", in, 9) == 0) {
warnx("Server switched back to legacy mode.\n"); warnx("Server switched back to legacy mode.");
lazymode = 0; lazymode = 0;
selecttimeout = 1; selecttimeout = 1;
return; return;
@ -2071,7 +2078,7 @@ handshake_lazyoff(int dns_fd)
if (!running) if (!running)
return; return;
warnx("No reply from server on legacy mode switch.\n"); warnx("No reply from server on legacy mode switch.");
} }
static int static int
@ -2111,8 +2118,7 @@ fragsize_check(char *in, int read, int proposed_fragsize, int *max_fragsize)
/* in[123] = 123; */ /* in[123] = 123; */
if ((in[2] & 0xff) != 107) { if ((in[2] & 0xff) != 107) {
fprintf(stderr, "\n"); warnx("\ncorruption at byte 2, this won't work. Try -O Base32, or other -T options.");
warnx("corruption at byte 2, this won't work. Try -O Base32, or other -T options.");
*max_fragsize = -1; *max_fragsize = -1;
return 1; return 1;
} }
@ -2150,7 +2156,7 @@ fragsize_check(char *in, int read, int proposed_fragsize, int *max_fragsize)
static int static int
handshake_autoprobe_fragsize(int dns_fd) handshake_autoprobe_fragsize(int dns_fd)
{ {
char in[4096]; char in[MAX_FRAGSIZE];
int i; int i;
int read; int read;
int proposed_fragsize = 768; int proposed_fragsize = 768;
@ -2158,7 +2164,7 @@ handshake_autoprobe_fragsize(int dns_fd)
int max_fragsize; int max_fragsize;
max_fragsize = 0; max_fragsize = 0;
fprintf(stderr, "Autoprobing max downstream fragment size... (skip with -m fragsize)\n"); fprintf(stderr, "Autoprobing max downstream fragment size... (skip with -m fragsize)");
while (running && range > 0 && (range >= 8 || max_fragsize < 300)) { while (running && range > 0 && (range >= 8 || max_fragsize < 300)) {
/* stop the slow probing early when we have enough bytes anyway */ /* stop the slow probing early when we have enough bytes anyway */
for (i=0; running && i<3 ;i++) { for (i=0; running && i<3 ;i++) {
@ -2191,16 +2197,14 @@ handshake_autoprobe_fragsize(int dns_fd)
} }
} }
if (!running) { if (!running) {
fprintf(stderr, "\n"); warnx("\nstopped while autodetecting fragment size (Try setting manually with -m)");
warnx("stopped while autodetecting fragment size (Try setting manually with -m)");
return 0; return 0;
} }
if (max_fragsize <= 6) { if (max_fragsize <= 6) {
/* Tried all the way down to 2 and found no good size. /* Tried all the way down to 2 and found no good size.
But we _did_ do all handshake before this, so there must But we _did_ do all handshake before this, so there must
be some workable connection. */ be some workable connection. */
fprintf(stderr, "\n"); warnx("\nfound no accepted fragment size.");
warnx("found no accepted fragment size.");
warnx("try setting -M to 200 or lower, or try other -T or -O options."); warnx("try setting -M to 200 or lower, or try other -T or -O options.");
return 0; return 0;
} }
@ -2238,7 +2242,7 @@ handshake_set_fragsize(int dns_fd, int fragsize)
if (read > 0) { if (read > 0) {
if (strncmp("BADFRAG", in, 7) == 0) { if (strncmp("BADFRAG", in, 7) == 0) {
fprintf(stderr, "Server rejected fragsize. Keeping default."); fprintf(stderr, "Server rejected fragsize. Keeping default.\n");
return; return;
} else if (strncmp("BADIP", in, 5) == 0) { } else if (strncmp("BADIP", in, 5) == 0) {
fprintf(stderr, "Server rejected sender IP address.\n"); fprintf(stderr, "Server rejected sender IP address.\n");

View File

@ -77,7 +77,9 @@ extern const unsigned char raw_header[RAW_HDR_LEN];
/* Unused RR type, never actually sent */ /* Unused RR type, never actually sent */
#define DOWNSTREAM_HDR 3 #define DOWNSTREAM_HDR 3
#define DOWNSTREAM_PING_HDR 7
#define UPSTREAM_HDR 6 #define UPSTREAM_HDR 6
#define UPSTREAM_PING 6
struct query { struct query {
char name[QUERY_NAME_SIZE]; char name[QUERY_NAME_SIZE];