From ecc681a60b5e78905495f116d025eb1929419186 Mon Sep 17 00:00:00 2001
From: ValdikSS <iam@valdikss.org.ru>
Date: Thu, 21 Jul 2022 14:47:13 +0300
Subject: [PATCH] OpenVPN detection & fragmentation/fake packet support

---
 src/Makefile     |  2 +-
 src/goodbyedpi.c | 30 ++++++++++++++++++++++++++----
 2 files changed, 27 insertions(+), 5 deletions(-)

diff --git a/src/Makefile b/src/Makefile
index d6c7829..c924745 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -19,7 +19,7 @@ CFLAGS = -std=c99 -pie -fPIE -pipe -I$(WINDIVERTHEADERS) -L$(WINDIVERTLIBS) \
          -Wnull-dereference -Warray-bounds=2 -Wimplicit-fallthrough=3 \
          -Wstringop-overflow=4 \
          -Wformat-signedness -Wstrict-overflow=2 -Wcast-align=strict \
-         -Wfloat-equal -Wcast-align -Wsign-conversion \
+         -Wfloat-equal -Wcast-align -Wsign-conversion -Wno-stringop-overflow -Wno-stringop-overread \
          #-fstack-protector-strong
 LDFLAGS = -fstack-protector -Wl,-O1,-pie,--dynamicbase,--nxcompat,--sort-common,--as-needed \
 -Wl,--image-base,0x140000000 -Wl,--disable-auto-image-base
diff --git a/src/goodbyedpi.c b/src/goodbyedpi.c
index 7f54e9e..f149b1e 100644
--- a/src/goodbyedpi.c
+++ b/src/goodbyedpi.c
@@ -171,6 +171,7 @@ static struct option long_options[] = {
     {"native-frag", no_argument,       0,  '*' },
     {"reverse-frag",no_argument,       0,  '(' },
     {"max-payload", optional_argument, 0,  '|' },
+    {"openvpn",     no_argument,       0,  '#' },
     {0,             0,                 0,   0  }
 };
 
@@ -433,6 +434,16 @@ static int extract_sni(const char *pktdata, unsigned int pktlen,
     return FALSE;
 }
 
+static inline int is_openvpn_handshake(const char *pktdata, unsigned int pktlen) {
+    /*
+     * 0x38 is P_CONTROL_HARD_RESET_CLIENT_V2 + peer_id(0),
+     * 0x50 is P_CONTROL_HARD_RESET_CLIENT_V3 + peer_id(0)
+     */
+    return pktlen >= 16
+           && ntohs(((uint16_t*)pktdata)[0]) == pktlen - 2
+           && (pktdata[2] == '\x38' || pktdata[2] == '\x50');
+}
+
 static inline void change_window_size(const PWINDIVERT_TCPHDR ppTcpHdr, unsigned int size) {
     if (size >= 1 && size <= 0xFFFFu) {
         ppTcpHdr->Window = htons((u_short)size);
@@ -543,6 +554,7 @@ int main(int argc, char *argv[]) {
     } packet_type;
     int i, should_reinject, should_recalc_checksum = 0;
     int sni_ok = 0;
+    int openvpn_handshake = 0;
     int opt;
     int packet_v4, packet_v6;
     HANDLE w_filter = NULL;
@@ -569,6 +581,7 @@ int main(int argc, char *argv[]) {
         do_dns_verb = 0, do_tcp_verb = 0, do_blacklist = 0,
         do_allow_no_sni = 0,
         do_fake_packet = 0,
+        do_openvpn = 0,
         do_auto_ttl = 0,
         do_wrong_chksum = 0,
         do_wrong_seq = 0,
@@ -849,6 +862,9 @@ int main(int argc, char *argv[]) {
                     free(autottl_copy);
                 }
                 break;
+            case '#': // --openvpn
+                do_openvpn = 1;
+                break;
             case '%': // --wrong-chksum
                 do_fake_packet = 1;
                 do_wrong_chksum = 1;
@@ -924,6 +940,7 @@ int main(int argc, char *argv[]) {
                 "                          (like file transfers) in already established sessions.\n"
                 "                          May skip some huge HTTP requests from being processed.\n"
                 "                          Default (if set): --max-payload 1200.\n"
+                " --openvpn                Detect OpenVPN TCP and fragment/send fake packet.\n"
                 "\n");
                 puts("LEGACY modesets:\n"
                 " -1          -p -r -s -f 2 -k 2 -n -e 2 (most compatible mode)\n"
@@ -971,7 +988,8 @@ int main(int argc, char *argv[]) {
            "Fake requests, TTL: %s (fixed: %hu, auto: %hu-%hu-%hu, min distance: %hu)\n"  /* 16 */
            "Fake requests, wrong checksum: %d\n"    /* 17 */
            "Fake requests, wrong SEQ/ACK: %d\n"     /* 18 */
-           "Max payload size: %hu\n",               /* 19 */
+           "Max payload size: %hu\n"                /* 19 */
+           "OpenVPN: %d\n",                         /* 20 */
            do_passivedpi,                                         /* 1 */
            (do_fragment_http ? http_fragment_size : 0),           /* 2 */
            (do_fragment_http_persistent ? http_fragment_size : 0),/* 3 */
@@ -992,7 +1010,8 @@ int main(int argc, char *argv[]) {
                do_auto_ttl ? auto_ttl_max : 0, ttl_min_nhops,
            do_wrong_chksum, /* 17 */
            do_wrong_seq,    /* 18 */
-           max_payload_size /* 19 */
+           max_payload_size, /* 19 */
+           do_openvpn        /* 20 */
           );
 
     if (do_fragment_http && http_fragment_size > 2 && !do_native_frag) {
@@ -1119,7 +1138,7 @@ int main(int argc, char *argv[]) {
                  */
                 else if (addr.Outbound &&
                         ((do_fragment_https ? packet_dataLen == https_fragment_size : 0) ||
-                         packet_dataLen > 16) &&
+                         packet_dataLen >= 16) &&
                          ppTcpHdr->DstPort != htons(80) &&
                          (do_fake_packet || do_native_frag)
                         )
@@ -1129,7 +1148,9 @@ int main(int argc, char *argv[]) {
                      * But if the packet is more than 2 bytes, check ClientHello byte.
                     */
                     if ((packet_dataLen == 2 && memcmp(packet_data, "\x16\x03", 2) == 0) ||
-                        (packet_dataLen >= 3 && memcmp(packet_data, "\x16\x03\x01", 3) == 0))
+                        (packet_dataLen >= 3 && memcmp(packet_data, "\x16\x03\x01", 3) == 0) ||
+                        (do_openvpn && (openvpn_handshake = is_openvpn_handshake(packet_data, packet_dataLen)))
+                       )
                     {
                         if (do_blacklist) {
                             sni_ok = extract_sni(packet_data, packet_dataLen,
@@ -1140,6 +1161,7 @@ int main(int argc, char *argv[]) {
                               blackwhitelist_check_hostname(host_addr, host_len)
                              ) ||
                              (do_blacklist && !sni_ok && do_allow_no_sni) ||
+                             (do_openvpn && openvpn_handshake) ||
                              (!do_blacklist)
                            )
                         {