Added option for multiple nameservers, used in round-robin

This commit is contained in:
frekky 2015-08-14 22:07:48 +08:00
parent 9c8a941729
commit 3e7cf55fe4
4 changed files with 81 additions and 38 deletions

View File

@ -62,8 +62,10 @@ static void handshake_lazyoff(int dns_fd);
static int running; static int running;
static const char *password; static const char *password;
static struct sockaddr_storage nameserv; /* Nameserver/domain info */
static int nameserv_len; static struct sockaddr_storage *nameserv_addrs;
static int nameserv_addrs_len;
static int current_nameserver;
static struct sockaddr_storage raw_serv; static struct sockaddr_storage raw_serv;
static int raw_serv_len; static int raw_serv_len;
static const char *topdomain; static const char *topdomain;
@ -137,6 +139,8 @@ client_init()
inpkt.len = 0; inpkt.len = 0;
inpkt.seqno = 0; inpkt.seqno = 0;
inpkt.fragment = 0; inpkt.fragment = 0;
current_nameserver = 0;
} }
void void
@ -152,10 +156,10 @@ client_get_conn()
} }
void void
client_set_nameserver(struct sockaddr_storage *addr, int addrlen) client_set_nameservers(struct sockaddr_storage *addr, int addrslen)
{ {
memcpy(&nameserv, addr, addrlen); nameserv_addrs = addr;
nameserv_len = addrlen; nameserv_addrs_len = addrslen;
} }
void void
@ -246,6 +250,14 @@ client_get_raw_addr()
return format_addr(&raw_serv, raw_serv_len); return format_addr(&raw_serv, raw_serv_len);
} }
void
client_rotate_nameserver()
{
current_nameserver ++;
if (current_nameserver >= nameserv_addrs_len)
current_nameserver = 0;
}
static void static void
send_query(int fd, char *hostname) send_query(int fd, char *hostname)
{ {
@ -273,7 +285,10 @@ send_query(int fd, char *hostname)
fprintf(stderr, " Sendquery: id %5d name[0] '%c'\n", q.id, hostname[0]); fprintf(stderr, " Sendquery: id %5d name[0] '%c'\n", q.id, hostname[0]);
#endif #endif
sendto(fd, packet, len, 0, (struct sockaddr*)&nameserv, nameserv_len); sendto(fd, packet, len, 0, (struct sockaddr*) &nameserv_addrs[current_nameserver],
sizeof(struct sockaddr_storage));
client_rotate_nameserver();
/* There are DNS relays that time out quickly but don't send anything /* There are DNS relays that time out quickly but don't send anything
back on timeout. back on timeout.
@ -798,13 +813,14 @@ tunnel_dns(int tun_fd, int dns_fd)
int up_ack_fragment; int up_ack_fragment;
int new_down_seqno; int new_down_seqno;
int new_down_fragment; int new_down_fragment;
struct query q; static struct query q;
unsigned long datalen; unsigned long datalen;
char buf[64*1024]; static char buf[64*1024];
int read; int read;
int send_something_now = 0; int send_something_now = 0;
memset(q.name, 0, sizeof(q.name)); memset(q, 0, sizeof(q));
memset(buf, 0, sizeof(buf));
read = read_dns_withq(dns_fd, tun_fd, buf, sizeof(buf), &q); read = read_dns_withq(dns_fd, tun_fd, buf, sizeof(buf), &q);
if (conn != CONN_DNS_NULL) if (conn != CONN_DNS_NULL)

View File

@ -24,7 +24,8 @@ void client_stop();
enum connection client_get_conn(); enum connection client_get_conn();
const char *client_get_raw_addr(); const char *client_get_raw_addr();
void client_set_nameserver(struct sockaddr_storage *, int); void client_rotate_nameserver();
void client_set_nameservers(struct sockaddr_storage *, int);
void client_set_topdomain(const char *cp); void client_set_topdomain(const char *cp);
void client_set_password(const char *cp); void client_set_password(const char *cp);
int client_set_qtype(char *qtype); int client_set_qtype(char *qtype);

View File

@ -67,6 +67,10 @@ extern const unsigned char raw_header[RAW_HDR_LEN];
# define DONT_FRAG_VALUE 1 # define DONT_FRAG_VALUE 1
#endif #endif
#ifndef GITREVISION
#define GITREVISION "GIT"
#endif
#define T_PRIVATE 65399 #define T_PRIVATE 65399
/* Undefined RR type; "private use" range, see http://www.bind9.net/dns-parameters */ /* Undefined RR type; "private use" range, see http://www.bind9.net/dns-parameters */
#define T_UNSET 65432 #define T_UNSET 65432

View File

@ -70,7 +70,7 @@ usage() {
fprintf(stderr, "Usage: %s [-v] [-h] [-f] [-r] [-u user] [-t chrootdir] [-d device] " fprintf(stderr, "Usage: %s [-v] [-h] [-f] [-r] [-u user] [-t chrootdir] [-d device] "
"[-P password] [-m maxfragsize] [-M maxlen] [-T type] [-O enc] [-L 0|1] [-I sec] " "[-P password] [-m maxfragsize] [-M maxlen] [-T type] [-O enc] [-L 0|1] [-I sec] "
"[-z context] [-F pidfile] [nameserver] topdomain\n", __progname); "[-z context] [-F pidfile] topdomain [nameserver ...]\n", __progname);
exit(2); exit(2);
} }
@ -81,7 +81,7 @@ help() {
fprintf(stderr, "iodine IP over DNS tunneling client\n"); fprintf(stderr, "iodine IP over DNS tunneling client\n");
fprintf(stderr, "Usage: %s [-v] [-h] [-f] [-r] [-u user] [-t chrootdir] [-d device] " fprintf(stderr, "Usage: %s [-v] [-h] [-f] [-r] [-u user] [-t chrootdir] [-d device] "
"[-P password] [-m maxfragsize] [-M maxlen] [-T type] [-O enc] [-L 0|1] [-I sec] " "[-P password] [-m maxfragsize] [-M maxlen] [-T type] [-O enc] [-L 0|1] [-I sec] "
"[-z context] [-F pidfile] [nameserver] topdomain\n", __progname); "[-z context] [-F pidfile] topdomain [nameserver1 [nameserver2 [nameserverN ...]]] \n", __progname);
fprintf(stderr, "Options to try if connection doesn't work:\n"); fprintf(stderr, "Options to try if connection doesn't work:\n");
fprintf(stderr, " -T force dns type: NULL, PRIVATE, TXT, SRV, MX, CNAME, A (default: autodetect)\n"); fprintf(stderr, " -T force dns type: NULL, PRIVATE, TXT, SRV, MX, CNAME, A (default: autodetect)\n");
fprintf(stderr, " -O force downstream encoding for -T other than NULL: Base32, Base64, Base64u,\n"); fprintf(stderr, " -O force downstream encoding for -T other than NULL: Base32, Base64, Base64u,\n");
@ -101,7 +101,8 @@ help() {
fprintf(stderr, " -d device to set tunnel device name\n"); fprintf(stderr, " -d device to set tunnel device name\n");
fprintf(stderr, " -z context, to apply specified SELinux context after initialization\n"); fprintf(stderr, " -z context, to apply specified SELinux context after initialization\n");
fprintf(stderr, " -F pidfile to write pid to a file\n"); fprintf(stderr, " -F pidfile to write pid to a file\n");
fprintf(stderr, "nameserver is the IP number/hostname of the relaying nameserver. if absent, /etc/resolv.conf is used\n"); fprintf(stderr, "nameserver is the IP/hostname of the relaying nameserver(s). if absent, /etc/resolv.conf is used\n");
fprintf(stderr, " multiple nameservers can be specified (used in round-robin). \n");
fprintf(stderr, "topdomain is the FQDN that is delegated to the tunnel endpoint.\n"); fprintf(stderr, "topdomain is the FQDN that is delegated to the tunnel endpoint.\n");
exit(0); exit(0);
@ -119,7 +120,6 @@ version() {
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
char *nameserv_host;
char *topdomain; char *topdomain;
char *errormsg; char *errormsg;
#ifndef WINDOWS32 #ifndef WINDOWS32
@ -145,10 +145,17 @@ main(int argc, char **argv)
#ifdef OPENBSD #ifdef OPENBSD
int rtable = 0; int rtable = 0;
#endif #endif
char *nameserv_host;
char **nameserv_hosts;
int nameserv_hosts_len;
struct sockaddr_storage nameservaddr; struct sockaddr_storage nameservaddr;
struct sockaddr_storage *nameserv_addrs;
size_t nameserv_addrs_len;
int nameservaddr_len; int nameservaddr_len;
int nameserv_family; int nameserv_family;
nameserv_addrs_len = 0;
nameserv_host = NULL; nameserv_host = NULL;
topdomain = NULL; topdomain = NULL;
errormsg = NULL; errormsg = NULL;
@ -280,36 +287,49 @@ main(int argc, char **argv)
argc -= optind; argc -= optind;
argv += optind; argv += optind;
switch (argc) { nameserv_hosts_len = argc - 1;
case 1: if (nameserv_hosts_len <= 0)
nameserv_host = get_resolvconf_addr(); nameserv_hosts_len = 1;
// Preallocate memory with expected number of hosts
nameserv_hosts = malloc(sizeof(char *) * nameserv_hosts_len);
nameserv_addrs = malloc(sizeof(struct sockaddr_storage) * nameserv_hosts_len);
if (argc == 0) {
usage();
/* NOT REACHED */
} else if (argc == 1) {
nameserv_hosts[0] = get_resolvconf_addr();
} else if (argc > 1)
for (int h = 0; h < nameserv_hosts_len; h++) nameserv_hosts[h] = strdup(argv[h + 1]);
topdomain = strdup(argv[0]); topdomain = strdup(argv[0]);
break;
case 2:
nameserv_host = argv[0];
topdomain = strdup(argv[1]);
break;
default:
usage();
/* NOTREACHED */
}
if (max_downstream_frag_size < 1 || max_downstream_frag_size > 0xffff) { for (int n = 0; n < nameserv_hosts_len; n++) {
warnx("Use a max frag size between 1 and 65535 bytes.\n"); nameserv_host = nameserv_hosts[n];
usage(); if (!nameserv_host) {
/* NOTREACHED */ errx(1, "Error processing nameserver hostnames!\n");
} }
if (nameserv_host) {
nameservaddr_len = get_addr(nameserv_host, DNS_PORT, nameserv_family, 0, &nameservaddr); nameservaddr_len = get_addr(nameserv_host, DNS_PORT, nameserv_family, 0, &nameservaddr);
if (nameservaddr_len < 0) { if (nameservaddr_len < 0) {
errx(1, "Cannot lookup nameserver '%s': %s ", errx(1, "Cannot lookup nameserver '%s': %s ",
nameserv_host, gai_strerror(nameservaddr_len)); nameserv_host, gai_strerror(nameservaddr_len));
} }
client_set_nameserver(&nameservaddr, nameservaddr_len); memcpy(&nameserv_addrs[n], &nameservaddr, sizeof(struct sockaddr_storage));
} else { nameserv_addrs_len ++;
nameserv_host = NULL;
}
if (nameserv_addrs_len <= 0 || !nameserv_hosts[0]) {
warnx("No nameserver found - not connected to any network?\n"); warnx("No nameserver found - not connected to any network?\n");
usage(); usage();
}
client_set_nameservers(nameserv_addrs, nameserv_addrs_len);
if (max_downstream_frag_size < 1 || max_downstream_frag_size > 0xffff) {
warnx("Use a max frag size between 1 and 65535 bytes.\n");
usage();
/* NOTREACHED */ /* NOTREACHED */
} }
@ -359,8 +379,10 @@ main(int argc, char **argv)
signal(SIGINT, sighandler); signal(SIGINT, sighandler);
signal(SIGTERM, sighandler); signal(SIGTERM, sighandler);
fprintf(stderr, "Sending DNS queries for %s to %s\n", fprintf(stderr, "Sending DNS queries for %s to ", topdomain);
topdomain, format_addr(&nameservaddr, nameservaddr_len)); for (int a = 0; a < nameserv_addrs_len; a++)
fprintf(stderr, "%s%s", format_addr(&nameserv_addrs[a], nameservaddr_len), (a != nameserv_addrs_len-1) ? ", " : "");
fprintf(stderr, "\n");
if (client_handshake(dns_fd, raw_mode, autodetect_frag_size, max_downstream_frag_size)) { if (client_handshake(dns_fd, raw_mode, autodetect_frag_size, max_downstream_frag_size)) {
retval = 1; retval = 1;