diff --git a/tpws/params.h b/tpws/params.h index 13bc36a..793c30c 100644 --- a/tpws/params.h +++ b/tpws/params.h @@ -13,8 +13,8 @@ #define HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT 3 #define HOSTLIST_AUTO_FAIL_TIME_DEFAULT 60 -enum splithttpreq { split_none = 0, split_method, split_host }; -enum tlsrec { tlsrec_none = 0, tlsrec_sni, tlsrec_pos }; +enum httpreqpos { httpreqpos_none = 0, httpreqpos_method, httpreqpos_host }; +enum tlspos { tlspos_none = 0, tlspos_sni, tlspos_sniext, tlspos_pos }; enum bindll { unwanted=0, no, prefer, force }; #define MAX_BINDS 32 @@ -47,9 +47,10 @@ struct params_s bool hostcase, hostdot, hosttab, hostnospace, methodspace, methodeol, unixeol, domcase; int hostpad; char hostspell[4]; - enum splithttpreq split_http_req; - enum tlsrec tlsrec; + enum httpreqpos split_http_req; + enum tlspos tlsrec; int tlsrec_pos; + enum tlspos split_tls; bool split_any_protocol; int split_pos; bool disorder, disorder_http, disorder_tls; diff --git a/tpws/tamper.c b/tpws/tamper.c index 0f6a468..a483805 100644 --- a/tpws/tamper.c +++ b/tpws/tamper.c @@ -9,7 +9,7 @@ #include // pHost points to "Host: ..." -bool find_host(uint8_t **pHost,uint8_t *buf,size_t bs) +static bool find_host(uint8_t **pHost,uint8_t *buf,size_t bs) { if (!*pHost) { @@ -23,6 +23,24 @@ bool find_host(uint8_t **pHost,uint8_t *buf,size_t bs) return !!*pHost; } +static size_t tls_pos(enum tlspos tpos_type, size_t tpos_pos, const uint8_t *tls, size_t sz, uint8_t type) +{ + size_t elen; + const uint8_t *ext; + switch(tpos_type) + { + case tlspos_sni: + case tlspos_sniext: + if (TLSFindExt(tls,sz,0,&ext,&elen,false)) + return (tpos_type==tlspos_sni) ? ext-tls+6 : ext-tls+1; + break; + case tlspos_pos: + return tpos_pos; + } + return 0; +} + + static const char *http_methods[] = { "GET /","POST /","HEAD /","OPTIONS /","PUT /","DELETE /","CONNECT /","TRACE /",NULL }; // segment buffer has at least 5 extra bytes to extend data block void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,size_t *size, size_t *split_pos, uint8_t *split_flags) @@ -200,10 +218,10 @@ void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,si } switch (params.split_http_req) { - case split_method: + case httpreqpos_method: *split_pos = method_len - 1 + params.methodeol + (params.methodeol && !params.unixeol); break; - case split_host: + case httpreqpos_host: if (find_host(&pHost,segment,*size)) *split_pos = pHost + 6 - bRemovedHostSpace - segment; break; @@ -220,7 +238,7 @@ void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,si } else if (IsTLSClientHello(segment,*size,false)) { - size_t tpos=0,elen; + size_t tpos=0,spos=0; const uint8_t *ext; if (!ctrack->l7proto) ctrack->l7proto=TLS; @@ -239,39 +257,38 @@ void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,si } else { - switch(params.tlsrec) + spos = tls_pos(params.split_tls, params.split_pos, segment, *size, 0); + + if ((5+*size)<=segment_buffer_size) { - case tlsrec_sni: - if (TLSFindExt(segment,*size,0,&ext,&elen,false)) - tpos = ext-segment+1; // between typical 1st and 2nd char of hostname - break; - case tlsrec_pos: - tpos = params.tlsrec_pos; - break; - default: - break; - } - if (tpos && (5+*size)<=segment_buffer_size) - { - // construct 2 TLS records from one - uint16_t l = pntoh16(segment+3); // length - if (l>=2) + tpos = tls_pos(params.tlsrec, params.tlsrec_pos, segment, *size, 0); + if (tpos>5) { - // length is checked in IsTLSClientHello and cannot exceed buffer size - if (tpos>=l) tpos=1; - VPRINT("making 2 TLS records at pos %zu",tpos) - memmove(segment+5+tpos+5,segment+5+tpos,*size-(5+tpos)); - segment[5+tpos] = segment[0]; - segment[5+tpos+1] = segment[1]; - segment[5+tpos+2] = segment[2]; - phton16(segment+5+tpos+3,l-tpos); - phton16(segment+3,tpos); - *size += 5; + // construct 2 TLS records from one + uint16_t l = pntoh16(segment+3); // length + if (l>=2) + { + // length is checked in IsTLSClientHello and cannot exceed buffer size + if ((tpos-5)>=l) tpos=5+1; + VPRINT("making 2 TLS records at pos %zu",tpos) + memmove(segment+tpos+5,segment+tpos,*size-tpos); + segment[tpos] = segment[0]; + segment[tpos+1] = segment[1]; + segment[tpos+2] = segment[2]; + phton16(segment+tpos+3,l-(tpos-5)); + phton16(segment+3,tpos-5); + *size += 5; + // split pos present and it is not before tlsrec split. increase split pos by tlsrec header size (5 bytes) + if (spos && spos>=tpos) spos+=5; + } } } - - if (params.split_pos < *size) - *split_pos = params.split_pos; + + if (spos && spos < *size) + { + VPRINT("split pos %zu",spos); + *split_pos = spos; + } if (params.disorder_tls) *split_flags |= SPLIT_FLAG_DISORDER; if (params.oob_tls) *split_flags |= SPLIT_FLAG_OOB; diff --git a/tpws/tpws b/tpws/tpws new file mode 100644 index 0000000..9aa91f1 Binary files /dev/null and b/tpws/tpws differ diff --git a/tpws/tpws.c b/tpws/tpws.c index 22f17da..e672fc1 100644 --- a/tpws/tpws.c +++ b/tpws/tpws.c @@ -172,7 +172,8 @@ static void exithelp(void) " --hostlist-auto-debug=\t; debug auto hostlist positives\n" "\nTAMPER:\n" " --split-http-req=method|host\t\t; split at specified logical part of plain http request\n" - " --split-pos=\t\t; split at specified pos. split-http-req takes precedence for http.\n" + " --split-tls=sni|sniext\t\t\t; split at specified logical part of TLS ClientHello\n" + " --split-pos=\t\t; split at specified pos. split-http-req or split-tls take precedence for http.\n" " --split-any-protocol\t\t\t; split not only http and https\n" #if defined(BSD) && !defined(__APPLE__) " --disorder[=http|tls]\t\t\t; when splitting simulate sending second fragment first (BSD sends entire message instead of first fragment, this is not good)\n" @@ -191,7 +192,7 @@ static void exithelp(void) " --methodspace\t\t\t\t; add extra space after method\n" " --methodeol\t\t\t\t; add end-of-line before method\n" " --unixeol\t\t\t\t; replace 0D0A to 0A\n" - " --tlsrec=sni\t\t\t\t; make 2 TLS records. split at SNI. don't split if SNI is not present\n" + " --tlsrec=sni|sniext\t\t\t; make 2 TLS records. split at specified logical part. don't split if SNI is not present\n" " --tlsrec-pos=\t\t\t; make 2 TLS records. split at specified pos\n" #ifdef __linux__ " --mss=\t\t\t\t; set client MSS. forces server to split messages but significantly decreases speed !\n" @@ -253,6 +254,28 @@ void save_default_ttl(void) } } +bool parse_httpreqpos(const char *s, enum httpreqpos *pos) +{ + if (!strcmp(s, "method")) + *pos = httpreqpos_method; + else if (!strcmp(s, "host")) + *pos = httpreqpos_host; + else + return false; + return true; +} +bool parse_tlspos(const char *s, enum tlspos *pos) +{ + if (!strcmp(s, "sni")) + *pos = tlspos_sni; + else if (!strcmp(s, "sniext")) + *pos = tlspos_sniext; + else + return false; + return true; +} + + void parse_params(int argc, char *argv[]) { int option_index = 0; @@ -302,40 +325,41 @@ void parse_params(int argc, char *argv[]) { "hostpad",required_argument,0,0 },// optidx=21 { "domcase",no_argument,0,0 },// optidx=22 { "split-http-req",required_argument,0,0 },// optidx=23 - { "split-pos",required_argument,0,0 },// optidx=24 - { "split-any-protocol",optional_argument,0,0},// optidx=25 - { "disorder",optional_argument,0,0 },// optidx=26 - { "oob",optional_argument,0,0 },// optidx=27 - { "oob-data",required_argument,0,0 },// optidx=28 - { "methodspace",no_argument,0,0 },// optidx=29 - { "methodeol",no_argument,0,0 },// optidx=30 - { "hosttab",no_argument,0,0 },// optidx=31 - { "unixeol",no_argument,0,0 },// optidx=32 - { "tlsrec",required_argument,0,0 },// optidx=33 - { "tlsrec-pos",required_argument,0,0 },// optidx=34 - { "hostlist",required_argument,0,0 },// optidx=35 - { "hostlist-exclude",required_argument,0,0 },// optidx=36 - { "hostlist-auto",required_argument,0,0}, // optidx=37 - { "hostlist-auto-fail-threshold",required_argument,0,0}, // optidx=38 - { "hostlist-auto-fail-time",required_argument,0,0}, // optidx=39 - { "hostlist-auto-debug",required_argument,0,0}, // optidx=40 - { "pidfile",required_argument,0,0 },// optidx=41 - { "debug",optional_argument,0,0 },// optidx=42 - { "local-rcvbuf",required_argument,0,0 },// optidx=43 - { "local-sndbuf",required_argument,0,0 },// optidx=44 - { "remote-rcvbuf",required_argument,0,0 },// optidx=45 - { "remote-sndbuf",required_argument,0,0 },// optidx=46 - { "socks",no_argument,0,0 },// optidx=47 - { "no-resolve",no_argument,0,0 },// optidx=48 - { "resolver-threads",required_argument,0,0 },// optidx=49 - { "skip-nodelay",no_argument,0,0 },// optidx=50 - { "tamper-start",required_argument,0,0 },// optidx=51 - { "tamper-cutoff",required_argument,0,0 },// optidx=52 + { "split-tls",required_argument,0,0 },// optidx=24 + { "split-pos",required_argument,0,0 },// optidx=25 + { "split-any-protocol",optional_argument,0,0},// optidx=26 + { "disorder",optional_argument,0,0 },// optidx=27 + { "oob",optional_argument,0,0 },// optidx=28 + { "oob-data",required_argument,0,0 },// optidx=29 + { "methodspace",no_argument,0,0 },// optidx=30 + { "methodeol",no_argument,0,0 },// optidx=31 + { "hosttab",no_argument,0,0 },// optidx=32 + { "unixeol",no_argument,0,0 },// optidx=33 + { "tlsrec",required_argument,0,0 },// optidx=34 + { "tlsrec-pos",required_argument,0,0 },// optidx=35 + { "hostlist",required_argument,0,0 },// optidx=36 + { "hostlist-exclude",required_argument,0,0 },// optidx=37 + { "hostlist-auto",required_argument,0,0}, // optidx=38 + { "hostlist-auto-fail-threshold",required_argument,0,0}, // optidx=39 + { "hostlist-auto-fail-time",required_argument,0,0}, // optidx=40 + { "hostlist-auto-debug",required_argument,0,0}, // optidx=41 + { "pidfile",required_argument,0,0 },// optidx=42 + { "debug",optional_argument,0,0 },// optidx=43 + { "local-rcvbuf",required_argument,0,0 },// optidx=44 + { "local-sndbuf",required_argument,0,0 },// optidx=45 + { "remote-rcvbuf",required_argument,0,0 },// optidx=46 + { "remote-sndbuf",required_argument,0,0 },// optidx=47 + { "socks",no_argument,0,0 },// optidx=48 + { "no-resolve",no_argument,0,0 },// optidx=49 + { "resolver-threads",required_argument,0,0 },// optidx=50 + { "skip-nodelay",no_argument,0,0 },// optidx=51 + { "tamper-start",required_argument,0,0 },// optidx=52 + { "tamper-cutoff",required_argument,0,0 },// optidx=53 #if defined(BSD) && !defined(__OpenBSD__) && !defined(__APPLE__) - { "enable-pf",no_argument,0,0 },// optidx=53 + { "enable-pf",no_argument,0,0 },// optidx=54 #elif defined(__linux__) - { "mss",required_argument,0,0 },// optidx=53 - { "mss-pf",required_argument,0,0 },// optidx=54 + { "mss",required_argument,0,0 },// optidx=54 + { "mss-pf",required_argument,0,0 },// optidx=55 #ifdef SPLICE_PRESENT { "nosplice",no_argument,0,0 },// optidx=55 #endif @@ -498,18 +522,22 @@ void parse_params(int argc, char *argv[]) params.tamper = true; break; case 23: /* split-http-req */ - if (!strcmp(optarg, "method")) - params.split_http_req = split_method; - else if (!strcmp(optarg, "host")) - params.split_http_req = split_host; - else + if (!parse_httpreqpos(optarg, ¶ms.split_http_req)) { fprintf(stderr, "Invalid argument for split-http-req\n"); exit_clean(1); } params.tamper = true; break; - case 24: /* split-pos */ + case 24: /* split-tls */ + if (!parse_tlspos(optarg, ¶ms.split_tls)) + { + fprintf(stderr, "Invalid argument for split-tls\n"); + exit_clean(1); + } + params.tamper = true; + break; + case 25: /* split-pos */ i = atoi(optarg); if (i>0) params.split_pos = i; @@ -520,10 +548,10 @@ void parse_params(int argc, char *argv[]) } params.tamper = true; break; - case 25: /* split-any-protocol */ + case 26: /* split-any-protocol */ params.split_any_protocol = true; break; - case 26: /* disorder */ + case 27: /* disorder */ if (optarg) { if (!strcmp(optarg,"http")) params.disorder_http=true; @@ -538,7 +566,7 @@ void parse_params(int argc, char *argv[]) params.disorder = true; save_default_ttl(); break; - case 27: /* oob */ + case 28: /* oob */ if (optarg) { if (!strcmp(optarg,"http")) params.oob_http=true; @@ -552,7 +580,7 @@ void parse_params(int argc, char *argv[]) else params.oob = true; break; - case 28: /* oob-data */ + case 29: /* oob-data */ { size_t l = strlen(optarg); unsigned int bt; @@ -565,35 +593,33 @@ void parse_params(int argc, char *argv[]) else params.oob_byte = (uint8_t)bt; } break; - case 29: /* methodspace */ + case 30: /* methodspace */ params.methodspace = true; params.tamper = true; break; - case 30: /* methodeol */ + case 31: /* methodeol */ params.methodeol = true; params.tamper = true; break; - case 31: /* hosttab */ + case 32: /* hosttab */ params.hosttab = true; params.tamper = true; break; - case 32: /* unixeol */ + case 33: /* unixeol */ params.unixeol = true; params.tamper = true; break; - case 33: /* tlsrec */ - if (!strcmp(optarg, "sni")) - params.tlsrec = tlsrec_sni; - else + case 34: /* tlsrec */ + if (!parse_tlspos(optarg, ¶ms.tlsrec)) { fprintf(stderr, "Invalid argument for tlsrec\n"); exit_clean(1); } params.tamper = true; break; - case 34: /* tlsrec-pos */ + case 35: /* tlsrec-pos */ if ((params.tlsrec_pos = atoi(optarg))>0) - params.tlsrec = tlsrec_pos; + params.tlsrec = tlspos_pos; else { fprintf(stderr, "Invalid argument for tlsrec-pos\n"); @@ -601,7 +627,7 @@ void parse_params(int argc, char *argv[]) } params.tamper = true; break; - case 35: /* hostlist */ + case 36: /* hostlist */ if (!strlist_add(¶ms.hostlist_files, optarg)) { fprintf(stderr, "strlist_add failed\n"); @@ -609,7 +635,7 @@ void parse_params(int argc, char *argv[]) } params.tamper = true; break; - case 36: /* hostlist-exclude */ + case 37: /* hostlist-exclude */ if (!strlist_add(¶ms.hostlist_exclude_files, optarg)) { fprintf(stderr, "strlist_add failed\n"); @@ -617,7 +643,7 @@ void parse_params(int argc, char *argv[]) } params.tamper = true; break; - case 37: /* hostlist-auto */ + case 38: /* hostlist-auto */ if (*params.hostlist_auto_filename) { fprintf(stderr, "only one auto hostlist is supported\n"); @@ -649,7 +675,7 @@ void parse_params(int argc, char *argv[]) params.hostlist_auto_filename[sizeof(params.hostlist_auto_filename) - 1] = '\0'; params.tamper = true; // need to detect blocks and update autohostlist. cannot just slice. break; - case 38: /* hostlist-auto-fail-threshold */ + case 39: /* hostlist-auto-fail-threshold */ params.hostlist_auto_fail_threshold = (uint8_t)atoi(optarg); if (params.hostlist_auto_fail_threshold<1 || params.hostlist_auto_fail_threshold>20) { @@ -657,7 +683,7 @@ void parse_params(int argc, char *argv[]) exit_clean(1); } break; - case 39: /* hostlist-auto-fail-time */ + case 40: /* hostlist-auto-fail-time */ params.hostlist_auto_fail_time = (uint8_t)atoi(optarg); if (params.hostlist_auto_fail_time<1) { @@ -665,7 +691,7 @@ void parse_params(int argc, char *argv[]) exit_clean(1); } break; - case 40: /* hostlist-auto-debug */ + case 41: /* hostlist-auto-debug */ { FILE *F = fopen(optarg,"a+t"); if (!F) @@ -680,48 +706,48 @@ void parse_params(int argc, char *argv[]) params.hostlist_auto_debuglog[sizeof(params.hostlist_auto_debuglog) - 1] = '\0'; } break; - case 41: /* pidfile */ + case 42: /* pidfile */ strncpy(params.pidfile,optarg,sizeof(params.pidfile)); params.pidfile[sizeof(params.pidfile)-1]='\0'; break; - case 42: + case 43: params.debug = optarg ? atoi(optarg) : 1; break; - case 43: /* local-rcvbuf */ + case 44: /* local-rcvbuf */ #ifdef __linux__ params.local_rcvbuf = atoi(optarg)/2; #else params.local_rcvbuf = atoi(optarg); #endif break; - case 44: /* local-sndbuf */ + case 45: /* local-sndbuf */ #ifdef __linux__ params.local_sndbuf = atoi(optarg)/2; #else params.local_sndbuf = atoi(optarg); #endif break; - case 45: /* remote-rcvbuf */ + case 46: /* remote-rcvbuf */ #ifdef __linux__ params.remote_rcvbuf = atoi(optarg)/2; #else params.remote_rcvbuf = atoi(optarg); #endif break; - case 46: /* remote-sndbuf */ + case 47: /* remote-sndbuf */ #ifdef __linux__ params.remote_sndbuf = atoi(optarg)/2; #else params.remote_sndbuf = atoi(optarg); #endif break; - case 47: /* socks */ + case 48: /* socks */ params.proxy_type = CONN_TYPE_SOCKS; break; - case 48: /* no-resolve */ + case 49: /* no-resolve */ params.no_resolve = true; break; - case 49: /* resolver-threads */ + case 50: /* resolver-threads */ params.resolver_threads = atoi(optarg); if (params.resolver_threads<1 || params.resolver_threads>300) { @@ -729,10 +755,10 @@ void parse_params(int argc, char *argv[]) exit_clean(1); } break; - case 50: /* skip-nodelay */ + case 51: /* skip-nodelay */ params.skip_nodelay = true; break; - case 51: /* tamper-start */ + case 52: /* tamper-start */ { const char *p=optarg; if (*p=='n') @@ -745,7 +771,7 @@ void parse_params(int argc, char *argv[]) params.tamper_start = atoi(p); } break; - case 52: /* tamper-cutoff */ + case 53: /* tamper-cutoff */ { const char *p=optarg; if (*p=='n') @@ -759,11 +785,11 @@ void parse_params(int argc, char *argv[]) } break; #if defined(BSD) && !defined(__OpenBSD__) && !defined(__APPLE__) - case 53: /* enable-pf */ + case 54: /* enable-pf */ params.pf_enable = true; break; #elif defined(__linux__) - case 53: /* mss */ + case 54: /* mss */ // this option does not work in any BSD and MacOS. OS may accept but it changes nothing params.mss = atoi(optarg); if (params.mss<88 || params.mss>32767) @@ -772,7 +798,7 @@ void parse_params(int argc, char *argv[]) exit_clean(1); } break; - case 54: /* mss-pf */ + case 55: /* mss-pf */ if (!pf_parse(optarg,¶ms.mss_pf)) { fprintf(stderr, "Invalid MSS port filter.\n"); @@ -780,7 +806,7 @@ void parse_params(int argc, char *argv[]) } break; #ifdef SPLICE_PRESENT - case 55: /* nosplice */ + case 56: /* nosplice */ params.nosplice = true; break; #endif @@ -802,6 +828,7 @@ void parse_params(int argc, char *argv[]) exit_clean(1); } if (!params.resolver_threads) params.resolver_threads = 5 + params.maxconn/50; + if (params.split_tls==tlspos_none && params.split_pos) params.split_tls=tlspos_pos; if (*params.hostlist_auto_filename) params.hostlist_auto_mod_time = file_mod_time(params.hostlist_auto_filename); if (!LoadIncludeHostLists())