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
545
src/server.c
545
src/server.c
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user