Implemented TCP forward function (can be used with SSH proxycommand)

This commit is contained in:
frekky 2016-02-07 21:59:00 +08:00
parent d46766bcc9
commit a5a936f4e4
11 changed files with 453 additions and 164 deletions

View File

@ -29,6 +29,7 @@
#include <zlib.h> #include <zlib.h>
#include <sys/time.h> #include <sys/time.h>
#include <time.h> #include <time.h>
#include <errno.h>
#ifdef WINDOWS32 #ifdef WINDOWS32
#include "windows.h" #include "windows.h"
@ -201,7 +202,7 @@ update_server_timeout(int handshake)
if (handshake) { if (handshake) {
/* Send ping handshake to set server timeout */ /* Send ping handshake to set server timeout */
return send_ping(1, -1, 1); return send_ping(1, -1, 1, 0);
} }
return -1; return -1;
} }
@ -447,7 +448,7 @@ send_packet(char cmd, const uint8_t *data, const size_t datalen)
} }
int int
send_ping(int ping_response, int ack, int set_timeout) send_ping(int ping_response, int ack, int set_timeout, int disconnect)
{ {
this.num_pings++; this.num_pings++;
if (this.conn == CONN_DNS_NULL) { if (this.conn == CONN_DNS_NULL) {
@ -468,14 +469,16 @@ send_ping(int ping_response, int ack, int set_timeout)
*(uint16_t *) (data + 7) = htons(this.downstream_timeout_ms); *(uint16_t *) (data + 7) = htons(this.downstream_timeout_ms);
/* update server frag/lazy timeout, ack flag, respond with ping flag */ /* update server frag/lazy timeout, ack flag, respond with ping flag */
data[9] = ((set_timeout & 1) << 4) | ((set_timeout & 1) << 3) | ((ack < 0 ? 0 : 1) << 2) | (ping_response & 1); data[9] = ((disconnect & 1) << 5) | ((set_timeout & 1) << 4) |
((set_timeout & 1) << 3) | ((ack < 0 ? 0 : 1) << 2) | (ping_response & 1);
data[10] = (this.rand_seed >> 8) & 0xff; data[10] = (this.rand_seed >> 8) & 0xff;
data[11] = (this.rand_seed >> 0) & 0xff; data[11] = (this.rand_seed >> 0) & 0xff;
this.rand_seed += 1; this.rand_seed += 1;
DEBUG(3, " SEND PING: respond %d, ack %d, %s(server %ld ms, downfrag %ld ms), flags %02X", DEBUG(3, " SEND PING: %srespond %d, ack %d, %s(server %ld ms, downfrag %ld ms), flags %02X, wup %u, wdn %u",
ping_response, ack, set_timeout ? "SET " : "", this.server_timeout_ms, disconnect ? "DISCONNECT! " : "", ping_response, ack, set_timeout ? "SET " : "",
this.downstream_timeout_ms, data[8]); this.server_timeout_ms, this.downstream_timeout_ms,
data[8], this.outbuf->windowsize, this.inbuf->windowsize);
id = send_packet('p', data, sizeof(data)); id = send_packet('p', data, sizeof(data));
@ -505,7 +508,7 @@ send_next_frag()
if (this.outbuf->numitems > 0) { if (this.outbuf->numitems > 0) {
/* There is stuff to send but we're out of sync, so send a ping /* There is stuff to send but we're out of sync, so send a ping
* to get things back in order and keep the packets flowing */ * to get things back in order and keep the packets flowing */
send_ping(1, this.next_downstream_ack, 1); send_ping(1, this.next_downstream_ack, 1, 0);
this.next_downstream_ack = -1; this.next_downstream_ack = -1;
window_tick(this.outbuf); window_tick(this.outbuf);
} }
@ -889,11 +892,11 @@ handshake_waitdns(char *buf, size_t buflen, char cmd, int timeout)
} }
int int
parse_data(uint8_t *data, size_t len, fragment *f, int *immediate) parse_data(uint8_t *data, size_t len, fragment *f, int *immediate, int *ping)
{ {
size_t headerlen = DOWNSTREAM_HDR; size_t headerlen = DOWNSTREAM_HDR;
int ping;
memset(f, 0, sizeof(fragment)); memset(f, 0, sizeof(fragment));
int error;
f->seqID = data[0]; f->seqID = data[0];
@ -902,12 +905,13 @@ parse_data(uint8_t *data, size_t len, fragment *f, int *immediate)
f->start = (data[2] >> 1) & 1; f->start = (data[2] >> 1) & 1;
f->compressed = (data[2] >> 2) & 1; f->compressed = (data[2] >> 2) & 1;
f->ack_other = (data[2] >> 3) & 1 ? data[1] : -1; f->ack_other = (data[2] >> 3) & 1 ? data[1] : -1;
ping = (data[2] >> 4) & 1; if (ping) *ping = (data[2] >> 4) & 1;
error = (data[2] >> 6) & 1;
if (immediate) if (immediate)
*immediate = (data[2] >> 5) & 1; *immediate = (data[2] >> 5) & 1;
if (ping) { /* Handle ping stuff */ if (ping && *ping) { /* Handle ping stuff */
static unsigned dn_start_seq, up_start_seq, dn_wsize, up_wsize; static unsigned dn_start_seq, up_start_seq, dn_wsize, up_wsize;
headerlen = DOWNSTREAM_PING_HDR; headerlen = DOWNSTREAM_PING_HDR;
@ -924,7 +928,52 @@ parse_data(uint8_t *data, size_t len, fragment *f, int *immediate)
f->len = len - headerlen; f->len = len - headerlen;
if (f->len > 0) if (f->len > 0)
memcpy(f->data, data + headerlen, MIN(f->len, sizeof(f->data))); memcpy(f->data, data + headerlen, MIN(f->len, sizeof(f->data)));
return ping; /* return ping flag (if corresponding query was a ping) */ return error; /* return ping flag (if corresponding query was a ping) */
}
static ssize_t
tunnel_stdin()
{
size_t datalen;
uint8_t out[64*1024];
uint8_t in[64*1024];
uint8_t *data;
ssize_t readlen;
readlen = read(STDIN_FILENO, in, sizeof(in));
DEBUG(4, " IN: %" L "d bytes on stdin, to be compressed: %d", readlen, this.compression_up);
if (readlen == 0) {
DEBUG(2, "EOF on stdin!");
return -1;
} else if (readlen < 0) {
warnx("Error %d reading from stdin: %s", errno, strerror(errno));
return -1;
}
if (this.conn != CONN_DNS_NULL || this.compression_up) {
datalen = sizeof(out);
compress2(out, &datalen, in, readlen, 9);
data = out;
} else {
datalen = readlen;
data = in;
}
if (this.conn == CONN_DNS_NULL) {
/* Check if outgoing buffer can hold data */
if (window_buffer_available(this.outbuf) < (datalen / MAX_FRAGSIZE) + 1) {
DEBUG(1, " Outgoing buffer full (%" L "u/%" L "u), not adding data!",
this.outbuf->numitems, this.outbuf->length);
return -1;
}
window_add_outgoing_data(this.outbuf, data, datalen, this.compression_up);
/* Don't send anything here to respect min. send interval */
} else {
send_raw_data(data, datalen);
}
return datalen;
} }
static int static int
@ -974,7 +1023,7 @@ tunnel_dns()
size_t datalen, buflen; size_t datalen, buflen;
uint8_t buf[64*1024], cbuf[64*1024], *data; uint8_t buf[64*1024], cbuf[64*1024], *data;
fragment f; fragment f;
int read, compressed, res, immediate; int read, compressed, ping, immediate, error;
memset(&q, 0, sizeof(q)); memset(&q, 0, sizeof(q));
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
@ -1060,27 +1109,35 @@ tunnel_dns()
this.num_recv++; this.num_recv++;
/* Decode the downstream data header and fragment-ify ready for processing */
res = parse_data(cbuf, read, &f, &immediate);
/* Mark query as received */ /* Mark query as received */
got_response(q.id, immediate, 0); got_response(q.id, immediate, 0);
if ((this.debug >= 3 && res) || (this.debug >= 2 && !res)) /* Decode the downstream data header and fragment-ify ready for processing */
error = parse_data(cbuf, read, &f, &immediate, &ping);
if ((this.debug >= 3 && ping) || (this.debug >= 2 && !ping))
fprintf(stderr, " RX %s; frag ID %3u, ACK %3d, compression %d, datalen %" L "u, s%d e%d\n", fprintf(stderr, " RX %s; frag ID %3u, ACK %3d, compression %d, datalen %" L "u, s%d e%d\n",
res ? "PING" : "DATA", f.seqID, f.ack_other, f.compressed, f.len, f.start, f.end); ping ? "PING" : "DATA", f.seqID, f.ack_other, f.compressed, f.len, f.start, f.end);
window_ack(this.outbuf, f.ack_other); window_ack(this.outbuf, f.ack_other);
window_tick(this.outbuf); window_tick(this.outbuf);
/* respond to TCP forwarding errors by shutting down */
if (error && this.use_remote_forward) {
f.data[f.len] = 0;
warnx("server: TCP forwarding error: %s", f.data);
this.running = 0;
return -1;
}
/* In lazy mode, we shouldn't get immediate replies to our most-recent /* In lazy mode, we shouldn't get immediate replies to our most-recent
query, only during heavy data transfer. Since this means the server query, only during heavy data transfer. Since this means the server
doesn't have any packets to send, send one relatively fast (but not doesn't have any packets to send, send one relatively fast (but not
too fast, to avoid runaway ping-pong loops..) */ too fast, to avoid runaway ping-pong loops..) */
/* Don't send anything too soon; no data waiting from server */ /* Don't send anything too soon; no data waiting from server */
if (f.len == 0) { if (f.len == 0) {
if (!res) if (!ping)
DEBUG(1, "[WARNING] Received downstream data fragment with 0 length and NOT a ping!"); DEBUG(1, "[WARNING] Received downstream data fragment with 0 length and NOT a ping!");
if (!this.lazymode) if (!this.lazymode)
this.send_ping_soon = 100; this.send_ping_soon = 100;
@ -1107,8 +1164,8 @@ tunnel_dns()
if (datalen > 0) { if (datalen > 0) {
if (compressed) { if (compressed) {
buflen = sizeof(buf); buflen = sizeof(buf);
if ((res = uncompress(buf, &buflen, cbuf, datalen)) != Z_OK) { if ((ping = uncompress(buf, &buflen, cbuf, datalen)) != Z_OK) {
DEBUG(1, "Uncompress failed (%d) for data len %" L "u: reassembled data corrupted or incomplete!", res, datalen); DEBUG(1, "Uncompress failed (%d) for data len %" L "u: reassembled data corrupted or incomplete!", ping, datalen);
datalen = 0; datalen = 0;
} else { } else {
datalen = buflen; datalen = buflen;
@ -1118,8 +1175,12 @@ tunnel_dns()
data = cbuf; data = cbuf;
} }
if (datalen) if (datalen) {
write_tun(this.tun_fd, data, datalen); if (this.use_remote_forward)
write(STDOUT_FILENO, data, datalen);
else
write_tun(this.tun_fd, data, datalen);
}
} }
/* Move window along after doing all data processing */ /* Move window along after doing all data processing */
@ -1135,7 +1196,7 @@ client_tunnel()
fd_set fds; fd_set fds;
int rv; int rv;
int i, use_min_send; int i, use_min_send;
int sending, total; int sending, total, maxfd;
time_t last_stats; time_t last_stats;
size_t sent_since_report, recv_since_report; size_t sent_since_report, recv_since_report;
@ -1191,7 +1252,7 @@ client_tunnel()
send_next_frag(); send_next_frag();
} else { } else {
/* Send ping if we didn't send anything yet */ /* Send ping if we didn't send anything yet */
send_ping(0, this.next_downstream_ack, (this.num_pings > 20 && this.num_pings % 50 == 0)); send_ping(0, this.next_downstream_ack, (this.num_pings > 20 && this.num_pings % 50 == 0), 0);
this.next_downstream_ack = -1; this.next_downstream_ack = -1;
} }
@ -1260,12 +1321,20 @@ client_tunnel()
} }
FD_ZERO(&fds); FD_ZERO(&fds);
if (this.conn != CONN_DNS_NULL || window_buffer_available(this.outbuf) > 16) { maxfd = 0;
if (this.conn != CONN_DNS_NULL || window_buffer_available(this.outbuf) > 1) {
/* Fill up outgoing buffer with available data if it has enough space /* Fill up outgoing buffer with available data if it has enough space
* The windowing protocol manages data retransmits, timeouts etc. */ * The windowing protocol manages data retransmits, timeouts etc. */
FD_SET(this.tun_fd, &fds); if (this.use_remote_forward) {
FD_SET(STDIN_FILENO, &fds);
maxfd = MAX(STDIN_FILENO, maxfd);
} else {
FD_SET(this.tun_fd, &fds);
maxfd = MAX(this.tun_fd, maxfd);
}
} }
FD_SET(this.dns_fd, &fds); FD_SET(this.dns_fd, &fds);
maxfd = MAX(this.dns_fd, maxfd);
DEBUG(4, "Waiting %ld ms before sending more... (min_send %d)", timeval_to_ms(&tv), use_min_send); DEBUG(4, "Waiting %ld ms before sending more... (min_send %d)", timeval_to_ms(&tv), use_min_send);
@ -1273,7 +1342,7 @@ client_tunnel()
gettimeofday(&now, NULL); gettimeofday(&now, NULL);
} }
i = select(MAX(this.tun_fd, this.dns_fd) + 1, &fds, NULL, NULL, &tv); i = select(maxfd + 1, &fds, NULL, NULL, &tv);
if (use_min_send && i > 0) { if (use_min_send && i > 0) {
/* enforce min_send_interval if we get interrupted by new tun data */ /* enforce min_send_interval if we get interrupted by new tun data */
@ -1299,7 +1368,7 @@ client_tunnel()
if (i == 0) { if (i == 0) {
/* timed out - no new packets recv'd */ /* timed out - no new packets recv'd */
} else { } else {
if (FD_ISSET(this.tun_fd, &fds)) { if (!this.use_remote_forward && FD_ISSET(this.tun_fd, &fds)) {
if (tunnel_tun() <= 0) if (tunnel_tun() <= 0)
continue; continue;
/* Returns -1 on error OR when quickly /* Returns -1 on error OR when quickly
@ -1307,11 +1376,22 @@ client_tunnel()
we need to _not_ do tunnel_dns() then. we need to _not_ do tunnel_dns() then.
If chunk sent, sets this.send_ping_soon=0. */ If chunk sent, sets this.send_ping_soon=0. */
} }
if (this.use_remote_forward && FD_ISSET(STDIN_FILENO, &fds)) {
if (tunnel_stdin() <= 0) {
fprintf(stderr, "server: closing remote TCP forward connection\n");
/* send ping to disconnect, don't care if it comes back */
send_ping(0, 0, 0, 1);
this.running = 0;
break;
}
}
if (FD_ISSET(this.dns_fd, &fds)) { if (FD_ISSET(this.dns_fd, &fds)) {
tunnel_dns(); tunnel_dns();
} }
} }
if (this.running == 0)
break;
} }
return rv; return rv;
@ -1369,6 +1449,7 @@ send_version(uint32_t version)
static void static void
send_login(char *login, int len) send_login(char *login, int len)
/* Send DNS login packet. See doc/proto_xxxxxxxx.txt for details */
{ {
uint8_t flags = 0, data[100]; uint8_t flags = 0, data[100];
int length = 17, addrlen = 0; int length = 17, addrlen = 0;
@ -1379,7 +1460,9 @@ send_login(char *login, int len)
memcpy(data + 1, login, 16); memcpy(data + 1, login, 16);
if (this.remote_forward_addr.ss_family != AF_UNSPEC) { /* if remote forward address is specified and not currently connecting */
if (this.remote_forward_connected != 2 &&
this.remote_forward_addr.ss_family != AF_UNSPEC) {
struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &this.remote_forward_addr; struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &this.remote_forward_addr;
struct sockaddr_in *s = (struct sockaddr_in *) &this.remote_forward_addr; struct sockaddr_in *s = (struct sockaddr_in *) &this.remote_forward_addr;
@ -1405,6 +1488,10 @@ send_login(char *login, int len)
} }
DEBUG(2, "Sending TCP forward login request: port %hu, length %d, addrlen %d", DEBUG(2, "Sending TCP forward login request: port %hu, length %d, addrlen %d",
port, length, addrlen); port, length, addrlen);
} else if (this.remote_forward_connected == 2) {
/* remote TCP forward connection in progress */
DEBUG(2, "Sending TCP forward login/poll request to check connection status.");
flags |= (1 << 4);
} }
data[0] = flags; data[0] = flags;
@ -1538,7 +1625,7 @@ static int
handshake_login(int seed) handshake_login(int seed)
{ {
char in[4096], login[16], server[65], client[65], flag; char in[4096], login[16], server[65], client[65], flag;
int mtu, read; int mtu, netmask, read, numwaiting = 0;
login_calculate(login, 16, this.password, seed); login_calculate(login, 16, this.password, seed);
@ -1550,31 +1637,84 @@ handshake_login(int seed)
in[MIN(read, sizeof(in))] = 0; /* Null terminate */ in[MIN(read, sizeof(in))] = 0; /* Null terminate */
if (read > 0) { if (read > 0) {
int netmask;
if (strncmp("LNAK", in, 4) == 0) { if (strncmp("LNAK", in, 4) == 0) {
fprintf(stderr, "Bad password\n"); fprintf(stderr, "Bad password\n");
return 1; return 1;
} else if (sscanf(in, "%c-%64[^-]-%64[^-]-%d-%d", /* not reached */
&flag, server, client, &mtu, &netmask) == 4) {
server[64] = 0;
client[64] = 0;
if (tun_setip(client, server, netmask) == 0 &&
tun_setmtu(mtu) == 0) {
fprintf(stderr, "Server tunnel IP is %s\n", server);
return 0;
} else {
errx(4, "Failed to set IP and MTU");
}
} else {
fprintf(stderr, "Received bad handshake: %.*s\n", read, in);
} }
flag = toupper(in[0]);
switch (flag) {
case 'I':
if (sscanf(in, "%c-%64[^-]-%64[^-]-%d-%d",
&flag, server, client, &mtu, &netmask) == 5) {
server[64] = 0;
client[64] = 0;
if (tun_setip(client, server, netmask) == 0 &&
tun_setmtu(mtu) == 0) {
fprintf(stderr, "Server tunnel IP is %s\n", server);
return 0;
} else {
errx(4, "Failed to set IP and MTU");
}
} else {
goto bad_handshake;
}
break;
case 'C':
if (!this.use_remote_forward) {
goto bad_handshake;
}
this.remote_forward_connected = 1;
fprintf(stderr, " done.");
return 0;
case 'W':
if (!this.use_remote_forward || this.remote_forward_connected == 1) {
goto bad_handshake;
}
this.remote_forward_connected = 2;
if (numwaiting == 0)
fprintf(stderr, "server: Opening Remote TCP forward.\n");
else
fprintf(stderr, "%.*s", numwaiting, "...............");
numwaiting ++;
/* wait a while before re-polling server, max 5 tries (14 seconds) */
if (numwaiting > 1)
sleep(numwaiting);
continue;
case 'E':
if (!this.use_remote_forward)
goto bad_handshake;
char errormsg[100];
strncpy(errormsg, in + 1, MIN(read, sizeof(errormsg)));
errormsg[99] = 0;
fprintf(stderr, "server: Remote TCP forward connection failed: %s\n", errormsg);
return 1;
default:
/* undefined flag */
bad_handshake:
fprintf(stderr, "Received bad handshake: %.*s\n", read, in);
break;
}
} }
fprintf(stderr, "Retrying login...\n"); fprintf(stderr, "Retrying login...\n");
} }
warnx("couldn't login to server"); if (numwaiting != 0)
warnx("Remote TCP forward connection timed out after 5 tries.");
else
warnx("couldn't login to server");
return 1; return 1;
} }
@ -2424,7 +2564,7 @@ handshake_set_timeout()
for (int i = 0; this.running && i < 5; i++) { for (int i = 0; this.running && i < 5; i++) {
id = this.autodetect_server_timeout ? id = this.autodetect_server_timeout ?
update_server_timeout(1) : send_ping(1, -1, 1); update_server_timeout(1) : send_ping(1, -1, 1, 0);
read = handshake_waitdns(in, sizeof(in), 'P', i + 1); read = handshake_waitdns(in, sizeof(in), 'P', i + 1);
got_response(id, 1, 0); got_response(id, 1, 0);
@ -2464,13 +2604,11 @@ client_handshake()
fprintf(stderr, "Using DNS type %s queries\n", client_get_qtype()); fprintf(stderr, "Using DNS type %s queries\n", client_get_qtype());
r = handshake_version(&seed); if ((r = handshake_version(&seed))) {
if (r) {
return r; return r;
} }
r = handshake_login(seed); if ((r = handshake_login(seed))) {
if (r) {
return r; return r;
} }
@ -2479,6 +2617,9 @@ client_handshake()
this.max_timeout_ms = 10000; this.max_timeout_ms = 10000;
this.compression_down = 1; this.compression_down = 1;
this.compression_up = 1; this.compression_up = 1;
if (this.use_remote_forward)
fprintf(stderr, "Warning: Remote TCP forwards over Raw (UDP) mode may be unreliable.\n"
" If forwarded connections are unstable, try using '-r' to force DNS tunnelling mode.\n");
} else { } else {
if (this.raw_mode == 0) { if (this.raw_mode == 0) {
fprintf(stderr, "Skipping raw mode\n"); fprintf(stderr, "Skipping raw mode\n");

View File

@ -47,6 +47,7 @@ struct client_instance {
/* Remote TCP forwarding stuff (for -R) */ /* Remote TCP forwarding stuff (for -R) */
struct sockaddr_storage remote_forward_addr; struct sockaddr_storage remote_forward_addr;
int use_remote_forward; /* 0 if no forwarding used */ int use_remote_forward; /* 0 if no forwarding used */
int remote_forward_connected;
int tun_fd; int tun_fd;
int dns_fd; int dns_fd;
@ -160,9 +161,9 @@ void client_set_hostname_maxlen(size_t i);
int client_handshake(); int client_handshake();
int client_tunnel(); int client_tunnel();
int parse_data(uint8_t *data, size_t len, fragment *f, int *immediate); int parse_data(uint8_t *data, size_t len, fragment *f, int *immediate, int*);
int handshake_waitdns(char *buf, size_t buflen, char cmd, int timeout); int handshake_waitdns(char *buf, size_t buflen, char cmd, int timeout);
void handshake_switch_options(int lazy, int compression, char denc); void handshake_switch_options(int lazy, int compression, char denc);
int send_ping(int ping_response, int ack, int timeout); int send_ping(int ping_response, int ack, int timeout, int);
#endif #endif

View File

@ -228,9 +228,15 @@ open_dns_from_host(char *host, int port, int addr_family, int flags)
} }
void void
close_dns(int fd) close_socket(int fd)
{ {
if (fd <= 0)
return;
#ifdef WINDOWS32
closesocket(fd);
#else
close(fd); close(fd);
#endif
} }
void void
@ -411,7 +417,7 @@ socket_set_blocking(int fd, int blocking)
return flags; return flags;
} }
if (fcntl(fd, F_SETFL, blocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK))) == -1) if (fcntl(fd, F_SETFL, !blocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK))) == -1)
return errno; return errno;
#endif #endif
@ -442,11 +448,14 @@ open_tcp_nonblocking(struct sockaddr_storage *addr, char **errormsg)
return -1; return -1;
} }
if (errormsg)
*errormsg = strerror(errno);
return fd; return fd;
} }
int int
check_tcp_status(int fd, char **error) check_tcp_error(int fd, char **error)
/* checks connected status of given socket. /* checks connected status of given socket.
* returns error code. 0 if connected or EINPROGRESS if connecting */ * returns error code. 0 if connected or EINPROGRESS if connecting */
{ {

View File

@ -159,10 +159,10 @@ int get_addr(char *, int, int, int, struct sockaddr_storage *);
int open_dns(struct sockaddr_storage *, size_t); int open_dns(struct sockaddr_storage *, size_t);
int open_dns_opt(struct sockaddr_storage *sockaddr, size_t sockaddr_len, int v6only); int open_dns_opt(struct sockaddr_storage *sockaddr, size_t sockaddr_len, int v6only);
int open_dns_from_host(char *host, int port, int addr_family, int flags); int open_dns_from_host(char *host, int port, int addr_family, int flags);
void close_dns(int); void close_socket(int);
int open_tcp_nonblocking(struct sockaddr_storage *addr, char **error); int open_tcp_nonblocking(struct sockaddr_storage *addr, char **error);
int check_tcp_status(int fd, char **error); int check_tcp_error(int fd, char **error);
void do_chroot(char *); void do_chroot(char *);
void do_setcon(char *); void do_setcon(char *);

View File

@ -304,23 +304,23 @@ version()
} }
static int static int
parse_tcp_forward_option() parse_tcp_forward_option(char *optstr)
{ {
char *remote_port_str, *remote_host_str; char *remote_port_str, *remote_host_str;
int retval; int retval;
if (strrchr(optarg, ':')) { if (strrchr(optstr, ':')) {
remote_port_str = strrchr(optarg, ':') + 1; remote_port_str = strrchr(optstr, ':') + 1;
if (optarg[0] == '[') { if (optstr[0] == '[') {
/* IPv6 address enclosed in square brackets */ /* IPv6 address enclosed in square brackets */
remote_host_str = optarg + 1; remote_host_str = optstr + 1;
/* replace closing bracket with null terminator */ /* replace closing bracket with null terminator */
*strchr(remote_host_str, ']') = 0; *strchr(remote_host_str, ']') = 0;
this.remote_forward_addr.ss_family = AF_INET6; this.remote_forward_addr.ss_family = AF_INET6;
retval = inet_pton(AF_INET6, remote_host_str, retval = inet_pton(AF_INET6, remote_host_str,
&((struct sockaddr_in6 *) &this.remote_forward_addr)->sin6_addr); &((struct sockaddr_in6 *) &this.remote_forward_addr)->sin6_addr);
} else { } else {
remote_host_str = optarg; remote_host_str = optstr;
/* replace separator with null terminator */ /* replace separator with null terminator */
*strchr(remote_host_str, ':') = 0; *strchr(remote_host_str, ':') = 0;
this.remote_forward_addr.ss_family = AF_INET; this.remote_forward_addr.ss_family = AF_INET;
@ -328,14 +328,15 @@ parse_tcp_forward_option()
&((struct sockaddr_in *) &this.remote_forward_addr)->sin_addr); &((struct sockaddr_in *) &this.remote_forward_addr)->sin_addr);
} }
} else { } else {
/* no address specified (use server localhost IPv4), optarg is port */ /* no address specified (use server localhost IPv4), optstr is port */
remote_port_str = optarg; remote_port_str = optstr;
this.remote_forward_addr.ss_family = AF_INET; this.remote_forward_addr.ss_family = AF_INET;
((struct sockaddr_in *) &this.remote_forward_addr)->sin_addr.s_addr = INADDR_LOOPBACK; ((struct sockaddr_in *) &this.remote_forward_addr)->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
retval = 1;
} }
if (retval <= 0) { if (!retval) {
errx(12, "Invalid remote forward address (-R)! Must be [host:]port,\n" warnx("Invalid remote forward address (-R)! Must be [host:]port,\n"
"where IPv6 addresses are enclosed in literal square brackets []."); "where IPv6 addresses are enclosed in literal square brackets [].");
usage(); usage();
/* not reached */ /* not reached */
@ -351,10 +352,10 @@ parse_tcp_forward_option()
if (this.remote_forward_addr.ss_family == AF_INET) { if (this.remote_forward_addr.ss_family == AF_INET) {
/* set port as sockaddr_in (IPv4) */ /* set port as sockaddr_in (IPv4) */
((struct sockaddr_in *) &this.remote_forward_addr)->sin_port = port; ((struct sockaddr_in *) &this.remote_forward_addr)->sin_port = htons(port);
} else { } else {
/* set port in IPv6 sockaddr */ /* set port in IPv6 sockaddr */
((struct sockaddr_in6 *) &this.remote_forward_addr)->sin6_port = port; ((struct sockaddr_in6 *) &this.remote_forward_addr)->sin6_port = htons(port);
} }
return port; return port;
} }
@ -510,7 +511,7 @@ main(int argc, char **argv)
/* Argument format: [host:]port */ /* Argument format: [host:]port */
if (!optarg) break; if (!optarg) break;
this.use_remote_forward = 1; this.use_remote_forward = 1;
remote_forward_port = parse_tcp_forward_option(); remote_forward_port = parse_tcp_forward_option(optarg);
break; break;
case OPT_NODROP: case OPT_NODROP:
// TODO implement TCP-over-tun optimisations // TODO implement TCP-over-tun optimisations
@ -711,13 +712,16 @@ main(int argc, char **argv)
read_password(this.password, sizeof(this.password)); read_password(this.password, sizeof(this.password));
} }
if ((this.tun_fd = open_tun(device)) == -1) { if (!this.use_remote_forward) {
retval = 1; if ((this.tun_fd = open_tun(device)) == -1) {
goto cleanup1; retval = 1;
goto cleanup;
}
} }
if ((this.dns_fd = open_dns_from_host(NULL, 0, nameservaddr.ss_family, AI_PASSIVE)) < 0) { if ((this.dns_fd = open_dns_from_host(NULL, 0, nameservaddr.ss_family, AI_PASSIVE)) < 0) {
retval = 1; retval = 1;
goto cleanup2; goto cleanup;
} }
#ifdef OPENBSD #ifdef OPENBSD
if (rtable > 0) if (rtable > 0)
@ -739,7 +743,7 @@ main(int argc, char **argv)
if (client_handshake()) { if (client_handshake()) {
retval = 1; retval = 1;
goto cleanup2; goto cleanup;
} }
if (this.conn == CONN_RAW_UDP) { if (this.conn == CONN_RAW_UDP) {
@ -774,10 +778,14 @@ main(int argc, char **argv)
client_tunnel(); client_tunnel();
cleanup2: cleanup:
close_dns(this.dns_fd); if (this.use_remote_forward)
close_tun(this.tun_fd); close(STDOUT_FILENO);
cleanup1: close_socket(this.dns_fd);
close_socket(this.tun_fd);
#ifdef WINDOWS32
WSACleanup();
#endif
return retval; return retval;
} }

View File

@ -636,13 +636,15 @@ main(int argc, char **argv)
server_tunnel(); server_tunnel();
syslog(LOG_INFO, "stopping"); syslog(LOG_INFO, "stopping");
close_dns(server.bind_fd); close_socket(server.bind_fd);
cleanup: cleanup:
if (server.dns_fds.v6fd >= 0) close_socket(server.dns_fds.v6fd);
close_dns(server.dns_fds.v6fd); close_socket(server.dns_fds.v4fd);
if (server.dns_fds.v4fd >= 0) close_socket(server.tun_fd);
close_dns(server.dns_fds.v4fd); #ifdef WINDOWS32
close_tun(server.tun_fd); WSACleanup();
#endif
/* TODO close user TCP forward sockets */
return retval; return retval;
} }

View File

@ -318,7 +318,7 @@ qmem_max_wait(int *touser, struct query **sendq)
QMEM_DEBUG(4, userid, "ANSWER q id %d, ACK %d; sent %" L "u of %" L "u + sending another %" L "u", QMEM_DEBUG(4, userid, "ANSWER q id %d, ACK %d; sent %" L "u of %" L "u + sending another %" L "u",
q->id, u->next_upstream_ack, sent, total, sending); q->id, u->next_upstream_ack, sent, total, sending);
send_data_or_ping(userid, q, 0, immediate); send_data_or_ping(userid, q, 0, immediate, NULL);
if (sending > 0) if (sending > 0)
sending--; sending--;
@ -428,14 +428,15 @@ send_version_response(int fd, version_ack_t ack, uint32_t payload, int userid, s
} }
void void
send_data_or_ping(int userid, struct query *q, int ping, int immediate) send_data_or_ping(int userid, struct query *q, int ping, int immediate, char *tcperror)
/* Sends current fragment to user, or a ping if no data available. /* Sends current fragment to user, or a ping if no data available.
ping: 1=force send ping (even if data available), 0=only send if no data. ping: 1=force send ping (even if data available), 0=only send if no data.
immediate: 1=not from qmem (ie. fresh query), 0=query is from qmem */ immediate: 1=not from qmem (ie. fresh query), 0=query is from qmem
disconnect: whether to tell user that TCP socket is closed (NULL if OK or pointer to error message) */
{ {
uint8_t pkt[MAX_FRAGSIZE + DOWNSTREAM_PING_HDR]; uint8_t pkt[MAX_FRAGSIZE + DOWNSTREAM_PING_HDR];
size_t datalen, headerlen; size_t datalen, headerlen;
fragment *f; fragment *f = NULL;
struct frag_buffer *out, *in; struct frag_buffer *out, *in;
in = users[userid].incoming; in = users[userid].incoming;
@ -443,7 +444,21 @@ send_data_or_ping(int userid, struct query *q, int ping, int immediate)
window_tick(out); window_tick(out);
f = window_get_next_sending_fragment(out, &users[userid].next_upstream_ack); if (!tcperror) {
f = window_get_next_sending_fragment(out, &users[userid].next_upstream_ack);
} else {
/* construct fake fragment containing error message. */
fragment fr;
f = &fr;
memset(f, 0, sizeof(fragment));
f->ack_other = -1;
f->len = strlen(tcperror);
memcpy(f->data, tcperror, f->len);
f->data[f->len] = 0;
f->start = 1;
f->end = 1;
DEBUG(2, "Sending ping with TCP forward disconnect; error: %s", f->data);
}
/* Build downstream data/ping header (see doc/proto_xxxxxxxx.txt) for details */ /* Build downstream data/ping header (see doc/proto_xxxxxxxx.txt) for details */
if (!f) { if (!f) {
@ -464,6 +479,8 @@ send_data_or_ping(int userid, struct query *q, int ping, int immediate)
/* If this is being responded to immediately (ie. not from qmem) */ /* If this is being responded to immediately (ie. not from qmem) */
pkt[2] |= (immediate & 1) << 5; pkt[2] |= (immediate & 1) << 5;
if (tcperror)
pkt[2] |= (1 << 6);
if (ping) { if (ping) {
/* set ping flag and build extra header */ /* set ping flag and build extra header */
@ -556,6 +573,23 @@ user_send_data(int userid, uint8_t *indata, size_t len, int compressed)
return ret; return ret;
} }
static int
user_send_tcp_disconnect(int userid, struct query *q, char *errormsg)
/* tell user that TCP socket has been disconnected */
{
users[userid].remote_forward_connected = -1;
close_socket(users[userid].remote_tcp_fd);
if (q == NULL)
q = qmem_get_next_response(userid);
if (q != NULL) {
send_data_or_ping(userid, q, 1, 0, errormsg);
users[userid].active = 0;
return 1;
}
users[userid].active = 0;
return 0;
}
static int static int
tunnel_bind() tunnel_bind()
{ {
@ -597,6 +631,37 @@ tunnel_bind()
return 0; return 0;
} }
static ssize_t
tunnel_tcp(int userid)
{
ssize_t len;
uint8_t buf[64*1024];
char *errormsg = NULL;
if (users[userid].remote_forward_connected != 1) {
DEBUG(2, "tunnel_tcp: user %d TCP socket not connected!", userid);
return 0;
}
len = read(users[userid].remote_tcp_fd, buf, sizeof(buf));
DEBUG(5, "read %ld bytes on TCP", len);
if (len == 0) {
DEBUG(1, "EOF on TCP forward for user %d; closing connection.", userid);
errormsg = "Connection closed by remote host.";
user_send_tcp_disconnect(userid, NULL, errormsg);
return -1;
} else if (len < 0) {
errormsg = strerror(errno);
DEBUG(1, "Error %d on TCP forward for user %d: %s", errno, userid, errormsg);
user_send_tcp_disconnect(userid, NULL, errormsg);
return -1;
}
user_send_data(userid, buf, (size_t) len, 0);
return len;
}
static int static int
tunnel_tun() tunnel_tun()
{ {
@ -696,7 +761,7 @@ int
server_tunnel() server_tunnel()
{ {
struct timeval tv; struct timeval tv;
fd_set fds; fd_set read_fds, write_fds;
int i; int i;
int userid; int userid;
struct query *answer_now = NULL; struct query *answer_now = NULL;
@ -710,31 +775,38 @@ server_tunnel()
/* max wait time based on pending queries */ /* max wait time based on pending queries */
tv = qmem_max_wait(&userid, &answer_now); tv = qmem_max_wait(&userid, &answer_now);
FD_ZERO(&fds); FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
maxfd = 0; maxfd = 0;
if (server.dns_fds.v4fd >= 0) { if (server.dns_fds.v4fd >= 0) {
FD_SET(server.dns_fds.v4fd, &fds); FD_SET(server.dns_fds.v4fd, &read_fds);
maxfd = MAX(server.dns_fds.v4fd, maxfd); maxfd = MAX(server.dns_fds.v4fd, maxfd);
} }
if (server.dns_fds.v6fd >= 0) { if (server.dns_fds.v6fd >= 0) {
FD_SET(server.dns_fds.v6fd, &fds); FD_SET(server.dns_fds.v6fd, &read_fds);
maxfd = MAX(server.dns_fds.v6fd, maxfd); maxfd = MAX(server.dns_fds.v6fd, maxfd);
} }
if (server.bind_fd) { if (server.bind_fd) {
/* wait for replies from real DNS */ /* wait for replies from real DNS */
FD_SET(server.bind_fd, &fds); FD_SET(server.bind_fd, &read_fds);
maxfd = MAX(server.bind_fd, maxfd); maxfd = MAX(server.bind_fd, maxfd);
} }
/* Don't read from tun if all users have filled outpacket queues */ /* Don't read from tun if all users have filled outpacket queues */
if(!all_users_waiting_to_send()) { if(!all_users_waiting_to_send()) {
FD_SET(server.tun_fd, &fds); FD_SET(server.tun_fd, &read_fds);
maxfd = MAX(server.tun_fd, maxfd); maxfd = MAX(server.tun_fd, maxfd);
} }
i = select(maxfd + 1, &fds, NULL, NULL, &tv); /* add connected user TCP forward FDs to read set */
maxfd = MAX(set_user_tcp_fds(&read_fds, 1), maxfd);
/* add connectING user TCP FDs to write set */
maxfd = MAX(set_user_tcp_fds(&write_fds, 2), maxfd);
i = select(maxfd + 1, &read_fds, &write_fds, NULL, &tv);
if(i < 0) { if(i < 0) {
if (server.running) if (server.running)
@ -757,16 +829,29 @@ server_tunnel()
} }
} }
} else { } else {
if (FD_ISSET(server.tun_fd, &fds)) { if (FD_ISSET(server.tun_fd, &read_fds)) {
tunnel_tun(); tunnel_tun();
} }
if (FD_ISSET(server.dns_fds.v4fd, &fds)) {
for (userid = 0; userid < created_users; userid++) {
if (FD_ISSET(users[userid].remote_tcp_fd, &read_fds) && users[userid].remoteforward_addr_len > 0) {
DEBUG(4, "tunnel_tcp called for user %d", userid);
tunnel_tcp(userid);
} else if (users[userid].remote_forward_connected == 2 &&
FD_ISSET(users[userid].remote_tcp_fd, &write_fds)) {
DEBUG(2, "User %d TCP socket now writable (connection established)", userid);
users[userid].remote_forward_connected = 1;
}
}
if (FD_ISSET(server.dns_fds.v4fd, &read_fds)) {
tunnel_dns(server.dns_fds.v4fd); tunnel_dns(server.dns_fds.v4fd);
} }
if (FD_ISSET(server.dns_fds.v6fd, &fds)) { if (FD_ISSET(server.dns_fds.v6fd, &read_fds)) {
tunnel_dns(server.dns_fds.v6fd); tunnel_dns(server.dns_fds.v6fd);
} }
if (FD_ISSET(server.bind_fd, &fds)) {
if (FD_ISSET(server.bind_fd, &read_fds)) {
tunnel_bind(); tunnel_bind();
} }
} }
@ -781,7 +866,7 @@ handle_full_packet(int userid, uint8_t *data, size_t len, int compressed)
size_t rawlen; size_t rawlen;
uint8_t out[64*1024], *rawdata; uint8_t out[64*1024], *rawdata;
struct ip *hdr; struct ip *hdr;
int touser; int touser = -1;
int ret; int ret;
/* Check if data needs to be uncompressed */ /* Check if data needs to be uncompressed */
@ -796,20 +881,28 @@ handle_full_packet(int userid, uint8_t *data, size_t len, int compressed)
} }
if (ret == Z_OK) { if (ret == Z_OK) {
hdr = (struct ip*) (out + 4); if (users[userid].remoteforward_addr_len == 0) {
touser = find_user_by_ip(hdr->ip_dst.s_addr); hdr = (struct ip*) (out + 4);
DEBUG(2, "FULL PKT: %" L "u bytes from user %d (touser %d)", len, userid, touser); touser = find_user_by_ip(hdr->ip_dst.s_addr);
if (touser == -1) { DEBUG(2, "FULL PKT: %" L "u bytes from user %d (touser %d)", len, userid, touser);
/* send the uncompressed packet to tun device */ if (touser == -1) {
write_tun(server.tun_fd, rawdata, rawlen); /* send the uncompressed packet to tun device */
} else { write_tun(server.tun_fd, rawdata, rawlen);
/* don't re-compress if possible */
if (users[touser].down_compression && compressed) {
user_send_data(touser, data, len, 1);
} else { } else {
user_send_data(touser, rawdata, rawlen, 0); /* don't re-compress if possible */
if (users[touser].down_compression && compressed) {
user_send_data(touser, data, len, 1);
} else {
user_send_data(touser, rawdata, rawlen, 0);
}
}
} else {
/* Write full pkt to user's remote forward TCP stream */
if ((ret = write(users[userid].remote_tcp_fd, rawdata, rawlen)) != rawlen) {
DEBUG(2, "Write error %d on TCP socket for user %d: %s", errno, userid, strerror(errno));
} }
} }
} else { } else {
DEBUG(2, "Discarded upstream data from user %d, uncompress() result: %d", userid, ret); DEBUG(2, "Discarded upstream data from user %d, uncompress() result: %d", userid, ret);
} }
@ -1170,6 +1263,7 @@ handle_dns_version(int dns_fd, struct query *q, uint8_t *domain, int domain_len)
u->hostlen = q->fromlen; u->hostlen = q->fromlen;
u->remote_forward_connected = 0; u->remote_forward_connected = 0;
u->remoteforward_addr_len = 0; u->remoteforward_addr_len = 0;
u->remote_tcp_fd = 0;
u->remoteforward_addr.ss_family = AF_UNSPEC; u->remoteforward_addr.ss_family = AF_UNSPEC;
u->fragsize = 100; /* very safe */ u->fragsize = 100; /* very safe */
u->conn = CONN_DNS_NULL; u->conn = CONN_DNS_NULL;
@ -1250,7 +1344,7 @@ handle_dns_login(int dns_fd, struct query *q, uint8_t *domain, int domain_len, i
{ {
uint8_t unpacked[512], flags; uint8_t unpacked[512], flags;
char logindata[16], *tmp[2], out[512], *reason = NULL; char logindata[16], *tmp[2], out[512], *reason = NULL;
char *errormsg = NULL; char *errormsg = NULL, fromaddr[100];
struct in_addr tempip; struct in_addr tempip;
char remote_tcp, remote_isnt_localhost, use_ipv6, poll_status; //, drop_packets; char remote_tcp, remote_isnt_localhost, use_ipv6, poll_status; //, drop_packets;
int length = 17, read, addrlen, login_ok = 1; int length = 17, read, addrlen, login_ok = 1;
@ -1279,8 +1373,10 @@ handle_dns_login(int dns_fd, struct query *q, uint8_t *domain, int domain_len, i
CHECK_LEN(read, length); CHECK_LEN(read, length);
strncpy(fromaddr, format_addr(&q->from, q->fromlen), 100);
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, fromaddr);
DEBUG(6, "Login: length=%d, flags=0x%02x, seed=0x%08x, hash=0x%016llx%016llx", DEBUG(6, "Login: length=%d, flags=0x%02x, seed=0x%08x, hash=0x%016llx%016llx",
length, flags, u->seed, *(unsigned long long *) (unpacked + 1), length, flags, u->seed, *(unsigned long long *) (unpacked + 1),
@ -1289,7 +1385,7 @@ handle_dns_login(int dns_fd, struct query *q, uint8_t *domain, int domain_len, i
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, "rejected login request from user #%d from %s; expected source %s", syslog(LOG_WARNING, "rejected login request from user #%d from %s; expected source %s",
userid, format_addr(&q->from, q->fromlen), format_addr(&u->host, u->hostlen)); userid, fromaddr, format_addr(&u->host, u->hostlen));
DEBUG(1, "Rejected login request from user %d: BADIP", userid); DEBUG(1, "Rejected login request from user %d: BADIP", userid);
return; return;
} }
@ -1314,12 +1410,12 @@ handle_dns_login(int dns_fd, struct query *q, uint8_t *domain, int domain_len, i
if (addrlen > 0) { if (addrlen > 0) {
if (use_ipv6) { if (use_ipv6) {
addr6->sin6_family = AF_INET6; addr6->sin6_family = AF_INET6;
addr6->sin6_port = port; addr6->sin6_port = htons(port);
u->remoteforward_addr_len = sizeof(*addr6); u->remoteforward_addr_len = sizeof(*addr6);
memcpy(&addr6->sin6_addr, unpacked + 19, MIN(sizeof(*addr6), addrlen)); memcpy(&addr6->sin6_addr, unpacked + 19, MIN(sizeof(*addr6), addrlen));
} else { } else {
addr->sin_family = AF_INET; addr->sin_family = AF_INET;
addr->sin_port = port; addr->sin_port = htons(port);
u->remoteforward_addr_len = sizeof(*addr); u->remoteforward_addr_len = sizeof(*addr);
memcpy(&addr->sin_addr, unpacked + 19, MIN(sizeof(*addr), addrlen)); memcpy(&addr->sin_addr, unpacked + 19, MIN(sizeof(*addr), addrlen));
} }
@ -1329,8 +1425,8 @@ handle_dns_login(int dns_fd, struct query *q, uint8_t *domain, int domain_len, i
port, login_ok ? "allowed" : "rejected"); port, login_ok ? "allowed" : "rejected");
} else { } else {
addr->sin_family = AF_INET; addr->sin_family = AF_INET;
addr->sin_port = port; addr->sin_port = htons(port);
addr->sin_addr.s_addr = INADDR_LOOPBACK; addr->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
DEBUG(1, "User %d requested TCP connection to localhost:%hu, %s.", userid, DEBUG(1, "User %d requested TCP connection to localhost:%hu, %s.", userid,
port, login_ok ? "allowed" : "rejected"); port, login_ok ? "allowed" : "rejected");
} }
@ -1347,12 +1443,11 @@ handle_dns_login(int dns_fd, struct query *q, uint8_t *domain, int domain_len, i
write_dns(dns_fd, q, "LNAK", 4, 'T'); write_dns(dns_fd, q, "LNAK", 4, 'T');
if (--u->authenticated >= 0) if (--u->authenticated >= 0)
u->authenticated = -1; u->authenticated = -1;
char *src_ip = format_addr(&q->from, q->fromlen);
int tries = abs(u->authenticated); int tries = abs(u->authenticated);
DEBUG(1, "rejected login from user %d (%s), tries: %d, reason: %s", DEBUG(1, "rejected login from user %d (%s), tries: %d, reason: %s",
userid, src_ip, tries, reason); userid, fromaddr, tries, reason);
syslog(LOG_WARNING, "rejected login request from user #%d from %s, %s; incorrect attempts: %d", syslog(LOG_WARNING, "rejected login request from user #%d from %s, %s; incorrect attempts: %d",
userid, src_ip, reason, tries); userid, fromaddr, reason, tries);
return; return;
} }
@ -1360,13 +1455,13 @@ handle_dns_login(int dns_fd, struct query *q, uint8_t *domain, int domain_len, i
u->authenticated++; u->authenticated++;
if (u->authenticated > 1 && !poll_status) if (u->authenticated > 1 && !poll_status)
syslog(LOG_WARNING, "duplicate login request from user #%d from %s", syslog(LOG_WARNING, "duplicate login request from user #%d from %s",
userid, format_addr(&u->host, u->hostlen)); userid, fromaddr);
if (remote_tcp) { if (remote_tcp) {
int tcp_fd; int tcp_fd;
DEBUG(1, "User %d connected from %s, starting TCP connection to %s.", userid, DEBUG(1, "User %d connected from %s, starting TCP connection to %s.", userid,
format_addr(&q->from, q->fromlen), format_addr(&u->remoteforward_addr, sizeof(struct sockaddr_storage))); fromaddr, format_addr(&u->remoteforward_addr, sizeof(struct sockaddr_storage)));
syslog(LOG_NOTICE, "accepted password from user #%d, connecting TCP forward", userid); syslog(LOG_NOTICE, "accepted password from user #%d, connecting TCP forward", userid);
/* Open socket and connect to TCP forward host:port */ /* Open socket and connect to TCP forward host:port */
@ -1377,28 +1472,38 @@ handle_dns_login(int dns_fd, struct query *q, uint8_t *domain, int domain_len, i
goto tcp_forward_error; goto tcp_forward_error;
} }
/* connection in progress */
out[0] = 'W'; out[0] = 'W';
read = 1; read = 1;
write_dns(dns_fd, q, out, read + 1, u->downenc); write_dns(dns_fd, q, out, read + 1, u->downenc);
u->tcp_fd = tcp_fd; u->remote_tcp_fd = tcp_fd;
u->remote_forward_connected = 2; /* connecting */
return; return;
} else if (poll_status) { } else if (poll_status) {
/* Check TCP forward connection status and update user data */
int retval; int retval;
if ((retval = check_tcp_status(u->tcp_fd, &errormsg)) == -1) { /* Check for connection errors */
goto tcp_forward_error; if ((retval = check_tcp_error(u->remote_tcp_fd, &errormsg)) != 0) {
/* if unacceptable error, tell user */
if (retval != EINPROGRESS)
goto tcp_forward_error;
} }
if (retval == EINPROGRESS)
u->remote_forward_connected = 2;
read = 1; read = 1;
out[1] = 0; out[1] = 0;
if (retval == 0) { /* check user TCP forward status flag, which is updated in server_tunnel
* when the file descriptor becomes writable (ie, connection established */
if (u->remote_forward_connected == 1) {
out[0] = 'C'; out[0] = 'C';
u->remote_forward_connected = 1; DEBUG(2, "User %d TCP forward connection established: %s", userid, errormsg);
} else if (retval == EINPROGRESS) { } else if (u->remote_forward_connected == 2) {
out[0] = 'W'; out[0] = 'W';
} else { DEBUG(3, "User %d TCP connection in progress: %s", userid, errormsg);
goto tcp_forward_error;
} }
write_dns(dns_fd, q, out, read + 1, u->downenc); write_dns(dns_fd, q, out, read + 1, u->downenc);
@ -1412,11 +1517,11 @@ handle_dns_login(int dns_fd, struct query *q, uint8_t *domain, int domain_len, i
tempip.s_addr = u->tun_ip; tempip.s_addr = u->tun_ip;
tmp[1] = strdup(inet_ntoa(tempip)); tmp[1] = strdup(inet_ntoa(tempip));
read = snprintf(out, sizeof(out) - 1, "-%s-%s-%d-%d", read = snprintf(out + 1, sizeof(out) - 1, "-%s-%s-%d-%d",
tmp[0], tmp[1], server.mtu, server.netmask); tmp[0], tmp[1], server.mtu, server.netmask);
DEBUG(1, "User %d connected from %s, tun_ip %s.", userid, DEBUG(1, "User %d connected from %s, tun_ip %s.", userid,
format_addr(&q->from, q->fromlen), tmp[1]); fromaddr, tmp[1]);
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]);
@ -1618,7 +1723,7 @@ handle_dns_ping(int dns_fd, struct query *q, int userid,
uint8_t *unpacked, size_t read) 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, tcp_disconnect;
unsigned qtimeout_ms, wtimeout_ms; unsigned qtimeout_ms, wtimeout_ms;
CHECK_LEN(read, UPSTREAM_PING); CHECK_LEN(read, UPSTREAM_PING);
@ -1628,23 +1733,36 @@ handle_dns_ping(int dns_fd, struct query *q, int userid,
return; return;
/* Unpack flags/options from ping header */ /* Unpack flags/options from ping header */
dn_ack = ((unpacked[10] >> 2) & 1) ? unpacked[1] : -1; dn_ack = ((unpacked[9] >> 2) & 1) ? unpacked[0] : -1;
up_winsize = unpacked[2]; up_winsize = unpacked[1];
dn_winsize = unpacked[3]; dn_winsize = unpacked[2];
up_seq = unpacked[4]; up_seq = unpacked[3];
dn_seq = unpacked[5]; dn_seq = unpacked[4];
/* 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 + 5));
wtimeout_ms = ntohs(*(uint16_t *) (unpacked + 8)); wtimeout_ms = ntohs(*(uint16_t *) (unpacked + 7));
respond = unpacked[10] & 1; respond = unpacked[9] & 1;
set_qtimeout = (unpacked[10] >> 3) & 1; set_qtimeout = (unpacked[9] >> 3) & 1;
set_wtimeout = (unpacked[10] >> 4) & 1; set_wtimeout = (unpacked[9] >> 4) & 1;
tcp_disconnect = (unpacked[9] >> 5) & 1;
DEBUG(3, "PING pkt user %d, down %d/%d, up %d/%d, ACK %d, %sqtime %u ms, %swtime %u ms, respond %d (flags %02X)", DEBUG(3, "PING pkt user %d, down %d/%d, up %d/%d, ACK %d, %sqtime %u ms, "
"%swtime %u ms, respond %d, tcp_close %d (flags %02X)",
userid, dn_seq, dn_winsize, up_seq, up_winsize, dn_ack, userid, dn_seq, dn_winsize, up_seq, up_winsize, dn_ack,
set_qtimeout ? "SET " : "", qtimeout_ms, set_wtimeout ? "SET " : "", set_qtimeout ? "SET " : "", qtimeout_ms, set_wtimeout ? "SET " : "",
wtimeout_ms, respond, unpacked[10]); wtimeout_ms, respond, tcp_disconnect, unpacked[9]);
if (tcp_disconnect) {
/* close user's TCP forward connection and mark user as inactive */
if (users[userid].remoteforward_addr_len == 0) {
DEBUG(1, "User %d attempted TCP disconnect but didn't request TCP forwarding!", userid);
} else {
DEBUG(1, "User %d closed remote TCP forward", userid);
close_socket(users[userid].remote_tcp_fd);
users[userid].active = 0;
}
}
if (set_qtimeout) { if (set_qtimeout) {
/* update user's query timeout if timeout flag set */ /* update user's query timeout if timeout flag set */
@ -1671,7 +1789,7 @@ handle_dns_ping(int dns_fd, struct query *q, int userid,
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;
users[userid].incoming->windowsize = up_winsize; users[userid].incoming->windowsize = up_winsize;
send_data_or_ping(userid, q, 1, 1); send_data_or_ping(userid, q, 1, 1, NULL);
return; return;
} }
@ -1682,7 +1800,7 @@ handle_dns_ping(int dns_fd, struct query *q, int userid,
void void
handle_dns_data(int dns_fd, struct query *q, uint8_t *domain, int domain_len, int userid) handle_dns_data(int dns_fd, struct query *q, uint8_t *domain, int domain_len, int userid)
{ {
uint8_t unpacked[512]; uint8_t unpacked[20];
static fragment f; static fragment f;
size_t len; size_t len;

View File

@ -158,6 +158,6 @@ void handle_null_request(int dns_fd, struct query *q, int domain_len);
void handle_ns_request(int dns_fd, struct query *q); void handle_ns_request(int dns_fd, struct query *q);
void handle_a_request(int dns_fd, struct query *q, int fakeip); void handle_a_request(int dns_fd, struct query *q, int fakeip);
void send_data_or_ping(int, struct query *, int, int); void send_data_or_ping(int, struct query *, int, int, char*);
#endif /* __SERVER_H__ */ #endif /* __SERVER_H__ */

View File

@ -457,13 +457,6 @@ open_tun(const char *tun_device)
#endif #endif
void
close_tun(int tun_fd)
{
if (tun_fd >= 0)
close(tun_fd);
}
#ifdef WINDOWS32 #ifdef WINDOWS32
int int
write_tun(int tun_fd, uint8_t *data, size_t len) write_tun(int tun_fd, uint8_t *data, size_t len)

View File

@ -236,3 +236,19 @@ check_authenticated_user_and_ip(int userid, struct query *q, int check_ip)
return 0; return 0;
} }
int
set_user_tcp_fds(fd_set *fds, int conn_status)
/* Add TCP forward FDs to fd_set for users with given connection status; returns largest FD added */
{
int max_fd = 0;
for (int userid = 0; userid < created_users; userid ++) {
if (user_active(userid) && users[userid].remoteforward_addr_len > 0
&& users[userid].remote_forward_connected == conn_status) {
FD_SET(users[userid].remote_tcp_fd, fds);
max_fd = MAX(max_fd, users[userid].remote_tcp_fd);
}
}
return max_fd;
}

View File

@ -37,8 +37,8 @@ struct tun_user {
socklen_t hostlen; socklen_t hostlen;
struct sockaddr_storage remoteforward_addr; struct sockaddr_storage remoteforward_addr;
socklen_t remoteforward_addr_len; /* 0 if no remote forwarding enabled */ socklen_t remoteforward_addr_len; /* 0 if no remote forwarding enabled */
int tcp_fd; int remote_tcp_fd;
int remote_forward_connected; int remote_forward_connected; /* 0 if not connected, -1 if error or 1 if OK */
struct frag_buffer *incoming; struct frag_buffer *incoming;
struct frag_buffer *outgoing; struct frag_buffer *outgoing;
int next_upstream_ack; int next_upstream_ack;
@ -67,5 +67,6 @@ int find_user_by_ip(uint32_t);
int find_available_user(); int find_available_user();
void user_switch_codec(int userid, struct encoder *enc); void user_switch_codec(int userid, struct encoder *enc);
void user_set_conn_type(int userid, enum connection c); void user_set_conn_type(int userid, enum connection c);
int set_user_tcp_fds(fd_set *fds, int);
#endif #endif