mirror of
https://github.com/yarrick/iodine.git
synced 2024-11-25 11:05:15 +00:00
Merged QMEM and DNS cache; removed implied lazy switch.
Improved immediate mode handling, however does not add data queries to QMEM in immediate mode (and does not work either).
This commit is contained in:
parent
e61b38b9a3
commit
c38e7d30a8
275
src/server.c
275
src/server.c
@ -118,18 +118,28 @@ send_raw(int fd, uint8_t *buf, size_t buflen, int user, int cmd, struct sockaddr
|
|||||||
sendto(fd, packet, len, 0, (struct sockaddr *) from, fromlen);
|
sendto(fd, packet, len, 0, (struct sockaddr *) from, fromlen);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ringbuffer Query Handling (qmem):
|
/* Ringbuffer Query Handling (qmem) and DNS Cache:
|
||||||
This is used to make the handling duplicates and query timeouts simpler
|
This is used to make the handling duplicates and query timeouts simpler
|
||||||
and all in one place.
|
and all handled in one place.
|
||||||
Using this, lazy mode should be possible with n queries (n <= windowsize)
|
Using this, lazy mode is possible with n queries (n <= windowsize)
|
||||||
|
|
||||||
New queries are placed consecutively in the buffer, replacing any old
|
New queries are placed consecutively in the buffer, replacing any old
|
||||||
queries (already responded to) if length == QMEM_LEN. Old queries are kept
|
queries (already responded to) if length == QMEM_LEN. Old queries are kept
|
||||||
to check for dupes etc.
|
as a record for duplicate requests. If a dupe is found and USE_DNSCACHE is
|
||||||
|
defined, the previous answer is sent (if it exists), otherwise an invalid
|
||||||
|
response is sent.
|
||||||
|
|
||||||
TODO: modify a bit to replace dnscache entirely?
|
On the DNS cache:
|
||||||
it seems the only difference is qmem doesn't store answers. */
|
This cache is implemented to better handle the aggressively impatient DNS
|
||||||
#ifdef QMEM_LEN
|
servers that very quickly re-send requests when we choose to not
|
||||||
|
immediately answer them in lazy mode. This cache works much better than
|
||||||
|
pruning(=dropping) the improper requests, since the DNS server will
|
||||||
|
actually get an answer instead of silence.
|
||||||
|
|
||||||
|
Because of the CMC in both ping and upstream data, unwanted cache hits
|
||||||
|
are prevented. Due to the combination of CMC and varying sequence IDs, it
|
||||||
|
is extremely unlikely that any duplicate answers will be incorrectly sent
|
||||||
|
during a session (given QMEM_LEN is not very large). */
|
||||||
|
|
||||||
#define QMEM_DEBUG(l, u, ...) \
|
#define QMEM_DEBUG(l, u, ...) \
|
||||||
if (debug >= l) {\
|
if (debug >= l) {\
|
||||||
@ -141,26 +151,32 @@ 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)
|
||||||
{
|
{
|
||||||
memset(&users[userid].qmem, 0, sizeof(struct query_buffer));
|
memset(&users[userid].qmem, 0, sizeof(struct qmem_buffer));
|
||||||
// users[userid].qmem.end = 1;
|
// 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].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
|
* If so, sends an "invalid" answer or one from DNS cache
|
||||||
* Returns 1 if new query, 0 if cached (and then answered) */
|
* Returns 1 if new query, 0 if cached (and then answered) */
|
||||||
{
|
{
|
||||||
struct query_buffer *buf;
|
struct qmem_buffer *buf;
|
||||||
struct query *pq;
|
struct query *pq;
|
||||||
|
char *data = "x";
|
||||||
|
char dataenc = 'T';
|
||||||
|
size_t len = 1;
|
||||||
|
int dnscache = 0;
|
||||||
buf = &users[userid].qmem;
|
buf = &users[userid].qmem;
|
||||||
|
|
||||||
/* Check if this is a duplicate query */
|
/* Check if this is a duplicate query */
|
||||||
for (size_t p = buf->start; p != buf->end; p = (p + 1) % QMEM_LEN) {
|
for (size_t p = buf->start; p != buf->end; p = (p + 1) % QMEM_LEN) {
|
||||||
pq = &buf->queries[p];
|
pq = &buf->queries[p].q;
|
||||||
if (pq->id != q->id)
|
if (pq->id != q->id)
|
||||||
continue;
|
continue;
|
||||||
if (pq->type != q->type)
|
if (pq->type != q->type)
|
||||||
@ -169,10 +185,19 @@ qmem_is_cached(int dns_fd, int userid, struct query *q)
|
|||||||
if (strcasecmp(pq->name, q->name))
|
if (strcasecmp(pq->name, q->name))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
QMEM_DEBUG(2, userid, "OUT for '%s' == duplicate, sending illegal reply", q->name);
|
/* Aha! A match! */
|
||||||
|
#ifdef USE_DNSCACHE
|
||||||
|
if (buf->queries[p].a.len) {
|
||||||
|
data = (char *)buf->queries[p].a.data;
|
||||||
|
len = buf->queries[p].a.len;
|
||||||
|
dataenc = users[userid].downenc;
|
||||||
|
dnscache = 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
QMEM_DEBUG(2, userid, "OUT from qmem for '%s', %s", q->name,
|
||||||
|
dnscache ? "answer from DNS cache" : "sending invalid response");
|
||||||
// TODO cache answers/respond using cache? (merge with dnscache)
|
// TODO cache answers/respond using cache? (merge with dnscache)
|
||||||
write_dns(dns_fd, q, "x", 1, 'T');
|
write_dns(dns_fd, q, data, len, dataenc);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
@ -182,7 +207,7 @@ static int
|
|||||||
qmem_append(int userid, struct query *q)
|
qmem_append(int userid, struct query *q)
|
||||||
/* Appends incoming query to the buffer. */
|
/* Appends incoming query to the buffer. */
|
||||||
{
|
{
|
||||||
struct query_buffer *buf;
|
struct qmem_buffer *buf;
|
||||||
buf = &users[userid].qmem;
|
buf = &users[userid].qmem;
|
||||||
|
|
||||||
if (buf->num_pending >= QMEM_LEN) {
|
if (buf->num_pending >= QMEM_LEN) {
|
||||||
@ -201,30 +226,44 @@ qmem_append(int userid, struct query *q)
|
|||||||
QMEM_DEBUG(5, userid, "add query ID %d, timeout %lu ms", q->id, timeval_to_ms(&users[userid].dns_timeout));
|
QMEM_DEBUG(5, userid, "add query ID %d, timeout %lu ms", q->id, timeval_to_ms(&users[userid].dns_timeout));
|
||||||
|
|
||||||
/* Copy query into buffer */
|
/* Copy query into buffer */
|
||||||
memcpy(&buf->queries[buf->end], q, sizeof(struct query));
|
memcpy(&buf->queries[buf->end].q, q, sizeof(struct query));
|
||||||
|
#ifdef USE_DNSCACHE
|
||||||
|
buf->queries[buf->end].a.len = 0;
|
||||||
|
#endif
|
||||||
buf->end = (buf->end + 1) % QMEM_LEN;
|
buf->end = (buf->end + 1) % QMEM_LEN;
|
||||||
buf->num_pending += 1;
|
buf->num_pending += 1;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
qmem_answered(int userid)
|
qmem_answered(int userid, uint8_t *data, size_t len)
|
||||||
/* Call when oldest/first/earliest query added has been answered */
|
/* Call when oldest/first/earliest query added has been answered */
|
||||||
{
|
{
|
||||||
struct query_buffer *buf;
|
struct qmem_buffer *buf;
|
||||||
size_t answered;
|
size_t answered;
|
||||||
buf = &users[userid].qmem;
|
buf = &users[userid].qmem;
|
||||||
|
|
||||||
if (buf->num_pending == 0) {
|
if (buf->num_pending == 0) {
|
||||||
/* Most likely caused by bugs somewhere else. */
|
/* Most likely caused by bugs somewhere else. */
|
||||||
QMEM_DEBUG(1, userid, "can't answer query that has already been answered! Fix bugs.");
|
QMEM_DEBUG(1, userid, "Query answered with 0 in qmem! Fix bugs.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
answered = buf->start_pending;
|
answered = buf->start_pending;
|
||||||
buf->start_pending = (buf->start_pending + 1) % QMEM_LEN;
|
buf->start_pending = (buf->start_pending + 1) % QMEM_LEN;
|
||||||
buf->num_pending -= 1;
|
buf->num_pending -= 1;
|
||||||
|
|
||||||
QMEM_DEBUG(3, userid, "query ID %d answered", buf->queries[answered].id);
|
#ifdef USE_DNSCACHE
|
||||||
|
/* Add answer to query entry */
|
||||||
|
if (len && data) {
|
||||||
|
if (len > 4096) {
|
||||||
|
QMEM_DEBUG(1, userid, "got answer with length >4096!");
|
||||||
|
}
|
||||||
|
memcpy(&buf->queries[answered].a.data, data, MIN(len, 4096));
|
||||||
|
buf->queries[answered].a.len = len;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QMEM_DEBUG(3, userid, "query ID %d answered", buf->queries[answered].q.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct query *
|
struct query *
|
||||||
@ -232,12 +271,12 @@ qmem_get_next_response(int userid)
|
|||||||
/* Gets oldest query to be responded to (for lazy mode) or NULL if none available
|
/* Gets oldest query to be responded to (for lazy mode) or NULL if none available
|
||||||
* The query is NOT marked as "answered" since that is done later. */
|
* The query is NOT marked as "answered" since that is done later. */
|
||||||
{
|
{
|
||||||
struct query_buffer *buf;
|
struct qmem_buffer *buf;
|
||||||
struct query *q;
|
struct query *q;
|
||||||
buf = &users[userid].qmem;
|
buf = &users[userid].qmem;
|
||||||
if (buf->length == 0 || buf->num_pending == 0)
|
if (buf->length == 0 || buf->num_pending == 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
q = &buf->queries[buf->start_pending];
|
q = &buf->queries[buf->start_pending].q;
|
||||||
QMEM_DEBUG(3, userid, "next response using cached query: ID %d", q->id);
|
QMEM_DEBUG(3, userid, "next response using cached query: ID %d", q->id);
|
||||||
return q;
|
return q;
|
||||||
}
|
}
|
||||||
@ -247,7 +286,7 @@ qmem_max_wait(struct dnsfd *dns_fds, int *touser, struct query **sendq)
|
|||||||
/* Gets max interval before the next query has to be responded to
|
/* Gets max interval before the next query has to be responded to
|
||||||
* Response(s) are sent automatically for queries if:
|
* Response(s) are sent automatically for queries if:
|
||||||
* - the query has timed out
|
* - the query has timed out
|
||||||
* - the user has data to send, pending ACKs or ping and spare pending queries
|
* - the user has data to send or pending ACKs, and spare pending queries
|
||||||
* - the user has excess pending queries (>downstream window size)
|
* - the user has excess pending queries (>downstream window size)
|
||||||
* Returns largest safe time to wait before next timeout */
|
* Returns largest safe time to wait before next timeout */
|
||||||
{
|
{
|
||||||
@ -266,28 +305,28 @@ qmem_max_wait(struct dnsfd *dns_fds, int *touser, struct query **sendq)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
u = &users[userid];
|
u = &users[userid];
|
||||||
qnum = u->qmem.start_pending;
|
|
||||||
|
|
||||||
if (u->qmem.num_pending == 0)
|
if (u->qmem.num_pending == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
||||||
/* Keep track of how many fragments we can send */
|
/* Keep track of how many fragments we can send */
|
||||||
total = window_sending(u->outgoing);
|
if (u->lazy) {
|
||||||
if (u->qmem.num_pending > u->outgoing->windowsize) {
|
total = window_sending(u->outgoing);
|
||||||
/* calculate number of "excess" queries */
|
if (u->qmem.num_pending > u->outgoing->windowsize) {
|
||||||
total = MAX(total, u->qmem.num_pending - u->outgoing->windowsize);
|
/* calculate number of "excess" queries */
|
||||||
|
total = MAX(total, u->qmem.num_pending - u->outgoing->windowsize);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* User in immediate mode, must answer all pending queries */
|
||||||
|
total = u->qmem.num_pending;
|
||||||
}
|
}
|
||||||
|
|
||||||
sending = total;
|
sending = total;
|
||||||
sent = 0;
|
sent = 0;
|
||||||
|
|
||||||
if ((!u->lazy) && u->qmem.num_pending > 0) {
|
qnum = u->qmem.start_pending;
|
||||||
QMEM_DEBUG(2, userid, "User switched to immediate mode, answering all pending queries...");
|
|
||||||
sending = u->qmem.num_pending;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (; qnum != u->qmem.end; qnum = (qnum + 1) % QMEM_LEN) {
|
for (; qnum != u->qmem.end; qnum = (qnum + 1) % QMEM_LEN) {
|
||||||
q = &u->qmem.queries[qnum];
|
q = &u->qmem.queries[qnum].q;
|
||||||
|
|
||||||
/* queries will always be in time order */
|
/* queries will always be in time order */
|
||||||
timeradd(&q->time_recv, &u->dns_timeout, &timeout);
|
timeradd(&q->time_recv, &u->dns_timeout, &timeout);
|
||||||
@ -295,18 +334,15 @@ qmem_max_wait(struct dnsfd *dns_fds, int *touser, struct query **sendq)
|
|||||||
/* respond to a query with ping/data if:
|
/* respond to a query with ping/data if:
|
||||||
* - query has timed out (ping, or data if available)
|
* - query has timed out (ping, or data if available)
|
||||||
* - user has pending data (always data)
|
* - user has pending data (always data)
|
||||||
* - user has pending ACK (either)
|
* - user has pending ACK (either) */
|
||||||
* - user has pending ping (always ping, with data if available) */
|
timersub(&now, &q->time_recv, &age);
|
||||||
timersub(&q->time_recv, &now, &age);
|
|
||||||
age_ms = timeval_to_ms(&age);
|
age_ms = timeval_to_ms(&age);
|
||||||
|
|
||||||
/* only consider "immediate" when age is negligible */
|
/* only consider "immediate" when age is negligible */
|
||||||
immediate = age_ms <= 10;
|
immediate = llabs(age_ms) <= 10;
|
||||||
|
|
||||||
if (debug >= 3) {
|
QMEM_DEBUG(3, userid, "Auto response to cached query: ID %d, %ld ms old (%s), timeout %ld ms",
|
||||||
QMEM_DEBUG(3, userid, "Auto response to cached query: ID %d, %ld ms old (%s), timeout %ld ms",
|
q->id, age_ms, immediate ? "immediate" : "lazy", timeval_to_ms(&u->dns_timeout));
|
||||||
q->id, age_ms, immediate ? "immediate" : "lazy", timeval_to_ms(&u->dns_timeout));
|
|
||||||
}
|
|
||||||
|
|
||||||
sent++;
|
sent++;
|
||||||
QMEM_DEBUG(4, userid, "ANSWER q id %d, ACK %d; sent %lu of %lu + sending another %lu",
|
QMEM_DEBUG(4, userid, "ANSWER q id %d, ACK %d; sent %lu of %lu + sending another %lu",
|
||||||
@ -351,85 +387,6 @@ qmem_max_wait(struct dnsfd *dns_fds, int *touser, struct query **sendq)
|
|||||||
|
|
||||||
return soonest;
|
return soonest;
|
||||||
}
|
}
|
||||||
#endif /* QMEM_LEN */
|
|
||||||
|
|
||||||
#ifdef DNSCACHE_LEN
|
|
||||||
|
|
||||||
/* On the DNS cache:
|
|
||||||
|
|
||||||
This cache is implemented to better handle the aggressively impatient DNS
|
|
||||||
servers that very quickly re-send requests when we choose to not
|
|
||||||
immediately answer them in lazy mode. This cache works much better than
|
|
||||||
pruning(=dropping) the improper requests, since the DNS server will
|
|
||||||
actually get an answer instead of silence.
|
|
||||||
|
|
||||||
Because of the CMC in both ping and upstream data, unwanted cache hits
|
|
||||||
are prevented. Data-CMC is only 36 counts, so our cache length should
|
|
||||||
not exceed 36/2=18 packets. (This quick rule assumes all packets are
|
|
||||||
otherwise equal, which they arent: up/downstream seq, TCP/IP headers and
|
|
||||||
the actual data
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void
|
|
||||||
save_to_dnscache(int userid, struct query *q, char *answer, int answerlen)
|
|
||||||
/* Store answer in our little DNS cache. */
|
|
||||||
{
|
|
||||||
int fill;
|
|
||||||
|
|
||||||
if (answerlen > sizeof(users[userid].dnscache_answer[fill]))
|
|
||||||
return; /* can't store this */
|
|
||||||
|
|
||||||
fill = users[userid].dnscache_lastfilled + 1;
|
|
||||||
if (fill >= DNSCACHE_LEN)
|
|
||||||
fill = 0;
|
|
||||||
|
|
||||||
memcpy(&(users[userid].dnscache_q[fill]), q, sizeof(struct query));
|
|
||||||
memcpy(users[userid].dnscache_answer[fill], answer, answerlen);
|
|
||||||
users[userid].dnscache_answerlen[fill] = answerlen;
|
|
||||||
|
|
||||||
users[userid].dnscache_lastfilled = fill;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
answer_from_dnscache(int dns_fd, int userid, struct query *q)
|
|
||||||
/* Checks cache and sends repeated answer if we alreay saw this query recently.
|
|
||||||
Returns: 1 = answer sent, drop this query, 0 = no answer sent, this is
|
|
||||||
a new query. */
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
int use;
|
|
||||||
|
|
||||||
for (i = 0; i < DNSCACHE_LEN ; i++) {
|
|
||||||
/* Try cache most-recent-first */
|
|
||||||
use = users[userid].dnscache_lastfilled - i;
|
|
||||||
if (use < 0)
|
|
||||||
use += DNSCACHE_LEN;
|
|
||||||
|
|
||||||
if (users[userid].dnscache_q[use].id == 0)
|
|
||||||
continue;
|
|
||||||
if (users[userid].dnscache_answerlen[use] <= 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (users[userid].dnscache_q[use].type != q->type ||
|
|
||||||
strcmp(users[userid].dnscache_q[use].name, q->name))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* okay, match */
|
|
||||||
DEBUG(1, "OUT user %d %s from dnscache", userid, q->name);
|
|
||||||
|
|
||||||
write_dns(dns_fd, q, users[userid].dnscache_answer[use],
|
|
||||||
users[userid].dnscache_answerlen[use],
|
|
||||||
users[userid].downenc);
|
|
||||||
|
|
||||||
q->id = 0; /* this query was used */
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* here only when no match found */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* DNSCACHE_LEN */
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
get_dns_fd(struct dnsfd *fds, struct sockaddr_storage *addr)
|
get_dns_fd(struct dnsfd *fds, struct sockaddr_storage *addr)
|
||||||
@ -560,13 +517,8 @@ send_data_or_ping(struct dnsfd *dns_fds, int userid, struct query *q,
|
|||||||
write_dns(get_dns_fd(dns_fds, &q->from), q, (char *)pkt,
|
write_dns(get_dns_fd(dns_fds, &q->from), q, (char *)pkt,
|
||||||
datalen + headerlen, users[userid].downenc);
|
datalen + headerlen, users[userid].downenc);
|
||||||
|
|
||||||
#ifdef DNSCACHE_LEN
|
|
||||||
save_to_dnscache(userid, q, (char *)pkt, datalen + headerlen);
|
|
||||||
#endif
|
|
||||||
#ifdef QMEM_LEN
|
|
||||||
/* mark query as answered */
|
/* mark query as answered */
|
||||||
qmem_answered(userid);
|
qmem_answered(userid, pkt, datalen + headerlen);
|
||||||
#endif
|
|
||||||
window_tick(out);
|
window_tick(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1234,7 +1186,6 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query
|
|||||||
if (version == PROTOCOL_VERSION) {
|
if (version == PROTOCOL_VERSION) {
|
||||||
userid = find_available_user();
|
userid = find_available_user();
|
||||||
if (userid >= 0) {
|
if (userid >= 0) {
|
||||||
int i;
|
|
||||||
struct tun_user *u = &users[userid];
|
struct tun_user *u = &users[userid];
|
||||||
u->seed = rand();
|
u->seed = rand();
|
||||||
/* Store remote IP number */
|
/* Store remote IP number */
|
||||||
@ -1262,16 +1213,7 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query
|
|||||||
window_buffer_clear(u->outgoing);
|
window_buffer_clear(u->outgoing);
|
||||||
window_buffer_clear(u->incoming);
|
window_buffer_clear(u->incoming);
|
||||||
u->next_upstream_ack = -1;
|
u->next_upstream_ack = -1;
|
||||||
#ifdef QMEM_LEN
|
|
||||||
qmem_init(userid);
|
qmem_init(userid);
|
||||||
#endif
|
|
||||||
#ifdef DNSCACHE_LEN
|
|
||||||
for (i = 0; i < DNSCACHE_LEN; i++) {
|
|
||||||
u->dnscache_q[i].id = 0;
|
|
||||||
u->dnscache_answerlen[i] = 0;
|
|
||||||
}
|
|
||||||
u->dnscache_lastfilled = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
DEBUG(1, "User %d connected with correct version from %s.",
|
DEBUG(1, "User %d connected with correct version from %s.",
|
||||||
userid, format_addr(&q->from, q->fromlen));
|
userid, format_addr(&q->from, q->fromlen));
|
||||||
@ -1620,7 +1562,7 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query
|
|||||||
} 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;
|
||||||
unsigned timeout_ms;
|
unsigned timeout_ms, set_timeout;
|
||||||
struct timeval timeout;
|
struct timeval timeout;
|
||||||
|
|
||||||
/* We can't handle id=0, that's "no packet" to the dnscache. So drop
|
/* We can't handle id=0, that's "no packet" to the dnscache. So drop
|
||||||
@ -1644,16 +1586,9 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query
|
|||||||
return; /* illegal id */
|
return; /* illegal id */
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DNSCACHE_LEN
|
|
||||||
/* Check if cached */
|
|
||||||
if (answer_from_dnscache(dns_fd, userid, q))
|
|
||||||
return;
|
|
||||||
#endif // XXX hmm these look very similar...
|
|
||||||
#ifdef QMEM_LEN
|
|
||||||
/* Check if cached */
|
/* Check if cached */
|
||||||
if (!qmem_is_cached(dns_fd, userid, q))
|
if (!qmem_is_cached(dns_fd, userid, q))
|
||||||
return;
|
return;
|
||||||
#endif
|
|
||||||
|
|
||||||
dn_ack = ((unpacked[8] >> 2) & 1) ? unpacked[1] : -1;
|
dn_ack = ((unpacked[8] >> 2) & 1) ? unpacked[1] : -1;
|
||||||
up_winsize = unpacked[2];
|
up_winsize = unpacked[2];
|
||||||
@ -1665,29 +1600,36 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query
|
|||||||
timeout = ms_to_timeval(timeout_ms);
|
timeout = ms_to_timeval(timeout_ms);
|
||||||
|
|
||||||
respond = unpacked[8] & 1;
|
respond = unpacked[8] & 1;
|
||||||
|
set_timeout = (unpacked[8] >> 3) & 1;
|
||||||
|
|
||||||
|
DEBUG(3, "PING pkt user %d, down %d/%d, up %d/%d, ACK %d, %stimeout %u ms, respond %d (flags %02X)",
|
||||||
|
userid, dn_seq, dn_winsize, up_seq, up_winsize, dn_ack,
|
||||||
|
set_timeout ? "SET " : "", timeout_ms, respond, unpacked[8]);
|
||||||
|
|
||||||
|
if (set_timeout) {
|
||||||
|
/* update user's query timeout if timeout flag set */
|
||||||
|
users[userid].dns_timeout = timeout;
|
||||||
|
|
||||||
|
/* if timeout is 0, we do not enable lazy mode but it is effectively the same */
|
||||||
|
int newlazy = !(timeout_ms == 0);
|
||||||
|
if (newlazy != users[userid].lazy)
|
||||||
|
DEBUG(2, "User %d: not setting lazymode to %d with timeout %u",
|
||||||
|
userid, newlazy, timeout_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
qmem_append(userid, q);
|
||||||
|
|
||||||
if (respond) {
|
if (respond) {
|
||||||
/* ping handshake - set windowsizes etc, respond NOW using this query
|
/* ping handshake - set windowsizes etc, respond NOW using this query
|
||||||
* NOTE: not added to qmem */
|
* NOTE: not added to qmem */
|
||||||
|
DEBUG(2, "PING HANDSHAKE set windowsizes (old/new) up: %d/%d, dn: %d/%d",
|
||||||
|
users[userid].outgoing->windowsize, dn_winsize, users[userid].incoming->windowsize, up_winsize);
|
||||||
users[userid].outgoing->windowsize = dn_winsize;
|
users[userid].outgoing->windowsize = dn_winsize;
|
||||||
users[userid].incoming->windowsize = up_winsize;
|
users[userid].incoming->windowsize = up_winsize;
|
||||||
send_data_or_ping(dns_fds, userid, q, 1, 1);
|
send_data_or_ping(dns_fds, userid, q, 1, 1);
|
||||||
} else {
|
return;
|
||||||
qmem_append(userid, q);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((unpacked[8] >> 3) & 1) {
|
|
||||||
/* update user's query timeout if timeout flag set */
|
|
||||||
users[userid].dns_timeout = timeout;
|
|
||||||
if (timeout_ms == 0) {
|
|
||||||
/* immediate mode is implied by server timeout of 0 */
|
|
||||||
users[userid].lazy = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG(3, "PING pkt from user %d, down %d/%d, up %d/%d, ACK %d, set timeout %u ms (flags %02X)",
|
|
||||||
userid, dn_seq, dn_winsize, up_seq, up_winsize, dn_ack, timeout_ms, unpacked[8]);
|
|
||||||
|
|
||||||
user_process_incoming_data(tun_fd, dns_fds, userid, dn_ack);
|
user_process_incoming_data(tun_fd, dns_fds, userid, dn_ack);
|
||||||
|
|
||||||
/* if respond flag not set, query waits in qmem and is used later */
|
/* if respond flag not set, query waits in qmem and is used later */
|
||||||
@ -1724,16 +1666,9 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query
|
|||||||
return; /* illegal IP */
|
return; /* illegal IP */
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DNSCACHE_LEN
|
|
||||||
/* Check if cached */
|
|
||||||
if (answer_from_dnscache(dns_fd, userid, q))
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
#ifdef QMEM_LEN
|
|
||||||
/* Check if cached */
|
/* Check if cached */
|
||||||
if (!qmem_is_cached(dns_fd, userid, q))
|
if (!qmem_is_cached(dns_fd, userid, q))
|
||||||
qmem_append(userid, q);
|
qmem_append(userid, q);
|
||||||
#endif
|
|
||||||
/* 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 */
|
||||||
len = sizeof(unpacked);
|
len = sizeof(unpacked);
|
||||||
@ -1758,7 +1693,9 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query
|
|||||||
/* Shouldn't normally happen; will always be reset after sending a packet. */
|
/* Shouldn't normally happen; will always be reset after sending a packet. */
|
||||||
DEBUG(1, "[WARNING] next_upstream_ack == %d for user %d.", users[userid].next_upstream_ack, userid);
|
DEBUG(1, "[WARNING] next_upstream_ack == %d for user %d.", users[userid].next_upstream_ack, userid);
|
||||||
}
|
}
|
||||||
users[userid].next_upstream_ack = window_process_incoming_fragment(users[userid].incoming, &f);
|
|
||||||
|
window_process_incoming_fragment(users[userid].incoming, &f);
|
||||||
|
users[userid].next_upstream_ack = f.seqID;
|
||||||
|
|
||||||
user_process_incoming_data(tun_fd, dns_fds, userid, f.ack_other);
|
user_process_incoming_data(tun_fd, dns_fds, userid, f.ack_other);
|
||||||
|
|
||||||
|
24
src/server.h
24
src/server.h
@ -38,13 +38,14 @@
|
|||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define DNSCACHE_LEN 10
|
|
||||||
/* Undefine to disable. Should be less than 18; also see comments in iodined.c */
|
|
||||||
|
|
||||||
/* Max number of incoming queries to hold at one time (recommended to be same as windowsize)
|
/* Max number of incoming queries to hold at one time (recommended to be same as windowsize)
|
||||||
* Memory = USERS * (sizeof(struct query_buffer) + sizeof(query) * QMEM_LEN) */
|
* Memory = USERS * (sizeof(struct query_buffer) + sizeof(query) * QMEM_LEN) */
|
||||||
#define QMEM_LEN 24
|
#define QMEM_LEN 24
|
||||||
|
|
||||||
|
#define USE_DNSCACHE
|
||||||
|
/* QMEM entries contain additional space for DNS responses.
|
||||||
|
* Undefine to disable. */
|
||||||
|
|
||||||
/* Number of fragments in outgoing buffer.
|
/* Number of fragments in outgoing buffer.
|
||||||
* Mem usage: USERS * (MAX_FRAGLEN * OUTFRAGBUF_LEN + sizeof(struct window_buffer)) */
|
* Mem usage: USERS * (MAX_FRAGLEN * OUTFRAGBUF_LEN + sizeof(struct window_buffer)) */
|
||||||
#define OUTFRAGBUF_LEN 64
|
#define OUTFRAGBUF_LEN 64
|
||||||
@ -87,8 +88,21 @@ typedef enum {
|
|||||||
VERSION_FULL
|
VERSION_FULL
|
||||||
} version_ack_t;
|
} version_ack_t;
|
||||||
|
|
||||||
struct query_buffer {
|
struct query_answer {
|
||||||
struct query queries[QMEM_LEN];
|
uint8_t data[4096];
|
||||||
|
size_t len;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct qmem_query {
|
||||||
|
struct query q;
|
||||||
|
#ifdef USE_DNSCACHE
|
||||||
|
struct query_answer a;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Struct used for QMEM + DNS cache */
|
||||||
|
struct qmem_buffer {
|
||||||
|
struct qmem_query queries[QMEM_LEN];
|
||||||
size_t start_pending; /* index of first "pending" query (ie. no response yet) */
|
size_t start_pending; /* index of first "pending" query (ie. no response yet) */
|
||||||
size_t start; /* index of first stored/pending query */
|
size_t start; /* index of first stored/pending query */
|
||||||
size_t end; /* index of space after last stored/pending query */
|
size_t end; /* index of space after last stored/pending query */
|
||||||
|
13
src/user.c
13
src/user.c
@ -132,10 +132,19 @@ all_users_waiting_to_send()
|
|||||||
outgoing buffer, so that sending back-to-back is possible
|
outgoing buffer, so that sending back-to-back is possible
|
||||||
without going through another select loop. */
|
without going through another select loop. */
|
||||||
{
|
{
|
||||||
for (int i = 0; i < usercount; i++)
|
int numactive = 0;
|
||||||
if (user_active(i))
|
for (int i = 0; i < usercount; i++) {
|
||||||
|
if (user_active(i)) {
|
||||||
if (users[i].outgoing->length - users[i].outgoing->numitems > 8)
|
if (users[i].outgoing->length - users[i].outgoing->numitems > 8)
|
||||||
return 0;
|
return 0;
|
||||||
|
numactive ++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* no users waiting if there are no users */
|
||||||
|
if (numactive == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
src/user.h
10
src/user.h
@ -47,15 +47,7 @@ struct tun_user {
|
|||||||
int fragsize;
|
int fragsize;
|
||||||
enum connection conn;
|
enum connection conn;
|
||||||
int lazy;
|
int lazy;
|
||||||
#ifdef QMEM_LEN
|
struct qmem_buffer qmem;
|
||||||
struct query_buffer qmem;
|
|
||||||
#endif
|
|
||||||
#ifdef DNSCACHE_LEN
|
|
||||||
struct query dnscache_q[DNSCACHE_LEN];
|
|
||||||
char dnscache_answer[DNSCACHE_LEN][4096];
|
|
||||||
int dnscache_answerlen[DNSCACHE_LEN];
|
|
||||||
int dnscache_lastfilled;
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct tun_user *users;
|
extern struct tun_user *users;
|
||||||
|
Loading…
Reference in New Issue
Block a user