diff --git a/README.md b/README.md index 2f88661..0b05542 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,8 @@ Usage: goodbyedpi.exe [OPTION...] --dns-addr [value] redirect UDP DNS requests to the supplied IP address (experimental) --dns-port [value] redirect UDP DNS requests to the supplied port (53 by default) --dns-verb print verbose DNS redirection messages + --blacklist [txtfile] perform HTTP tricks only to host names and subdomains from + supplied text file. This option can be supplied multiple times. -1 -p -r -s -f 2 -e 2 (most compatible mode, default) -2 -p -r -s -f 2 -e 40 (better speed yet still compatible) diff --git a/blackwhitelist.c b/blackwhitelist.c new file mode 100644 index 0000000..f7b6e8d --- /dev/null +++ b/blackwhitelist.c @@ -0,0 +1,108 @@ +/* + * Blacklist for GoodbyeDPI HTTP DPI circumvention tricks + * + * This is a simple domain hash table. + * Domain records are added from a text file, where every + * domain is separated with a new line. + */ +#include +#include +#include "goodbyedpi.h" +#include "uthash.h" +#include "getline.h" + +typedef struct blackwhitelist_record { + const char *host; + UT_hash_handle hh; /* makes this structure hashable */ +} blackwhitelist_record_t; + +static blackwhitelist_record_t *blackwhitelist = NULL; + +static int check_get_hostname(const char *host) { + blackwhitelist_record_t *tmp_record = NULL; + if (!blackwhitelist) return FALSE; + + HASH_FIND_STR(blackwhitelist, host, tmp_record); + if (tmp_record) { + debug("check_get_hostname found host\n"); + return TRUE; + } + debug("check_get_hostname host not found\n"); + return FALSE; +} + +static int add_hostname(const char *host) { + if (!host) + return FALSE; + + int host_len = strlen(host); + + blackwhitelist_record_t *tmp_record = malloc(sizeof(blackwhitelist_record_t)); + char *host_c = malloc(host_len + 1); + + if (!check_get_hostname(host)) { + strncpy(host_c, host, host_len); + host_c[host_len] = '\0'; + tmp_record->host = host_c; + HASH_ADD_KEYPTR(hh, blackwhitelist, tmp_record->host, + strlen(tmp_record->host), tmp_record); + debug("Added host %s\n", host_c); + return TRUE; + } + debug("Not added host %s\n", host); + free(tmp_record); + free(host_c); + return FALSE; +} + +int blackwhitelist_load_list(const char *filename) { + char *line = malloc(HOST_MAXLEN + 1); + size_t linelen = HOST_MAXLEN + 1; + int cnt = 0; + ssize_t read; + + FILE *fp = fopen(filename, "r"); + if (!fp) return FALSE; + + while ((read = getline(&line, &linelen, fp)) != -1) { + /* works with both \n and \r\n */ + line[strcspn(line, "\r\n")] = '\0'; + if (strlen(line) > HOST_MAXLEN) { + printf("WARNING: host %s exceeds maximum host length and has not been added\n", + line); + continue; + } + if (add_hostname(line)) + cnt++; + } + free(line); + if (!blackwhitelist) return FALSE; + printf("Loaded %d hosts from file %s\n", cnt, filename); + fclose(fp); + return TRUE; +} + +int blackwhitelist_check_hostname(const char *host_addr, int host_len) { + char current_host[HOST_MAXLEN + 1]; + char *tokenized_host = NULL; + + if (host_len > HOST_MAXLEN) return FALSE; + if (host_addr && host_len) { + memcpy(current_host, host_addr, host_len); + current_host[host_len] = '\0'; + } + + if (check_get_hostname(current_host)) + return TRUE; + + tokenized_host = strchr(current_host, '.'); + while (tokenized_host != NULL && tokenized_host < (current_host + HOST_MAXLEN)) { + /* Search hostname only if there is next token */ + if (strchr(tokenized_host + 1, '.') && check_get_hostname(tokenized_host + 1)) + return TRUE; + tokenized_host = strchr(tokenized_host + 1, '.'); + } + + debug("____blackwhitelist_check_hostname FALSE: host %s\n", current_host); + return FALSE; +} diff --git a/blackwhitelist.h b/blackwhitelist.h new file mode 100644 index 0000000..55506bc --- /dev/null +++ b/blackwhitelist.h @@ -0,0 +1,2 @@ +int blackwhitelist_load_list(const char *filename); +int blackwhitelist_check_hostname(const char *host_addr, int host_len); diff --git a/dnsredir.c b/dnsredir.c index 1cece8b..5e5e34e 100644 --- a/dnsredir.c +++ b/dnsredir.c @@ -13,6 +13,7 @@ #include #include #include +#include "goodbyedpi.h" #include "dnsredir.h" #include "uthash.h" @@ -21,12 +22,6 @@ #define DNS_CLEANUP_INTERVAL_SEC 30 -#ifndef debug -#define debug(...) do {} while (0) -#else -#define debug(...) printf(...) -#endif - /* HACK! * uthash uses strlen() for HASH_FIND_STR. * We have null bytes in our key, so we can't use strlen() diff --git a/getline.c b/getline.c new file mode 100644 index 0000000..5896867 --- /dev/null +++ b/getline.c @@ -0,0 +1,92 @@ +/* $NetBSD: getdelim.c,v 1.2 2015/12/25 20:12:46 joerg Exp $ */ +/* NetBSD-src: getline.c,v 1.2 2014/09/16 17:23:50 christos Exp */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include "getline.h" + +#if !HAVE_GETDELIM + +ssize_t +getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp) +{ + char *ptr, *eptr; + + + if (*buf == NULL || *bufsiz == 0) { + *bufsiz = BUFSIZ; + if ((*buf = malloc(*bufsiz)) == NULL) + return -1; + } + + for (ptr = *buf, eptr = *buf + *bufsiz;;) { + int c = fgetc(fp); + if (c == -1) { + if (feof(fp)) { + ssize_t diff = (ssize_t)(ptr - *buf); + if (diff != 0) { + *ptr = '\0'; + return diff; + } + } + return -1; + } + *ptr++ = c; + if (c == delimiter) { + *ptr = '\0'; + return ptr - *buf; + } + if (ptr + 2 >= eptr) { + char *nbuf; + size_t nbufsiz = *bufsiz * 2; + ssize_t d = ptr - *buf; + if ((nbuf = realloc(*buf, nbufsiz)) == NULL) + return -1; + *buf = nbuf; + *bufsiz = nbufsiz; + eptr = nbuf + nbufsiz; + ptr = nbuf + d; + } + } +} + +#endif + +#if !HAVE_GETLINE + +ssize_t +getline(char **buf, size_t *bufsiz, FILE *fp) +{ + return getdelim(buf, bufsiz, '\n', fp); +} + +#endif diff --git a/getline.h b/getline.h new file mode 100644 index 0000000..6ace508 --- /dev/null +++ b/getline.h @@ -0,0 +1,7 @@ +#if !HAVE_GETDELIM +ssize_t getdelim(char **, size_t *, int, FILE *); +#endif + +#if !HAVE_GETLINE +ssize_t getline(char **, size_t *, FILE *); +#endif diff --git a/goodbyedpi.c b/goodbyedpi.c index 756be99..02086b9 100644 --- a/goodbyedpi.c +++ b/goodbyedpi.c @@ -10,7 +10,9 @@ #include #include #include "windivert.h" +#include "goodbyedpi.h" #include "dnsredir.h" +#include "blackwhitelist.h" #define die() do { printf("Something went wrong!\n" \ "Make sure you're running this program with administrator privileges\n"); \ @@ -22,7 +24,6 @@ #define TCP_HDR_LEN 20 #define IPV4_TOTALLEN_OFFSET 2 #define TCP_WINDOWSIZE_OFFSET 14 -#define HOST_MAXLEN 253 #define DIVERT_NO_LOCALNETS_DST "(" \ "(ip.DstAddr < 127.0.0.1 or ip.DstAddr > 127.255.255.255) and " \ @@ -63,6 +64,7 @@ static struct option long_options[] = { {"dns-addr", required_argument, 0, 'd' }, {"dns-port", required_argument, 0, 'g' }, {"dns-verb", no_argument, 0, 'v' }, + {"blacklist", required_argument, 0, 'b' }, {0, 0, 0, 0 } }; @@ -244,7 +246,7 @@ int main(int argc, char *argv[]) { do_host_removespace = 0, do_additional_space = 0, do_http_allports = 0, do_host_mixedcase = 0, do_dns_redirect = 0, - do_dns_verb = 0; + do_dns_verb = 0, do_blacklist = 0; int http_fragment_size = 2; int https_fragment_size = 2; uint32_t dns_addr = 0; @@ -364,6 +366,13 @@ int main(int argc, char *argv[]) { case 'v': do_dns_verb = 1; break; + case 'b': + do_blacklist = 1; + if (!blackwhitelist_load_list(optarg)) { + printf("Can't load blacklist from file!\n"); + exit(EXIT_FAILURE); + } + break; default: printf("Usage: goodbyedpi.exe [OPTION...]\n" " -p block passive DPI\n" @@ -378,6 +387,8 @@ int main(int argc, char *argv[]) { " --dns-addr [value] redirect UDP DNS requests to the supplied IP address (experimental)\n" " --dns-port [value] redirect UDP DNS requests to the supplied port (53 by default)\n" " --dns-verb print verbose DNS redirection messages\n" + " --blacklist [txtfile] perform HTTP tricks only to host names and subdomains from\n" + " supplied text file. This option can be supplied multiple times.\n" "\n" " -1 -p -r -s -f 2 -e 2 (most compatible mode, default)\n" " -2 -p -r -s -f 2 -e 40 (better speed yet still compatible)\n" @@ -466,11 +477,13 @@ int main(int argc, char *argv[]) { /* Find Host header */ if (find_header_and_get_info(packet_data, packet_dataLen, - http_host_find, &hdr_name_addr, &hdr_value_addr, &hdr_value_len)) { + http_host_find, &hdr_name_addr, &hdr_value_addr, &hdr_value_len) && + hdr_value_len > 0 && hdr_value_len <= HOST_MAXLEN && + (do_blacklist ? blackwhitelist_check_hostname(hdr_value_addr, hdr_value_len) : 1)) { host_addr = hdr_value_addr; host_len = hdr_value_len; - if (do_host_mixedcase && host_len > 0 && host_len <= HOST_MAXLEN) { + if (do_host_mixedcase) { mix_case(host_addr, host_len); should_recalc_checksum = 1; } @@ -511,8 +524,7 @@ int main(int argc, char *argv[]) { * * Nothing is done if User-Agent header is missing. */ - if (host_len > 0 && host_len <= HOST_MAXLEN && - useragent_addr && useragent_len > 0) { + if (useragent_addr && useragent_len > 0) { /* useragent_addr is in the beginning of User-Agent value */ if (useragent_addr > host_addr) { diff --git a/goodbyedpi.h b/goodbyedpi.h new file mode 100644 index 0000000..7d45a82 --- /dev/null +++ b/goodbyedpi.h @@ -0,0 +1,7 @@ +#define HOST_MAXLEN 253 + +#ifndef DEBUG +#define debug(...) do {} while (0) +#else +#define debug(...) printf(__VA_ARGS__) +#endif