Added downstream window frag timeout and id=0 will now work.

This commit is contained in:
frekky 2015-10-27 17:29:41 +08:00
parent 50c9cb28ec
commit 08adc5da71

View File

@ -150,21 +150,19 @@ send_raw(int fd, uint8_t *buf, size_t buflen, int user, int cmd, struct sockaddr
static void static void
qmem_init(int userid) qmem_init(int userid)
/* initialize user QMEM and DNS cache (if enabled) */
{ {
memset(&users[userid].qmem, 0, sizeof(struct qmem_buffer)); memset(&users[userid].qmem, 0, sizeof(struct qmem_buffer));
// users[userid].qmem.end = 1;
for (size_t i = 0; i < QMEM_LEN; i++) { for (size_t i = 0; i < QMEM_LEN; i++) {
users[userid].qmem.queries[i].q.id = -1; users[userid].qmem.queries[i].q.id = -1;
} }
// TODO dns cache init in qmem
} }
static int static int
qmem_is_cached(int dns_fd, int userid, struct query *q) qmem_is_cached(int dns_fd, int userid, struct query *q)
/* Check if an answer for a particular query is cached in qmem /* Check if an answer for a particular query is cached in qmem
* If so, sends an "invalid" answer or one from DNS cache * If so, sends an "invalid" answer or one from DNS cache
* Returns 1 if new query, 0 if cached (and then answered) */ * Returns 0 if new query (ie. not cached), 1 if cached (and then answered) */
{ {
struct qmem_buffer *buf; struct qmem_buffer *buf;
struct query *pq; struct query *pq;
@ -186,7 +184,9 @@ qmem_is_cached(int dns_fd, int userid, struct query *q)
continue; continue;
/* Aha! A match! */ /* Aha! A match! */
#ifdef USE_DNSCACHE #ifdef USE_DNSCACHE
/* Check if answer is in DNS cache */
if (buf->queries[p].a.len) { if (buf->queries[p].a.len) {
data = (char *)buf->queries[p].a.data; data = (char *)buf->queries[p].a.data;
len = buf->queries[p].a.len; len = buf->queries[p].a.len;
@ -194,14 +194,14 @@ qmem_is_cached(int dns_fd, int userid, struct query *q)
dnscache = 1; dnscache = 1;
} }
#endif #endif
QMEM_DEBUG(2, userid, "OUT from qmem for '%s', %s", q->name, QMEM_DEBUG(2, userid, "OUT from qmem for '%s', %s", q->name,
dnscache ? "answer from DNS cache" : "sending invalid response"); dnscache ? "answer from DNS cache" : "sending invalid response");
// TODO cache answers/respond using cache? (merge with dnscache)
write_dns(dns_fd, q, data, len, dataenc); write_dns(dns_fd, q, data, len, dataenc);
return 0;
}
return 1; return 1;
} }
return 0;
}
static int static int
qmem_append(int userid, struct query *q) qmem_append(int userid, struct query *q)
@ -211,15 +211,16 @@ qmem_append(int userid, struct query *q)
buf = &users[userid].qmem; buf = &users[userid].qmem;
if (buf->num_pending >= QMEM_LEN) { if (buf->num_pending >= QMEM_LEN) {
/* this means we have QMEM_LEN *pending* queries; don't overwrite */ /* this means we have QMEM_LEN *pending* queries; overwrite oldest one
QMEM_DEBUG(2, userid, "full of pending queries. Not appending query with id %d.", q->id); * to prevent buildup of ancient queries */
return 0; QMEM_DEBUG(2, userid, "Full of pending queries! Replacing old query %d with new %d.",
buf->queries[buf->start].q.id, q->id);
} }
if (buf->length < QMEM_LEN) { if (buf->length < QMEM_LEN) {
buf->length++; buf->length++;
} else { } else {
/* will replace already answered query in this spot */ /* will replace oldest query (in buf->queries[buf->start]) */
buf->start = (buf->start + 1) % QMEM_LEN; buf->start = (buf->start + 1) % QMEM_LEN;
} }
@ -1071,8 +1072,6 @@ write_dns(int fd, struct query *q, char *data, size_t datalen, char downenc)
char buf[64*1024]; char buf[64*1024];
int len = 0; int len = 0;
// TODO: respond to duplicate queries here + handling qmem stuff
if (q->type == T_CNAME || q->type == T_A) { if (q->type == T_CNAME || q->type == T_A) {
char cnamebuf[1024]; /* max 255 */ char cnamebuf[1024]; /* max 255 */
@ -1263,7 +1262,6 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query
tmp[0], tmp[1], my_mtu, netmask); tmp[0], tmp[1], my_mtu, netmask);
write_dns(dns_fd, q, (char *)out, read, users[userid].downenc); write_dns(dns_fd, q, (char *)out, read, users[userid].downenc);
q->id = 0;
syslog(LOG_NOTICE, "accepted password from user #%d, given IP %s", userid, tmp[1]); syslog(LOG_NOTICE, "accepted password from user #%d, given IP %s", userid, tmp[1]);
free(tmp[1]); free(tmp[1]);
@ -1303,7 +1301,7 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query
} }
write_dns(dns_fd, q, reply, length, 'T'); write_dns(dns_fd, q, reply, length, 'T');
} else if(in[0] == 'Z' || in[0] == 'z') { } else if(in[0] == 'Z' || in[0] == 'z') { /* Upstream codec check */
/* Check for case conservation and chars not allowed according to RFC */ /* Check for case conservation and chars not allowed according to RFC */
/* Reply with received hostname as data */ /* Reply with received hostname as data */
@ -1434,11 +1432,12 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query
if (bits) { if (bits) {
int f = users[userid].fragsize; int f = users[userid].fragsize;
users[userid].outgoing->maxfraglen = (bits * f) / 8 - DOWNSTREAM_PING_HDR; users[userid].outgoing->maxfraglen = (bits * f) / 8 - DOWNSTREAM_PING_HDR;
DEBUG(1, "Setting max downstream data length to %u bytes for user %d; bits %d (%c)",
users[userid].outgoing->maxfraglen, userid, bits, users[userid].downenc);
users[userid].downenc_bits = bits; users[userid].downenc_bits = bits;
} }
DEBUG(1, "Options for user %d: down compression %d, data bits %d/maxlen %u (enc '%c'), lazy %d.",
userid, tmp_comp, bits, users[userid].outgoing->maxfraglen, tmp_downenc, tmp_lazy);
/* Store any changes */ /* Store any changes */
users[userid].down_compression = tmp_comp; users[userid].down_compression = tmp_comp;
users[userid].downenc = tmp_downenc; users[userid].downenc = tmp_downenc;
@ -1561,17 +1560,8 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query
return; return;
} else if(in[0] == 'P' || in[0] == 'p') { /* Ping request */ } else if(in[0] == 'P' || in[0] == 'p') { /* Ping request */
int dn_seq, up_seq, dn_winsize, up_winsize, dn_ack; int dn_seq, up_seq, dn_winsize, up_winsize, dn_ack;
int respond; int respond, set_qtimeout, set_wtimeout;
unsigned timeout_ms, set_timeout; unsigned qtimeout_ms, wtimeout_ms;
struct timeval timeout;
/* We can't handle id=0, that's "no packet" to the dnscache. So drop
request completely. Note that DNS servers rewrite the id.
We'll drop 1 in 64k times. If DNS server retransmits with
different id, then all okay.
TODO don't use ID=0 to check if query */
if (q->id == 0)
return;
read = unpack_data(unpacked, sizeof(unpacked), in + 1, domain_len - 1, b32); read = unpack_data(unpacked, sizeof(unpacked), in + 1, domain_len - 1, b32);
if (read < UPSTREAM_PING) { if (read < UPSTREAM_PING) {
@ -1587,34 +1577,42 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query
} }
/* Check if cached */ /* Check if cached */
if (!qmem_is_cached(dns_fd, userid, q)) if (qmem_is_cached(dns_fd, userid, q))
return; return;
dn_ack = ((unpacked[8] >> 2) & 1) ? unpacked[1] : -1; dn_ack = ((unpacked[10] >> 2) & 1) ? unpacked[1] : -1;
up_winsize = unpacked[2]; up_winsize = unpacked[2];
dn_winsize = unpacked[3]; dn_winsize = unpacked[3];
up_seq = unpacked[4]; up_seq = unpacked[4];
dn_seq = unpacked[5]; dn_seq = unpacked[5];
timeout_ms = ntohs(*(uint16_t *) (unpacked + 6)); /* Query timeout and window frag timeout */
timeout = ms_to_timeval(timeout_ms); qtimeout_ms = ntohs(*(uint16_t *) (unpacked + 6));
wtimeout_ms = ntohs(*(uint16_t *) (unpacked + 8));
respond = unpacked[8] & 1; respond = unpacked[10] & 1;
set_timeout = (unpacked[8] >> 3) & 1; set_qtimeout = (unpacked[10] >> 3) & 1;
set_wtimeout = (unpacked[10] >> 4) & 1;
DEBUG(3, "PING pkt user %d, down %d/%d, up %d/%d, ACK %d, %stimeout %u ms, respond %d (flags %02X)", DEBUG(3, "PING pkt user %d, down %d/%d, up %d/%d, ACK %d, %sqtime %u ms, %swtime %u ms, respond %d (flags %02X)",
userid, dn_seq, dn_winsize, up_seq, up_winsize, dn_ack, userid, dn_seq, dn_winsize, up_seq, up_winsize, dn_ack,
set_timeout ? "SET " : "", timeout_ms, respond, unpacked[8]); set_qtimeout ? "SET " : "", qtimeout_ms, set_wtimeout ? "SET " : "",
wtimeout_ms, respond, unpacked[10]);
if (set_timeout) { if (set_qtimeout) {
/* update user's query timeout if timeout flag set */ /* update user's query timeout if timeout flag set */
users[userid].dns_timeout = timeout; users[userid].dns_timeout = ms_to_timeval(qtimeout_ms);
/* if timeout is 0, we do not enable lazy mode but it is effectively the same */ /* if timeout is 0, we do not enable lazy mode but it is effectively the same */
int newlazy = !(timeout_ms == 0); int newlazy = !(qtimeout_ms == 0);
if (newlazy != users[userid].lazy) if (newlazy != users[userid].lazy)
DEBUG(2, "User %d: not setting lazymode to %d with timeout %u", DEBUG(2, "User %d: not setting lazymode to %d with timeout %u",
userid, newlazy, timeout_ms); userid, newlazy, qtimeout_ms);
}
if (set_wtimeout) {
/* update sending window fragment ACK timeout */
users[userid].outgoing->timeout = ms_to_timeval(wtimeout_ms);
} }
qmem_append(userid, q); qmem_append(userid, q);
@ -1642,16 +1640,6 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query
if (domain_len < UPSTREAM_HDR + 1) if (domain_len < UPSTREAM_HDR + 1)
return; return;
/* We can't handle id=0, that's "no packet" to us. So drop
request completely. Note that DNS servers rewrite the id.
We'll drop 1 in 64k times. If DNS server retransmits with
different id, then all okay.
Else client doesn't get our ack, and will retransmit in 1 second. */
if (q->id == 0) {
DEBUG(1, "Query with ID 0!");
return;
}
if ((in[0] >= '0' && in[0] <= '9')) if ((in[0] >= '0' && in[0] <= '9'))
code = in[0] - '0'; code = in[0] - '0';
if ((in[0] >= 'a' && in[0] <= 'f')) if ((in[0] >= 'a' && in[0] <= 'f'))
@ -1667,7 +1655,11 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query
} }
/* Check if cached */ /* Check if cached */
if (!qmem_is_cached(dns_fd, userid, q)) if (qmem_is_cached(dns_fd, userid, q)) {
/* if is cached, by this point it has already been answered */
return;
}
qmem_append(userid, q); qmem_append(userid, q);
/* Decode upstream data header - see docs/proto_XXXXXXXX.txt */ /* Decode upstream data header - see docs/proto_XXXXXXXX.txt */
/* First byte (after userid) = CMC (ignored); skip 2 bytes */ /* First byte (after userid) = CMC (ignored); skip 2 bytes */