mirror of
https://github.com/yarrick/iodine.git
synced 2024-11-28 20:45:12 +00:00
Client side sliding window implementation (requires further testing)
This commit is contained in:
parent
0fcd8d337d
commit
83f70608fc
478
src/client.c
478
src/client.c
@ -55,6 +55,7 @@
|
|||||||
#include "login.h"
|
#include "login.h"
|
||||||
#include "tun.h"
|
#include "tun.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
#include "window.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
|
|
||||||
static void handshake_lazyoff(int dns_fd);
|
static void handshake_lazyoff(int dns_fd);
|
||||||
@ -72,20 +73,18 @@ static const char *topdomain;
|
|||||||
|
|
||||||
static uint16_t rand_seed;
|
static uint16_t rand_seed;
|
||||||
|
|
||||||
/* Current up/downstream IP packet */
|
/* Current up/downstream window data */
|
||||||
static struct packet outpkt;
|
static struct frag_buffer *outbuf;
|
||||||
static struct packet inpkt;
|
static struct frag_buffer *inbuf;
|
||||||
int outchunkresent = 0;
|
/* Next downstream seqID to be ACK'd (-1 if none pending) */
|
||||||
|
static int next_downstream_ack;
|
||||||
|
|
||||||
/* My userid at the server */
|
/* My userid at the server */
|
||||||
static char userid;
|
static char userid;
|
||||||
static char userid_char; /* used when sending (lowercase) */
|
static char userid_char; /* used when sending (lowercase) */
|
||||||
static char userid_char2; /* also accepted when receiving (uppercase) */
|
static char userid_char2; /* also accepted when receiving (uppercase) */
|
||||||
|
|
||||||
/* DNS id for next packet */
|
|
||||||
static uint16_t chunkid;
|
static uint16_t chunkid;
|
||||||
static uint16_t chunkid_prev;
|
|
||||||
static uint16_t chunkid_prev2;
|
|
||||||
|
|
||||||
/* Base32 encoder used for non-data packets and replies */
|
/* Base32 encoder used for non-data packets and replies */
|
||||||
static struct encoder *b32;
|
static struct encoder *b32;
|
||||||
@ -129,17 +128,13 @@ client_init()
|
|||||||
conn = CONN_DNS_NULL;
|
conn = CONN_DNS_NULL;
|
||||||
|
|
||||||
chunkid = ((unsigned int) rand()) & 0xFFFF;
|
chunkid = ((unsigned int) rand()) & 0xFFFF;
|
||||||
chunkid_prev = 0;
|
|
||||||
chunkid_prev2 = 0;
|
|
||||||
|
|
||||||
outpkt.len = 0;
|
// TODO: user-set window size (command line option)
|
||||||
outpkt.seqno = 0;
|
outbuf = window_buffer_init(100, 10, hostname_maxlen, WINDOW_SENDING);
|
||||||
outpkt.fragment = 0;
|
/* Incoming buffer max fragsize doesn't matter */
|
||||||
outchunkresent = 0;
|
inbuf = window_buffer_init(100, 10, 1200, WINDOW_RECVING);
|
||||||
inpkt.len = 0;
|
|
||||||
inpkt.seqno = 0;
|
|
||||||
inpkt.fragment = 0;
|
|
||||||
|
|
||||||
|
next_downstream_ack = -1;
|
||||||
current_nameserver = 0;
|
current_nameserver = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,8 +260,6 @@ send_query(int fd, char *hostname)
|
|||||||
struct query q;
|
struct query q;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
chunkid_prev2 = chunkid_prev;
|
|
||||||
chunkid_prev = chunkid;
|
|
||||||
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 */
|
||||||
@ -342,15 +335,17 @@ send_raw(int fd, char *buf, int buflen, int user, int cmd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
send_raw_data(int dns_fd)
|
send_raw_data(int dns_fd) // TODO: fix send_raw
|
||||||
{
|
{
|
||||||
send_raw(dns_fd, outpkt.data, outpkt.len, userid, RAW_HDR_CMD_DATA);
|
errx(1, "send_raw_data NEEDS FIXING!");
|
||||||
outpkt.len = 0;
|
/*send_raw(dns_fd, outpkt.data, outpkt.len, userid, RAW_HDR_CMD_DATA);
|
||||||
|
outpkt.len = 0;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
send_packet(int fd, char cmd, const char *data, const size_t datalen)
|
send_packet(int fd, char cmd, const char *data, const size_t datalen)
|
||||||
|
/* Base32 encodes data and sends as single DNS query */
|
||||||
{
|
{
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
|
|
||||||
@ -364,72 +359,77 @@ send_packet(int fd, char cmd, const char *data, const size_t datalen)
|
|||||||
static inline int
|
static inline int
|
||||||
is_sending()
|
is_sending()
|
||||||
{
|
{
|
||||||
return (outpkt.len != 0);
|
return (outbuf->numitems > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
send_chunk(int fd)
|
|
||||||
{
|
|
||||||
char buf[4096];
|
|
||||||
int avail;
|
|
||||||
int code;
|
|
||||||
char *p;
|
|
||||||
static int datacmc = 0;
|
|
||||||
char *datacmcchars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
||||||
|
|
||||||
p = outpkt.data;
|
static void
|
||||||
p += outpkt.offset;
|
send_next_frag(int fd)
|
||||||
avail = outpkt.len - outpkt.offset;
|
/* Sends next available fragment of data from the outgoing window buffer */
|
||||||
|
{
|
||||||
|
static uint8_t buf[MAX_FRAGSIZE];
|
||||||
|
size_t len;
|
||||||
|
int code;
|
||||||
|
static int datacmc = 0;
|
||||||
|
static char *datacmcchars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
fragment *f;
|
||||||
|
|
||||||
|
/* Get next fragment to send */
|
||||||
|
f = window_get_next_sending_fragment(outbuf, next_downstream_ack);
|
||||||
|
window_tick(outbuf);
|
||||||
|
if (f == NULL) {
|
||||||
|
if (is_sending()) {
|
||||||
|
/* 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 */
|
||||||
|
send_ping(fd);
|
||||||
|
}
|
||||||
|
return; /* nothing to send - why was this called? */
|
||||||
|
}
|
||||||
|
|
||||||
/* Note: must be same, or smaller than send_fragsize_probe() */
|
/* Note: must be same, or smaller than send_fragsize_probe() */
|
||||||
outpkt.sentlen = build_hostname(buf + 5, sizeof(buf) - 5, p, avail,
|
len = build_hostname(buf, sizeof(buf), f->data, f->len, topdomain, dataenc, hostname_maxlen);
|
||||||
topdomain, dataenc, hostname_maxlen);
|
|
||||||
|
|
||||||
/* 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 */
|
||||||
|
|
||||||
code = ((outpkt.seqno & 7) << 2) | ((outpkt.fragment & 15) >> 2);
|
buf[1] = datacmcchars[datacmc]; /* Second byte is data-CMC */
|
||||||
buf[1] = b32_5to8(code); /* Second byte is 3 bits seqno, 2 upper bits fragment count */
|
|
||||||
|
|
||||||
code = ((outpkt.fragment & 3) << 3) | (inpkt.seqno & 7);
|
code = (f->seqID & 0xF8) >> 3;
|
||||||
buf[2] = b32_5to8(code); /* Third byte is 2 bits lower fragment count, 3 bits downstream packet seqno */
|
buf[2] = b32_5to8(code); /* Second byte is 5 bits seqno */
|
||||||
|
|
||||||
code = ((inpkt.fragment & 15) << 1) | (outpkt.sentlen == avail);
|
code = ((f->seqID & 7) << 2) | ((f->ack_other & 0xC0) >> 6);
|
||||||
buf[3] = b32_5to8(code); /* Fourth byte is 4 bits downstream fragment count, 1 bit last frag flag */
|
buf[3] = b32_5to8(code); /* Third byte is 3 bits seqno, 2 bits downstream ACK */
|
||||||
|
|
||||||
|
code = ((f->ack_other & 0x3E) >> 1);
|
||||||
|
buf[4] = b32_5to8(code); /* Fourth byte is 5 bits downstream ACK */
|
||||||
|
|
||||||
|
code = (f->ack_other & 1) << 4 | ((f->ack_other < 0 ? 0 : 1) << 3) | (f->is_nack << 2) | (f->start << 1) | f->end;
|
||||||
|
buf[5] = b32_5to8(code); /* Fifth byte is 1 bit downstream ACK, bit flags isACK, isNACK, first, last */
|
||||||
|
|
||||||
buf[4] = datacmcchars[datacmc]; /* Fifth byte is data-CMC */
|
|
||||||
datacmc++;
|
datacmc++;
|
||||||
if (datacmc >= 36)
|
if (datacmc >= 36)
|
||||||
datacmc = 0;
|
datacmc = 0;
|
||||||
|
|
||||||
#if 0
|
|
||||||
fprintf(stderr, " Send: down %d/%d up %d/%d, %d bytes\n",
|
|
||||||
inpkt.seqno, inpkt.fragment, outpkt.seqno, outpkt.fragment,
|
|
||||||
outpkt.sentlen);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
send_query(fd, buf);
|
send_query(fd, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
send_ping(int fd)
|
send_ping(int fd, int ping_response) // TODO: setup window sync stuff in ping
|
||||||
{
|
{
|
||||||
if (conn == CONN_DNS_NULL) {
|
if (conn == CONN_DNS_NULL) {
|
||||||
char data[4];
|
char data[4];
|
||||||
|
|
||||||
data[0] = userid;
|
data[0] = userid;
|
||||||
data[1] = ((inpkt.seqno & 7) << 4) | (inpkt.fragment & 15);
|
data[1] = inbuf->start_seq_id & 0xff;
|
||||||
data[2] = (rand_seed >> 8) & 0xff;
|
data[2] = outbuf->start_seq_id & 0xff;
|
||||||
data[3] = (rand_seed >> 0) & 0xff;
|
data[3] = outbuf->windowsize & 0xff;
|
||||||
|
data[4] = inbuf->windowsize & 0xff;
|
||||||
|
data[5] = ping_response & 1;
|
||||||
|
data[6] = (rand_seed >> 8) & 0xff;
|
||||||
|
data[7] = (rand_seed >> 0) & 0xff;
|
||||||
rand_seed++;
|
rand_seed++;
|
||||||
|
|
||||||
#if 0
|
|
||||||
fprintf(stderr, " Send: down %d/%d (ping)\n",
|
|
||||||
inpkt.seqno, inpkt.fragment);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
send_packet(fd, 'p', data, sizeof(data));
|
send_packet(fd, 'p', data, sizeof(data));
|
||||||
} else {
|
} else {
|
||||||
send_raw(fd, NULL, 0, userid, RAW_HDR_CMD_PING);
|
send_raw(fd, NULL, 0, userid, RAW_HDR_CMD_PING);
|
||||||
@ -574,8 +574,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, int buflen, struct query *q)
|
||||||
/* FIXME: tun_fd needed for raw handling */
|
|
||||||
/* 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.
|
||||||
@ -763,37 +762,44 @@ handshake_waitdns(int dns_fd, char *buf, int buflen, char c1, char c2, int timeo
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
parse_data(uint8_t *data, size_t len, fragment *f)
|
||||||
|
{
|
||||||
|
memset(f, 0, sizeof(fragment));
|
||||||
|
f->seqID = data[0];
|
||||||
|
f->start = data[3] & 1;
|
||||||
|
f->end = (data[3] >> 1) & 1;
|
||||||
|
f->is_nack = (data[3] >> 2) & 1;
|
||||||
|
f->ack_other = (data[3] >> 3) & 1 ? data[1] : -1;
|
||||||
|
f->compressed = (data[3] >> 4) & 1;
|
||||||
|
f->len = len - 3;
|
||||||
|
memcpy(f->data, data + 3, MIN(f->len, sizeof(f->data)));
|
||||||
|
return (data[3] >> 5) & 1; /* 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)
|
||||||
{
|
{
|
||||||
unsigned long outlen;
|
size_t outlen, inlen;
|
||||||
unsigned long inlen;
|
uint8_t out[64*1024];
|
||||||
char out[64*1024];
|
uint8_t in[64*1024];
|
||||||
char in[64*1024];
|
|
||||||
ssize_t read;
|
ssize_t read;
|
||||||
|
|
||||||
if ((read = read_tun(tun_fd, in, sizeof(in))) <= 0)
|
if ((read = read_tun(tun_fd, in, sizeof(in))) <= 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* We may be here only to empty the tun device; then return -1
|
/* Give outgoing buffer all new data if it can hold it */
|
||||||
to force continue in select loop. */
|
if (window_buffer_available(outbuf) > read / MAX_FRAGSIZE)
|
||||||
if (is_sending())
|
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
outlen = sizeof(out);
|
outlen = sizeof(out);
|
||||||
inlen = read;
|
inlen = read;
|
||||||
compress2((uint8_t*)out, &outlen, (uint8_t*)in, inlen, 9);
|
compress2(out, &outlen, in, inlen, 9);
|
||||||
|
|
||||||
memcpy(outpkt.data, out, MIN(outlen, sizeof(outpkt.data)));
|
window_add_outgoing_data(outbuf, out, outlen, 1);
|
||||||
outpkt.sentlen = 0;
|
|
||||||
outpkt.offset = 0;
|
|
||||||
outpkt.seqno = (outpkt.seqno + 1) & 7;
|
|
||||||
outpkt.len = outlen;
|
|
||||||
outpkt.fragment = 0;
|
|
||||||
outchunkresent = 0;
|
|
||||||
|
|
||||||
if (conn == CONN_DNS_NULL) {
|
if (conn == CONN_DNS_NULL) {
|
||||||
send_chunk(dns_fd);
|
send_next_frag(dns_fd);
|
||||||
|
|
||||||
send_ping_soon = 0;
|
send_ping_soon = 0;
|
||||||
} else {
|
} else {
|
||||||
@ -809,19 +815,17 @@ tunnel_dns(int tun_fd, int dns_fd)
|
|||||||
static long packrecv = 0;
|
static long packrecv = 0;
|
||||||
static long packrecv_oos = 0;
|
static long packrecv_oos = 0;
|
||||||
static long packrecv_servfail = 0;
|
static long packrecv_servfail = 0;
|
||||||
int up_ack_seqno;
|
|
||||||
int up_ack_fragment;
|
|
||||||
int new_down_seqno;
|
|
||||||
int new_down_fragment;
|
|
||||||
static struct query q;
|
static struct query q;
|
||||||
unsigned long datalen;
|
size_t datalen;
|
||||||
static char buf[64*1024];
|
static uint8_t buf[64*1024], cbuf[64*1024];
|
||||||
int read;
|
static fragment f;
|
||||||
|
int read, compressed, res;
|
||||||
int send_something_now = 0;
|
int send_something_now = 0;
|
||||||
|
|
||||||
memset(q, 0, sizeof(q));
|
memset(&q, 0, sizeof(q));
|
||||||
memset(buf, 0, sizeof(buf));
|
memset(buf, 0, sizeof(buf));
|
||||||
read = read_dns_withq(dns_fd, tun_fd, buf, sizeof(buf), &q);
|
memset(cbuf, 0, sizeof(cbuf));
|
||||||
|
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 */
|
||||||
@ -842,7 +846,7 @@ tunnel_dns(int tun_fd, int dns_fd)
|
|||||||
return -1; /* nothing done */
|
return -1; /* nothing done */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (read < 2) {
|
if (read < 3) {
|
||||||
/* 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. */
|
||||||
|
|
||||||
@ -874,12 +878,11 @@ tunnel_dns(int tun_fd, int dns_fd)
|
|||||||
if (read == 1)
|
if (read == 1)
|
||||||
fprintf(stderr, " q=%c id %5d 1-byte illegal \"QMEM\" reply\n", q.name[0], q.id);
|
fprintf(stderr, " q=%c id %5d 1-byte illegal \"QMEM\" reply\n", q.name[0], q.id);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
send_ping_soon = 900;
|
send_ping_soon = 900;
|
||||||
return -1; /* nothing done */
|
return -1; /* nothing done */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (read == 5 && !strncmp("BADIP", buf, 5)) {
|
if (read == 5 && !strncmp("BADIP", (char *)cbuf, 5)) {
|
||||||
warnx("BADIP: Server rejected sender IP address (maybe iodined -c will help), or server kicked us due to timeout. Will exit if no downstream data is received in 60 seconds.");
|
warnx("BADIP: Server rejected sender IP address (maybe iodined -c will help), or server kicked us due to timeout. Will exit if no downstream data is received in 60 seconds.");
|
||||||
return -1; /* nothing done */
|
return -1; /* nothing done */
|
||||||
}
|
}
|
||||||
@ -889,217 +892,80 @@ tunnel_dns(int tun_fd, int dns_fd)
|
|||||||
send_ping_soon = 0;
|
send_ping_soon = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Decode the data header, update seqno and frag;
|
/* Decode the downstream data header and fragment-ify ready for processing */
|
||||||
already checked read>=2
|
res = parse_data(cbuf, read, &f);
|
||||||
Note that buf[] gets overwritten when down-pkt complete */
|
|
||||||
new_down_seqno = (buf[1] >> 5) & 7;
|
|
||||||
new_down_fragment = (buf[1] >> 1) & 15;
|
|
||||||
up_ack_seqno = (buf[0] >> 4) & 7;
|
|
||||||
up_ack_fragment = buf[0] & 15;
|
|
||||||
|
|
||||||
#if 0
|
/* if this response was a reverse ping/response to a ping: do something different? */
|
||||||
fprintf(stderr, " Recv: id %5d down %d/%d up %d/%d, %d bytes\n",
|
if (res) {
|
||||||
q.id, new_down_seqno, new_down_fragment, up_ack_seqno,
|
// TODO: handle pings to resync window
|
||||||
up_ack_fragment, read);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Downstream data traffic */
|
|
||||||
|
|
||||||
if (read > 2 && new_down_seqno != inpkt.seqno &&
|
|
||||||
recent_seqno(inpkt.seqno, new_down_seqno)) {
|
|
||||||
/* This is the previous seqno, or a bit earlier.
|
|
||||||
Probably out-of-sequence dupe due to unreliable
|
|
||||||
intermediary DNS. Don't get distracted, but send
|
|
||||||
ping quickly to get things back in order.
|
|
||||||
Ping will send our current seqno idea.
|
|
||||||
If it's really a new packet that skipped multiple seqnos
|
|
||||||
(why??), server will re-send and drop a few times and
|
|
||||||
eventually everything will work again. */
|
|
||||||
read = 2;
|
|
||||||
send_ping_soon = 500;
|
|
||||||
/* Still process upstream ack, if any */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window_ack(outbuf, f.ack_other);
|
||||||
|
|
||||||
|
/* In lazy mode, we shouldn't get immediate replies to our most-recent
|
||||||
|
query, only during heavy data transfer. Since this means the server
|
||||||
|
doesn't have any packets to send, send one relatively fast (but not
|
||||||
|
too fast, to avoid runaway ping-pong loops..) */
|
||||||
|
if (f.len == 0) {
|
||||||
|
send_ping_soon = 700;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get next ACK if nothing already pending */
|
||||||
|
if (next_downstream_ack < 0) {
|
||||||
|
if ((next_downstream_ack = window_get_next_ack(inbuf)) < 0) {
|
||||||
|
next_downstream_ack = -1;
|
||||||
|
} else {
|
||||||
|
send_something_now = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Downstream data traffic */
|
||||||
|
if (!window_process_incoming_fragment(inbuf, &f)) {
|
||||||
|
/* Packet outside window - old, duped and irrelevant */
|
||||||
|
packrecv_oos ++;
|
||||||
|
return -1; /* nothing done */
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: check OOS packet count and send ping to update params
|
||||||
|
|
||||||
|
/* Okay, we have a recent downstream packet */
|
||||||
|
lastdownstreamtime = time(NULL);
|
||||||
if (!(packrecv & 0x1000000))
|
if (!(packrecv & 0x1000000))
|
||||||
packrecv++;
|
packrecv++;
|
||||||
send_query_recvcnt++; /* overflow doesn't matter */
|
send_query_recvcnt++; /* overflow doesn't matter */
|
||||||
|
|
||||||
/* Don't process any non-recent stuff any further.
|
datalen = window_reassemble_data(inbuf, cbuf, sizeof(cbuf), &compressed);
|
||||||
No need to remember more than 3 ids: in practice any older replies
|
if (datalen != 0) {
|
||||||
arrive after new/current replies, and whatever data the old replies
|
if (compressed) {
|
||||||
have, it has become useless in the mean time.
|
if ((res = uncompress(buf, &datalen, cbuf, datalen)) == Z_OK) {
|
||||||
Actually, ever since iodined is replying to both the original query
|
|
||||||
and the last dupe, this hardly triggers any more.
|
|
||||||
*/
|
|
||||||
if (q.id != chunkid && q.id != chunkid_prev && q.id != chunkid_prev2) {
|
|
||||||
packrecv_oos++;
|
|
||||||
#if 0
|
|
||||||
fprintf(stderr, " q=%c Packs received = %8ld Out-of-sequence = %8ld\n", q.name[0], packrecv, packrecv_oos);
|
|
||||||
#endif
|
|
||||||
if (lazymode && packrecv < 1000 && packrecv_oos == 5) {
|
|
||||||
if (selecttimeout > 1)
|
|
||||||
warnx("Hmm, getting some out-of-sequence DNS replies. Setting interval to 1 (use -I1 next time on this network). If data traffic still has large hiccups, try if -L0 works better.");
|
|
||||||
else
|
|
||||||
warnx("Hmm, getting some out-of-sequence DNS replies. If data traffic often has large hiccups, try running with -L0 .");
|
|
||||||
selecttimeout = 1;
|
|
||||||
send_query_sendcnt = 0;
|
|
||||||
send_query_recvcnt = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (send_something_now) {
|
|
||||||
send_ping(dns_fd);
|
|
||||||
send_ping_soon = 0;
|
|
||||||
}
|
|
||||||
return -1; /* nothing done */
|
|
||||||
}
|
|
||||||
#if 0
|
|
||||||
fprintf(stderr, " q=%c Packs received = %8ld Out-of-sequence = %8ld\n", q.name[0], packrecv, packrecv_oos);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Okay, we have a recent downstream packet */
|
|
||||||
lastdownstreamtime = time(NULL);
|
|
||||||
|
|
||||||
/* In lazy mode, we shouldn't get much replies to our most-recent
|
|
||||||
query, only during heavy data transfer. Since this means the server
|
|
||||||
doesn't have any packets left, send one relatively fast (but not
|
|
||||||
too fast, to avoid runaway ping-pong loops..) */
|
|
||||||
if (q.id == chunkid && lazymode) {
|
|
||||||
if (!send_ping_soon || send_ping_soon > 900)
|
|
||||||
send_ping_soon = 900;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (read == 2 && new_down_seqno != inpkt.seqno &&
|
|
||||||
!recent_seqno(inpkt.seqno, new_down_seqno)) {
|
|
||||||
/* This is a seqno that we didn't see yet, but it has
|
|
||||||
no data any more. Possible since iodined will send
|
|
||||||
fitting packs just once and not wait for ack.
|
|
||||||
Real data got lost, or will arrive shortly.
|
|
||||||
Update our idea of the seqno, and drop any waiting
|
|
||||||
old pack. Send ping to get things back on track. */
|
|
||||||
inpkt.seqno = new_down_seqno;
|
|
||||||
inpkt.fragment = new_down_fragment;
|
|
||||||
inpkt.len = 0;
|
|
||||||
send_ping_soon = 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (read > 2) {
|
|
||||||
/* "if" with easy exit */
|
|
||||||
|
|
||||||
if (new_down_seqno != inpkt.seqno) {
|
|
||||||
/* New packet (and not dupe of recent; checked above) */
|
|
||||||
/* Forget any old packet, even if incomplete */
|
|
||||||
inpkt.seqno = new_down_seqno;
|
|
||||||
inpkt.fragment = new_down_fragment; /* hopefully 0 */
|
|
||||||
inpkt.len = 0;
|
|
||||||
} else if (inpkt.fragment == 0 && new_down_fragment == 0 &&
|
|
||||||
inpkt.len == 0) {
|
|
||||||
/* Weird situation: we probably got a no-data reply
|
|
||||||
for this seqno (see above), and the actual data
|
|
||||||
is following now. */
|
|
||||||
/* okay, nothing to do here, just so that next else-if
|
|
||||||
doesn't trigger */
|
|
||||||
} else if (new_down_fragment <= inpkt.fragment) {
|
|
||||||
/* Same packet but duplicate fragment, ignore.
|
|
||||||
If the server didn't get our ack for it, the next
|
|
||||||
ping or chunk will do that. */
|
|
||||||
send_ping_soon = 500;
|
|
||||||
break;
|
|
||||||
} else if (new_down_fragment > inpkt.fragment + 1) {
|
|
||||||
/* Quite impossible. We missed a fragment, but the
|
|
||||||
server got our ack for it and is sending the next
|
|
||||||
fragment already. Don't handle it but let server
|
|
||||||
re-send and drop. */
|
|
||||||
send_ping_soon = 500;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
inpkt.fragment = new_down_fragment;
|
|
||||||
|
|
||||||
datalen = MIN(read - 2, sizeof(inpkt.data) - inpkt.len);
|
|
||||||
|
|
||||||
/* we are here only when read > 2, so datalen "always" >=1 */
|
|
||||||
|
|
||||||
/* Skip 2 byte data header and append to packet */
|
|
||||||
memcpy(&inpkt.data[inpkt.len], &buf[2], datalen);
|
|
||||||
inpkt.len += datalen;
|
|
||||||
|
|
||||||
if (buf[1] & 1) { /* If last fragment flag is set */
|
|
||||||
/* Uncompress packet and send to tun */
|
|
||||||
/* RE-USES buf[] */
|
|
||||||
datalen = sizeof(buf);
|
|
||||||
if (uncompress((uint8_t*)buf, &datalen, (uint8_t*) inpkt.data, inpkt.len) == Z_OK) {
|
|
||||||
write_tun(tun_fd, buf, datalen);
|
write_tun(tun_fd, buf, datalen);
|
||||||
|
} else {
|
||||||
|
warnx("Uncompress failed (%d): reassembled data corrupted or incomplete!", res);
|
||||||
}
|
}
|
||||||
inpkt.len = 0;
|
|
||||||
/* Keep .seqno and .fragment as is, so that we won't
|
|
||||||
reassemble from duplicate fragments */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Send anything to ack the received seqno/frag, and get more */
|
|
||||||
if (inpkt.len == 0) {
|
|
||||||
/* was last frag; wait just a trifle because our
|
|
||||||
tun will probably return TCP-ack immediately.
|
|
||||||
5msec = 200 DNSreq/sec */
|
|
||||||
send_ping_soon = 5;
|
send_ping_soon = 5;
|
||||||
} else {
|
|
||||||
/* server certainly has more data */
|
|
||||||
send_something_now = 1;
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
break;
|
/* Send anything to ack the received seqno/frag, and get more */
|
||||||
|
/* 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Move window along after doing all data processing */
|
||||||
|
window_tick(inbuf);
|
||||||
|
|
||||||
/* NOTE: buf[] was overwritten when down-packet complete */
|
/* NOTE: buf[] was overwritten when down-packet complete */
|
||||||
|
|
||||||
|
|
||||||
/* Upstream data traffic */
|
/* Upstream data traffic */
|
||||||
|
|
||||||
if (is_sending()) {
|
if (is_sending()) {
|
||||||
/* already checked read>=2 */
|
/* More to send - next fragment*/
|
||||||
#if 0
|
send_next_frag(dns_fd);
|
||||||
fprintf(stderr, "Got ack for %d,%d - expecting %d,%d - id=%d cur=%d prev=%d prev2=%d\n",
|
send_ping_soon = 2;
|
||||||
up_ack_seqno, up_ack_fragment, outpkt.seqno, outpkt.fragment,
|
send_something_now = 0;
|
||||||
q.id, chunkid, chunkid_prev, chunkid_prev2);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (up_ack_seqno == outpkt.seqno &&
|
|
||||||
up_ack_fragment == outpkt.fragment) {
|
|
||||||
/* Okay, previously sent fragment has arrived */
|
|
||||||
|
|
||||||
outpkt.offset += outpkt.sentlen;
|
|
||||||
if (outpkt.offset >= outpkt.len) {
|
|
||||||
/* Packet completed */
|
|
||||||
outpkt.offset = 0;
|
|
||||||
outpkt.len = 0;
|
|
||||||
outpkt.sentlen = 0;
|
|
||||||
outchunkresent = 0;
|
|
||||||
|
|
||||||
/* Normally, server still has a query in queue,
|
|
||||||
but sometimes not. So send a ping.
|
|
||||||
(Comment this out and you'll see occasional
|
|
||||||
hiccups.)
|
|
||||||
But since the server often still has a
|
|
||||||
query and we can expect a TCP-ack returned
|
|
||||||
from our tun device quickly in many cases,
|
|
||||||
don't be too fast.
|
|
||||||
20msec still is 50 DNSreq/second... */
|
|
||||||
if (!send_ping_soon || send_ping_soon > 20)
|
|
||||||
send_ping_soon = 20;
|
|
||||||
} else {
|
|
||||||
/* More to send */
|
|
||||||
outpkt.fragment++;
|
|
||||||
outchunkresent = 0;
|
|
||||||
send_chunk(dns_fd);
|
|
||||||
send_ping_soon = 0;
|
|
||||||
send_something_now = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* else: Some wrong fragment has arrived, or old fragment is
|
|
||||||
acked again, mostly by ping responses.
|
|
||||||
Don't resend chunk, usually not needed; select loop will
|
|
||||||
re-send on timeout (1sec if is_sending()). */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* 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);
|
send_ping(dns_fd);
|
||||||
@ -1137,20 +1003,17 @@ client_tunnel(int tun_fd, int dns_fd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
FD_ZERO(&fds);
|
FD_ZERO(&fds);
|
||||||
if (!is_sending() || outchunkresent >= 2) {
|
if (window_buffer_available(outbuf) > 5 || 1) {
|
||||||
/* If re-sending upstream data, chances are that
|
/* Fill up outgoing buffer with available data
|
||||||
we're several seconds behind already and TCP
|
* The windowing protocol manages data retransmits, timeouts etc.
|
||||||
will start filling tun buffer with (useless)
|
* TODO: is this even necessary? tunnel_tun seems to be more useful */
|
||||||
retransmits.
|
|
||||||
Get up-to-date fast by simply dropping stuff,
|
|
||||||
that's what TCP is designed to handle. */
|
|
||||||
FD_SET(tun_fd, &fds);
|
FD_SET(tun_fd, &fds);
|
||||||
}
|
}
|
||||||
FD_SET(dns_fd, &fds);
|
FD_SET(dns_fd, &fds);
|
||||||
|
|
||||||
i = select(MAX(tun_fd, dns_fd) + 1, &fds, NULL, NULL, &tv);
|
i = select(MAX(tun_fd, dns_fd) + 1, &fds, NULL, NULL, &tv);
|
||||||
|
|
||||||
if (lastdownstreamtime + 60 < time(NULL)) {
|
if (difftime(lastdownstreamtime, time(NULL)) >= 60) {
|
||||||
warnx("No downstream data received in 60 seconds, shutting down.");
|
warnx("No downstream data received in 60 seconds, shutting down.");
|
||||||
running = 0;
|
running = 0;
|
||||||
}
|
}
|
||||||
@ -1159,28 +1022,15 @@ client_tunnel(int tun_fd, int dns_fd)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
err(1, "select");
|
err(1, "select < 0");
|
||||||
|
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
/* timeout */
|
/* timeout */
|
||||||
if (is_sending()) {
|
if (is_sending()) {
|
||||||
/* Re-send current fragment; either frag
|
if (outbuf->resends < 3) { // if too many retransmits/timeouts/waiting etc.
|
||||||
or ack probably dropped somewhere.
|
send_next_frag(dns_fd);
|
||||||
But problem: no cache-miss-counter,
|
|
||||||
so hostname will be identical.
|
|
||||||
Just drop whole packet after 3 retries,
|
|
||||||
and TCP retransmit will solve it.
|
|
||||||
NOTE: tun dropping above should be
|
|
||||||
>=(value_here - 1) */
|
|
||||||
if (outchunkresent < 3) {
|
|
||||||
outchunkresent++;
|
|
||||||
send_chunk(dns_fd);
|
|
||||||
} else {
|
} else {
|
||||||
outpkt.offset = 0;
|
outbuf->resends = 0;
|
||||||
outpkt.len = 0;
|
|
||||||
outpkt.sentlen = 0;
|
|
||||||
outchunkresent = 0;
|
|
||||||
|
|
||||||
send_ping(dns_fd);
|
send_ping(dns_fd);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1199,8 +1049,7 @@ client_tunnel(int tun_fd, int dns_fd)
|
|||||||
If chunk sent, sets send_ping_soon=0. */
|
If chunk sent, sets send_ping_soon=0. */
|
||||||
}
|
}
|
||||||
if (FD_ISSET(dns_fd, &fds)) {
|
if (FD_ISSET(dns_fd, &fds)) {
|
||||||
if (tunnel_dns(tun_fd, dns_fd) <= 0)
|
tunnel_dns(tun_fd, dns_fd);
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2329,7 +2178,7 @@ handshake_autoprobe_fragsize(int dns_fd)
|
|||||||
warnx("stopped 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 <= 2) {
|
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. */
|
||||||
@ -2338,8 +2187,8 @@ handshake_autoprobe_fragsize(int dns_fd)
|
|||||||
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;
|
||||||
}
|
}
|
||||||
/* data header adds 2 bytes */
|
/* data header adds 6 bytes */
|
||||||
fprintf(stderr, "will use %d-2=%d\n", max_fragsize, max_fragsize - 2);
|
fprintf(stderr, "will use %d-6=%d\n", max_fragsize, max_fragsize - 6);
|
||||||
|
|
||||||
/* need 1200 / 16frags = 75 bytes fragsize */
|
/* need 1200 / 16frags = 75 bytes fragsize */
|
||||||
if (max_fragsize < 82) {
|
if (max_fragsize < 82) {
|
||||||
@ -2473,6 +2322,11 @@ client_handshake(int dns_fd, int raw_mode, int autodetect_frag_size, int fragsiz
|
|||||||
|
|
||||||
if (autodetect_frag_size) {
|
if (autodetect_frag_size) {
|
||||||
fragsize = handshake_autoprobe_fragsize(dns_fd);
|
fragsize = handshake_autoprobe_fragsize(dns_fd);
|
||||||
|
if (fragsize > MAX_FRAGSIZE) {
|
||||||
|
/* This is very unlikely except perhaps over LAN */
|
||||||
|
fprintf(stderr, "Can transfer fragsize of %d, however iodine has been compiled with MAX_FRAGSIZE = %d. To fully utilize this connection, please recompile iodine/iodined.");
|
||||||
|
fragsize = MAX_FRAGSIZE;
|
||||||
|
}
|
||||||
if (!fragsize) {
|
if (!fragsize) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
16
src/common.c
16
src/common.c
@ -455,22 +455,6 @@ errx(int eval, const char *fmt, ...)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
int recent_seqno(int ourseqno, int gotseqno)
|
|
||||||
/* Return 1 if we've seen gotseqno recently (current or up to 3 back).
|
|
||||||
Return 0 if gotseqno is new (or very old).
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < 4; i++, ourseqno--) {
|
|
||||||
if (ourseqno < 0)
|
|
||||||
ourseqno = 7;
|
|
||||||
if (gotseqno == ourseqno)
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef WINDOWS32
|
#ifndef WINDOWS32
|
||||||
/* Set FD_CLOEXEC flag on file descriptor.
|
/* Set FD_CLOEXEC flag on file descriptor.
|
||||||
* This stops it from being inherited by system() calls.
|
* This stops it from being inherited by system() calls.
|
||||||
|
13
src/common.h
13
src/common.h
@ -76,15 +76,8 @@ extern const unsigned char raw_header[RAW_HDR_LEN];
|
|||||||
#define T_UNSET 65432
|
#define T_UNSET 65432
|
||||||
/* Unused RR type, never actually sent */
|
/* Unused RR type, never actually sent */
|
||||||
|
|
||||||
struct packet
|
#define DOWNSTREAM_HDR 3
|
||||||
{
|
#define UPSTREAM_HDR 6
|
||||||
int len; /* Total packet length */
|
|
||||||
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 {
|
struct query {
|
||||||
char name[QUERY_NAME_SIZE];
|
char name[QUERY_NAME_SIZE];
|
||||||
@ -134,8 +127,6 @@ void errx(int eval, const char *fmt, ...);
|
|||||||
void warnx(const char *fmt, ...);
|
void warnx(const char *fmt, ...);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int recent_seqno(int , int);
|
|
||||||
|
|
||||||
#ifndef WINDOWS32
|
#ifndef WINDOWS32
|
||||||
void fd_set_close_on_exec(int fd);
|
void fd_set_close_on_exec(int fd);
|
||||||
#endif
|
#endif
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "version.h"
|
||||||
#include "tun.h"
|
#include "tun.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
@ -110,10 +111,8 @@ help() {
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
version() {
|
version() {
|
||||||
|
|
||||||
fprintf(stderr, "iodine IP over DNS tunneling client\n");
|
fprintf(stderr, "iodine IP over DNS tunneling client\n");
|
||||||
fprintf(stderr, "Git version: %s\n", GITREVISION);
|
fprintf(stderr, "Git version: %s\n; protocol version %08X", GITREVISION, PROTOCOL_VERSION);
|
||||||
|
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user