Server-side sliding window implementation mostly finished. Requires

testing.
This commit is contained in:
frekky 2015-08-21 23:23:24 +08:00
parent 844abefcf8
commit d8c08191cc
3 changed files with 134 additions and 96 deletions

View File

@ -294,6 +294,8 @@ main(int argc, char **argv)
retval = 0; retval = 0;
server_init();
#ifdef WINDOWS32 #ifdef WINDOWS32
WSAStartup(req_version, &wsa_data); WSAStartup(req_version, &wsa_data);
#endif #endif

0
src/osflags Executable file → Normal file
View File

View File

@ -44,6 +44,7 @@
#include "tun.h" #include "tun.h"
#include "fw_query.h" #include "fw_query.h"
#include "server.h" #include "server.h"
#include "window.h"
#ifdef HAVE_SYSTEMD #ifdef HAVE_SYSTEMD
# include <systemd/sd-daemon.h> # include <systemd/sd-daemon.h>
@ -119,15 +120,6 @@ send_raw(int fd, uint8_t *buf, size_t buflen, int user, int cmd, struct query *q
sendto(fd, packet, len, 0, (struct sockaddr *) &q->from, q->fromlen); sendto(fd, packet, len, 0, (struct sockaddr *) &q->from, q->fromlen);
} }
static void
start_new_outpacket(int userid, uint8_t *data, size_t datalen)
/* Copies data to .outpacket and resets all counters.
data is expected to be compressed already. */
{
// TODO: window add to outgoing data
}
int int
answer_from_qmem(int dns_fd, struct query *q, unsigned char *qmem_cmc, answer_from_qmem(int dns_fd, struct query *q, unsigned char *qmem_cmc,
unsigned short *qmem_type, int qmem_len, unsigned short *qmem_type, int qmem_len,
@ -378,10 +370,41 @@ forward_query(int bind_fd, struct query *q)
} }
} }
void
send_ping_response(int dns_fd, int userid, struct query *q)
{
static uint8_t pkt[10];
size_t datalen = 5;
/* Build downstream data + ping header (see doc/proto_xxxxxxxx.txt) for details */
pkt[0] = users[userid].outgoing->start_seq_id & 0xFF;
pkt[1] = users[userid].incoming->start_seq_id & 0xFF;
pkt[2] = (1 << 5); /* ping flag */
pkt[3] = users[userid].outgoing->windowsize & 0xFF;
pkt[4] = users[userid].incoming->windowsize & 0xFF;
write_dns(dns_fd, q, (char *)pkt, datalen, users[userid].downenc);
if (q->id2 != 0) { /* rotate pending duplicate queries */
q->id = q->id2;
q->fromlen = q->fromlen2;
memcpy(&(q->from), &(q->from2), q->fromlen2);
if (debug >= 1)
warnx("OUT again to last duplicate");
write_dns(dns_fd, q, (char *)pkt, datalen, users[userid].downenc);
}
save_to_qmem_pingordata(userid, q);
#ifdef DNSCACHE_LEN
save_to_dnscache(userid, q, (char *)pkt, datalen + 2);
#endif
q->id = 0; /* this query is used */
}
static int static int
send_frag_or_dataless(int dns_fd, int userid, struct query *q) send_frag_or_dataless(int dns_fd, int userid, struct query *q)
/* Sends current fragment to user, or dataless packet if there is no /* Sends current fragment to user, or a ping if no data available.
current fragment available (-> normal "quiet" ping reply).
Does not update anything, except: Does not update anything, except:
- discards q always (query is used) - discards q always (query is used)
- forgets entire users[userid].outpacket if it was sent in one go, - forgets entire users[userid].outpacket if it was sent in one go,
@ -390,43 +413,60 @@ send_frag_or_dataless(int dns_fd, int userid, struct query *q)
0 = don't call us again for now. 0 = don't call us again for now.
*/ */
{ {
char pkt[4096]; static uint8_t pkt[MAX_FRAGSIZE + DOWNSTREAM_HDR];
int datalen = 0; int ping = 0;
size_t datalen;
fragment *f;
struct frag_buffer *out;
/* TODO: If re-sent too many times, drop entire packet */ out = users[userid].outgoing;
/* Build downstream data header (see doc/proto_xxxxxxxx.txt) */ f = window_get_next_sending_fragment(out, users[userid].next_upstream_ack);
if (!f && user_sending(userid)) {
/* Need to tell client to sync stuff - send data header with ping stuff */
ping = 1;
} /* TODO: If re-sent too many times, drop stuff */
// /* First byte is 1 bit compression flag, 3 bits upstream seqno, 4 bits upstream fragment */ /* Build downstream data header (see doc/proto_xxxxxxxx.txt) for details */
// pkt[0] = (1<<7) | ((users[userid].inpacket.seqno & 7) << 4) | pkt[0] = f->seqID & 0xFF;
// (users[userid].inpacket.fragment & 15); pkt[1] = f->ack_other & 0xFF;
// /* Second byte is 3 bits downstream seqno, 4 bits downstream fragment, 1 bit last flag */ pkt[2] = ((ping & 1) << 5) | ((f->compressed & 1) << 4) | ((f->ack_other < 0 ? 0 : 1) << 3)
// pkt[1] = ((users[userid].outpacket.seqno & 7) << 5) | | (f->is_nack << 2) | (f->start << 1) | f->end;
// ((users[userid].outpacket.fragment & 15) << 1) | (last & 1);
if (debug >= 1) { if (ping) {
// TODO: display packet data pkt[3] = out->windowsize & 0xFF;
pkt[4] = users[userid].incoming->windowsize & 0xFF;
datalen = 5;
} else {
datalen = DOWNSTREAM_HDR + f->len;
if (datalen > sizeof(pkt)) {
warnx("send_frag_or_dataless: fragment too large to send!");
return 0;
}
memcpy(&pkt, f->data, f->len);
} }
write_dns(dns_fd, q, pkt, datalen + 2, users[userid].downenc); write_dns(dns_fd, q, (char *)pkt, datalen, users[userid].downenc);
if (q->id2 != 0) { if (q->id2 != 0) { /* reply to any duplicates */
q->id = q->id2; q->id = q->id2;
q->fromlen = q->fromlen2; q->fromlen = q->fromlen2;
memcpy(&(q->from), &(q->from2), q->fromlen2); memcpy(&(q->from), &(q->from2), q->fromlen2);
if (debug >= 1) if (debug >= 1)
warnx("OUT again to last duplicate"); warnx("OUT again to last duplicate");
write_dns(dns_fd, q, pkt, datalen + 2, users[userid].downenc); write_dns(dns_fd, q, (char *)pkt, datalen, users[userid].downenc);
} }
save_to_qmem_pingordata(userid, q); save_to_qmem_pingordata(userid, q);
#ifdef DNSCACHE_LEN #ifdef DNSCACHE_LEN
save_to_dnscache(userid, q, pkt, datalen + 2); save_to_dnscache(userid, q, (char *)pkt, datalen + 2);
#endif #endif
/* this query has been */
q->id = 0;
window_tick(out);
q->id = 0; /* this query is used */ /* call again if we have more things to send */
return users[userid].outgoing->numitems > 0;
return 0; /* don't call us again */
} }
static void static void
@ -446,6 +486,7 @@ send_version_response(int fd, version_ack_t ack, uint32_t payload, int userid, s
break; break;
} }
// TODO: use htonl for compatibility with big-endian systems
out[4] = ((payload >> 24) & 0xff); out[4] = ((payload >> 24) & 0xff);
out[5] = ((payload >> 16) & 0xff); out[5] = ((payload >> 16) & 0xff);
out[6] = ((payload >> 8) & 0xff); out[6] = ((payload >> 8) & 0xff);
@ -526,7 +567,7 @@ tunnel_tun(int tun_fd, struct dnsfd *dns_fds)
if (users[userid].conn == CONN_DNS_NULL) { if (users[userid].conn == CONN_DNS_NULL) {
start_new_outpacket(userid, out, outlen); window_add_outgoing_data(users[userid].outgoing, out, outlen, 1);
/* Start sending immediately if query is waiting */ /* Start sending immediately if query is waiting */
if (users[userid].q_sendrealsoon.id != 0) { if (users[userid].q_sendrealsoon.id != 0) {
@ -633,15 +674,14 @@ server_tunnel(int tun_fd, struct dnsfd *dns_fds, int bind_fd, int max_idle_time)
tv.tv_usec = 0; tv.tv_usec = 0;
/* Adjust timeout if there is anything to send realsoon. /* Adjust timeout if there is anything to send realsoon.
Clients won't be sending new data until we send our ack, Clients won't be sending new data until we send our ack, TODO: adjust stuff in this function
so don't keep them waiting long. This only triggers at so don't keep them waiting long. This only triggers at
final upstream fragments, which is about once per eight final upstream fragments, which is about once per eight
requests during heavy upstream traffic. requests during heavy upstream traffic.
20msec: ~8 packs every 1/50sec = ~400 DNSreq/sec, 20msec: ~8 packets every 1/50sec = ~400 DNSreq/sec,
or ~1200bytes every 1/50sec = ~0.5 Mbit/sec upstream */ or ~1200bytes every 1/50sec = ~0.5 Mbit/sec upstream */
for (userid = 0; userid < created_users; userid++) { for (userid = 0; userid < created_users; userid++) {
if (users[userid].active && !users[userid].disabled && if (user_active(userid)) {
users[userid].last_pkt + 60 > time(NULL)) {
users[userid].q_sendrealsoon_new = 0; users[userid].q_sendrealsoon_new = 0;
if (users[userid].q_sendrealsoon.id != 0) { if (users[userid].q_sendrealsoon.id != 0) {
tv.tv_sec = 0; tv.tv_sec = 0;
@ -683,7 +723,7 @@ server_tunnel(int tun_fd, struct dnsfd *dns_fds, int bind_fd, int max_idle_time)
return 1; return 1;
} }
if (i==0) { if (i == 0) {
if (max_idle_time) { if (max_idle_time) {
/* only trigger the check if that's worth ( ie, no need to loop over if there /* only trigger the check if that's worth ( ie, no need to loop over if there
is something to send */ is something to send */
@ -714,11 +754,8 @@ server_tunnel(int tun_fd, struct dnsfd *dns_fds, int bind_fd, int max_idle_time)
/* Send realsoon's if tun or dns didn't already */ /* Send realsoon's if tun or dns didn't already */
for (userid = 0; userid < created_users; userid++) for (userid = 0; userid < created_users; userid++)
if (users[userid].active && !users[userid].disabled && if (user_active(userid) && users[userid].q_sendrealsoon.id != 0 &&
users[userid].last_pkt + 60 > time(NULL) && users[userid].conn == CONN_DNS_NULL && !users[userid].q_sendrealsoon_new) {
users[userid].q_sendrealsoon.id != 0 &&
users[userid].conn == CONN_DNS_NULL &&
!users[userid].q_sendrealsoon_new) {
int dns_fd = get_dns_fd(dns_fds, &users[userid].q_sendrealsoon.from); int dns_fd = get_dns_fd(dns_fds, &users[userid].q_sendrealsoon.from);
send_frag_or_dataless(dns_fd, userid, &users[userid].q_sendrealsoon); send_frag_or_dataless(dns_fd, userid, &users[userid].q_sendrealsoon);
} }
@ -749,7 +786,7 @@ handle_full_packet(int tun_fd, struct dnsfd *dns_fds, int userid, uint8_t *data,
/* send the compressed(!) packet to other client */ /* send the compressed(!) packet to other client */
if (users[touser].conn == CONN_DNS_NULL) { if (users[touser].conn == CONN_DNS_NULL) {
if (window_buffer_available(users[touser].outgoing) * users[touser].outgoing->maxfraglen >= len) { if (window_buffer_available(users[touser].outgoing) * users[touser].outgoing->maxfraglen >= len) {
start_new_outpacket(touser, data, len); window_add_outgoing_data(users[touser].outgoing, data, len, 1);
/* Start sending immediately if query is waiting */ /* Start sending immediately if query is waiting */
if (users[touser].q_sendrealsoon.id != 0) { if (users[touser].q_sendrealsoon.id != 0) {
@ -781,9 +818,7 @@ handle_raw_login(uint8_t *packet, size_t len, struct query *q, int fd, int useri
/* can't use check_authenticated_user_and_ip() since IP address will be different, /* can't use check_authenticated_user_and_ip() since IP address will be different,
so duplicate here except IP address */ so duplicate here except IP address */
if (userid < 0 || userid >= created_users) return; if (userid < 0 || userid >= created_users) return;
if (!users[userid].active || users[userid].disabled) return; if (!check_authenticated_user_and_ip(userid, q)) return;
if (!users[userid].authenticated) return;
if (users[userid].last_pkt + 60 < time(NULL)) return;
if (debug >= 1) { if (debug >= 1) {
fprintf(stderr, "IN login raw, len %lu, from user %d\n", len, userid); fprintf(stderr, "IN login raw, len %lu, from user %d\n", len, userid);
@ -1128,10 +1163,10 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query
char in[512]; char in[512];
char logindata[16]; char logindata[16];
char out[64*1024]; char out[64*1024];
char unpacked[64*1024]; static char unpacked[64*1024];
char *tmp[2]; char *tmp[2];
int userid; int userid;
int read; size_t read;
userid = -1; userid = -1;
@ -1181,6 +1216,7 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query
// TODO: client specified window size // TODO: client specified window size
users[userid].incoming = window_buffer_init(128, 10, MAX_FRAGSIZE, WINDOW_RECVING); users[userid].incoming = window_buffer_init(128, 10, MAX_FRAGSIZE, WINDOW_RECVING);
users[userid].outgoing = window_buffer_init(16, 10, users[userid].fragsize, WINDOW_SENDING); users[userid].outgoing = window_buffer_init(16, 10, users[userid].fragsize, WINDOW_SENDING);
users[userid].next_upstream_ack = -1;
#ifdef DNSCACHE_LEN #ifdef DNSCACHE_LEN
{ {
for (i = 0; i < DNSCACHE_LEN; i++) { for (i = 0; i < DNSCACHE_LEN; i++) {
@ -1517,9 +1553,9 @@ 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') { } else if(in[0] == 'P' || in[0] == 'p') {
int dn_seq; int dn_seq, up_seq, dn_wins, up_wins;
int dn_frag;
int didsend = 0; int didsend = 0;
int respond;
/* We can't handle id=0, that's "no packet" to us. So drop /* We can't handle id=0, that's "no packet" to us. So drop
request completely. Note that DNS servers rewrite the id. request completely. Note that DNS servers rewrite the id.
@ -1531,7 +1567,7 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query
return; 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 < 4) if (read < 7)
return; return;
/* Ping packet, store userid */ /* Ping packet, store userid */
@ -1591,22 +1627,22 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query
return; return;
} }
// TODO new ping header dn_seq = unpacked[1];
dn_seq = unpacked[1] >> 4; up_seq = unpacked[2];
dn_frag = unpacked[1] & 15; up_wins = unpacked[3];
dn_wins = unpacked[4];
respond = unpacked[5] & 1;
if (debug >= 1) { if (debug >= 1) {
fprintf(stderr, "PING pkt from user %d, ack for downstream %d/%d\n", fprintf(stderr, "PING pkt from user %d, down %d/%d, up %d/%d\n", userid, dn_seq, dn_wins, up_seq, up_wins);
userid, dn_seq, dn_frag);
} }
// TODO: process incoming ACK for downstream data
/* If there is a query that must be returned real soon, do it. /* If there is a query that must be returned real soon, do it.
May contain new downstream data if the ping had a new ack. May contain new downstream data if the ping had a new ack.
Otherwise, may also be re-sending old data. */ Otherwise, may also be re-sending old data. */
if (users[userid].q_sendrealsoon.id != 0) { if (users[userid].q_sendrealsoon.id != 0) {
send_frag_or_dataless(dns_fd, userid, &users[userid].q_sendrealsoon); if (respond) send_ping_response(dns_fd, userid, &users[userid].q_sendrealsoon);
else send_frag_or_dataless(dns_fd, userid, &users[userid].q_sendrealsoon);
} }
/* We need to store a new query, so if there still is an /* We need to store a new query, so if there still is an
@ -1616,9 +1652,12 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query
(This is duplicate data if we had q_sendrealsoon above.) */ (This is duplicate data if we had q_sendrealsoon above.) */
if (users[userid].q.id != 0) { if (users[userid].q.id != 0) {
didsend = 1; didsend = 1;
if (send_frag_or_dataless(dns_fd, userid, &users[userid].q) == 1) if (respond)
/* new packet from queue, send immediately */ send_ping_response(dns_fd, userid, &users[userid].q);
didsend = 0; else
if (send_frag_or_dataless(dns_fd, userid, &users[userid].q) == 1)
/* new packet from queue, send immediately */
didsend = 0;
} }
/* Save new query and time info */ /* Save new query and time info */
@ -1634,9 +1673,9 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query
} else if((in[0] >= '0' && in[0] <= '9') /* Data packet */ } else if((in[0] >= '0' && in[0] <= '9') /* Data packet */
|| (in[0] >= 'a' && in[0] <= 'f') || (in[0] >= 'a' && in[0] <= 'f')
|| (in[0] >= 'A' && in[0] <= 'F')) { || (in[0] >= 'A' && in[0] <= 'F')) {
int upstream_ok = 1;
int didsend = 0; int didsend = 0;
int code = -1; int code = -1;
static fragment f;
/* Need 6 char header + >=1 char data */ /* Need 6 char header + >=1 char data */
if (domain_len < 7) if (domain_len < 7)
@ -1705,42 +1744,43 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query
} }
users[userid].q_sendrealsoon.id2 = q->id; users[userid].q_sendrealsoon.id2 = q->id;
users[userid].q_sendrealsoon.fromlen2 = q->fromlen; users[userid].q_sendrealsoon.fromlen2 = q->fromlen;
memcpy(&(users[userid].q_sendrealsoon.from2), memcpy(&(users[userid].q_sendrealsoon.from2), &(q->from), q->fromlen);
&(q->from), q->fromlen);
return; return;
} }
/* Decode data header */ /* Decode upstream data header - see docs/proto_XXXXXXXX.txt */
// up_seq = (b32_8to5(in[1]) >> 2) & 7; /* First byte (after userid) = CMC (ignored?) */
// up_frag = ((b32_8to5(in[1]) & 3) << 2) | ((b32_8to5(in[2]) >> 3) & 3); f.seqID = (b32_8to5(in[2]) << 2) | (b32_8to5(in[3]) >> 2);
// dn_seq = (b32_8to5(in[2]) & 7); f.ack_other = (b32_8to5(in[5]) & 8) ? ((b32_8to5(in[3]) & 3) << 6)
// dn_frag = b32_8to5(in[3]) >> 1; | (b32_8to5(in[4]) << 1) | (b32_8to5(in[5]) & 1) : -1;
// lastfrag = b32_8to5(in[3]) & 1; TODO: new data header f.is_nack = (b32_8to5(in[5]) >> 2) & 1;
f.start = (b32_8to5(in[5]) >> 1) & 1;
f.end = b32_8to5(in[5]) & 1;
// TODO: handle following scenarios /* Decode remainder of data with user encoding */
/* Got repeated old packet _with data_, probably read = unpack_data(unpacked, sizeof(unpacked), in + UPSTREAM_HDR,
because client didn't receive our ack. So re-send domain_len - UPSTREAM_HDR, users[userid].encoder);
our ack(+data) immediately to keep things flowing
fast.
If it's a _really_ old frag, it's a nameserver
that tries again, and sending our current (non-
matching) fragno won't be a problem. */
/* Duplicate of recent upstream data packet; probably f.len = MIN(read, MAX_FRAGSIZE);
need to answer this to keep DNS server happy */ memcpy(f.data, unpacked, read);
// upstream_ok = 0;
/* Really new packet has arrived, no recent duplicate */ window_process_incoming_fragment(users[userid].incoming, &f);
/* Forget any old packet, even if incomplete */
if (upstream_ok) { window_ack(users[userid].outgoing, f.ack_other);
/* decode with this user's encoding */
read = unpack_data(unpacked, sizeof(unpacked), &(in[UPSTREAM_HDR]), /* if waiting for an ACK to be sent back upstream (on incoming buffer) */
domain_len - 5, users[userid].encoder); if (users[userid].next_upstream_ack < 0) {
// TODO append fragment to window users[userid].next_upstream_ack = window_get_next_ack(users[userid].incoming);
} }
// TODO reassemble data
read = window_reassemble_data(users[userid].incoming, (uint8_t *)unpacked, sizeof(unpacked), NULL);
if (read > 0) { /* Data reassembled successfully + cleared out of buffer */
handle_full_packet(tun_fd, dns_fds, userid, (uint8_t *)unpacked, read);
}
window_tick(users[userid].incoming);
/* If there is a query that must be returned real soon, do it. /* If there is a query that must be returned real soon, do it.
Includes an ack of the just received upstream fragment, Includes an ack of the just received upstream fragment,
@ -1801,13 +1841,9 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query
if (!didsend) // TODO: also check if sending if (!didsend) // TODO: also check if sending
send_frag_or_dataless(dns_fd, userid, &users[userid].q); send_frag_or_dataless(dns_fd, userid, &users[userid].q);
else if (!didsend || !users[userid].lazy) { else if (!didsend || !users[userid].lazy) {
if (upstream_ok) { /* rotate pending queries */ memcpy(&(users[userid].q_sendrealsoon), &(users[userid].q), sizeof(struct query));
memcpy(&(users[userid].q_sendrealsoon), &(users[userid].q), sizeof(struct query)); users[userid].q_sendrealsoon_new = 1;
users[userid].q_sendrealsoon_new = 1; users[userid].q.id = 0; /* used */
users[userid].q.id = 0; /* used */
} else {
send_frag_or_dataless(dns_fd, userid, &users[userid].q);
}
} }
} }
} }