mirror of
https://github.com/yarrick/iodine.git
synced 2024-11-28 20:45:12 +00:00
Cleaned up handle_null_request, some protocol changes
This commit is contained in:
parent
ae55020b03
commit
88b11bffeb
553
src/server.c
553
src/server.c
@ -420,10 +420,7 @@ send_version_response(int fd, version_ack_t ack, uint32_t payload, int userid, s
|
||||
break;
|
||||
}
|
||||
|
||||
out[4] = ((payload >> 24) & 0xff);
|
||||
out[5] = ((payload >> 16) & 0xff);
|
||||
out[6] = ((payload >> 8) & 0xff);
|
||||
out[7] = ((payload) & 0xff);
|
||||
*(uint32_t *) (out + 4) = htonl(payload);
|
||||
out[8] = userid & 0xff;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#define CHECK_LEN(l, x) \
|
||||
if (l < x) { \
|
||||
write_dns(dns_fd, q, "BADLEN", 6, 'T'); \
|
||||
return; \
|
||||
}
|
||||
|
||||
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. */
|
||||
handle_dns_version(int dns_fd, struct query *q, int domain_len)
|
||||
{
|
||||
struct in_addr tempip;
|
||||
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 */
|
||||
uint8_t unpacked[512];
|
||||
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 */
|
||||
if (read > 4) {
|
||||
if (read >= 4) {
|
||||
/* Received V + 32bits version (network byte order) */
|
||||
version = ntohl(*(uint32_t *) unpacked);
|
||||
} /* 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();
|
||||
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];
|
||||
u->seed = rand();
|
||||
/* Store remote IP number */
|
||||
memcpy(&(u->host), &(q->from), q->fromlen);
|
||||
u->hostlen = q->fromlen;
|
||||
|
||||
u->fragsize = 100; /* very safe */
|
||||
u->conn = CONN_DNS_NULL;
|
||||
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) {
|
||||
u->downenc = 'R';
|
||||
@ -1179,45 +1185,86 @@ handle_null_request(int dns_fd, struct query *q, int domain_len)
|
||||
u->downenc = 'T';
|
||||
u->downenc_bits = 5;
|
||||
}
|
||||
u->down_compression = 1;
|
||||
|
||||
send_version_response(dns_fd, VERSION_ACK, u->seed, userid, q);
|
||||
|
||||
syslog(LOG_INFO, "Accepted version for user #%d from %s",
|
||||
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.",
|
||||
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);
|
||||
syslog(LOG_INFO, "dropped user from %s, sent bad version %08X",
|
||||
format_addr(&q->from, q->fromlen), version);
|
||||
}
|
||||
return;
|
||||
} else if (in[0] == 'L' || in[0] == 'l') { /* Login request */
|
||||
read = unpack_data(unpacked, sizeof(unpacked), in + 1, domain_len - 1, b32);
|
||||
if (read < 17) {
|
||||
}
|
||||
|
||||
void
|
||||
handle_dns_downstream_codec_check(int dns_fd, struct query *q, int domain_len)
|
||||
{
|
||||
int codec;
|
||||
char *datap;
|
||||
int datalen;
|
||||
uint8_t unpacked[10];
|
||||
|
||||
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');
|
||||
return;
|
||||
}
|
||||
|
||||
/* Login phase, handle auth */
|
||||
userid = unpacked[0];
|
||||
codec = toupper(q->name[1]);
|
||||
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.",
|
||||
userid, format_addr(&q->from, q->fromlen));
|
||||
|
||||
if (check_user_and_ip(userid, q, server.check_ip) != 0) {
|
||||
write_dns(dns_fd, q, "BADIP", 5, 'T');
|
||||
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);
|
||||
|
||||
if (read >= 18 && (memcmp(logindata, unpacked + 1, 16) == 0)) {
|
||||
/* Store login ok */
|
||||
users[userid].authenticated = 1;
|
||||
/* Store login ok, count number of auth attempts */
|
||||
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 */
|
||||
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;
|
||||
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);
|
||||
|
||||
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]);
|
||||
|
||||
free(tmp[1]);
|
||||
free(tmp[0]);
|
||||
} else {
|
||||
write_dns(dns_fd, q, "LNAK", 4, 'T');
|
||||
syslog(LOG_WARNING, "rejected login request from user #%d from %s, bad password",
|
||||
userid, format_addr(&q->from, q->fromlen));
|
||||
if (--users[userid].authenticated >= 0)
|
||||
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];
|
||||
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';
|
||||
if (q->from.ss_family == AF_INET) {
|
||||
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');
|
||||
} 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 */
|
||||
/* No userid here, reply with lowest-grade downenc */
|
||||
write_dns(dns_fd, q, (char *)in, domain_len, 'T');
|
||||
return;
|
||||
} else if(in[0] == 'S' || in[0] == 's') { /* Switch upstream codec */
|
||||
void
|
||||
handle_dns_upstream_codec_switch(int dns_fd, struct query *q, int domain_len, int userid,
|
||||
uint8_t *unpacked, size_t read)
|
||||
{
|
||||
int codec;
|
||||
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]);
|
||||
|
||||
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]);
|
||||
codec = unpacked[0];
|
||||
|
||||
switch (codec) {
|
||||
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);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
} else if(in[0] == 'O' || in[0] == 'o') { /* Protocol options */
|
||||
int bits = 0;
|
||||
int numopts;
|
||||
char *opts;
|
||||
}
|
||||
|
||||
void
|
||||
handle_dns_set_options(int dns_fd, struct query *q, int domain_len, int userid,
|
||||
uint8_t *unpacked, size_t read)
|
||||
{
|
||||
uint8_t bits = 0;
|
||||
char *encname = "BADCODEC";
|
||||
|
||||
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 */
|
||||
tmp_lazy = users[userid].lazy;
|
||||
tmp_comp = users[userid].down_compression;
|
||||
tmp_downenc = users[userid].downenc;
|
||||
|
||||
opts = (char *) in + 3;
|
||||
|
||||
for (int i = 0; i < numopts; i++) {
|
||||
switch (toupper(opts[i])) {
|
||||
case 'T':
|
||||
switch (unpacked[0] & 0x7C) {
|
||||
case (1 << 6): /* Base32 */
|
||||
tmp_downenc = 'T';
|
||||
encname = "Base32";
|
||||
bits = 5;
|
||||
break;
|
||||
case 'S':
|
||||
case (1 << 5): /* Base64 */
|
||||
tmp_downenc = 'S';
|
||||
encname = "Base64";
|
||||
bits = 6;
|
||||
break;
|
||||
case 'U':
|
||||
case (1 << 4): /* Base64u */
|
||||
tmp_downenc = 'U';
|
||||
bits = 6;
|
||||
encname = "Base64u";
|
||||
bits = 26;
|
||||
break;
|
||||
case 'V':
|
||||
case (1 << 3): /* Base128 */
|
||||
tmp_downenc = 'V';
|
||||
encname = "Base128";
|
||||
bits = 7;
|
||||
break;
|
||||
case 'R':
|
||||
case (1 << 2): /* Raw */
|
||||
tmp_downenc = 'R';
|
||||
encname = "Raw";
|
||||
bits = 8;
|
||||
break;
|
||||
case 'L':
|
||||
tmp_lazy = 1;
|
||||
break;
|
||||
case 'I':
|
||||
tmp_lazy = 0;
|
||||
break;
|
||||
case 'C':
|
||||
tmp_comp = 1;
|
||||
break;
|
||||
case 'D':
|
||||
tmp_comp = 0;
|
||||
break;
|
||||
default:
|
||||
default: /* Invalid (More than 1 encoding bit set) */
|
||||
write_dns(dns_fd, q, "BADCODEC", 8, users[userid].downenc);
|
||||
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 */
|
||||
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].lazy = tmp_lazy;
|
||||
|
||||
write_dns(dns_fd, q, opts, numopts, users[userid].downenc);
|
||||
return;
|
||||
} else if(in[0] == 'Y' || in[0] == 'y') { /* Downstream codec check */
|
||||
int i;
|
||||
char *datap;
|
||||
int datalen;
|
||||
write_dns(dns_fd, q, encname, strlen(encname), users[userid].downenc);
|
||||
}
|
||||
|
||||
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 */
|
||||
|
||||
switch (i) {
|
||||
case 1:
|
||||
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 */
|
||||
void
|
||||
handle_dns_fragsize_probe(int dns_fd, struct query *q, int domain_len, int userid,
|
||||
uint8_t *unpacked, size_t read)
|
||||
/* Downstream fragsize probe packet */
|
||||
{
|
||||
int req_frag_size;
|
||||
|
||||
if (domain_len < 16) { /* we'd better have some chars for data... */
|
||||
write_dns(dns_fd, q, "BADLEN", 6, 'T');
|
||||
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 */
|
||||
}
|
||||
/* There should be some data in the query */
|
||||
CHECK_LEN(domain_len, 16);
|
||||
|
||||
req_frag_size = ntohs(*(uint16_t *) (unpacked + 1));
|
||||
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);
|
||||
} else {
|
||||
char buf[2048];
|
||||
char buf[MAX_FRAGSIZE];
|
||||
int i;
|
||||
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;
|
||||
/* make checkable pseudo-random sequence */
|
||||
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;
|
||||
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 */
|
||||
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 */
|
||||
}
|
||||
{
|
||||
int max_frag_size;
|
||||
max_frag_size = ntohs(*(uint16_t *)unpacked);
|
||||
|
||||
max_frag_size = ntohs(*(uint16_t *)(unpacked + 1));
|
||||
if (max_frag_size < 2) {
|
||||
if (max_frag_size < 2 || max_frag_size > MAX_FRAGSIZE) {
|
||||
write_dns(dns_fd, q, "BADFRAG", 7, users[userid].downenc);
|
||||
} else {
|
||||
users[userid].fragsize = max_frag_size;
|
||||
users[userid].outgoing->maxfraglen = (users[userid].downenc_bits * max_frag_size) /
|
||||
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)",
|
||||
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 respond, set_qtimeout, set_wtimeout;
|
||||
unsigned qtimeout_ms, wtimeout_ms;
|
||||
|
||||
read = unpack_data(unpacked, sizeof(unpacked), in + 1, domain_len - 1, b32);
|
||||
if (read < UPSTREAM_PING) {
|
||||
DEBUG(1, "Invalid ping! Length %" L "u", read);
|
||||
return;
|
||||
}
|
||||
CHECK_LEN(read, UPSTREAM_PING);
|
||||
|
||||
/* Check userid */
|
||||
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 */
|
||||
/* Check if query is cached */
|
||||
if (qmem_is_cached(dns_fd, userid, q))
|
||||
return;
|
||||
|
||||
/* Unpack flags/options from ping header */
|
||||
dn_ack = ((unpacked[10] >> 2) & 1) ? unpacked[1] : -1;
|
||||
up_winsize = unpacked[2];
|
||||
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 */
|
||||
qtimeout_ms = ntohs(*(uint16_t *) (unpacked + 6));
|
||||
wtimeout_ms = ntohs(*(uint16_t *) (unpacked + 8));
|
||||
|
||||
respond = unpacked[10] & 1;
|
||||
set_qtimeout = (unpacked[10] >> 3) & 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 */
|
||||
int newlazy = !(qtimeout_ms == 0);
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1598,7 +1542,7 @@ handle_null_request(int dns_fd, struct query *q, int domain_len)
|
||||
|
||||
if (respond) {
|
||||
/* 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",
|
||||
users[userid].outgoing->windowsize, dn_winsize, users[userid].incoming->windowsize, up_winsize);
|
||||
users[userid].outgoing->windowsize = dn_winsize;
|
||||
@ -1607,31 +1551,19 @@ handle_null_request(int dns_fd, struct query *q, int domain_len)
|
||||
return;
|
||||
}
|
||||
|
||||
user_process_incoming_data(userid, dn_ack);
|
||||
|
||||
/* if respond flag not set, query waits in qmem and is used later */
|
||||
} else if (isxdigit(in[0])) { /* Upstream data packet */
|
||||
int code = 0;
|
||||
user_process_incoming_data(userid, dn_ack);
|
||||
}
|
||||
|
||||
void
|
||||
handle_dns_data(int dns_fd, struct query *q, int domain_len, int userid)
|
||||
{
|
||||
uint8_t unpacked[512];
|
||||
static fragment f;
|
||||
size_t len;
|
||||
|
||||
/* Need 6 char header + >=1 char data */
|
||||
if (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_LEN(domain_len, UPSTREAM_HDR + 1);
|
||||
|
||||
/* Check if cached */
|
||||
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 */
|
||||
/* First byte (after userid) = CMC (ignored); skip 2 bytes */
|
||||
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];
|
||||
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;
|
||||
|
||||
/* 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);
|
||||
|
||||
DEBUG(3, "frag seq %3u, datalen %5lu, ACK %3d, compression %1d, s%1d e%1d",
|
||||
@ -1672,9 +1604,106 @@ 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,
|
||||
* 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
|
||||
handle_ns_request(int dns_fd, struct query *q)
|
||||
|
Loading…
Reference in New Issue
Block a user