#36, upstream traffic now sent in raw mode

This commit is contained in:
Erik Ekman 2009-08-06 19:48:55 +00:00
parent e2c8ac3985
commit 5d3b502ec6
5 changed files with 183 additions and 80 deletions

View file

@ -21,8 +21,13 @@
#define RAW_HDR_LEN 4
#define RAW_HDR_IDENT_LEN 3
#define RAW_HDR_CMD 3
#define RAW_HDR_CMD_LOGIN 0x01
#define RAW_HDR_CMD_DATA 0x02
#define RAW_HDR_CMD_LOGIN 0x10
#define RAW_HDR_CMD_DATA 0x20
#define RAW_HDR_CMD_MASK 0xF0
#define RAW_HDR_USR_MASK 0x0F
#define RAW_HDR_GET_CMD(x) ((x)[RAW_HDR_CMD] & RAW_HDR_CMD_MASK)
#define RAW_HDR_GET_USR(x) ((x)[RAW_HDR_CMD] & RAW_HDR_USR_MASK)
extern const unsigned char raw_header[RAW_HDR_LEN];
#ifdef WINDOWS32
@ -85,6 +90,12 @@ struct query {
int fromlen;
};
enum connection {
CONN_RAW_UDP,
CONN_DNS_NULL,
CONN_MAX
};
void check_superuser(void (*usage_fn)(void));
int open_dns(int, in_addr_t);
void close_dns(int);

View file

@ -60,6 +60,7 @@ WSADATA wsa_data;
static void send_ping(int fd);
static void send_chunk(int fd);
static void send_raw_data(int fd);
static int build_hostname(char *buf, size_t buflen,
const char *data, const size_t datalen,
const char *topdomain, struct encoder *encoder);
@ -94,6 +95,9 @@ static struct encoder *b32;
* Defaults to Base32, can be changed after handshake */
static struct encoder *dataenc;
/* My connection mode */
static enum connection conn;
#if !defined(BSD) && !defined(__GLIBC__)
static char *__progname;
#endif
@ -120,7 +124,7 @@ send_query(int fd, char *hostname)
}
static void
send_raw(int fd, char *buf, int buflen, int cmd)
send_raw(int fd, char *buf, int buflen, int user, int cmd)
{
char packet[4096];
int len;
@ -131,7 +135,7 @@ send_raw(int fd, char *buf, int buflen, int cmd)
memcpy(&packet[RAW_HDR_LEN], buf, len);
len += RAW_HDR_LEN;
packet[RAW_HDR_CMD] = cmd;
packet[RAW_HDR_CMD] = cmd | (user & 0x0F);
sendto(fd, packet, len, 0, (struct sockaddr*)&raw_serv, sizeof(raw_serv));
}
@ -263,7 +267,11 @@ tunnel_tun(int tun_fd, int dns_fd)
outpkt.seqno++;
outpkt.fragment = 0;
send_chunk(dns_fd);
if (conn == CONN_DNS_NULL) {
send_chunk(dns_fd);
} else {
send_raw_data(dns_fd);
}
return read;
}
@ -327,7 +335,7 @@ tunnel(int tun_fd, int dns_fd)
FD_ZERO(&fds);
if (!is_sending()) {
if ((!is_sending()) || conn == CONN_RAW_UDP) {
FD_SET(tun_fd, &fds);
}
FD_SET(dns_fd, &fds);
@ -340,7 +348,7 @@ tunnel(int tun_fd, int dns_fd)
if (i < 0)
err(1, "select");
if (i == 0) /* timeout */
if (i == 0 && conn == CONN_DNS_NULL) /* timeout */
send_ping(dns_fd);
else {
if (FD_ISSET(tun_fd, &fds)) {
@ -357,6 +365,12 @@ tunnel(int tun_fd, int dns_fd)
return rv;
}
static void
send_raw_data(int dns_fd)
{
send_raw(dns_fd, outpkt.data, outpkt.len, userid, RAW_HDR_CMD_DATA);
}
static void
send_chunk(int fd)
{
@ -507,11 +521,10 @@ send_ip_request(int fd, int userid)
static void
send_raw_udp_login(int dns_fd, int userid, int seed)
{
char buf[17];
char buf[16];
login_calculate(buf, 16, password, seed + 1);
buf[16] = userid;
send_raw(dns_fd, buf, sizeof(buf), RAW_HDR_CMD_LOGIN);
send_raw(dns_fd, buf, sizeof(buf), userid, RAW_HDR_CMD_LOGIN);
}
static void
@ -715,7 +728,7 @@ handshake_raw_udp(int dns_fd, int seed)
if (!remoteaddr) {
fprintf(stderr, " failed to get IP.\n");
return 1;
return 0;
}
fprintf(stderr, " at %s: ", inet_ntoa(server));
fflush(stderr);
@ -743,16 +756,15 @@ handshake_raw_udp(int dns_fd, int seed)
if(r > 0) {
/* recv() needed for windows, dont change to read() */
len = recv(dns_fd, in, sizeof(in), 0);
if (len >= (17 + RAW_HDR_LEN)) {
if (len >= (16 + RAW_HDR_LEN)) {
char hash[16];
login_calculate(hash, 16, password, seed - 1);
if (memcmp(in, raw_header, RAW_HDR_IDENT_LEN) == 0
&& in[RAW_HDR_CMD] == RAW_HDR_CMD_LOGIN
&& memcmp(&in[RAW_HDR_LEN], hash, sizeof(hash)) == 0
&& in[16 + RAW_HDR_LEN] == userid) {
&& RAW_HDR_GET_CMD(in) == RAW_HDR_CMD_LOGIN
&& memcmp(&in[RAW_HDR_LEN], hash, sizeof(hash)) == 0) {
fprintf(stderr, "OK\n");
return 0;
return 1;
}
}
}
@ -761,7 +773,7 @@ handshake_raw_udp(int dns_fd, int seed)
}
fprintf(stderr, "failed\n");
return 1;
return 0;
}
static int
@ -1016,22 +1028,24 @@ handshake(int dns_fd, int autodetect_frag_size, int fragsize)
return r;
}
handshake_raw_udp(dns_fd, seed);
if (handshake_raw_udp(dns_fd, seed)) {
conn = CONN_RAW_UDP;
} else {
case_preserved = handshake_case_check(dns_fd);
case_preserved = handshake_case_check(dns_fd);
if (case_preserved) {
handshake_switch_codec(dns_fd);
}
if (autodetect_frag_size) {
fragsize = handshake_autoprobe_fragsize(dns_fd);
if (!fragsize) {
return 1;
if (case_preserved) {
handshake_switch_codec(dns_fd);
}
}
handshake_set_fragsize(dns_fd, fragsize);
if (autodetect_frag_size) {
fragsize = handshake_autoprobe_fragsize(dns_fd);
if (!fragsize) {
return 1;
}
}
handshake_set_fragsize(dns_fd, fragsize);
}
return 0;
}
@ -1178,6 +1192,7 @@ main(int argc, char **argv)
b32 = get_base32_encoder();
dataenc = get_base32_encoder();
conn = CONN_DNS_NULL;
retval = 0;
@ -1312,7 +1327,11 @@ main(int argc, char **argv)
goto cleanup2;
}
fprintf(stderr, "Sending queries for %s to %s\n", topdomain, nameserv_addr);
if (conn == CONN_DNS_NULL) {
fprintf(stderr, "Sending queries for %s to %s\n", topdomain, nameserv_addr);
} else {
fprintf(stderr, "Sending raw traffic directly to %s\n", inet_ntoa(raw_serv.sin_addr));
}
if (foreground == 0)
do_detach();

View file

@ -85,8 +85,9 @@ static int debug;
static char *__progname;
#endif
static int read_dns(int, struct query *);
static int read_dns(int, int, struct query *);
static void write_dns(int, struct query *, char *, int);
static void handle_full_packet(int, int);
static void
sigint(int sig)
@ -295,15 +296,12 @@ static void
handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len)
{
struct in_addr tempip;
struct ip *hdr;
unsigned long outlen;
char in[512];
char logindata[16];
char out[64*1024];
char unpacked[64*1024];
char *tmp[2];
int userid;
int touser;
int version;
int code;
int read;
@ -589,30 +587,7 @@ handle_null_request(int tun_fd, int dns_fd, struct query *q, int domain_len)
}
if (lastfrag & 1) { /* packet is complete */
int ret;
outlen = sizeof(out);
ret = uncompress((uint8_t*)out, &outlen,
(uint8_t*)users[userid].inpacket.data, users[userid].inpacket.len);
if (ret == Z_OK) {
hdr = (struct ip*) (out + 4);
touser = find_user_by_ip(hdr->ip_dst.s_addr);
if (touser == -1) {
/* send the uncompressed packet to tun device */
write_tun(tun_fd, out, outlen);
} else {
/* send the compressed packet to other client
* if another packet is queued, throw away this one. TODO build queue */
if (users[touser].outpacket.len == 0) {
memcpy(users[touser].outpacket.data, users[userid].inpacket.data, users[userid].inpacket.len);
users[touser].outpacket.len = users[userid].inpacket.len;
}
}
} else {
fprintf(stderr, "Discarded data, uncompress() result: %d\n", ret);
}
users[userid].inpacket.len = users[userid].inpacket.offset = 0;
handle_full_packet(tun_fd, userid);
}
/* Update seqno and maybe send immediate response packet */
update_downstream_seqno(dns_fd, userid, dn_seq, dn_frag);
@ -730,7 +705,7 @@ tunnel_dns(int tun_fd, int dns_fd, int bind_fd)
int domain_len;
int inside_topdomain;
if ((read = read_dns(dns_fd, &q)) <= 0)
if ((read = read_dns(dns_fd, tun_fd, &q)) <= 0)
return 0;
if (debug >= 2) {
@ -820,15 +795,15 @@ tunnel(int tun_fd, int dns_fd, int bind_fd)
}
}
} else {
if(FD_ISSET(tun_fd, &fds)) {
if (FD_ISSET(tun_fd, &fds)) {
tunnel_tun(tun_fd, dns_fd);
continue;
}
if(FD_ISSET(dns_fd, &fds)) {
if (FD_ISSET(dns_fd, &fds)) {
tunnel_dns(tun_fd, dns_fd, bind_fd);
continue;
}
if(FD_ISSET(bind_fd, &fds)) {
if (FD_ISSET(bind_fd, &fds)) {
tunnel_bind(bind_fd, dns_fd);
continue;
}
@ -839,7 +814,42 @@ tunnel(int tun_fd, int dns_fd, int bind_fd)
}
static void
send_raw(int fd, char *buf, int buflen, int cmd, struct query *q)
handle_full_packet(int tun_fd, int userid)
{
unsigned long outlen;
char out[64*1024];
int touser;
int ret;
outlen = sizeof(out);
ret = uncompress((uint8_t*)out, &outlen,
(uint8_t*)users[userid].inpacket.data, users[userid].inpacket.len);
if (ret == Z_OK) {
struct ip *hdr;
hdr = (struct ip*) (out + 4);
touser = find_user_by_ip(hdr->ip_dst.s_addr);
if (touser == -1) {
/* send the uncompressed packet to tun device */
write_tun(tun_fd, out, outlen);
} else {
/* send the compressed packet to other client
* if another packet is queued, throw away this one. TODO build queue */
if (users[touser].outpacket.len == 0) {
memcpy(users[touser].outpacket.data, users[userid].inpacket.data, users[userid].inpacket.len);
users[touser].outpacket.len = users[userid].inpacket.len;
}
}
} else {
fprintf(stderr, "Discarded data, uncompress() result: %d\n", ret);
}
users[userid].inpacket.len = users[userid].inpacket.offset = 0;
}
static void
send_raw(int fd, char *buf, int buflen, int user, int cmd, struct query *q)
{
char packet[4096];
int len;
@ -850,51 +860,96 @@ send_raw(int fd, char *buf, int buflen, int cmd, struct query *q)
memcpy(&packet[RAW_HDR_LEN], buf, len);
len += RAW_HDR_LEN;
packet[RAW_HDR_CMD] = cmd;
packet[RAW_HDR_CMD] = cmd | (user & 0x0F);
sendto(fd, packet, len, 0, &q->from, q->fromlen);
}
static void
handle_raw_login(char *packet, int len, struct query *q, int fd)
handle_raw_login(char *packet, int len, struct query *q, int fd, int userid)
{
int userid;
char myhash[16];
if (len < 17) return;
if (len < 16) return;
userid = packet[16];
if (userid < 0 || userid > created_users) return;
if (!users[userid].active) return;
/* User sends hash of seed + 1 */
login_calculate(myhash, 16, password, users[userid].seed + 1);
if (memcmp(packet, myhash, 16) == 0) {
struct sockaddr_in *tempin;
/* Update query and time info for user */
users[userid].last_pkt = time(NULL);
memcpy(&(users[userid].q), q, sizeof(struct query));
/* Store remote IP number */
tempin = (struct sockaddr_in *) &(q->from);
memcpy(&(users[userid].host), &(tempin->sin_addr), sizeof(struct in_addr));
/* Correct hash, reply with hash of seed - 1 */
user_set_conn_type(userid, CONN_RAW_UDP);
users[userid].last_pkt = time(NULL);
login_calculate(myhash, 16, password, users[userid].seed - 1);
memcpy(packet, myhash, 16);
send_raw(fd, packet, 17, RAW_HDR_CMD_LOGIN, q);
send_raw(fd, myhash, 16, userid, RAW_HDR_CMD_LOGIN, q);
}
}
static int
raw_decode(char *packet, int len, struct query *q, int fd)
static void
handle_raw_data(char *packet, int len, struct query *q, int dns_fd, int tun_fd, int userid)
{
if (check_user_and_ip(userid, q) != 0) {
return;
}
/* Update query and time info for user */
users[userid].last_pkt = time(NULL);
memcpy(&(users[userid].q), q, sizeof(struct query));
/* copy to packet buffer, update length */
users[userid].inpacket.offset = 0;
memcpy(users[userid].inpacket.data, packet, len);
users[userid].inpacket.len = len;
if (debug >= 1) {
fprintf(stderr, "IN pkt raw, total %d, from user %d\n",
users[userid].inpacket.len, userid);
}
handle_full_packet(tun_fd, userid);
}
static int
raw_decode(char *packet, int len, struct query *q, int dns_fd, int tun_fd)
{
int raw_user;
/* minimum length */
if (len < RAW_HDR_LEN) return 0;
/* should start with header */
if (memcmp(packet, raw_header, RAW_HDR_IDENT_LEN)) return 0;
if (packet[RAW_HDR_CMD] == RAW_HDR_CMD_LOGIN) {
handle_raw_login(&packet[RAW_HDR_LEN], len - RAW_HDR_LEN, q, fd);
} else {
warnx("Unhandled raw command %02X\n", packet[RAW_HDR_CMD]);
raw_user = RAW_HDR_GET_USR(packet);
printf("raw %02x\n", packet[RAW_HDR_CMD]);
switch (RAW_HDR_GET_CMD(packet)) {
case RAW_HDR_CMD_LOGIN:
/* Login challenge */
handle_raw_login(&packet[RAW_HDR_LEN], len - RAW_HDR_LEN, q, dns_fd, raw_user);
break;
case RAW_HDR_CMD_DATA:
/* Data packet */
handle_raw_data(&packet[RAW_HDR_LEN], len - RAW_HDR_LEN, q, dns_fd, tun_fd, raw_user);
break;
default:
warnx("Unhandled raw command %02X from user %d", RAW_HDR_GET_CMD(packet), raw_user);
break;
}
return 1;
}
static int
read_dns(int fd, struct query *q)
read_dns(int fd, int tun_fd, struct query *q) /* FIXME: tun_fd is because of raw_decode() below */
{
struct sockaddr_in from;
socklen_t addrlen;
@ -929,7 +984,7 @@ read_dns(int fd, struct query *q)
q->fromlen = addrlen;
/* TODO do not handle raw packets here! */
if (raw_decode(packet, r, q, fd)) {
if (raw_decode(packet, r, q, fd, tun_fd)) {
return 0;
}
if (dns_decode(NULL, 0, q, QR_QUERY, packet, r) < 0) {
@ -1177,7 +1232,7 @@ main(int argc, char **argv)
}
topdomain = strdup(argv[1]);
if(strlen(topdomain) <= 128) {
if (strlen(topdomain) <= 128) {
if(check_topdomain(topdomain)) {
warnx("Topdomain contains invalid characters.");
usage();

View file

@ -90,6 +90,7 @@ init_users(in_addr_t my_ip, int netbits)
users[i].out_acked_seqno = 0;
users[i].out_acked_fragment = 0;
users[i].fragsize = 4096;
users[i].conn = CONN_DNS_NULL;
}
return created_users;
@ -161,6 +162,8 @@ find_available_user()
if ((!users[i].active || users[i].last_pkt + 60 < time(NULL)) && !users[i].disabled) {
users[i].active = 1;
users[i].last_pkt = time(NULL);
users[i].fragsize = 4096;
users[i].conn = CONN_DNS_NULL;
ret = i;
break;
}
@ -176,3 +179,16 @@ user_switch_codec(int userid, struct encoder *enc)
users[userid].encoder = enc;
}
void
user_set_conn_type(int userid, enum connection c)
{
if (userid < 0 || userid >= USERS)
return;
if (c < 0 || c >= CONN_MAX)
return;
users[userid].conn = c;
}

View file

@ -34,6 +34,7 @@ struct user {
int out_acked_seqno;
int out_acked_fragment;
int fragsize;
enum connection conn;
};
extern struct user users[USERS];
@ -44,5 +45,6 @@ int find_user_by_ip(uint32_t);
int all_users_waiting_to_send();
int find_available_user();
void user_switch_codec(int userid, struct encoder *enc);
void user_set_conn_type(int userid, enum connection c);
#endif