Modified options packet to change more than 1 option at a time

This commit is contained in:
frekky 2015-09-28 20:06:23 +08:00
parent 4f16dd10e6
commit e454a7edb5
4 changed files with 119 additions and 238 deletions

View File

@ -111,12 +111,14 @@ Options:
Client sends: Client sends:
First byte o or O First byte o or O
5 bits coded as Base32 char, meaning userid 5 bits coded as Base32 char, meaning userid
1 char, meaning option number of options (n) as decimal digit
n chars, each a valid option (to be processed in order)
CMC as 3 Base32 chars CMC as 3 Base32 chars
Server sends: Server sends:
Full name of option if accepted. After this, option immediately takes Option chars in the same order as request, indicating that options were
effect in server. accepted by the server.
BADCODEC if not accepted. Previous situation remains. BADCODEC if not accepted. Previous situation remains.
BADLEN if number of options doesn't match length of query.
All options affect only the requesting client. All options affect only the requesting client.
Option chars: Option chars:

View File

@ -474,7 +474,7 @@ send_query(int fd, uint8_t *hostname)
" always work any more. Start with -L0 next time on this network."); " always work any more. Start with -L0 next time on this network.");
lazymode = 0; lazymode = 0;
server_timeout_ms = 0; server_timeout_ms = 0;
handshake_lazyoff(fd); handshake_switch_options(fd, 0, compression_down, downenc);
} }
} }
} }
@ -1455,51 +1455,21 @@ send_codec_switch(int fd, int userid, int bits)
} }
static void static void
send_compression_switch(int fd, int userid) send_server_options(int fd, int userid, int lazy, int compression, char denc, char *options)
/* Options must be length >=4 */
{ {
char buf[512] = "o_____."; char buf[512] = "oU3___CMC.";
buf[1] = b32_5to8(userid);
buf[2] = compression_down ? 'c' : 'd';
buf[3] = b32_5to8((rand_seed >> 10) & 0x1f);
buf[4] = b32_5to8((rand_seed >> 5) & 0x1f);
buf[5] = b32_5to8((rand_seed) & 0x1f);
rand_seed++;
strncat(buf, topdomain, 512 - strlen(buf));
send_query(fd, (uint8_t *)buf);
}
static void
send_downenc_switch(int fd, int userid)
{
char buf[512] = "o_____.";
buf[1] = b32_5to8(userid);
buf[2] = tolower(downenc);
buf[3] = b32_5to8((rand_seed >> 10) & 0x1f);
buf[4] = b32_5to8((rand_seed >> 5) & 0x1f);
buf[5] = b32_5to8((rand_seed) & 0x1f);
rand_seed++;
strncat(buf, topdomain, 512 - strlen(buf));
send_query(fd, (uint8_t *)buf);
}
static void
send_lazy_switch(int fd, int userid)
{
char buf[512] = "o_____.";
buf[1] = b32_5to8(userid); buf[1] = b32_5to8(userid);
if (lazymode) options[0] = tolower(denc);
buf[2] = 'l'; options[1] = lazy ? 'l' : 'i';
else options[2] = compression ? 'c' : 'd';
buf[2] = 'i'; options[3] = 0;
strncpy(buf + 3, options, 3);
buf[3] = b32_5to8((rand_seed >> 10) & 0x1f); buf[6] = b32_5to8((rand_seed >> 10) & 0x1f);
buf[4] = b32_5to8((rand_seed >> 5) & 0x1f); buf[7] = b32_5to8((rand_seed >> 5) & 0x1f);
buf[5] = b32_5to8((rand_seed ) & 0x1f); buf[8] = b32_5to8((rand_seed) & 0x1f);
rand_seed++; rand_seed++;
strncat(buf, topdomain, 512 - strlen(buf)); strncat(buf, topdomain, 512 - strlen(buf));
@ -2185,87 +2155,54 @@ codec_revert:
fprintf(stderr, "Falling back to upstream codec %s\n", dataenc->name); fprintf(stderr, "Falling back to upstream codec %s\n", dataenc->name);
} }
static void void
handshake_switch_downcompression(int dns_fd) handshake_switch_options(int dns_fd, int lazy, int compression, char denc)
{ {
char in[4096]; char in[4096];
int i;
int read; int read;
char *status; char *dname, *comp_status, *lazy_status;
char opts[4];
status = compression_down ? "on" : "off"; comp_status = compression ? "enabled" : "disabled";
fprintf(stderr, "Switching %s downstream data compression\n", status);
for (i = 0; running && i < 5; i++) {
send_compression_switch(dns_fd, userid);
read = handshake_waitdns(dns_fd, in, sizeof(in), 'O', i+1);
if (read > 0) {
if (strncmp("BADLEN", in, 6) == 0) {
fprintf(stderr, "Server got bad message length.\n");
goto fail;
} else if (strncmp("BADIP", in, 5) == 0) {
fprintf(stderr, "Server rejected sender IP address.\n");
goto fail;
} else if (strncmp("BADCODEC", in, 8) == 0) {
fprintf(stderr, "Server rejected the compression option.\n");
goto fail;
}
in[read] = 0; /* zero terminate */
fprintf(stderr, "Server %s downstream compression\n", in);
return;
}
fprintf(stderr, "Retrying downstream compression switch...\n");
}
if (!running)
return;
fprintf(stderr, "No reply from server on downstream compression switch.\n");
fail:
fprintf(stderr, "Failed to switch %s downstream data compression\n", status);
}
static void
handshake_switch_downenc(int dns_fd)
{
char in[4096];
int i;
int read;
char *dname;
dname = "Base32"; dname = "Base32";
if (downenc == 'S') if (denc == 'S')
dname = "Base64"; dname = "Base64";
else if (downenc == 'U') else if (denc == 'U')
dname = "Base64u"; dname = "Base64u";
else if (downenc == 'V') else if (denc == 'V')
dname = "Base128"; dname = "Base128";
else if (downenc == 'R') else if (denc == 'R')
dname = "Raw"; dname = "Raw";
fprintf(stderr, "Switching downstream to codec %s\n", dname); lazy_status = lazy ? "lazy" : "immediate";
for (i=0; running && i<5 ;i++) {
send_downenc_switch(dns_fd, userid); fprintf(stderr, "Switching server options: %s mode, downstream codec %s, compression %s...\n",
lazy_status, dname, comp_status);
for (int i = 0; running && i < 5; i++) {
read = handshake_waitdns(dns_fd, in, sizeof(in), 'O', i+1); send_server_options(dns_fd, userid, lazy, compression, denc, opts);
read = handshake_waitdns(dns_fd, in, sizeof(in) - 1, 'O', i + 1);
if (read > 0) { if (read > 0) {
if (strncmp("BADLEN", in, 6) == 0) { if (strncmp("BADLEN", in, 6) == 0) {
fprintf(stderr, "Server got bad message length.\n"); fprintf(stderr, "Server got bad message length.\n");
goto codec_revert; goto opt_revert;
} else if (strncmp("BADIP", in, 5) == 0) { } else if (strncmp("BADIP", in, 5) == 0) {
fprintf(stderr, "Server rejected sender IP address.\n"); fprintf(stderr, "Server rejected sender IP address.\n");
goto codec_revert; goto opt_revert;
} else if (strncmp("BADCODEC", in, 8) == 0) { } else if (strncmp("BADCODEC", in, 8) == 0) {
fprintf(stderr, "Server rejected the selected codec.\n"); fprintf(stderr, "Server rejected the selected options.\n");
goto codec_revert; goto opt_revert;
} else if (strncasecmp(opts, in + 3, 3) != 0) {
fprintf(stderr, "Server failed to change options.\n");
goto opt_revert;
} }
in[read] = 0; /* zero terminate */ fprintf(stderr, "Switched server options successfully. (%s)\n", opts);
fprintf(stderr, "Server switched downstream to codec %s\n", in); lazymode = lazy;
compression_down = compression;
downenc = denc;
return; return;
} }
@ -2276,79 +2213,9 @@ handshake_switch_downenc(int dns_fd)
fprintf(stderr, "No reply from server on codec switch.\n"); fprintf(stderr, "No reply from server on codec switch.\n");
codec_revert: opt_revert:
fprintf(stderr, "Falling back to downstream codec Base32\n"); fprintf(stderr, "Falling back to previous configuration, downstream codec %s.\n",
} dataenc->name);
static void
handshake_try_lazy(int dns_fd)
{
char in[4096];
int i;
int read;
fprintf(stderr, "Switching to lazy mode for low-latency\n");
for (i=0; running && i<5; i++) {
send_lazy_switch(dns_fd, userid);
read = handshake_waitdns(dns_fd, in, sizeof(in), 'O', i+1);
if (read > 0) {
if (strncmp("BADLEN", in, 6) == 0) {
fprintf(stderr, "Server got bad message length.\n");
goto codec_revert;
} else if (strncmp("BADIP", in, 5) == 0) {
fprintf(stderr, "Server rejected sender IP address.\n");
goto codec_revert;
} else if (strncmp("BADCODEC", in, 8) == 0) {
fprintf(stderr, "Server rejected lazy mode.\n");
goto codec_revert;
} else if (strncmp("Lazy", in, 4) == 0) {
fprintf(stderr, "Server switched to lazy mode\n");
lazymode = 1;
return;
}
}
fprintf(stderr, "Retrying lazy mode switch...\n");
}
if (!running)
return;
fprintf(stderr, "No reply from server on lazy switch.\n");
codec_revert:
fprintf(stderr, "Falling back to legacy mode\n");
lazymode = 0;
max_timeout_ms = 1000;
}
void
handshake_lazyoff(int dns_fd)
/* Used in the middle of data transfer, timing is different and no error msgs */
{
char in[4096];
int i;
int read;
for (i=0; running && i<5; i++) {
send_lazy_switch(dns_fd, userid);
read = handshake_waitdns(dns_fd, in, sizeof(in), 'O', 1);
if (read == 9 && strncmp("Immediate", in, 9) == 0) {
warnx("Server switched back to legacy mode.");
lazymode = 0;
max_timeout_ms = 1000;
return;
}
}
if (!running)
return;
warnx("No reply from server on legacy mode switch.");
} }
static int static int
@ -2600,16 +2467,8 @@ client_handshake(int dns_fd, int raw_mode, int autodetect_frag_size, int fragsiz
if (!running) if (!running)
return -1; return -1;
handshake_switch_downenc(dns_fd); /* Set options for compression, lazymode and downstream codec */
if (!running) handshake_switch_options(dns_fd, lazymode, compression_down, downenc);
return -1;
if (!compression_down)
handshake_switch_downcompression(dns_fd);
if (lazymode) {
handshake_try_lazy(dns_fd);
}
if (!running) if (!running)
return -1; return -1;

View File

@ -49,7 +49,7 @@ void client_set_hostname_maxlen(size_t i);
int client_handshake(int dns_fd, int raw_mode, int autodetect_frag_size, int fragsize); int client_handshake(int dns_fd, int raw_mode, int autodetect_frag_size, int fragsize);
int client_tunnel(int tun_fd, int dns_fd); int client_tunnel(int tun_fd, int dns_fd);
void handshake_lazyoff(int dns_fd); void handshake_switch_options(int dns_fd, int lazy, int compression, char denc);
void send_ping(int fd, int ping_response, int ack); void send_ping(int fd, int ping_response, int ack);
#endif #endif

View File

@ -1388,7 +1388,12 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query
} }
return; return;
} else if(in[0] == 'O' || in[0] == 'o') { /* Protocol options */ } else if(in[0] == 'O' || in[0] == 'o') { /* Protocol options */
if (domain_len < 3) { /* len at least 3, example: "O1T" */ int bits = 0;
int numopts;
char num[2], *opts;
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'); write_dns(dns_fd, q, "BADLEN", 6, 'T');
return; return;
} }
@ -1400,59 +1405,67 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query
return; /* illegal id */ return; /* illegal id */
} }
int bits = 0; num[0] = in[2];
switch (toupper(in[2])) { num[1] = 0;
case 'T': numopts = atoi(num);
users[userid].downenc = 'T';
write_dns(dns_fd, q, "Base32", 6, users[userid].downenc); if (domain_len != numopts + 6 || numopts == 0) {
bits = 5; write_dns(dns_fd, q, "BADLEN", 6, 'T');
break;
case 'S':
users[userid].downenc = 'S';
write_dns(dns_fd, q, "Base64", 6, users[userid].downenc);
bits = 6;
break;
case 'U':
users[userid].downenc = 'U';
write_dns(dns_fd, q, "Base64u", 7, users[userid].downenc);
bits = 6;
break;
case 'V':
users[userid].downenc = 'V';
write_dns(dns_fd, q, "Base128", 7, users[userid].downenc);
bits = 7;
break;
case 'R':
users[userid].downenc = 'R';
write_dns(dns_fd, q, "Raw", 3, users[userid].downenc);
bits = 8;
break;
case 'L':
users[userid].lazy = 1;
write_dns(dns_fd, q, "Lazy", 4, users[userid].downenc);
break;
case 'I':
users[userid].lazy = 0;
write_dns(dns_fd, q, "Immediate", 9, users[userid].downenc);
break;
case 'C':
users[userid].down_compression = 1;
write_dns(dns_fd, q, "Enabled", 7, users[userid].downenc);
break;
case 'D':
users[userid].down_compression = 0;
write_dns(dns_fd, q, "Disabled", 8, users[userid].downenc);
break;
default:
write_dns(dns_fd, q, "BADCODEC", 8, users[userid].downenc);
break;
} }
/* 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':
tmp_downenc = 'T';
bits = 5;
break;
case 'S':
tmp_downenc = 'S';
bits = 6;
break;
case 'U':
tmp_downenc = 'U';
bits = 6;
break;
case 'V':
tmp_downenc = 'V';
bits = 7;
break;
case 'R':
tmp_downenc = 'R';
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:
write_dns(dns_fd, q, "BADCODEC", 8, users[userid].downenc);
return;
}
}
/* 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) { if ((q->type == T_NULL || q->type == T_PRIVATE) && !bits) {
users[userid].downenc = 'R'; users[userid].downenc = 'R';
bits = 8; bits = 8;
if (debug >= 3) if (debug >= 3)
warnx("Assuming raw data encoding due to NULL/PRIVATE requests for user %d.", userid); warnx("Assuming raw data encoding with NULL/PRIVATE requests for user %d.", userid);
} }
if (bits) { if (bits) {
int f = users[userid].fragsize; int f = users[userid].fragsize;
@ -1462,6 +1475,13 @@ handle_null_request(int tun_fd, int dns_fd, struct dnsfd *dns_fds, struct query
users[userid].outgoing->maxfraglen, userid, bits, users[userid].downenc); users[userid].outgoing->maxfraglen, userid, bits, users[userid].downenc);
users[userid].downenc_bits = bits; users[userid].downenc_bits = bits;
} }
/* Store any changes */
users[userid].down_compression = tmp_comp;
users[userid].downenc = tmp_downenc;
users[userid].lazy = tmp_lazy;
write_dns(dns_fd, q, opts, numopts, users[userid].downenc);
return; return;
} else if(in[0] == 'Y' || in[0] == 'y') { /* Downstream codec check */ } else if(in[0] == 'Y' || in[0] == 'y') { /* Downstream codec check */
int i; int i;