From 5d3b502ec68b3b9d9162e59d0601e8c87e39a682 Mon Sep 17 00:00:00 2001 From: Erik Ekman Date: Thu, 6 Aug 2009 19:48:55 +0000 Subject: [PATCH] #36, upstream traffic now sent in raw mode --- src/common.h | 15 ++++- src/iodine.c | 77 +++++++++++++++---------- src/iodined.c | 153 ++++++++++++++++++++++++++++++++++---------------- src/user.c | 16 ++++++ src/user.h | 2 + 5 files changed, 183 insertions(+), 80 deletions(-) diff --git a/src/common.h b/src/common.h index 28fc2bb..dd7db6e 100644 --- a/src/common.h +++ b/src/common.h @@ -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); diff --git a/src/iodine.c b/src/iodine.c index 9b1dcba..41cb9cc 100644 --- a/src/iodine.c +++ b/src/iodine.c @@ -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(); diff --git a/src/iodined.c b/src/iodined.c index e1f4b94..60b84ca 100644 --- a/src/iodined.c +++ b/src/iodined.c @@ -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(); diff --git a/src/user.c b/src/user.c index b15665b..7250251 100644 --- a/src/user.c +++ b/src/user.c @@ -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; +} + diff --git a/src/user.h b/src/user.h index 51fdfbc..ba1e696 100644 --- a/src/user.h +++ b/src/user.h @@ -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