Cleaned up handle_null_request, some protocol changes

This commit is contained in:
frekky 2016-01-22 21:55:52 +08:00
parent ae55020b03
commit 88b11bffeb

View File

@ -420,10 +420,7 @@ send_version_response(int fd, version_ack_t ack, uint32_t payload, int userid, s
break; break;
} }
out[4] = ((payload >> 24) & 0xff); *(uint32_t *) (out + 4) = htonl(payload);
out[5] = ((payload >> 16) & 0xff);
out[6] = ((payload >> 8) & 0xff);
out[7] = ((payload) & 0xff);
out[8] = userid & 0xff; out[8] = userid & 0xff;
write_dns(fd, q, out, sizeof(out), users[userid].downenc); write_dns(fd, q, out, sizeof(out), users[userid].downenc);
@ -1128,49 +1125,58 @@ write_dns(int fd, struct query *q, char *data, size_t datalen, char downenc)
sendto(fd, buf, len, 0, (struct sockaddr*)&q->from, q->fromlen); sendto(fd, buf, len, 0, (struct sockaddr*)&q->from, q->fromlen);
} }
#define CHECK_LEN(l, x) \
if (l < x) { \
write_dns(dns_fd, q, "BADLEN", 6, 'T'); \
return; \
}
void void
handle_null_request(int dns_fd, struct query *q, int domain_len) handle_dns_version(int dns_fd, struct query *q, int domain_len)
/* Handles a NULL DNS request. See doc/proto_XXXXXXXX.txt for details on iodine protocol. */
{ {
struct in_addr tempip; uint8_t unpacked[512];
uint8_t in[512];
char logindata[16];
uint8_t out[64*1024];
static uint8_t unpacked[64*1024];
char *tmp[2];
int userid;
size_t read;
userid = -1;
/* Everything here needs at least two chars in the name */
if (domain_len < 2)
return;
memcpy(in, q->name, MIN(domain_len, sizeof(in)));
DEBUG(3, "NULL request length %d/%" L "u, command '%c'", domain_len, sizeof(in), in[0]);
if(in[0] == 'V' || in[0] == 'v') { /* Version request */
uint32_t version = !PROTOCOL_VERSION; uint32_t version = !PROTOCOL_VERSION;
int userid, read;
read = unpack_data(unpacked, sizeof(unpacked), in + 1, domain_len - 1, b32); read = unpack_data(unpacked, sizeof(unpacked), (uint8_t *)q->name + 1, domain_len - 1, b32);
printf("unpacked: %08X, %08X, %08X\n", *(uint32_t *) unpacked, *((uint32_t *) unpacked), ntohl(*((uint32_t *) unpacked)));
/* Version greeting, compare and send ack/nak */ /* Version greeting, compare and send ack/nak */
if (read > 4) { if (read >= 4) {
/* Received V + 32bits version (network byte order) */ /* Received V + 32bits version (network byte order) */
version = ntohl(*(uint32_t *) unpacked); version = ntohl(*(uint32_t *) unpacked);
} /* if invalid pkt, just send VNAK */ } /* if invalid pkt, just send VNAK */
if (version == PROTOCOL_VERSION) { if (version != PROTOCOL_VERSION) {
send_version_response(dns_fd, VERSION_NACK, PROTOCOL_VERSION, 0, q);
syslog(LOG_INFO, "dropped user from %s, sent bad version %08X",
format_addr(&q->from, q->fromlen), version);
return;
}
userid = find_available_user(); userid = find_available_user();
if (userid >= 0) { if (userid < 0) {
/* No space for another user */
send_version_response(dns_fd, VERSION_FULL, created_users, 0, q);
syslog(LOG_INFO, "dropped user from %s, server full",
format_addr(&q->from, q->fromlen));
return;
}
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 */
memcpy(&(u->host), &(q->from), q->fromlen); memcpy(&(u->host), &(q->from), q->fromlen);
u->hostlen = q->fromlen; u->hostlen = q->fromlen;
u->fragsize = 100; /* very safe */
u->conn = CONN_DNS_NULL;
u->encoder = get_base32_encoder(); u->encoder = get_base32_encoder();
u->down_compression = 1;
u->lazy = 0;
u->next_upstream_ack = -1;
u->outgoing->maxfraglen = u->encoder->get_raw_length(u->fragsize) - DOWNSTREAM_PING_HDR;
window_buffer_clear(u->outgoing);
window_buffer_clear(u->incoming);
qmem_init(userid);
if (q->type == T_NULL || q->type == T_PRIVATE) { if (q->type == T_NULL || q->type == T_PRIVATE) {
u->downenc = 'R'; u->downenc = 'R';
@ -1179,45 +1185,86 @@ handle_null_request(int dns_fd, struct query *q, int domain_len)
u->downenc = 'T'; u->downenc = 'T';
u->downenc_bits = 5; u->downenc_bits = 5;
} }
u->down_compression = 1;
send_version_response(dns_fd, VERSION_ACK, u->seed, userid, q); send_version_response(dns_fd, VERSION_ACK, u->seed, userid, q);
syslog(LOG_INFO, "Accepted version for user #%d from %s", syslog(LOG_INFO, "Accepted version for user #%d from %s",
userid, format_addr(&q->from, q->fromlen)); userid, format_addr(&q->from, q->fromlen));
u->fragsize = 100; /* very safe */
u->conn = CONN_DNS_NULL;
u->lazy = 0;
// TODO: client specified window size
u->outgoing->maxfraglen = u->encoder->get_raw_length(u->fragsize) - DOWNSTREAM_PING_HDR;
window_buffer_clear(u->outgoing);
window_buffer_clear(u->incoming);
u->next_upstream_ack = -1;
qmem_init(userid);
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));
} else {
/* No space for another user */
send_version_response(dns_fd, VERSION_FULL, created_users, 0, q);
syslog(LOG_INFO, "dropped user from %s, server full",
format_addr(&q->from, q->fromlen));
} }
} else {
send_version_response(dns_fd, VERSION_NACK, PROTOCOL_VERSION, 0, q); void
syslog(LOG_INFO, "dropped user from %s, sent bad version %08X", handle_dns_downstream_codec_check(int dns_fd, struct query *q, int domain_len)
format_addr(&q->from, q->fromlen), version); {
} int codec;
return; char *datap;
} else if (in[0] == 'L' || in[0] == 'l') { /* Login request */ int datalen;
read = unpack_data(unpacked, sizeof(unpacked), in + 1, domain_len - 1, b32); uint8_t unpacked[10];
if (read < 17) {
unpack_data(unpacked, sizeof(unpacked), (uint8_t *)q->name + 2, MIN(domain_len - 2, 4), b32);
switch (unpacked[0]) { /* check variant */
case 1:
datap = DOWNCODECCHECK1;
datalen = DOWNCODECCHECK1_LEN;
break;
default:
write_dns(dns_fd, q, "BADLEN", 6, 'T'); write_dns(dns_fd, q, "BADLEN", 6, 'T');
return; return;
} }
/* Login phase, handle auth */ codec = toupper(q->name[1]);
userid = unpacked[0]; switch (codec) {
case 'T':
case 'S':
case 'U':
case 'V':
if (q->type == T_TXT ||
q->type == T_SRV || q->type == T_MX ||
q->type == T_CNAME || q->type == T_A) {
write_dns(dns_fd, q, datap, datalen, codec);
return;
}
break;
case 'R':
if (q->type == T_NULL || q->type == T_TXT) {
write_dns(dns_fd, q, datap, datalen, 'R');
return;
}
break;
}
/* if still here, then codec not available */
write_dns(dns_fd, q, "BADCODEC", 8, 'T');
}
void
handle_dns_login(int dns_fd, struct query *q, int domain_len, int userid)
{
uint8_t unpacked[512], flags;
char logindata[16], *tmp[2], out[512];
struct in_addr tempip;
char remote_tcp, remote_isnt_localhost, use_ipv6, drop_packets;
int length = 18, read;
read = unpack_data(unpacked, sizeof(unpacked), (uint8_t *) q->name, domain_len, b32);
/* Decode flags and calculate min. length */
flags = unpacked[1];
remote_tcp = flags & 1;
remote_isnt_localhost = (flags & 2) >> 1;
use_ipv6 = (flags & 4) >> 2;
drop_packets = (flags & 8) >> 3;
length += (remote_tcp ? 2 : 0) + (remote_isnt_localhost ? (use_ipv6 ? 16 : 4) : 0);
CHECK_LEN(read, length);
DEBUG(2, "Received login request for user %d from %s.", DEBUG(2, "Received login request for user %d from %s.",
userid, format_addr(&q->from, q->fromlen)); userid, format_addr(&q->from, q->fromlen));
if (check_user_and_ip(userid, q, server.check_ip) != 0) { if (check_user_and_ip(userid, q, server.check_ip) != 0) {
write_dns(dns_fd, q, "BADIP", 5, 'T'); write_dns(dns_fd, q, "BADIP", 5, 'T');
syslog(LOG_WARNING, "dropped login request from user #%d from %s; expected source %s", syslog(LOG_WARNING, "dropped login request from user #%d from %s; expected source %s",
@ -1228,8 +1275,11 @@ handle_null_request(int dns_fd, struct query *q, int domain_len)
login_calculate(logindata, 16, server.password, users[userid].seed); login_calculate(logindata, 16, server.password, users[userid].seed);
if (read >= 18 && (memcmp(logindata, unpacked + 1, 16) == 0)) { if (read >= 18 && (memcmp(logindata, unpacked + 1, 16) == 0)) {
/* Store login ok */ /* Store login ok, count number of auth attempts */
users[userid].authenticated = 1; users[userid].authenticated++;
if (users[userid].authenticated > 1)
syslog(LOG_WARNING, "duplicate login request from user #%d from %s",
userid, format_addr(&users[userid].host, users[userid].hostlen));
/* Send ip/mtu/netmask info */ /* Send ip/mtu/netmask info */
tempip.s_addr = server.my_ip; tempip.s_addr = server.my_ip;
@ -1237,31 +1287,29 @@ handle_null_request(int dns_fd, struct query *q, int domain_len)
tempip.s_addr = users[userid].tun_ip; tempip.s_addr = users[userid].tun_ip;
tmp[1] = strdup(inet_ntoa(tempip)); tmp[1] = strdup(inet_ntoa(tempip));
read = snprintf((char *)out, sizeof(out), "%s-%s-%d-%d", read = snprintf(out, sizeof(out), "%c-%s-%s-%d-%d", b32_5to8(flags),
tmp[0], tmp[1], server.mtu, server.netmask); tmp[0], tmp[1], server.mtu, server.netmask);
write_dns(dns_fd, q, (char *)out, read, users[userid].downenc); write_dns(dns_fd, q, out, read, users[userid].downenc);
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]);
free(tmp[0]); free(tmp[0]);
} else { } else {
write_dns(dns_fd, q, "LNAK", 4, 'T'); write_dns(dns_fd, q, "LNAK", 4, 'T');
syslog(LOG_WARNING, "rejected login request from user #%d from %s, bad password", if (--users[userid].authenticated >= 0)
userid, format_addr(&q->from, q->fromlen)); users[userid].authenticated = -1;
syslog(LOG_WARNING, "rejected login request from user #%d from %s, bad password; incorrect attempts: %d",
userid, format_addr(&q->from, q->fromlen), abs(users[userid].authenticated));
} }
} }
return; }
} else if(in[0] == 'I' || in[0] == 'i') { /* IP address request */
void
handle_dns_ip_request(int dns_fd, struct query *q, int domain_len, int userid)
{
char reply[17]; char reply[17];
int length; int length;
userid = b32_8to5(in[1]);
if (check_authenticated_user_and_ip(userid, q, server.check_ip) != 0) {
write_dns(dns_fd, q, "BADIP", 5, 'T');
return; /* illegal id */
}
reply[0] = 'I'; reply[0] = 'I';
if (q->from.ss_family == AF_INET) { if (q->from.ss_family == AF_INET) {
if (server.ns_ip != INADDR_ANY) { if (server.ns_ip != INADDR_ANY) {
@ -1280,29 +1328,16 @@ handle_null_request(int dns_fd, struct query *q, int domain_len)
} }
write_dns(dns_fd, q, reply, length, 'T'); write_dns(dns_fd, q, reply, length, 'T');
} else if(in[0] == 'Z' || in[0] == 'z') { /* Upstream codec check */ }
/* Check for case conservation and chars not allowed according to RFC */
/* Reply with received hostname as data */ void
/* No userid here, reply with lowest-grade downenc */ handle_dns_upstream_codec_switch(int dns_fd, struct query *q, int domain_len, int userid,
write_dns(dns_fd, q, (char *)in, domain_len, 'T'); uint8_t *unpacked, size_t read)
return; {
} else if(in[0] == 'S' || in[0] == 's') { /* Switch upstream codec */
int codec; int codec;
struct encoder *enc; struct encoder *enc;
if (domain_len < 3) { /* len at least 3, example: "S15" */
write_dns(dns_fd, q, "BADLEN", 6, 'T');
return;
}
userid = b32_8to5(in[1]); codec = unpacked[0];
if (check_authenticated_user_and_ip(userid, q, server.check_ip) != 0) {
write_dns(dns_fd, q, "BADIP", 5, 'T');
return; /* illegal id */
}
codec = b32_8to5(in[2]);
switch (codec) { switch (codec) {
case 5: /* 5 bits per byte = base32 */ case 5: /* 5 bits per byte = base32 */
@ -1329,78 +1364,55 @@ handle_null_request(int dns_fd, struct query *q, int domain_len)
write_dns(dns_fd, q, "BADCODEC", 8, users[userid].downenc); write_dns(dns_fd, q, "BADCODEC", 8, users[userid].downenc);
break; break;
} }
return; }
} else if(in[0] == 'O' || in[0] == 'o') { /* Protocol options */
int bits = 0; void
int numopts; handle_dns_set_options(int dns_fd, struct query *q, int domain_len, int userid,
char *opts; uint8_t *unpacked, size_t read)
{
uint8_t bits = 0;
char *encname = "BADCODEC";
int tmp_lazy, tmp_downenc, tmp_comp; int tmp_lazy, tmp_downenc, tmp_comp;
if (domain_len < 7) { /* len at least 7, example: "oa1tcmc" */
write_dns(dns_fd, q, "BADLEN", 6, 'T');
return;
}
userid = b32_8to5(in[1]);
if (check_authenticated_user_and_ip(userid, q, server.check_ip) != 0) {
write_dns(dns_fd, q, "BADIP", 5, 'T');
return; /* illegal id */
}
numopts = in[2] - '0';
if (domain_len < numopts + 6 || numopts == 0 || numopts > 9) {
write_dns(dns_fd, q, "BADLEN", 6, 'T');
return; /* invalid packet */
}
/* Temporary variables: don't change anything until all options parsed */ /* Temporary variables: don't change anything until all options parsed */
tmp_lazy = users[userid].lazy; tmp_lazy = users[userid].lazy;
tmp_comp = users[userid].down_compression; tmp_comp = users[userid].down_compression;
tmp_downenc = users[userid].downenc; tmp_downenc = users[userid].downenc;
opts = (char *) in + 3; switch (unpacked[0] & 0x7C) {
case (1 << 6): /* Base32 */
for (int i = 0; i < numopts; i++) {
switch (toupper(opts[i])) {
case 'T':
tmp_downenc = 'T'; tmp_downenc = 'T';
encname = "Base32";
bits = 5; bits = 5;
break; break;
case 'S': case (1 << 5): /* Base64 */
tmp_downenc = 'S'; tmp_downenc = 'S';
encname = "Base64";
bits = 6; bits = 6;
break; break;
case 'U': case (1 << 4): /* Base64u */
tmp_downenc = 'U'; tmp_downenc = 'U';
bits = 6; encname = "Base64u";
bits = 26;
break; break;
case 'V': case (1 << 3): /* Base128 */
tmp_downenc = 'V'; tmp_downenc = 'V';
encname = "Base128";
bits = 7; bits = 7;
break; break;
case 'R': case (1 << 2): /* Raw */
tmp_downenc = 'R'; tmp_downenc = 'R';
encname = "Raw";
bits = 8; bits = 8;
break; break;
case 'L': default: /* Invalid (More than 1 encoding bit set) */
tmp_lazy = 1;
break;
case 'I':
tmp_lazy = 0;
break;
case 'C':
tmp_comp = 1;
break;
case 'D':
tmp_comp = 0;
break;
default:
write_dns(dns_fd, q, "BADCODEC", 8, users[userid].downenc); write_dns(dns_fd, q, "BADCODEC", 8, users[userid].downenc);
return; return;
} }
}
tmp_comp = (unpacked[0] & 2) >> 1; /* compression flag */
tmp_lazy = (unpacked[0] & 1); /* lazy mode flag */
/* Automatically switch to raw encoding if PRIVATE or NULL request */ /* Automatically switch to raw encoding if PRIVATE or NULL request */
if ((q->type == T_NULL || q->type == T_PRIVATE) && !bits) { if ((q->type == T_NULL || q->type == T_PRIVATE) && !bits) {
@ -1422,78 +1434,26 @@ handle_null_request(int dns_fd, struct query *q, int domain_len)
users[userid].downenc = tmp_downenc; users[userid].downenc = tmp_downenc;
users[userid].lazy = tmp_lazy; users[userid].lazy = tmp_lazy;
write_dns(dns_fd, q, opts, numopts, users[userid].downenc); write_dns(dns_fd, q, encname, strlen(encname), users[userid].downenc);
return;
} else if(in[0] == 'Y' || in[0] == 'y') { /* Downstream codec check */
int i;
char *datap;
int datalen;
if (domain_len < 6) { /* len at least 6, example: "YTxCMC" */
write_dns(dns_fd, q, "BADLEN", 6, 'T');
return;
} }
i = b32_8to5(in[2]); /* check variant */ void
handle_dns_fragsize_probe(int dns_fd, struct query *q, int domain_len, int userid,
switch (i) { uint8_t *unpacked, size_t read)
case 1: /* Downstream fragsize probe packet */
datap = DOWNCODECCHECK1; {
datalen = DOWNCODECCHECK1_LEN;
break;
default:
write_dns(dns_fd, q, "BADLEN", 6, 'T');
return;
}
switch (toupper(in[1])) {
case 'T':
case 'S':
case 'U':
case 'V':
if (q->type == T_TXT ||
q->type == T_SRV || q->type == T_MX ||
q->type == T_CNAME || q->type == T_A) {
write_dns(dns_fd, q, datap, datalen, toupper(in[1]));
return;
}
break;
case 'R':
if (q->type == T_NULL || q->type == T_TXT) {
write_dns(dns_fd, q, datap, datalen, 'R');
return;
}
break;
}
/* if still here, then codec not available */
write_dns(dns_fd, q, "BADCODEC", 8, 'T');
return;
} else if(in[0] == 'R' || in[0] == 'r') { /* Downstream fragsize probe */
int req_frag_size; int req_frag_size;
if (domain_len < 16) { /* we'd better have some chars for data... */ /* There should be some data in the query */
write_dns(dns_fd, q, "BADLEN", 6, 'T'); CHECK_LEN(domain_len, 16);
return;
}
/* Downstream fragsize probe packet */
read = unpack_data(unpacked, sizeof(unpacked), in + 1, 5, b32);
userid = unpacked[0];
if (check_authenticated_user_and_ip(userid, q, server.check_ip) != 0) {
write_dns(dns_fd, q, "BADIP", 5, 'T');
return; /* illegal id */
}
req_frag_size = ntohs(*(uint16_t *) (unpacked + 1)); req_frag_size = ntohs(*(uint16_t *) (unpacked + 1));
DEBUG(3, "Got downstream fragsize probe from user %d, required fragsize %d", userid, req_frag_size); DEBUG(3, "Got downstream fragsize probe from user %d, required fragsize %d", userid, req_frag_size);
if (req_frag_size < 2 || req_frag_size > 2047) { if (req_frag_size < 2 || req_frag_size > MAX_FRAGSIZE) {
write_dns(dns_fd, q, "BADFRAG", 7, users[userid].downenc); write_dns(dns_fd, q, "BADFRAG", 7, users[userid].downenc);
} else { } else {
char buf[2048]; char buf[MAX_FRAGSIZE];
int i; int i;
unsigned int v = ((unsigned int) rand()) & 0xff; unsigned int v = ((unsigned int) rand()) & 0xff;
@ -1502,63 +1462,48 @@ handle_null_request(int dns_fd, struct query *q, int domain_len)
buf[1] = req_frag_size & 0xff; buf[1] = req_frag_size & 0xff;
/* make checkable pseudo-random sequence */ /* make checkable pseudo-random sequence */
buf[2] = 107; buf[2] = 107;
for (i = 3; i < 2048; i++, v = (v + 107) & 0xff) for (i = 3; i < MAX_FRAGSIZE; i++, v = (v + 107) & 0xff)
buf[i] = v; buf[i] = v;
write_dns(dns_fd, q, buf, req_frag_size, users[userid].downenc); write_dns(dns_fd, q, buf, req_frag_size, users[userid].downenc);
} }
return;
} else if(in[0] == 'N' || in[0] == 'n') { /* Downstream fragsize */
int max_frag_size;
read = unpack_data(unpacked, sizeof(unpacked), in + 1, domain_len - 1, b32);
if (read < 3) {
write_dns(dns_fd, q, "BADLEN", 6, 'T');
return;
} }
void
handle_dns_set_fragsize(int dns_fd, struct query *q, int domain_len, int userid,
uint8_t *unpacked, size_t read)
/* Downstream fragsize packet */ /* Downstream fragsize packet */
userid = unpacked[0]; {
if (check_authenticated_user_and_ip(userid, q, server.check_ip) != 0) { int max_frag_size;
write_dns(dns_fd, q, "BADIP", 5, 'T'); max_frag_size = ntohs(*(uint16_t *)unpacked);
return; /* illegal id */
}
max_frag_size = ntohs(*(uint16_t *)(unpacked + 1)); if (max_frag_size < 2 || max_frag_size > MAX_FRAGSIZE) {
if (max_frag_size < 2) {
write_dns(dns_fd, q, "BADFRAG", 7, users[userid].downenc); write_dns(dns_fd, q, "BADFRAG", 7, users[userid].downenc);
} else { } else {
users[userid].fragsize = max_frag_size; users[userid].fragsize = max_frag_size;
users[userid].outgoing->maxfraglen = (users[userid].downenc_bits * max_frag_size) / users[userid].outgoing->maxfraglen = (users[userid].downenc_bits * max_frag_size) /
8 - DOWNSTREAM_PING_HDR; 8 - DOWNSTREAM_PING_HDR;
write_dns(dns_fd, q, (char *) unpacked + 1, 2, users[userid].downenc); write_dns(dns_fd, q, (char *)unpacked, 2, users[userid].downenc);
DEBUG(1, "Setting max downstream data length to %u bytes for user %d; %d bits (%c)", DEBUG(1, "Setting max downstream data length to %u bytes for user %d; %d bits (%c)",
users[userid].outgoing->maxfraglen, userid, users[userid].downenc_bits, users[userid].downenc); users[userid].outgoing->maxfraglen, userid, users[userid].downenc_bits, users[userid].downenc);
} }
return; }
} else if(in[0] == 'P' || in[0] == 'p') { /* Ping request */
void
handle_dns_ping(int dns_fd, struct query *q, int domain_len, int userid,
uint8_t *unpacked, size_t read)
{
int dn_seq, up_seq, dn_winsize, up_winsize, dn_ack; int dn_seq, up_seq, dn_winsize, up_winsize, dn_ack;
int respond, set_qtimeout, set_wtimeout; int respond, set_qtimeout, set_wtimeout;
unsigned qtimeout_ms, wtimeout_ms; unsigned qtimeout_ms, wtimeout_ms;
read = unpack_data(unpacked, sizeof(unpacked), in + 1, domain_len - 1, b32); CHECK_LEN(read, UPSTREAM_PING);
if (read < UPSTREAM_PING) {
DEBUG(1, "Invalid ping! Length %" L "u", read);
return;
}
/* Check userid */ /* Check if query is cached */
userid = unpacked[0];
if (check_authenticated_user_and_ip(userid, q, server.check_ip) != 0) {
write_dns(dns_fd, q, "BADIP", 5, 'T');
return; /* illegal id */
}
/* Check if cached */
if (qmem_is_cached(dns_fd, userid, q)) if (qmem_is_cached(dns_fd, userid, q))
return; return;
/* Unpack flags/options from ping header */
dn_ack = ((unpacked[10] >> 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];
@ -1568,7 +1513,6 @@ handle_null_request(int dns_fd, struct query *q, int domain_len)
/* Query timeout and window frag timeout */ /* Query timeout and window frag timeout */
qtimeout_ms = ntohs(*(uint16_t *) (unpacked + 6)); qtimeout_ms = ntohs(*(uint16_t *) (unpacked + 6));
wtimeout_ms = ntohs(*(uint16_t *) (unpacked + 8)); wtimeout_ms = ntohs(*(uint16_t *) (unpacked + 8));
respond = unpacked[10] & 1; respond = unpacked[10] & 1;
set_qtimeout = (unpacked[10] >> 3) & 1; set_qtimeout = (unpacked[10] >> 3) & 1;
set_wtimeout = (unpacked[10] >> 4) & 1; set_wtimeout = (unpacked[10] >> 4) & 1;
@ -1585,7 +1529,7 @@ handle_null_request(int dns_fd, struct query *q, int domain_len)
/* 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 = !(qtimeout_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 changing lazymode to %d with timeout %u",
userid, newlazy, qtimeout_ms); userid, newlazy, qtimeout_ms);
} }
@ -1598,7 +1542,7 @@ handle_null_request(int dns_fd, struct query *q, int domain_len)
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: still added to qmem (for cache) even though responded to immediately */
DEBUG(2, "PING HANDSHAKE set windowsizes (old/new) up: %d/%d, dn: %d/%d", 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].incoming->windowsize, up_winsize);
users[userid].outgoing->windowsize = dn_winsize; users[userid].outgoing->windowsize = dn_winsize;
@ -1607,31 +1551,19 @@ handle_null_request(int dns_fd, struct query *q, int domain_len)
return; return;
} }
user_process_incoming_data(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 */
} else if (isxdigit(in[0])) { /* Upstream data packet */ user_process_incoming_data(userid, dn_ack);
int code = 0; }
void
handle_dns_data(int dns_fd, struct query *q, int domain_len, int userid)
{
uint8_t unpacked[512];
static fragment f; static fragment f;
size_t len; size_t len;
/* Need 6 char header + >=1 char data */ /* Need 6 char header + >=1 char data */
if (domain_len < UPSTREAM_HDR + 1) CHECK_LEN(domain_len, UPSTREAM_HDR + 1);
return;
if ((in[0] >= '0' && in[0] <= '9'))
code = in[0] - '0';
if ((in[0] >= 'a' && in[0] <= 'f'))
code = in[0] - 'a' + 10;
if ((in[0] >= 'A' && in[0] <= 'F'))
code = in[0] - 'A' + 10;
userid = code;
/* Check user and sending IP address */
if (check_authenticated_user_and_ip(userid, q, server.check_ip) != 0) {
write_dns(dns_fd, q, "BADIP", 5, 'T');
return; /* illegal IP */
}
/* Check if cached */ /* Check if cached */
if (qmem_is_cached(dns_fd, userid, q)) { if (qmem_is_cached(dns_fd, userid, q)) {
@ -1643,7 +1575,7 @@ handle_null_request(int dns_fd, struct query *q, int domain_len)
/* 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);
read = b32->decode(unpacked, &len, in + 2, 5); b32->decode(unpacked, &len, (uint8_t *)q->name + 2, 5);
f.seqID = unpacked[0]; f.seqID = unpacked[0];
unpacked[2] >>= 4; /* Lower 4 bits are unused */ unpacked[2] >>= 4; /* Lower 4 bits are unused */
@ -1653,7 +1585,7 @@ handle_null_request(int dns_fd, struct query *q, int domain_len)
f.end = unpacked[2] & 1; f.end = unpacked[2] & 1;
/* Decode remainder of data with user encoding into fragment */ /* Decode remainder of data with user encoding into fragment */
f.len = unpack_data(f.data, MAX_FRAGSIZE, in + UPSTREAM_HDR, f.len = unpack_data(f.data, MAX_FRAGSIZE, (uint8_t *)q->name + UPSTREAM_HDR,
domain_len - UPSTREAM_HDR, users[userid].encoder); domain_len - UPSTREAM_HDR, users[userid].encoder);
DEBUG(3, "frag seq %3u, datalen %5lu, ACK %3d, compression %1d, s%1d e%1d", DEBUG(3, "frag seq %3u, datalen %5lu, ACK %3d, compression %1d, s%1d e%1d",
@ -1673,8 +1605,105 @@ handle_null_request(int dns_fd, struct query *q, int domain_len)
/* Nothing to do. ACK for this fragment is sent later in qmem_max_wait, /* Nothing to do. ACK for this fragment is sent later in qmem_max_wait,
* using an old query. This is left in qmem until needed/times out */ * using an old query. This is left in qmem until needed/times out */
} }
void
handle_null_request(int dns_fd, struct query *q, int domain_len)
/* Handles a NULL DNS request. See doc/proto_XXXXXXXX.txt for details on iodine protocol. */
{
char cmd, userchar;
int userid = -1;
/* Everything here needs at least 5 chars in the name:
* cmd, userid and more data or at least 3 bytes CMC */
if (domain_len < 5)
return;
cmd = toupper(q->name[0]);
DEBUG(3, "NULL request length %d/%" L "u, command '%c'", domain_len, sizeof(q->name), cmd);
/* Commands that do not care about userid */
if (cmd == 'V') { /* Version check - before userid is assigned*/
handle_dns_version(dns_fd, q, domain_len);
return;
}
else if (cmd == 'Z') { /* Upstream codec check - user independent */
/* Check for case conservation and chars not allowed according to RFC */
/* Reply with received hostname as data (encoded in base32) */
write_dns(dns_fd, q, q->name, domain_len, 'T');
return;
}
else if (cmd == 'Y') { /* Downstream codec check - user independent*/
handle_dns_downstream_codec_check(dns_fd, q, domain_len);
return;
} }
/* Get userid from query (always 2nd byte in hex except for data packets) */
if (isxdigit(cmd)) {
/* Upstream data packet - first byte is userid in hex */
userchar = cmd;
cmd = 'd'; /* flag for data packet - not part of protocol */
} else {
userchar = toupper(q->name[1]);
}
if (isxdigit(userchar)) {
userid = (userchar >= 'A' && userchar <= 'F') ?
(userchar - 'A' + 10) : (userchar - '0');
} else {
/* Invalid user ID or bad DNS query */
write_dns(dns_fd, q, "BADLEN", 5, 'T');
}
/* Login request - after version check successful, do not check auth yet */
if (cmd == 'L') {
handle_dns_login(dns_fd, q, domain_len, userid);
return;
}
/* Check user IP and authentication status */
if (check_authenticated_user_and_ip(userid, q, server.check_ip) != 0) {
write_dns(dns_fd, q, "BADIP", 5, 'T');
return;
}
if (cmd == 'd') { /* Upstream data packet */
handle_dns_data(dns_fd, q, domain_len, userid);
return;
} else if (cmd == 'I') { /* IP request packet - no base32 data */
handle_dns_ip_request(dns_fd, q, domain_len, userid);
}
/* Following commands have everything after cmd and userid in base32
* All bytes that are not valid base32 are decoded to 0 */
uint8_t unpacked[512];
size_t raw_len;
raw_len = unpack_data(unpacked, sizeof(unpacked), (uint8_t *)q->name + 2, domain_len - 2, b32);
if (raw_len < 3) /* always at least 3 bytes after decoding at least 5 bytes */
return; /* Just in case. */
switch (cmd) {
case 'S':
handle_dns_upstream_codec_switch(dns_fd, q, domain_len, userid, unpacked, raw_len);
break;
case 'O':
handle_dns_set_options(dns_fd, q, domain_len, userid, unpacked, raw_len);
break;
case 'R':
handle_dns_fragsize_probe(dns_fd, q, domain_len, userid, unpacked, raw_len);
break;
case 'N':
handle_dns_set_fragsize(dns_fd, q, domain_len, userid, unpacked, raw_len);
break;
case 'P':
handle_dns_ping(dns_fd, q, domain_len, userid, unpacked, raw_len);
break;
default:
DEBUG(2, "Invalid DNS query! cmd = %c, hostname = '%*s'",
cmd, domain_len, q->name);
}
}
void void
handle_ns_request(int dns_fd, struct query *q) handle_ns_request(int dns_fd, struct query *q)