diff --git a/src/common.c b/src/common.c index c6c8539..04f3a92 100644 --- a/src/common.c +++ b/src/common.c @@ -1,5 +1,4 @@ -/* - * Copyright (c) 2006 Bjorn Andersson , Erik Ekman +/* Copyright (c) 2006 Bjorn Andersson , Erik Ekman * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -70,3 +69,4 @@ close_dns(int fd) { close(fd); } + diff --git a/src/common.h b/src/common.h index 427c6e5..97787d0 100644 --- a/src/common.h +++ b/src/common.h @@ -17,6 +17,9 @@ #ifndef __COMMON_H__ #define __COMMON_H__ +#ifndef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) +#endif #ifndef MAX #define MAX(a,b) ((a)>(b)?(a):(b)) #endif diff --git a/src/dns.c b/src/dns.c index a45a061..d350596 100644 --- a/src/dns.c +++ b/src/dns.c @@ -37,126 +37,18 @@ #include "encoding.h" #include "read.h" -// For FreeBSD -#ifndef MIN -#define MIN(a,b) ((a)<(b)?(a):(b)) -#endif - - -static int dns_write(int, int, char *, int, char); -static void dns_query(int, int, char *, int); static int decodepacket(const char*, char*, int); -static struct sockaddr_in peer; -static char topdomain[256]; - -// Current IP packet -static char activepacket[4096]; -static int lastlen; -static int packetpos; -static int packetlen; -static uint16_t chunkid; - -static uint16_t pingid; +static char *topdomain; +/* XXX: only used from server, remove! */ void dns_set_topdomain(const char *domain) { - strncpy(topdomain, domain, sizeof(topdomain) - 1); - topdomain[sizeof(topdomain) - 1] = '\0'; + topdomain = strdup(domain); } -int -dns_settarget(const char *host) -{ - struct hostent *h; - - // Init dns target struct - h = gethostbyname(host); - if (!h) { - printf("Could not resolve name %s, exiting\n", host); - return -1; - } - - memset(&peer, 0, sizeof(peer)); - peer.sin_family = AF_INET; - peer.sin_port = htons(53); - peer.sin_addr = *((struct in_addr *) h->h_addr); - - // Init chunk id - chunkid = 0; - pingid = 0; - - return 0; -} - - -int -dns_sending() -{ - return (packetlen != 0); -} - -static void -dns_send_chunk(int fd) -{ - int avail; - char *p; - - p = activepacket; - p += packetpos; - avail = packetlen - packetpos; - lastlen = dns_write(fd, ++chunkid, p, avail, 0); -} - -void -dns_handle_tun(int fd, char *data, int len) -{ - memcpy(activepacket, data, MIN(len, sizeof(activepacket))); - lastlen = 0; - packetpos = 0; - packetlen = len; - - dns_send_chunk(fd); -} - -void -dns_ping(int dns_fd) -{ - char data[2]; - if (dns_sending()) { - lastlen = 0; - packetpos = 0; - packetlen = 0; - } - data[0] = (pingid & 0xFF00) >> 8; - data[1] = (pingid & 0xFF); - dns_write(dns_fd, ++pingid, data, 2, 'P'); -} - -void -dns_login(int dns_fd, char *login, int len) -{ - char data[18]; - memcpy(data, login, MIN(len, 16)); - data[16] = (pingid & 0xFF00) >> 8; - data[17] = (pingid & 0xFF); - dns_write(dns_fd, ++pingid, data, 18, 'L'); -} - -void -dns_send_version(int dns_fd, int version) -{ - char data[6]; - int v; - - v = htonl(version); - memcpy(data, &v, 4); - data[4] = (pingid & 0xFF00) >> 8; - data[5] = (pingid & 0xFF); - dns_write(dns_fd, ++pingid, data, 6, 'V'); -} int dns_encode(char *buf, size_t buflen, struct query *q, qr_t qr, char *data, size_t datalen) @@ -171,7 +63,7 @@ dns_encode(char *buf, size_t buflen, struct query *q, qr_t qr, char *data, size_ header = (HEADER*)buf; header->id = htons(q->id); - header->qr = qr; + header->qr = (qr == QR_ANSWER); header->opcode = 0; header->aa = (qr == QR_ANSWER); header->tc = 0; @@ -220,9 +112,6 @@ dns_encode(char *buf, size_t buflen, struct query *q, qr_t qr, char *data, size_ putshort(&p, 0x8000); // Z putshort(&p, 0x0000); // Data length break; - default: - errx(1, "dns_encode: qr is wrong!!!"); - /* NOTREACHED */ } len = p - buf; @@ -250,9 +139,8 @@ dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, siz header = (HEADER*)packet; // Reject short packets - if (packetlen < sizeof(HEADER)) { - return rv; - } + if (packetlen < sizeof(HEADER)) + return 0; if (header->qr != qr) { warnx("header->qr does not match the requested qr"); @@ -274,6 +162,9 @@ dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, siz return -1; } + if (q != NULL) + q->id = header->id; + readname(packet, packetlen, &data, name, sizeof(name)); readshort(packet, &data, &type); readshort(packet, &data, &class); @@ -318,80 +209,28 @@ dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, siz return rv; } -static void -dns_query(int fd, int id, char *host, int type) -{ - char buf[1024]; - struct query q; - int peerlen; - int len; - - q.id = id; - q.type = type; - - len = dns_encode(buf, sizeof(buf), &q, QR_QUERY, host, strlen(host)); - - peerlen = sizeof(peer); - sendto(fd, buf, len, 0, (struct sockaddr*)&peer, peerlen); -} - -static int -dns_write(int fd, int id, char *buf, int len, char flag) +int +dns_build_hostname(char *buf, size_t buflen, char *data, size_t datalen, char *topdomain) { int avail; int written; int encoded; - char data[257]; - char *d; + char *b; - avail = 0xFF - strlen(topdomain) - 2; - memset(data, 0, sizeof(data)); - d = data; - written = encode_data(buf, len, avail, d, flag); - encoded = strlen(data); - d += encoded; - if (*d != '.') { - *d++ = '.'; + avail = MIN(0xFF, buflen) - strlen(topdomain) - 2; + memset(buf, 0, buflen); + b = buf; + written = encode_data(data, datalen, avail, b); + encoded = strlen(buf); + b += encoded; + if (*b != '.') { + *b++ = '.'; } - strncpy(d, topdomain, strlen(topdomain)+1); + strncpy(b, topdomain, strlen(topdomain)+1); - dns_query(fd, id, data, T_NULL); return written; } -int -dns_read(int fd, char *buf, int buflen) -{ - int r; - socklen_t addrlen; - char packet[64*1024]; - struct sockaddr_in from; - HEADER *header; - - addrlen = sizeof(struct sockaddr); - r = recvfrom(fd, packet, sizeof(packet), 0, (struct sockaddr*)&from, &addrlen); - if(r == -1) { - perror("recvfrom"); - return 0; - } - - header = (HEADER*)packet; - if (dns_sending() && chunkid == ntohs(header->id)) { - /* Got ACK on sent packet */ - packetpos += lastlen; - if (packetpos == packetlen) { - /* Packet completed */ - packetpos = 0; - packetlen = 0; - lastlen = 0; - } else { - /* More to send */ - dns_send_chunk(fd); - } - } - return dns_decode(buf, buflen, NULL, QR_ANSWER, packet, r); -} - int dns_encode_hostname(const char *host, char *buffer, int size) { @@ -441,6 +280,7 @@ dnsd_send(int fd, struct query *q, char *data, int datalen) static int decodepacket(const char *name, char *buf, int buflen) { + /* int len; char *domain; @@ -450,5 +290,7 @@ decodepacket(const char *name, char *buf, int buflen) if (len == buflen) return -1; return len; + */ + return 0; } diff --git a/src/dns.h b/src/dns.h index 33c45ec..dad6fd2 100644 --- a/src/dns.h +++ b/src/dns.h @@ -24,18 +24,18 @@ typedef enum { QR_ANSWER = 1 } qr_t; -int dns_settarget(const char*); void dns_set_topdomain(const char*); int dns_sending(); void dns_handle_tun(int, char *, int); void dns_ping(int); -void dns_send_version(int, int); void dns_login(int, char *, int); int dns_read(int, char *, int); int dns_encode_hostname(const char *, char *, int); int dns_encode(char *, size_t, struct query *, qr_t, char *, size_t); int dns_decode(char *, size_t, struct query *, qr_t, char *, size_t); + +int dns_build_hostname(char *, size_t, char *, size_t, char *); void dnsd_send(int, struct query*, char *, int); diff --git a/src/encoding.c b/src/encoding.c index 17092b0..e74ba79 100644 --- a/src/encoding.c +++ b/src/encoding.c @@ -102,7 +102,7 @@ decode_chunk(char *dest, char *src) } int -encode_data(char *buf, int len, int space, char *dest, char flag) +encode_data(char *buf, int len, int space, char *dest) { int final; int write; @@ -127,15 +127,6 @@ encode_data(char *buf, int len, int space, char *dest, char flag) chunks = write / RAW_CHUNK; leftovers = write % RAW_CHUNK; - // flag is special character to be placed first in the encoded data - if (flag != 0) { - *dest = flag; - } else { - // First byte is 0 for middle packet and 1 for last packet - *dest = '0' + final; - } - dest++; - memset(encoded, 0, sizeof(encoded)); ep = encoded; dp = buf; @@ -182,12 +173,6 @@ decode_data(char *dest, int size, const char *src, char *srcend) char *pp; char *ep; - // Copy flag - len = 1; - *dest = *src; - dest++; - src++; - memset(encoded, 0, sizeof(encoded)); ep = encoded; while(len < size && src < srcend) { diff --git a/src/encoding.h b/src/encoding.h index 3d1ef34..c16b1b9 100644 --- a/src/encoding.h +++ b/src/encoding.h @@ -17,7 +17,7 @@ #ifndef _ENCODING_H_ #define _ENCODING_H_ -int encode_data(char *, int, int, char *, char); +int encode_data(char *, int, int, char *); int decode_data(char *, int, const char *, char *); #endif /* _ENCODING_H_ */ diff --git a/src/iodine.c b/src/iodine.c index f1173d1..fe4d566 100644 --- a/src/iodine.c +++ b/src/iodine.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,10 @@ #include #include #include +#include +#ifdef DARWIN +#include +#endif #include "common.h" #include "dns.h" @@ -36,15 +41,99 @@ #include "tun.h" #include "version.h" +static void send_ping(int fd); + int running = 1; char password[33]; +struct sockaddr_in peer; +static char *topdomain; + +uint16_t rand_seed; + +/* Current IP packet */ +static char activepacket[4096]; +static int lastlen; +static int packetpos; +static int packetlen; +static uint16_t chunkid; + static void sighandler(int sig) { running = 0; } +static void +dns_send_chunk(int fd) +{ + char packet[512]; + struct query q; + char buf[256]; + int avail; + char *p; + int len; + + q.id = rand_seed; + q.type = T_NULL; + + p = activepacket; + p += packetpos; + avail = packetlen - packetpos; + + lastlen = dns_build_hostname(buf + 1, sizeof(buf) - 1, p, avail, topdomain); + if (lastlen == avail) + buf[0] = '1'; + else + buf[0] = '0'; + + len = dns_encode(packet, sizeof(packet), &q, QR_QUERY, buf, strlen(buf)); + + sendto(fd, packet, len, 0, (struct sockaddr*)&peer, sizeof(peer)); +} + +int +dns_sending() +{ + return (packetlen != 0); +} + +int +dns_read(int fd, char *buf, int buflen) +{ + struct sockaddr_in from; + char packet[64*1024]; + socklen_t addrlen; + struct query q; + int rv; + int r; + + addrlen = sizeof(struct sockaddr); + r = recvfrom(fd, packet, sizeof(packet), 0, (struct sockaddr*)&from, &addrlen); + if(r == -1) { + perror("recvfrom"); + return 0; + } + + rv = dns_decode(buf, buflen, &q, QR_ANSWER, packet, r); + + if (dns_sending() && chunkid == q.id) { + /* Got ACK on sent packet */ + packetpos += lastlen; + if (packetpos == packetlen) { + /* Packet completed */ + packetpos = 0; + packetlen = 0; + lastlen = 0; + } else { + /* More to send */ + dns_send_chunk(fd); + } + } + return rv; +} + + static int tunnel_tun(int tun_fd, int dns_fd) { @@ -59,7 +148,13 @@ tunnel_tun(int tun_fd, int dns_fd) outlen = sizeof(out); inlen = read; compress2(out, &outlen, in, inlen, 9); - dns_handle_tun(dns_fd, out, outlen); + + memcpy(activepacket, out, MIN(outlen, sizeof(activepacket))); + lastlen = 0; + packetpos = 0; + packetlen = outlen; + + dns_send_chunk(dns_fd); } return read; @@ -82,7 +177,7 @@ tunnel_dns(int tun_fd, int dns_fd) write_tun(tun_fd, out, outlen); if (!dns_sending()) - dns_ping(dns_fd); + send_ping(dns_fd); } return read; @@ -115,7 +210,7 @@ tunnel(int tun_fd, int dns_fd) } if (i == 0) /* timeout */ - dns_ping(dns_fd); + send_ping(dns_fd); else { if(FD_ISSET(tun_fd, &fds)) { if (tunnel_tun(tun_fd, dns_fd) <= 0) @@ -131,6 +226,91 @@ tunnel(int tun_fd, int dns_fd) return rv; } +void +send_login(int fd, char *login, int len) +{ + char packet[512]; + struct query q; + char buf[256]; + char data[18]; + + q.id = rand_seed; + q.type = T_NULL; + + memset(data, 0, sizeof(data)); + memcpy(data, login, MIN(len, 16)); + + data[16] = (rand_seed >> 8) & 0xff; + data[17] = (rand_seed >> 0) & 0xff; + + rand_seed++; + + buf[0] = 'L'; + len = dns_build_hostname(buf + 1, sizeof(buf) - 1, data, sizeof(data), topdomain); + len = dns_encode(packet, sizeof(packet), &q, QR_QUERY, buf, strlen(buf)); + + sendto(fd, packet, len, 0, (struct sockaddr*)&peer, sizeof(peer)); +} + +static void +send_ping(int fd) +{ + + char packet[512]; + struct query q; + char buf[256]; + char data[2]; + int len; + + if (dns_sending()) { + lastlen = 0; + packetpos = 0; + packetlen = 0; + } + + q.id = rand_seed; + + data[0] = (rand_seed >> 8) & 0xff; + data[1] = (rand_seed >> 0) & 0xff; + + rand_seed++; + + buf[0] = 'P'; + len = dns_build_hostname(buf + 1, sizeof(buf) - 1, data, sizeof(data), topdomain); + len = dns_encode(packet, sizeof(packet), &q, QR_QUERY, buf, strlen(buf)); + + sendto(fd, packet, len, 0, (struct sockaddr*)&peer, sizeof(peer)); +} + +void +send_version(int fd, uint32_t version) +{ + char packet[512]; + struct query q; + char buf[256]; + char data[6]; + int len; + + q.id = rand_seed; + q.type = T_NULL; + + data[0] = (version >> 24) & 0xff; + data[1] = (version >> 16) & 0xff; + data[2] = (version >> 8) & 0xff; + data[3] = (version >> 0) & 0xff; + + data[4] = (rand_seed >> 8) & 0xff; + data[5] = (rand_seed >> 0) & 0xff; + + rand_seed++; + + buf[0] = 'V'; + len = dns_build_hostname(buf + 1, sizeof(buf) - 1, data, sizeof(data), topdomain); + len = dns_encode(packet, sizeof(packet), &q, QR_QUERY, buf, strlen(buf)); + + sendto(fd, packet, len, 0, (struct sockaddr*)&peer, sizeof(peer)); +} + static int handshake(int dns_fd) { @@ -151,7 +331,7 @@ handshake(int dns_fd) tv.tv_sec = i + 1; tv.tv_usec = 0; - dns_send_version(dns_fd, VERSION); + send_version(dns_fd, VERSION); FD_ZERO(&fds); FD_SET(dns_fd, &fds); @@ -185,8 +365,9 @@ handshake(int dns_fd) } } - if (i == 4) - return 1; + if (i == 4) + errx(1, "couldn't connect to server"); + printf("Retrying version check...\n"); } @@ -195,7 +376,7 @@ handshake(int dns_fd) tv.tv_sec = i + 1; tv.tv_usec = 0; - dns_login(dns_fd, login, 16); + send_login(dns_fd, login, 16); FD_ZERO(&fds); FD_SET(dns_fd, &fds); @@ -237,6 +418,24 @@ handshake(int dns_fd) return 1; } +int +set_target(const char *host) +{ + struct hostent *h; + + // Init dns target struct + if ((h = gethostbyname(host)) <= 0) + err(1, "couldn't resovle name %s", host); + + memset(&peer, 0, sizeof(peer)); + peer.sin_family = AF_INET; + peer.sin_port = htons(53); + peer.sin_addr = *((struct in_addr *) h->h_addr); + + return 0; +} + + static void usage() { extern char *__progname; @@ -330,7 +529,9 @@ main(int argc, char **argv) if (argc != 2) usage(); - + + topdomain = strdup(argv[1]); + if(username) { pw = getpwnam(username); if (!pw) { @@ -347,10 +548,9 @@ main(int argc, char **argv) if ((tun_fd = open_tun(device)) == -1) goto cleanup1; - dns_set_topdomain(argv[1]); if ((dns_fd = open_dns(0, INADDR_ANY)) == -1) goto cleanup2; - if (dns_settarget(argv[0]) == -1) + if (set_target(argv[0]) == -1) goto cleanup2; signal(SIGINT, sighandler); diff --git a/src/iodined.c b/src/iodined.c index 5e70ccb..0726604 100644 --- a/src/iodined.c +++ b/src/iodined.c @@ -44,6 +44,8 @@ struct packet packetbuf; struct packet outpacket; int outid; +static char *topdomain; + struct query q; struct user u; @@ -383,7 +385,7 @@ main(int argc, char **argv) argc -= optind; argv += optind; - + if (geteuid() != 0) { printf("Run as root and you'll be happy.\n"); usage(); @@ -392,6 +394,8 @@ main(int argc, char **argv) if (argc != 2) usage(); + topdomain = strdup(argv[1]); + if (username) { pw = getpwnam(username); if (!pw) { @@ -420,7 +424,6 @@ main(int argc, char **argv) goto cleanup0; if (tun_setip(argv[0]) != 0 || tun_setmtu(mtu) != 0) goto cleanup1; - dns_set_topdomain(argv[1]); if ((dnsd_fd = open_dns(port, listen_ip)) == -1) goto cleanup2;