mirror of
https://github.com/yarrick/iodine.git
synced 2024-11-25 02:55:15 +00:00
Implemented TCP forward function (can be used with SSH proxycommand)
This commit is contained in:
parent
d46766bcc9
commit
a5a936f4e4
247
src/client.c
247
src/client.c
@ -29,6 +29,7 @@
|
||||
#include <zlib.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef WINDOWS32
|
||||
#include "windows.h"
|
||||
@ -201,7 +202,7 @@ update_server_timeout(int handshake)
|
||||
|
||||
if (handshake) {
|
||||
/* Send ping handshake to set server timeout */
|
||||
return send_ping(1, -1, 1);
|
||||
return send_ping(1, -1, 1, 0);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@ -447,7 +448,7 @@ send_packet(char cmd, const uint8_t *data, const size_t datalen)
|
||||
}
|
||||
|
||||
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++;
|
||||
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);
|
||||
|
||||
/* 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[11] = (this.rand_seed >> 0) & 0xff;
|
||||
this.rand_seed += 1;
|
||||
|
||||
DEBUG(3, " SEND PING: respond %d, ack %d, %s(server %ld ms, downfrag %ld ms), flags %02X",
|
||||
ping_response, ack, set_timeout ? "SET " : "", this.server_timeout_ms,
|
||||
this.downstream_timeout_ms, data[8]);
|
||||
DEBUG(3, " SEND PING: %srespond %d, ack %d, %s(server %ld ms, downfrag %ld ms), flags %02X, wup %u, wdn %u",
|
||||
disconnect ? "DISCONNECT! " : "", ping_response, ack, set_timeout ? "SET " : "",
|
||||
this.server_timeout_ms, this.downstream_timeout_ms,
|
||||
data[8], this.outbuf->windowsize, this.inbuf->windowsize);
|
||||
|
||||
id = send_packet('p', data, sizeof(data));
|
||||
|
||||
@ -505,7 +508,7 @@ send_next_frag()
|
||||
if (this.outbuf->numitems > 0) {
|
||||
/* 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 */
|
||||
send_ping(1, this.next_downstream_ack, 1);
|
||||
send_ping(1, this.next_downstream_ack, 1, 0);
|
||||
this.next_downstream_ack = -1;
|
||||
window_tick(this.outbuf);
|
||||
}
|
||||
@ -889,11 +892,11 @@ handshake_waitdns(char *buf, size_t buflen, char cmd, int timeout)
|
||||
}
|
||||
|
||||
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;
|
||||
int ping;
|
||||
memset(f, 0, sizeof(fragment));
|
||||
int error;
|
||||
|
||||
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->compressed = (data[2] >> 2) & 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)
|
||||
*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;
|
||||
|
||||
headerlen = DOWNSTREAM_PING_HDR;
|
||||
@ -924,7 +928,52 @@ parse_data(uint8_t *data, size_t len, fragment *f, int *immediate)
|
||||
f->len = len - headerlen;
|
||||
if (f->len > 0)
|
||||
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
|
||||
@ -974,7 +1023,7 @@ tunnel_dns()
|
||||
size_t datalen, buflen;
|
||||
uint8_t buf[64*1024], cbuf[64*1024], *data;
|
||||
fragment f;
|
||||
int read, compressed, res, immediate;
|
||||
int read, compressed, ping, immediate, error;
|
||||
|
||||
memset(&q, 0, sizeof(q));
|
||||
memset(buf, 0, sizeof(buf));
|
||||
@ -1060,27 +1109,35 @@ tunnel_dns()
|
||||
|
||||
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 */
|
||||
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",
|
||||
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_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
|
||||
query, only during heavy data transfer. Since this means the server
|
||||
doesn't have any packets to send, send one relatively fast (but not
|
||||
too fast, to avoid runaway ping-pong loops..) */
|
||||
/* Don't send anything too soon; no data waiting from server */
|
||||
if (f.len == 0) {
|
||||
if (!res)
|
||||
if (!ping)
|
||||
DEBUG(1, "[WARNING] Received downstream data fragment with 0 length and NOT a ping!");
|
||||
if (!this.lazymode)
|
||||
this.send_ping_soon = 100;
|
||||
@ -1107,8 +1164,8 @@ tunnel_dns()
|
||||
if (datalen > 0) {
|
||||
if (compressed) {
|
||||
buflen = sizeof(buf);
|
||||
if ((res = uncompress(buf, &buflen, cbuf, datalen)) != Z_OK) {
|
||||
DEBUG(1, "Uncompress failed (%d) for data len %" L "u: reassembled data corrupted or incomplete!", res, datalen);
|
||||
if ((ping = uncompress(buf, &buflen, cbuf, datalen)) != Z_OK) {
|
||||
DEBUG(1, "Uncompress failed (%d) for data len %" L "u: reassembled data corrupted or incomplete!", ping, datalen);
|
||||
datalen = 0;
|
||||
} else {
|
||||
datalen = buflen;
|
||||
@ -1118,8 +1175,12 @@ tunnel_dns()
|
||||
data = cbuf;
|
||||
}
|
||||
|
||||
if (datalen)
|
||||
write_tun(this.tun_fd, data, datalen);
|
||||
if (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 */
|
||||
@ -1135,7 +1196,7 @@ client_tunnel()
|
||||
fd_set fds;
|
||||
int rv;
|
||||
int i, use_min_send;
|
||||
int sending, total;
|
||||
int sending, total, maxfd;
|
||||
time_t last_stats;
|
||||
size_t sent_since_report, recv_since_report;
|
||||
|
||||
@ -1191,7 +1252,7 @@ client_tunnel()
|
||||
send_next_frag();
|
||||
} else {
|
||||
/* 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;
|
||||
}
|
||||
|
||||
@ -1260,12 +1321,20 @@ client_tunnel()
|
||||
}
|
||||
|
||||
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
|
||||
* 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);
|
||||
maxfd = MAX(this.dns_fd, maxfd);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
/* enforce min_send_interval if we get interrupted by new tun data */
|
||||
@ -1299,7 +1368,7 @@ client_tunnel()
|
||||
if (i == 0) {
|
||||
/* timed out - no new packets recv'd */
|
||||
} else {
|
||||
if (FD_ISSET(this.tun_fd, &fds)) {
|
||||
if (!this.use_remote_forward && FD_ISSET(this.tun_fd, &fds)) {
|
||||
if (tunnel_tun() <= 0)
|
||||
continue;
|
||||
/* Returns -1 on error OR when quickly
|
||||
@ -1307,11 +1376,22 @@ client_tunnel()
|
||||
we need to _not_ do tunnel_dns() then.
|
||||
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)) {
|
||||
tunnel_dns();
|
||||
}
|
||||
}
|
||||
if (this.running == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return rv;
|
||||
@ -1369,6 +1449,7 @@ send_version(uint32_t version)
|
||||
|
||||
static void
|
||||
send_login(char *login, int len)
|
||||
/* Send DNS login packet. See doc/proto_xxxxxxxx.txt for details */
|
||||
{
|
||||
uint8_t flags = 0, data[100];
|
||||
int length = 17, addrlen = 0;
|
||||
@ -1379,7 +1460,9 @@ send_login(char *login, int len)
|
||||
|
||||
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_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",
|
||||
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;
|
||||
@ -1538,7 +1625,7 @@ static int
|
||||
handshake_login(int seed)
|
||||
{
|
||||
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);
|
||||
|
||||
@ -1550,31 +1637,84 @@ handshake_login(int seed)
|
||||
in[MIN(read, sizeof(in))] = 0; /* Null terminate */
|
||||
|
||||
if (read > 0) {
|
||||
int netmask;
|
||||
if (strncmp("LNAK", in, 4) == 0) {
|
||||
fprintf(stderr, "Bad password\n");
|
||||
return 1;
|
||||
} else if (sscanf(in, "%c-%64[^-]-%64[^-]-%d-%d",
|
||||
&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);
|
||||
/* not reached */
|
||||
}
|
||||
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");
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@ -2424,7 +2564,7 @@ handshake_set_timeout()
|
||||
for (int i = 0; this.running && i < 5; i++) {
|
||||
|
||||
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);
|
||||
got_response(id, 1, 0);
|
||||
@ -2464,13 +2604,11 @@ client_handshake()
|
||||
|
||||
fprintf(stderr, "Using DNS type %s queries\n", client_get_qtype());
|
||||
|
||||
r = handshake_version(&seed);
|
||||
if (r) {
|
||||
if ((r = handshake_version(&seed))) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = handshake_login(seed);
|
||||
if (r) {
|
||||
if ((r = handshake_login(seed))) {
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -2479,6 +2617,9 @@ client_handshake()
|
||||
this.max_timeout_ms = 10000;
|
||||
this.compression_down = 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 {
|
||||
if (this.raw_mode == 0) {
|
||||
fprintf(stderr, "Skipping raw mode\n");
|
||||
|
@ -47,6 +47,7 @@ struct client_instance {
|
||||
/* Remote TCP forwarding stuff (for -R) */
|
||||
struct sockaddr_storage remote_forward_addr;
|
||||
int use_remote_forward; /* 0 if no forwarding used */
|
||||
int remote_forward_connected;
|
||||
|
||||
int tun_fd;
|
||||
int dns_fd;
|
||||
@ -160,9 +161,9 @@ void client_set_hostname_maxlen(size_t i);
|
||||
int client_handshake();
|
||||
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);
|
||||
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
|
||||
|
15
src/common.c
15
src/common.c
@ -228,9 +228,15 @@ open_dns_from_host(char *host, int port, int addr_family, int flags)
|
||||
}
|
||||
|
||||
void
|
||||
close_dns(int fd)
|
||||
close_socket(int fd)
|
||||
{
|
||||
if (fd <= 0)
|
||||
return;
|
||||
#ifdef WINDOWS32
|
||||
closesocket(fd);
|
||||
#else
|
||||
close(fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@ -411,7 +417,7 @@ socket_set_blocking(int fd, int blocking)
|
||||
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;
|
||||
|
||||
#endif
|
||||
@ -442,11 +448,14 @@ open_tcp_nonblocking(struct sockaddr_storage *addr, char **errormsg)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (errormsg)
|
||||
*errormsg = strerror(errno);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int
|
||||
check_tcp_status(int fd, char **error)
|
||||
check_tcp_error(int fd, char **error)
|
||||
/* checks connected status of given socket.
|
||||
* returns error code. 0 if connected or EINPROGRESS if connecting */
|
||||
{
|
||||
|
@ -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_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);
|
||||
void close_dns(int);
|
||||
void close_socket(int);
|
||||
|
||||
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_setcon(char *);
|
||||
|
54
src/iodine.c
54
src/iodine.c
@ -304,23 +304,23 @@ version()
|
||||
}
|
||||
|
||||
static int
|
||||
parse_tcp_forward_option()
|
||||
parse_tcp_forward_option(char *optstr)
|
||||
{
|
||||
char *remote_port_str, *remote_host_str;
|
||||
int retval;
|
||||
|
||||
if (strrchr(optarg, ':')) {
|
||||
remote_port_str = strrchr(optarg, ':') + 1;
|
||||
if (optarg[0] == '[') {
|
||||
if (strrchr(optstr, ':')) {
|
||||
remote_port_str = strrchr(optstr, ':') + 1;
|
||||
if (optstr[0] == '[') {
|
||||
/* IPv6 address enclosed in square brackets */
|
||||
remote_host_str = optarg + 1;
|
||||
remote_host_str = optstr + 1;
|
||||
/* replace closing bracket with null terminator */
|
||||
*strchr(remote_host_str, ']') = 0;
|
||||
this.remote_forward_addr.ss_family = AF_INET6;
|
||||
retval = inet_pton(AF_INET6, remote_host_str,
|
||||
&((struct sockaddr_in6 *) &this.remote_forward_addr)->sin6_addr);
|
||||
} else {
|
||||
remote_host_str = optarg;
|
||||
remote_host_str = optstr;
|
||||
/* replace separator with null terminator */
|
||||
*strchr(remote_host_str, ':') = 0;
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
/* no address specified (use server localhost IPv4), optarg is port */
|
||||
remote_port_str = optarg;
|
||||
/* no address specified (use server localhost IPv4), optstr is port */
|
||||
remote_port_str = optstr;
|
||||
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) {
|
||||
errx(12, "Invalid remote forward address (-R)! Must be [host:]port,\n"
|
||||
if (!retval) {
|
||||
warnx("Invalid remote forward address (-R)! Must be [host:]port,\n"
|
||||
"where IPv6 addresses are enclosed in literal square brackets [].");
|
||||
usage();
|
||||
/* not reached */
|
||||
@ -351,10 +352,10 @@ parse_tcp_forward_option()
|
||||
|
||||
if (this.remote_forward_addr.ss_family == AF_INET) {
|
||||
/* 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 {
|
||||
/* 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;
|
||||
}
|
||||
@ -510,7 +511,7 @@ main(int argc, char **argv)
|
||||
/* Argument format: [host:]port */
|
||||
if (!optarg) break;
|
||||
this.use_remote_forward = 1;
|
||||
remote_forward_port = parse_tcp_forward_option();
|
||||
remote_forward_port = parse_tcp_forward_option(optarg);
|
||||
break;
|
||||
case OPT_NODROP:
|
||||
// TODO implement TCP-over-tun optimisations
|
||||
@ -711,13 +712,16 @@ main(int argc, char **argv)
|
||||
read_password(this.password, sizeof(this.password));
|
||||
}
|
||||
|
||||
if ((this.tun_fd = open_tun(device)) == -1) {
|
||||
retval = 1;
|
||||
goto cleanup1;
|
||||
if (!this.use_remote_forward) {
|
||||
if ((this.tun_fd = open_tun(device)) == -1) {
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if ((this.dns_fd = open_dns_from_host(NULL, 0, nameservaddr.ss_family, AI_PASSIVE)) < 0) {
|
||||
retval = 1;
|
||||
goto cleanup2;
|
||||
goto cleanup;
|
||||
}
|
||||
#ifdef OPENBSD
|
||||
if (rtable > 0)
|
||||
@ -739,7 +743,7 @@ main(int argc, char **argv)
|
||||
|
||||
if (client_handshake()) {
|
||||
retval = 1;
|
||||
goto cleanup2;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (this.conn == CONN_RAW_UDP) {
|
||||
@ -774,10 +778,14 @@ main(int argc, char **argv)
|
||||
|
||||
client_tunnel();
|
||||
|
||||
cleanup2:
|
||||
close_dns(this.dns_fd);
|
||||
close_tun(this.tun_fd);
|
||||
cleanup1:
|
||||
cleanup:
|
||||
if (this.use_remote_forward)
|
||||
close(STDOUT_FILENO);
|
||||
close_socket(this.dns_fd);
|
||||
close_socket(this.tun_fd);
|
||||
#ifdef WINDOWS32
|
||||
WSACleanup();
|
||||
#endif
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
@ -636,13 +636,15 @@ main(int argc, char **argv)
|
||||
server_tunnel();
|
||||
|
||||
syslog(LOG_INFO, "stopping");
|
||||
close_dns(server.bind_fd);
|
||||
close_socket(server.bind_fd);
|
||||
cleanup:
|
||||
if (server.dns_fds.v6fd >= 0)
|
||||
close_dns(server.dns_fds.v6fd);
|
||||
if (server.dns_fds.v4fd >= 0)
|
||||
close_dns(server.dns_fds.v4fd);
|
||||
close_tun(server.tun_fd);
|
||||
close_socket(server.dns_fds.v6fd);
|
||||
close_socket(server.dns_fds.v4fd);
|
||||
close_socket(server.tun_fd);
|
||||
#ifdef WINDOWS32
|
||||
WSACleanup();
|
||||
#endif
|
||||
/* TODO close user TCP forward sockets */
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
248
src/server.c
248
src/server.c
@ -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",
|
||||
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)
|
||||
sending--;
|
||||
@ -428,14 +428,15 @@ send_version_response(int fd, version_ack_t ack, uint32_t payload, int userid, s
|
||||
}
|
||||
|
||||
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.
|
||||
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];
|
||||
size_t datalen, headerlen;
|
||||
fragment *f;
|
||||
fragment *f = NULL;
|
||||
struct frag_buffer *out, *in;
|
||||
|
||||
in = users[userid].incoming;
|
||||
@ -443,7 +444,21 @@ send_data_or_ping(int userid, struct query *q, int ping, int immediate)
|
||||
|
||||
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 */
|
||||
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) */
|
||||
pkt[2] |= (immediate & 1) << 5;
|
||||
if (tcperror)
|
||||
pkt[2] |= (1 << 6);
|
||||
|
||||
if (ping) {
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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
|
||||
tunnel_bind()
|
||||
{
|
||||
@ -597,6 +631,37 @@ tunnel_bind()
|
||||
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
|
||||
tunnel_tun()
|
||||
{
|
||||
@ -696,7 +761,7 @@ int
|
||||
server_tunnel()
|
||||
{
|
||||
struct timeval tv;
|
||||
fd_set fds;
|
||||
fd_set read_fds, write_fds;
|
||||
int i;
|
||||
int userid;
|
||||
struct query *answer_now = NULL;
|
||||
@ -710,31 +775,38 @@ server_tunnel()
|
||||
/* max wait time based on pending queries */
|
||||
tv = qmem_max_wait(&userid, &answer_now);
|
||||
|
||||
FD_ZERO(&fds);
|
||||
FD_ZERO(&read_fds);
|
||||
FD_ZERO(&write_fds);
|
||||
maxfd = 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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
if (server.bind_fd) {
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* Don't read from tun if all users have filled outpacket queues */
|
||||
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);
|
||||
}
|
||||
|
||||
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 (server.running)
|
||||
@ -757,16 +829,29 @@ server_tunnel()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (FD_ISSET(server.tun_fd, &fds)) {
|
||||
if (FD_ISSET(server.tun_fd, &read_fds)) {
|
||||
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);
|
||||
}
|
||||
if (FD_ISSET(server.dns_fds.v6fd, &fds)) {
|
||||
if (FD_ISSET(server.dns_fds.v6fd, &read_fds)) {
|
||||
tunnel_dns(server.dns_fds.v6fd);
|
||||
}
|
||||
if (FD_ISSET(server.bind_fd, &fds)) {
|
||||
|
||||
if (FD_ISSET(server.bind_fd, &read_fds)) {
|
||||
tunnel_bind();
|
||||
}
|
||||
}
|
||||
@ -781,7 +866,7 @@ handle_full_packet(int userid, uint8_t *data, size_t len, int compressed)
|
||||
size_t rawlen;
|
||||
uint8_t out[64*1024], *rawdata;
|
||||
struct ip *hdr;
|
||||
int touser;
|
||||
int touser = -1;
|
||||
int ret;
|
||||
|
||||
/* 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) {
|
||||
hdr = (struct ip*) (out + 4);
|
||||
touser = find_user_by_ip(hdr->ip_dst.s_addr);
|
||||
DEBUG(2, "FULL PKT: %" L "u bytes from user %d (touser %d)", len, userid, touser);
|
||||
if (touser == -1) {
|
||||
/* send the uncompressed packet to tun device */
|
||||
write_tun(server.tun_fd, rawdata, rawlen);
|
||||
} else {
|
||||
/* don't re-compress if possible */
|
||||
if (users[touser].down_compression && compressed) {
|
||||
user_send_data(touser, data, len, 1);
|
||||
if (users[userid].remoteforward_addr_len == 0) {
|
||||
hdr = (struct ip*) (out + 4);
|
||||
touser = find_user_by_ip(hdr->ip_dst.s_addr);
|
||||
DEBUG(2, "FULL PKT: %" L "u bytes from user %d (touser %d)", len, userid, touser);
|
||||
if (touser == -1) {
|
||||
/* send the uncompressed packet to tun device */
|
||||
write_tun(server.tun_fd, rawdata, rawlen);
|
||||
} 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 {
|
||||
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->remote_forward_connected = 0;
|
||||
u->remoteforward_addr_len = 0;
|
||||
u->remote_tcp_fd = 0;
|
||||
u->remoteforward_addr.ss_family = AF_UNSPEC;
|
||||
u->fragsize = 100; /* very safe */
|
||||
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;
|
||||
char logindata[16], *tmp[2], out[512], *reason = NULL;
|
||||
char *errormsg = NULL;
|
||||
char *errormsg = NULL, fromaddr[100];
|
||||
struct in_addr tempip;
|
||||
char remote_tcp, remote_isnt_localhost, use_ipv6, poll_status; //, drop_packets;
|
||||
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);
|
||||
|
||||
strncpy(fromaddr, format_addr(&q->from, q->fromlen), 100);
|
||||
|
||||
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",
|
||||
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) {
|
||||
write_dns(dns_fd, q, "BADIP", 5, 'T');
|
||||
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);
|
||||
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 (use_ipv6) {
|
||||
addr6->sin6_family = AF_INET6;
|
||||
addr6->sin6_port = port;
|
||||
addr6->sin6_port = htons(port);
|
||||
u->remoteforward_addr_len = sizeof(*addr6);
|
||||
memcpy(&addr6->sin6_addr, unpacked + 19, MIN(sizeof(*addr6), addrlen));
|
||||
} else {
|
||||
addr->sin_family = AF_INET;
|
||||
addr->sin_port = port;
|
||||
addr->sin_port = htons(port);
|
||||
u->remoteforward_addr_len = sizeof(*addr);
|
||||
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");
|
||||
} else {
|
||||
addr->sin_family = AF_INET;
|
||||
addr->sin_port = port;
|
||||
addr->sin_addr.s_addr = INADDR_LOOPBACK;
|
||||
addr->sin_port = htons(port);
|
||||
addr->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
DEBUG(1, "User %d requested TCP connection to localhost:%hu, %s.", userid,
|
||||
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');
|
||||
if (--u->authenticated >= 0)
|
||||
u->authenticated = -1;
|
||||
char *src_ip = format_addr(&q->from, q->fromlen);
|
||||
int tries = abs(u->authenticated);
|
||||
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",
|
||||
userid, src_ip, reason, tries);
|
||||
userid, fromaddr, reason, tries);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1360,13 +1455,13 @@ handle_dns_login(int dns_fd, struct query *q, uint8_t *domain, int domain_len, i
|
||||
u->authenticated++;
|
||||
if (u->authenticated > 1 && !poll_status)
|
||||
syslog(LOG_WARNING, "duplicate login request from user #%d from %s",
|
||||
userid, format_addr(&u->host, u->hostlen));
|
||||
userid, fromaddr);
|
||||
|
||||
if (remote_tcp) {
|
||||
int tcp_fd;
|
||||
|
||||
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);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* connection in progress */
|
||||
out[0] = 'W';
|
||||
read = 1;
|
||||
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;
|
||||
} else if (poll_status) {
|
||||
/* Check TCP forward connection status and update user data */
|
||||
int retval;
|
||||
|
||||
if ((retval = check_tcp_status(u->tcp_fd, &errormsg)) == -1) {
|
||||
goto tcp_forward_error;
|
||||
/* Check for connection errors */
|
||||
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;
|
||||
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';
|
||||
u->remote_forward_connected = 1;
|
||||
} else if (retval == EINPROGRESS) {
|
||||
DEBUG(2, "User %d TCP forward connection established: %s", userid, errormsg);
|
||||
} else if (u->remote_forward_connected == 2) {
|
||||
out[0] = 'W';
|
||||
} else {
|
||||
goto tcp_forward_error;
|
||||
DEBUG(3, "User %d TCP connection in progress: %s", userid, errormsg);
|
||||
}
|
||||
|
||||
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;
|
||||
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);
|
||||
|
||||
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]);
|
||||
|
||||
free(tmp[1]);
|
||||
@ -1618,7 +1723,7 @@ handle_dns_ping(int dns_fd, struct query *q, int userid,
|
||||
uint8_t *unpacked, size_t read)
|
||||
{
|
||||
int dn_seq, up_seq, dn_winsize, up_winsize, dn_ack;
|
||||
int respond, set_qtimeout, set_wtimeout;
|
||||
int respond, set_qtimeout, set_wtimeout, tcp_disconnect;
|
||||
unsigned qtimeout_ms, wtimeout_ms;
|
||||
|
||||
CHECK_LEN(read, UPSTREAM_PING);
|
||||
@ -1628,23 +1733,36 @@ handle_dns_ping(int dns_fd, struct query *q, int userid,
|
||||
return;
|
||||
|
||||
/* Unpack flags/options from ping header */
|
||||
dn_ack = ((unpacked[10] >> 2) & 1) ? unpacked[1] : -1;
|
||||
up_winsize = unpacked[2];
|
||||
dn_winsize = unpacked[3];
|
||||
up_seq = unpacked[4];
|
||||
dn_seq = unpacked[5];
|
||||
dn_ack = ((unpacked[9] >> 2) & 1) ? unpacked[0] : -1;
|
||||
up_winsize = unpacked[1];
|
||||
dn_winsize = unpacked[2];
|
||||
up_seq = unpacked[3];
|
||||
dn_seq = unpacked[4];
|
||||
|
||||
/* Query timeout and window frag timeout */
|
||||
qtimeout_ms = ntohs(*(uint16_t *) (unpacked + 6));
|
||||
wtimeout_ms = ntohs(*(uint16_t *) (unpacked + 8));
|
||||
respond = unpacked[10] & 1;
|
||||
set_qtimeout = (unpacked[10] >> 3) & 1;
|
||||
set_wtimeout = (unpacked[10] >> 4) & 1;
|
||||
qtimeout_ms = ntohs(*(uint16_t *) (unpacked + 5));
|
||||
wtimeout_ms = ntohs(*(uint16_t *) (unpacked + 7));
|
||||
respond = unpacked[9] & 1;
|
||||
set_qtimeout = (unpacked[9] >> 3) & 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,
|
||||
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) {
|
||||
/* 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;
|
||||
send_data_or_ping(userid, q, 1, 1);
|
||||
send_data_or_ping(userid, q, 1, 1, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1682,7 +1800,7 @@ handle_dns_ping(int dns_fd, struct query *q, int userid,
|
||||
void
|
||||
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;
|
||||
size_t len;
|
||||
|
||||
|
@ -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_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__ */
|
||||
|
@ -457,13 +457,6 @@ open_tun(const char *tun_device)
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
close_tun(int tun_fd)
|
||||
{
|
||||
if (tun_fd >= 0)
|
||||
close(tun_fd);
|
||||
}
|
||||
|
||||
#ifdef WINDOWS32
|
||||
int
|
||||
write_tun(int tun_fd, uint8_t *data, size_t len)
|
||||
|
16
src/user.c
16
src/user.c
@ -236,3 +236,19 @@ check_authenticated_user_and_ip(int userid, struct query *q, int check_ip)
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -37,8 +37,8 @@ struct tun_user {
|
||||
socklen_t hostlen;
|
||||
struct sockaddr_storage remoteforward_addr;
|
||||
socklen_t remoteforward_addr_len; /* 0 if no remote forwarding enabled */
|
||||
int tcp_fd;
|
||||
int remote_forward_connected;
|
||||
int remote_tcp_fd;
|
||||
int remote_forward_connected; /* 0 if not connected, -1 if error or 1 if OK */
|
||||
struct frag_buffer *incoming;
|
||||
struct frag_buffer *outgoing;
|
||||
int next_upstream_ack;
|
||||
@ -67,5 +67,6 @@ int find_user_by_ip(uint32_t);
|
||||
int find_available_user();
|
||||
void user_switch_codec(int userid, struct encoder *enc);
|
||||
void user_set_conn_type(int userid, enum connection c);
|
||||
int set_user_tcp_fds(fd_set *fds, int);
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user