From 78217e90bbeb5c831f48cea7142b225b53e97f8f Mon Sep 17 00:00:00 2001 From: xulang726 Date: Mon, 6 Nov 2017 18:14:32 +0800 Subject: [PATCH 1/7] avoid logging.c compile error add -lrt to end of LDFLAGS --- src/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index 00bac78..6c6dc85 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2,10 +2,10 @@ # a lot of flags INCLUDE_DIRS = -I. -Istrategies -CC = gcc +CC = gcc -std=gnu99 CUSTOM_DEFINES = #-DEVALUATION# -DTEST CFLAGS = -g -c $(CUSTOM_DEFINES) $(INCLUDE_DIRS) # -Wall -LDFLAGS = -lnetfilter_queue -lnfnetlink -lhiredis -lev +LDFLAGS = -lnetfilter_queue -lnfnetlink -lhiredis -lev -lrt SOURCES = main.c helper.c logging.c socket.c strategy.c cache.c dns.c dnscli.c feedback.c test.c order.c redis.c memcache.c ttl_probing.c discrepancy.c STRATEGIES = $(shell find strategies -type f -iname '*.c') OBJECTS = $(SOURCES:.c=.o) $(STRATEGIES:.c=.o) From 44255503159adff7850a9bc159ae1cf4d17ee76e Mon Sep 17 00:00:00 2001 From: Zhongjie Wang Date: Sat, 10 Feb 2018 21:31:31 -0800 Subject: [PATCH 2/7] added support for https protocol --- distgen.sh | 25 --- src/helper.c | 36 ++++ src/helper.h | 3 + src/main.c | 518 +++++++++++++++++++++++++++++---------------------- 4 files changed, 331 insertions(+), 251 deletions(-) delete mode 100755 distgen.sh diff --git a/distgen.sh b/distgen.sh deleted file mode 100755 index c809e31..0000000 --- a/distgen.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash -SRC_DIR=src -DIST_DIR=dist -sudo rm $DIST_DIR -rf -mkdir $DIST_DIR -cp $SRC_DIR/* $DIST_DIR -rm $DIST_DIR/*.o -rm $DIST_DIR/intangd -rm $DIST_DIR/distgen.sh -cp $SRC_DIR/tools/parse_log.py $DIST_DIR/ -cp $SRC_DIR/tools/dump_stats_from_log.py $DIST_DIR/ -cp $SRC_DIR/tools/dump_stats_from_log2.py $DIST_DIR/ -cp $SRC_DIR/tools/dump_stats.py $DIST_DIR/ -cp $SRC_DIR/tools/dump_stats2.py $DIST_DIR/ -cp $SRC_DIR/tools/tools.py $DIST_DIR/ -cp $SRC_DIR/tools/measure_ttl.py $DIST_DIR/ -cp $SRC_DIR/tools/measure_all_ttl.py $DIST_DIR/ -cp $SRC_DIR/tools/measure_all_ttl_dns.py $DIST_DIR/ -cp $SRC_DIR/test/*.sh $DIST_DIR/ -cp $SRC_DIR/test/*.py $DIST_DIR/ -tar zcvf intang.tar.gz $DIST_DIR -#zip -qr intang.zip $DIST_DIR -sudo rm $DIST_DIR -rf -echo "Done." - diff --git a/src/helper.c b/src/helper.c index ccbdfe6..7c02b62 100644 --- a/src/helper.c +++ b/src/helper.c @@ -264,3 +264,39 @@ int startswith(const char *a, const char *b) { return (strncmp(a, b, strlen(b)) == 0); } +// not 100% accurate, may have false-positive +int is_https_client_hello(const char *payload) { + if (payload[0] == 0x16) // Type == Handshake + { + if (payload[1] == 0x03) // Version + { + if (payload[5] == 0x01) // Type == ClientHello + { + if (payload[9] == 0x03) // First byte of Version + { + return 1; + } + } + } + } + return 0; +} + +// not 100% accurate, may have false-positive +int is_https_server_hello(const char *payload) { + if (payload[0] == 0x16) // Type == Handshake + { + if (payload[1] == 0x03) // First byte of Version + { + if (payload[5] == 0x02) // Type == ServerHello + { + if (payload[9] == 0x03) // First byte of Version + { + return 1; + } + } + } + } + return 0; +} + diff --git a/src/helper.h b/src/helper.h index 3aeb113..027e8a2 100644 --- a/src/helper.h +++ b/src/helper.h @@ -42,5 +42,8 @@ int is_blocked_ip(const char *ip); int startswith(const char *a, const char *b); +int is_https_client_hello(const char *payload); +int is_https_server_hello(const char *payload); + #endif diff --git a/src/main.c b/src/main.c index 9025727..bbba32a 100644 --- a/src/main.c +++ b/src/main.c @@ -65,6 +65,7 @@ int opt_inject_syn_and_syn_ack_with_one_ttl = 1; // 0 - disable, 1 - enable int opt_protect_http_protocol = 1; +int opt_protect_https_protocol = 1; // 0 - disable, 1 - enable int opt_protect_dns_protocol = 0; @@ -142,7 +143,7 @@ int stop_redis_server() void signal_handler(int signum) { - printf("Daemon exited unexpectedly. Signal %d recved.\n", signum); + //printf("Daemon exited unexpectedly. Signal %d recved.\n", signum); log_debug("Signal %d recved.", signum); cleanup(); log_info("Daemon exited."); @@ -160,7 +161,7 @@ int register_signal_handlers() return -1; } if (signal(SIGQUIT, signal_handler) == SIG_ERR) { - log_error("register SIGQUIT` handler failed."); + log_error("register SIGQUIT handler failed."); return -1; } if (signal(SIGTERM, signal_handler) == SIG_ERR) { @@ -248,6 +249,7 @@ int add_iptables_rules() // discard incoming ICMP packet, otherwise there will be socker error "No route to host" sprintf(cmd, "iptables -A INPUT -p icmp -j DROP"); system(cmd); + // Dulicate rules may be generated by the following code. But it doesn't matter, unless it does... if (opt_protect_http_protocol) { // monitor incoming RST sprintf(cmd, "iptables -A INPUT -p tcp --sport 80 --tcp-flags RST RST -j NFQUEUE --queue-num %d", NF_QUEUE_NUM); @@ -271,6 +273,26 @@ int add_iptables_rules() sprintf(cmd, "iptables -A INPUT -p tcp --sport 80 --tcp-flags SYN,ACK,RST ACK -m u32 --u32 '0>>22&0x3C@ 12>>26&0x3C@ 0=0x48545450' -j NFQUEUE --queue-num %d", NF_QUEUE_NUM); system(cmd); } + if (opt_protect_https_protocol) { + // monitor incoming RST + sprintf(cmd, "iptables -A INPUT -p tcp --sport 443 --tcp-flags RST RST -j NFQUEUE --queue-num %d", NF_QUEUE_NUM); + system(cmd); + // monitor outgoing SYN + sprintf(cmd, "iptables -A OUTPUT -p tcp --dport 443 --tcp-flags SYN,ACK SYN -m mark ! --mark %d -j NFQUEUE --queue-num %d", MARK, NF_QUEUE_NUM); + system(cmd); + // monitor incoming SYN-ACK + sprintf(cmd, "iptables -A INPUT -p tcp --sport 443 --tcp-flags SYN,ACK SYN,ACK -j NFQUEUE --queue-num %d", NF_QUEUE_NUM); + system(cmd); + // intercept outgoing ACK + sprintf(cmd, "iptables -A OUTPUT -p tcp --dport 443 --tcp-flags SYN,ACK,RST ACK -m mark ! --mark %d -m length --length 0:80 -j NFQUEUE --queue-num %d", MARK, NF_QUEUE_NUM); + system(cmd); + // intercept outgoing HTTPS ClientHello message + sprintf(cmd, "iptables -A OUTPUT -p tcp --dport 443 --tcp-flags SYN,ACK,RST ACK -m mark ! --mark %d -m u32 --u32 '0>>22&0x3C@ 12>>26&0x3C@ 0&0xFFFF0000=0x16030000' -j NFQUEUE --queue-num %d", MARK, NF_QUEUE_NUM); + system(cmd); + // intercept incoming HTTPS ServerHello message + sprintf(cmd, "iptables -A INPUT -p tcp --sport 443 --tcp-flags SYN,ACK,RST ACK -m u32 --u32 '0>>22&0x3C@ 12>>26&0x3C@ 0&0xFFFF0000=0x16030000' -j NFQUEUE --queue-num %d", NF_QUEUE_NUM); + system(cmd); + } if (opt_protect_dns_protocol) { // monitor incoming RST sprintf(cmd, "iptables -A INPUT -p tcp --sport 53 --tcp-flags RST RST -j NFQUEUE --queue-num %d", NF_QUEUE_NUM); @@ -363,6 +385,26 @@ int remove_iptables_rules() sprintf(cmd, "iptables -D INPUT -p tcp --sport 80 --tcp-flags SYN,ACK,RST ACK -m u32 --u32 '0>>22&0x3C@ 12>>26&0x3C@ 0=0x48545450' -j NFQUEUE --queue-num %d", NF_QUEUE_NUM); system(cmd); } + if (opt_protect_https_protocol) { + // monitor incoming RST + sprintf(cmd, "iptables -D INPUT -p tcp --sport 443 --tcp-flags RST RST -j NFQUEUE --queue-num %d", NF_QUEUE_NUM); + system(cmd); + // monitor outgoing SYN + sprintf(cmd, "iptables -D OUTPUT -p tcp --dport 443 --tcp-flags SYN,ACK SYN -m mark ! --mark %d -j NFQUEUE --queue-num %d", MARK, NF_QUEUE_NUM); + system(cmd); + // monitor incoming SYN-ACK + sprintf(cmd, "iptables -D INPUT -p tcp --sport 443 --tcp-flags SYN,ACK SYN,ACK -j NFQUEUE --queue-num %d", NF_QUEUE_NUM); + system(cmd); + // intercept outgoing ACK + sprintf(cmd, "iptables -D OUTPUT -p tcp --dport 443 --tcp-flags SYN,ACK,RST ACK -m mark ! --mark %d -m length --length 0:80 -j NFQUEUE --queue-num %d", MARK, NF_QUEUE_NUM); + system(cmd); + // intercept outgoing HTTPS ClientHello message + sprintf(cmd, "iptables -D OUTPUT -p tcp --dport 443 --tcp-flags SYN,ACK,RST ACK -m mark ! --mark %d -m u32 --u32 '0>>22&0x3C@ 12>>26&0x3C@ 0&0xFFFF0000=0x16030000' -j NFQUEUE --queue-num %d", MARK, NF_QUEUE_NUM); + system(cmd); + // intercept incoming HTTPS ServerHello message + sprintf(cmd, "iptables -D INPUT -p tcp --sport 443 --tcp-flags SYN,ACK,RST ACK -m u32 --u32 '0>>22&0x3C@ 12>>26&0x3C@ 0&0xFFFF0000=0x16030000' -j NFQUEUE --queue-num %d", NF_QUEUE_NUM); + system(cmd); + } if (opt_protect_dns_protocol) { // monitor incoming RST sprintf(cmd, "iptables -D INPUT -p tcp --sport 53 --tcp-flags RST RST -j NFQUEUE --queue-num %d", NF_QUEUE_NUM); @@ -587,6 +629,7 @@ void cleanup() /* Process UDP packets * Return 0 to accept packet, otherwise to drop packet + * inout: 0, incoming packet; 1, outgoing packet. */ int process_udp_packet(struct mypacket *packet, char inout) { @@ -653,7 +696,7 @@ int process_udp_packet(struct mypacket *packet, char inout) flag = 1; } - if (dport == 53) { + if (inout == 1 && dport == 53) { // Process outgoing DNS requests log_debug("[UDP] Sent a DNS request from %s:%d to %s:%d.", sip, sport, dip, dport); @@ -681,7 +724,8 @@ int process_udp_packet(struct mypacket *packet, char inout) log_debugv("[EVAL] DNS UDP request %d", txn_id); } } - else if (sport == 53) { + else if (inout == 0 && sport == 53) { + // Process incoming DNS responses if (opt_protect_dns_protocol == 1 && (opt_dns_only_blacklisted == 0 || opt_dns_only_blacklisted == 1 && flag)) { // the response must be sent by ourself @@ -707,6 +751,7 @@ int process_udp_packet(struct mypacket *packet, char inout) /* Process TCP packets * Return 0 to accept packet, otherwise to drop packet + * inout: 0, incoming packet; 1, outgoing packet. */ int process_tcp_packet(struct mypacket *packet, char inout) { @@ -747,127 +792,130 @@ int process_tcp_packet(struct mypacket *packet, char inout) // return 0; //} - if (tcphdr->th_flags == TCP_SYN) { - // Processing outgoing SYN packet. + if (inout == 1) { + // processing outgoing packet + if (tcphdr->th_flags == TCP_SYN) { + // Processing outgoing SYN packet. - // Uploading diagnostic log is disabled. (2017.9.1) - //if (strcmp(dip, FEEDBACK_SERVER_IP) == 0) - // return 0; - + // Uploading diagnostic log is disabled. (2017.9.1) + //if (strcmp(dip, FEEDBACK_SERVER_IP) == 0) + // return 0; - // choose a strategy for the newly created connection - int sid = choose_strategy_by_historical_result(iphdr->daddr); - log_debug("Using strategy %s", g_strats[sid].name); - set_sid(&fourtp, sid); - //cache_strategy(&fourtp, sid); + // choose a strategy for the newly created connection + int sid = choose_strategy_by_historical_result(iphdr->daddr); + log_debug("Using strategy %s", g_strats[sid].name); + set_sid(&fourtp, sid); + //cache_strategy(&fourtp, sid); - if (g_strats[sid].process_syn) { - ret = g_strats[sid].process_syn(packet); -#ifndef EVALUATION - if (ret) { - need_evaluation(&fourtp); - } -#endif - } - } - if (tcphdr->th_flags == (TCP_SYN | TCP_ACK)) { - // Got a SYN-ACK from server - - // send an ACK with 1 TTL to make home router happy - if (opt_inject_ack_with_one_ttl) - send_ACK_with_one_ttl(dip, dport, sip, sport, tcphdr->th_ack, htonl(ntohl(tcphdr->th_seq)+1)); - - struct fourtuple reverse_fourtp; - reverse_fourtp.saddr = iphdr->daddr; - reverse_fourtp.daddr = iphdr->saddr; - reverse_fourtp.sport = tcphdr->th_dport; - reverse_fourtp.dport = tcphdr->th_sport; - - int sid = get_sid(&reverse_fourtp); - if (sid >= 0) { - if (get_ttl(iphdr->saddr) == 0) { - // if TTL hasn't been initialized - // find initial TTL from SYN/ACK packet - int ttl = choose_appropriate_ttl(iphdr->ttl); - set_ttl(iphdr->saddr, ttl); - } - if (g_strats[sid].process_synack) { - ret = g_strats[sid].process_synack(packet); + if (g_strats[sid].process_syn) { + ret = g_strats[sid].process_syn(packet); #ifndef EVALUATION if (ret) { - need_evaluation(&reverse_fourtp); + need_evaluation(&fourtp); } #endif } } - else if (sid == -1) { - ret = process_synack_for_ttl_probing(packet); - } + else if ((tcphdr->th_flags & TCP_ACK) && + !(tcphdr->th_flags & (TCP_SYN | TCP_RST))) { + // Processing outgoing data packet. + // ignore ACK packets without payload + if (packet->payload_len == 0) + return 0; + + if (dport == 80) { + if ((payload[0] == 'G' && payload[1] == 'E' && + payload[2] == 'T' && payload[3] == ' ') || + (payload[0] == 'P' && payload[1] == 'O' && + payload[2] == 'S' && payload[3] == 'T' && + payload[4] == ' ')) { + // Got a outgoing HTTP request + log_debug("[TCP] Sent a HTTP request from %s:%d to %s:%d.", sip, sport, dip, dport); + int i, j, k, l; + + //char req_line[1000]; + //for (i = 0; i < 1000; i++) { + // if (payload[i] == '\r' || payload[i] == '\n') { + // req_line[i] = 0; + // break; + // } + // req_line[i] = payload[i]; + //} + + // Generate the HTTP request line. Format: GET/POST domain/url. e.g. GET www.google.com/index.php + char req_line2[1000]; + // copy GET/POST + for (i = 0; payload[i] != ' ' && i < packet->payload_len; i++) { + req_line2[i] = payload[i]; + } + req_line2[i++] = ' '; + k = i; + + // find Host field + for (j = i; j < packet->payload_len; j++) { + if (payload[j] == 'H' && payload[j+1] == 'o' && + payload[j+2] == 's' && payload[j+3] == 't' && + payload[j+4] == ':' && (payload[j-1] == '\r' || payload[j-1] == '\n')) { + j += 5; + // copy Host value + while (payload[j] == ' ') j++; + for (l = 0; l < 99 && j+l < packet->payload_len; l++) { + if (payload[j+l] == '\r' || payload[j+l] == '\n') + break; + req_line2[k++] = payload[j+l]; + } + break; + } + } - //if (opt_inject_syn_and_syn_ack_with_one_ttl) - // send_one_ttl_SYN_and_SYN_ACK(dip, dport, sip, sport, tcphdr->th_ack, tcphdr->th_seq); - } - else if ((tcphdr->th_flags & TCP_ACK) && - !(tcphdr->th_flags & (TCP_SYN | TCP_RST))) { - // ignore ACK packets without payload - if (packet->payload_len == 0) - return 0; + // copy the rest of request line + for (; i < 900 && i < packet->payload_len; i++) { + if (payload[i] == '\r' || payload[i] == '\n') { + break; + } + req_line2[k++] = payload[i]; + } + req_line2[k] = 0; + + log_debug("[TCP] %s", req_line2); - if (dport == 80) { - if ((payload[0] == 'G' && payload[1] == 'E' && - payload[2] == 'T' && payload[3] == ' ') || - (payload[0] == 'P' && payload[1] == 'O' && - payload[2] == 'S' && payload[3] == 'T' && - payload[4] == ' ')) { - // Got a outgoing HTTP request - log_debug("[TCP] Sent a HTTP request from %s:%d to %s:%d.", sip, sport, dip, dport); - int i, j, k, l; - - //char req_line[1000]; - //for (i = 0; i < 1000; i++) { - // if (payload[i] == '\r' || payload[i] == '\n') { - // req_line[i] = 0; - // break; - // } - // req_line[i] = payload[i]; - //} - - // Generate the HTTP request line. Format: GET/POST domain/url. e.g. GET www.google.com/index.php - char req_line2[1000]; - // copy GET/POST - for (i = 0; payload[i] != ' ' && i < packet->payload_len; i++) { - req_line2[i] = payload[i]; - } - req_line2[i++] = ' '; - k = i; - - // find Host field - for (j = i; j < packet->payload_len; j++) { - if (payload[j] == 'H' && payload[j+1] == 'o' && - payload[j+2] == 's' && payload[j+3] == 't' && - payload[j+4] == ':' && (payload[j-1] == '\r' || payload[j-1] == '\n')) { - j += 5; - // copy Host value - while (payload[j] == ' ') j++; - for (l = 0; l < 99 && j+l < packet->payload_len; l++) { - if (payload[j+l] == '\r' || payload[j+l] == '\n') - break; - req_line2[k++] = payload[j+l]; + int sid = get_sid(&fourtp); + if (sid >= 0 && g_strats[sid].process_request) { + ret = g_strats[sid].process_request(packet); +#ifndef EVALUATION + if (ret) { + need_evaluation(&fourtp); } - break; +#endif } - } +#ifdef EVALUATION + if (strstr(req_line2, "ultrasurf") || strstr(req_line2, "goodword")) { + need_evaluation(&fourtp); + } +#endif - // copy the rest of request line - for (; i < 900 && i < packet->payload_len; i++) { - if (payload[i] == '\r' || payload[i] == '\n') { - break; + cache_http_request(&fourtp, req_line2); + } + } + else if (dport == 443 && is_https_client_hello(payload)) { + log_debug("Got a ClientHello message."); + // it's an HTTPS ClientHello message. + // Keep in mind that Tor or other protocols can also uses port 443! + // Use the same logic as Tor and OpenVPN for now. + int sid = get_sid(&fourtp); + if (sid >= 0 && g_strats[sid].process_request) { + ret = g_strats[sid].process_request(packet); + if (ret) { + if (opt_inject_syn_and_syn_ack_with_one_ttl == 1) + send_one_ttl_SYN(sip, sport, dip, dport, tcphdr->th_seq); + else if (opt_inject_syn_and_syn_ack_with_one_ttl == 2) + send_one_ttl_SYN_and_SYN_ACK(sip, sport, dip, dport, tcphdr->th_seq, tcphdr->th_ack); } - req_line2[k++] = payload[i]; } - req_line2[k] = 0; - - log_debug("[TCP] %s", req_line2); + } + else if (dport == 53) { + // Got a DNS request over TCP + log_debug("[TCP] Sent a DNS request from %s:%d to %s:%d.", sip, sport, dip, dport); int sid = get_sid(&fourtp); if (sid >= 0 && g_strats[sid].process_request) { @@ -878,125 +926,37 @@ int process_tcp_packet(struct mypacket *packet, char inout) } #endif } -#ifdef EVALUATION - if (strstr(req_line2, "ultrasurf") || strstr(req_line2, "goodword")) { - need_evaluation(&fourtp); - } -#endif - - cache_http_request(&fourtp, req_line2); - } - } - else if (sport == 80) { - if (payload[0] == 'H' && payload[1] == 'T' && payload[2] == 'T' && payload[3] == 'P') { - // Got a incoming HTTP response - log_debug("[TCP] Got a HTTP response from %s:%d to %s:%d.", sip, sport, dip, dport); - process_http_response(&fourtp, tcphdr->th_seq, iphdr->ttl); - } - } - else if (dport == 53) { - // Got a DNS request over TCP - log_debug("[TCP] Sent a DNS request from %s:%d to %s:%d.", sip, sport, dip, dport); - int sid = get_sid(&fourtp); - if (sid >= 0 && g_strats[sid].process_request) { - ret = g_strats[sid].process_request(packet); -#ifndef EVALUATION - if (ret) { - need_evaluation(&fourtp); - } -#endif + cache_dns_tcp_request(&fourtp); } - - cache_dns_tcp_request(&fourtp); - } - else if (sport == 53) { - // Got a DNS response over TCP, maybe triggered by our app, or maybe not - log_debug("[TCP] Got a DNS response from %s:%d to %s:%d.", sip, sport, dip, dport); - // parse the DNS response to get the first qname - const unsigned char *dns_payload = packet->payload + 2; - struct mydnshdr *dnshdr = (struct mydnshdr*)dns_payload; - unsigned short txn_id = htons(dnshdr->txn_id); - int qdcount = ntohs(dnshdr->questions); - char qname[MAX_QNAME_LEN]; - if (qdcount > 0) { - //log_debug("Questions: %d", qdcount); - unsigned char *ptr = (unsigned char *)(dnshdr + 1); - { - struct mydnsquery query; - int j = 0, l; - while (1) { - for (l = *ptr++; l != 0; l--) { - query.qname[j++] = *ptr++; - if (j >= MAX_QNAME_LEN) { - while (*ptr != 0) ptr++; - break; - } - } - if (*ptr == 0) { - query.qname[j] = 0; - ptr++; - break; - } - query.qname[j++] = '.'; + else if (dport == opt_vpn_port) { + // outgoing packet + int sid = get_sid(&fourtp); + if (sid >= 0 && g_strats[sid].process_request) { + ret = g_strats[sid].process_request(packet); + if (ret) { + if (opt_inject_syn_and_syn_ack_with_one_ttl == 1) + send_one_ttl_SYN(sip, sport, dip, dport, tcphdr->th_seq); + else if (opt_inject_syn_and_syn_ack_with_one_ttl == 2) + send_one_ttl_SYN_and_SYN_ACK(sip, sport, dip, dport, tcphdr->th_seq, tcphdr->th_ack); } - query.qtype = (ptr[0] << 8) + ptr[1]; - query.qclass = (ptr[2] << 8) + ptr[3]; - - log_debug("DNS Query: %s %d %d", query.qname, query.qtype, query.qclass); - - // use the first query to calc hash - qname[0] = 0; - strncat(qname, query.qname, MAX_QNAME_LEN - 1); } - - // Tell the caching thread to process the dns udp response - // use DNS transaction ID and first query name as unique ID - // transaction ID alone may cause collision - process_dns_tcp_response(txn_id, qname, &fourtp, tcphdr->th_seq, iphdr->ttl, packet->payload, packet->payload_len); - } - } - else if (dport == opt_vpn_port) { - // outgoing packet - int sid = get_sid(&fourtp); - if (sid >= 0 && g_strats[sid].process_request) { - ret = g_strats[sid].process_request(packet); - if (ret) { - if (opt_inject_syn_and_syn_ack_with_one_ttl == 1) - send_one_ttl_SYN(sip, sport, dip, dport, tcphdr->th_seq); - else if (opt_inject_syn_and_syn_ack_with_one_ttl == 2) - send_one_ttl_SYN_and_SYN_ACK(sip, sport, dip, dport, tcphdr->th_seq, tcphdr->th_ack); - } - } - } - else if (sport == opt_vpn_port) { - // incomine packet - } - else if (dport == opt_tor_port) { - // outgoing packet - int sid = get_sid(&fourtp); - if (sid >= 0 && g_strats[sid].process_request) { - ret = g_strats[sid].process_request(packet); - if (ret) { - if (opt_inject_syn_and_syn_ack_with_one_ttl == 1) - send_one_ttl_SYN(sip, sport, dip, dport, tcphdr->th_seq); - else if (opt_inject_syn_and_syn_ack_with_one_ttl == 2) - send_one_ttl_SYN_and_SYN_ACK(sip, sport, dip, dport, tcphdr->th_seq, tcphdr->th_ack); + else if (dport == opt_tor_port) { + // outgoing packet + int sid = get_sid(&fourtp); + if (sid >= 0 && g_strats[sid].process_request) { + ret = g_strats[sid].process_request(packet); + if (ret) { + if (opt_inject_syn_and_syn_ack_with_one_ttl == 1) + send_one_ttl_SYN(sip, sport, dip, dport, tcphdr->th_seq); + else if (opt_inject_syn_and_syn_ack_with_one_ttl == 2) + send_one_ttl_SYN_and_SYN_ACK(sip, sport, dip, dport, tcphdr->th_seq, tcphdr->th_ack); + } } } - } - else if (sport == opt_tor_port) { - // incomine packet - } - else { - // TODO: for all other protocols. This branch is a piece of temporary code, should be re-write. - if (inout == 0) { - // incoming packet - log_debug("this is an incoming packet."); - } else { - // outgoing packet + // TODO: for all other protocols. This branch is a piece of temporary code, should be re-write. log_debug("this is an outgoing packet."); int sid = get_sid(&fourtp); if (sid >= 0 && g_strats[sid].process_request) { @@ -1010,13 +970,119 @@ int process_tcp_packet(struct mypacket *packet, char inout) } } } - } - else if (tcphdr->th_flags & TCP_RST) { - // Got an incoming RST - log_debug("[TCP] Got an incoming RST from %s:%d to %s:%d.", sip, sport, dip, dport); + } else if (inout == 0) { + // processing incoming packet + if (tcphdr->th_flags == (TCP_SYN | TCP_ACK)) { + // Got a SYN-ACK from server + + // send an ACK with 1 TTL to make home router happy + if (opt_inject_ack_with_one_ttl) + send_ACK_with_one_ttl(dip, dport, sip, sport, tcphdr->th_ack, htonl(ntohl(tcphdr->th_seq)+1)); + + struct fourtuple reverse_fourtp; + reverse_fourtp.saddr = iphdr->daddr; + reverse_fourtp.daddr = iphdr->saddr; + reverse_fourtp.sport = tcphdr->th_dport; + reverse_fourtp.dport = tcphdr->th_sport; + + int sid = get_sid(&reverse_fourtp); + if (sid >= 0) { + if (get_ttl(iphdr->saddr) == 0) { + // if TTL hasn't been initialized + // find initial TTL from SYN/ACK packet + int ttl = choose_appropriate_ttl(iphdr->ttl); + set_ttl(iphdr->saddr, ttl); + } + if (g_strats[sid].process_synack) { + ret = g_strats[sid].process_synack(packet); +#ifndef EVALUATION + if (ret) { + need_evaluation(&reverse_fourtp); + } +#endif + } + } + else if (sid == -1) { + ret = process_synack_for_ttl_probing(packet); + } - process_incoming_RST(packet); - } + //if (opt_inject_syn_and_syn_ack_with_one_ttl) + // send_one_ttl_SYN_and_SYN_ACK(dip, dport, sip, sport, tcphdr->th_ack, tcphdr->th_seq); + } + else if ((tcphdr->th_flags & TCP_ACK) && + !(tcphdr->th_flags & (TCP_SYN | TCP_RST))) { + // Processing incoming data packet. + if (sport == 80) { + if (payload[0] == 'H' && payload[1] == 'T' && payload[2] == 'T' && payload[3] == 'P') { + // Got a incoming HTTP response + log_debug("[TCP] Got a HTTP response from %s:%d to %s:%d.", sip, sport, dip, dport); + process_http_response(&fourtp, tcphdr->th_seq, iphdr->ttl); + } + } + else if (sport == 53) { + // Got a DNS response over TCP, maybe triggered by our app, or maybe not + log_debug("[TCP] Got a DNS response from %s:%d to %s:%d.", sip, sport, dip, dport); + // parse the DNS response to get the first qname + const unsigned char *dns_payload = packet->payload + 2; + struct mydnshdr *dnshdr = (struct mydnshdr*)dns_payload; + unsigned short txn_id = htons(dnshdr->txn_id); + int qdcount = ntohs(dnshdr->questions); + char qname[MAX_QNAME_LEN]; + if (qdcount > 0) { + //log_debug("Questions: %d", qdcount); + unsigned char *ptr = (unsigned char *)(dnshdr + 1); + { + struct mydnsquery query; + int j = 0, l; + while (1) { + for (l = *ptr++; l != 0; l--) { + query.qname[j++] = *ptr++; + if (j >= MAX_QNAME_LEN) { + while (*ptr != 0) ptr++; + break; + } + } + if (*ptr == 0) { + query.qname[j] = 0; + ptr++; + break; + } + query.qname[j++] = '.'; + } + query.qtype = (ptr[0] << 8) + ptr[1]; + query.qclass = (ptr[2] << 8) + ptr[3]; + + log_debug("DNS Query: %s %d %d", query.qname, query.qtype, query.qclass); + + // use the first query to calc hash + qname[0] = 0; + strncat(qname, query.qname, MAX_QNAME_LEN - 1); + } + + // Tell the caching thread to process the dns udp response + // use DNS transaction ID and first query name as unique ID + // transaction ID alone may cause collision + process_dns_tcp_response(txn_id, qname, &fourtp, tcphdr->th_seq, iphdr->ttl, packet->payload, packet->payload_len); + + } + } + else if (sport == 443 && is_https_server_hello(payload)) { + // it's an HTTPS ServerHello message. + // Keep in mind that Tor or other protocols can also uses port 443! + log_debug("Got a ServerHello message."); + } + else if (sport == opt_vpn_port) { + // incomine packet + } + else if (sport == opt_tor_port) { + // incomine packet + } + else { + // TODO: for all other protocols. This branch is a piece of temporary code, should be re-write. + log_debug("this is an incoming packet."); + } + } + } return ret; } From 020d2f309cc6f7d7c3f3d5d69a098aa35684eacc Mon Sep 17 00:00:00 2001 From: Nefurtity <34592403+Nefurtity@users.noreply.github.com> Date: Sat, 3 Mar 2018 04:11:53 +0800 Subject: [PATCH 3/7] Rewrite the key to store "rst:%u_%hu_%u_%hu" info. The key stored "rst:%u_%hu_%u_%hu" info may be overwritten, set it again. --- src/cache.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cache.c b/src/cache.c index 5d1be8f..80254ef 100644 --- a/src/cache.c +++ b/src/cache.c @@ -684,6 +684,8 @@ void _process_incoming_RST(struct tcpinfo *info) set_aflag2(info->daddr, info->saddr); } + // The key may has been overwritten, need to set it again. + sprintf(key, "rst:%u_%hu_%u_%hu", info->saddr, info->sport, info->daddr, info->dport); ttl_set = insert_ttl(info->ttl, ttl_set); set_int_ex(key, ttl_set, RST_CACHE_TIMEOUT); } From 479bebd12f24bbf92a62744fef89873541e46074 Mon Sep 17 00:00:00 2001 From: Zhongjie Wang Date: Tue, 1 May 2018 14:21:33 -0700 Subject: [PATCH 4/7] fix typo --- src/helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helper.c b/src/helper.c index 7c02b62..979fffe 100644 --- a/src/helper.c +++ b/src/helper.c @@ -145,7 +145,7 @@ void show_packet(struct mypacket *packet) printf("\t+ SPort: %d\n", ntohs(packet->tcphdr->th_sport)); printf("\t+ DPort: %d\n", ntohs(packet->tcphdr->th_dport)); printf("\t+ Seq num: %08x\n", ntohl(packet->tcphdr->th_seq)); - printf("\t+ Ack num: %08x\n", ntohl(packet->tcphdr->th_sport)); + printf("\t+ Ack num: %08x\n", ntohl(packet->tcphdr->th_ack)); printf("\t+ Data offset: %d\n", packet->tcphdr->th_off); printf("\t+ TCP flags: %s\n", tcp_flags(packet->tcphdr->th_flags)); printf("\t+ Window: %d\n", ntohs(packet->tcphdr->th_win)); From b4546178e741710bef81621e989b1a28032b7562 Mon Sep 17 00:00:00 2001 From: Jason Cooke Date: Tue, 9 Jul 2019 09:23:17 +1200 Subject: [PATCH 5/7] docs: fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 48f7a62..c218444 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ Source Code Organization Disclaimer ================== -INTANG is a reasearch-oriented project. Anyone using it should be aware of the potential risks and responsible for his/her own actions against the censorship authority. +INTANG is a research-oriented project. Anyone using it should be aware of the potential risks and responsible for his/her own actions against the censorship authority. Contact ================== From c87f657ef65cf76bc3fe43c9a43096d51eba11ab Mon Sep 17 00:00:00 2001 From: MarlonKong <63358401+MarlonKong@users.noreply.github.com> Date: Thu, 28 Jul 2022 22:34:15 -0700 Subject: [PATCH 6/7] Update README.md Fix a grammar error. Correct the juxtaposition component error of the sentence. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 48f7a62..a2e4190 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Introduction ================== -INTANG is research project for circumventing the "TCP reset attack" from the Great Firewall of China (GFW) by disrupting/desynchronizing the TCP Control Block (TCB) on the censorship devices. INTANG runs as a client-side only tool in background to protect the TCP connections from being interfered (or even monitored) by the GFW. It works on TCP/IP layers instead of application layer, thus considered more general and can help all application layer protocols, e.g. HTTP, DNS over TCP, OpenVPN, Tor, evading censorship. It can also be run on a proxy to make the deployment easier for those who are incapable of running INTANG (using OSes other than Linux or doesn't have root privillige). +INTANG is a research project for circumventing the "TCP reset attack" from the Great Firewall of China (GFW) by disrupting/desynchronizing the TCP Control Block (TCB) on the censorship devices. INTANG runs as a client-side only tool in background to protect the TCP connections from being interfered (or even monitored) by the GFW. It works on TCP/IP layers instead of application layer, thus considered more general and can help all application layer protocols to evade censorship, e.g. HTTP, DNS over TCP, OpenVPN, Tor. It can also be run on a proxy to make the deployment easier for those who are incapable of running INTANG (using OSes other than Linux or doesn't have root privillige). Platform ================== From a27bfc0e56f7df404170a3524a05703de456a2ea Mon Sep 17 00:00:00 2001 From: TomAPU <44659204+TomAPU@users.noreply.github.com> Date: Wed, 11 Jun 2025 00:05:24 -0700 Subject: [PATCH 7/7] Update README.md with a stronger security warning --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index b9f723d..94b73a6 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,11 @@ Disclaimer ================== INTANG is a research-oriented project. Anyone using it should be aware of the potential risks and responsible for his/her own actions against the censorship authority. +During internal review, we discovered multiple remotely triggerable vulnerabilities. Some of these issues may lead to serious consequences, including potential remote code execution under certain conditions. While we are making efforts to address and patch some of these problems, **the system as a whole should still be considered insecure** for any real-world use. + +Please use this software **strictly for research and educational purposes in controlled environments only**. + + Contact ================== Any questions could be direct to intang.box@gmail.com