diff --git a/config.default b/config.default index 5c66a57..fb4cb71 100644 --- a/config.default +++ b/config.default @@ -55,7 +55,7 @@ TPPORT_SOCKS=987 # appends ipset/zapret-hosts-auto.txt as normal list TPWS_SOCKS_OPT=" --filter-tcp=80 --methodeol --new ---filter-tcp=443 --split-tls=sni --disorder +--filter-tcp=443 --split-pos=1,midsld --disorder " TPWS_ENABLE=0 @@ -65,7 +65,7 @@ TPWS_PORTS=80,443 # appends ipset/zapret-hosts-auto.txt as normal list TPWS_OPT=" --filter-tcp=80 --methodeol --new ---filter-tcp=443 --split-tls=sni --disorder +--filter-tcp=443 --split-pos=1,midsld --disorder " NFQWS_ENABLE=0 @@ -89,8 +89,8 @@ NFQWS_UDP_PKT_IN=0 # hostlist markers are replaced to empty string if MODE_FILTER does not satisfy # appends ipset/zapret-hosts-auto.txt as normal list NFQWS_OPT=" ---filter-tcp=80 --dpi-desync=fake,split2 --dpi-desync-fooling=md5sig --new ---filter-tcp=443 --dpi-desync=fake,disorder2 --dpi-desync-fooling=md5sig --new +--filter-tcp=80 --dpi-desync=fake,multisplit --dpi-desync-split-pos=method+2 --dpi-desync-fooling=md5sig --new +--filter-tcp=443 --dpi-desync=fake,multidisorder --dpi-desync-split-pos=1,midsld --dpi-desync-fooling=badseq,md5sig --new --filter-udp=443 --dpi-desync=fake --dpi-desync-repeats=6 " diff --git a/init.d/sysv/functions b/init.d/sysv/functions index 2ba59cd..0897607 100644 --- a/init.d/sysv/functions +++ b/init.d/sysv/functions @@ -169,20 +169,16 @@ run_daemon() local DAEMONBASE="$(basename "$2")" local PIDFILE=$PIDDIR/$DAEMONBASE$1.pid echo "Starting daemon $1: $2 $3" - if exists start-stop-daemon ; then - start-stop-daemon -S -p "$PIDFILE" -m -b -x "$2" -- $3 + if [ -f "$PIDFILE" ] && pgrep -F "$PIDFILE" "$DAEMONBASE" >/dev/null; then + echo already running else - if [ -f "$PIDFILE" ] && pgrep -F "$PIDFILE" "$DAEMONBASE" >/dev/null; then - echo already running + "$2" $3 >/dev/null & + PID=$! + if [ -n "$PID" ]; then + echo $PID >$PIDFILE else - "$2" $3 >/dev/null 2>/dev/null & - PID=$! - if [ -n "$PID" ]; then - echo $PID >$PIDFILE - else - echo could not start daemon $1 : $2 $3 - false - fi + echo could not start daemon $1 : $2 $3 + false fi fi } diff --git a/nfq/Makefile b/nfq/Makefile index e2a1779..8e4c058 100644 --- a/nfq/Makefile +++ b/nfq/Makefile @@ -5,7 +5,7 @@ CFLAGS_MAC = -mmacosx-version-min=10.8 CFLAGS_CYGWIN = -Wno-address-of-packed-member -static LIBS_LINUX = -lnetfilter_queue -lnfnetlink -lz LIBS_BSD = -lz -LIBS_CYGWIN = -lz -Lwindows/windivert -Iwindows -lwlanapi -lole32 -loleaut32 -luuid +LIBS_CYGWIN = -lz -Lwindows/windivert -Iwindows -lwlanapi -lole32 -loleaut32 LIBS_CYGWIN32 = -lwindivert32 LIBS_CYGWIN64 = -lwindivert64 RES_CYGWIN32 = windows/res/32/winmanifest.o windows/res/32/winicon.o diff --git a/nfq/darkmagic.c b/nfq/darkmagic.c index 8457c3c..5a583b0 100644 --- a/nfq/darkmagic.c +++ b/nfq/darkmagic.c @@ -1871,3 +1871,34 @@ void verdict_udp_csum_fix(uint8_t verdict, struct udphdr *udphdr, size_t transpo udp_fix_checksum(udphdr,transport_len,ip,ip6hdr); } } + +void dbgprint_socket_buffers(int fd) +{ + if (params.debug) + { + int v; + socklen_t sz; + sz = sizeof(int); + if (!getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &v, &sz)) + DLOG("fd=%d SO_RCVBUF=%d\n", fd, v); + sz = sizeof(int); + if (!getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &v, &sz)) + DLOG("fd=%d SO_SNDBUF=%d\n", fd, v); + } +} +bool set_socket_buffers(int fd, int rcvbuf, int sndbuf) +{ + DLOG("set_socket_buffers fd=%d rcvbuf=%d sndbuf=%d\n", fd, rcvbuf, sndbuf); + if (rcvbuf && setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(int)) < 0) + { + DLOG_PERROR("setsockopt (SO_RCVBUF)"); + return false; + } + if (sndbuf && setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(int)) < 0) + { + DLOG_PERROR("setsockopt (SO_SNDBUF)"); + return false; + } + dbgprint_socket_buffers(fd); + return true; +} diff --git a/nfq/darkmagic.h b/nfq/darkmagic.h index 58f0718..1426682 100644 --- a/nfq/darkmagic.h +++ b/nfq/darkmagic.h @@ -22,6 +22,7 @@ #endif #ifdef __CYGWIN__ +#define INITGUID #include "windivert/windivert.h" #endif @@ -247,3 +248,6 @@ void do_nat(bool bOutbound, struct ip *ip, struct ip6_hdr *ip6, struct tcphdr *t void verdict_tcp_csum_fix(uint8_t verdict, struct tcphdr *tcphdr, size_t transport_len, struct ip *ip, struct ip6_hdr *ip6hdr); void verdict_udp_csum_fix(uint8_t verdict, struct udphdr *udphdr, size_t transport_len, struct ip *ip, struct ip6_hdr *ip6hdr); + +void dbgprint_socket_buffers(int fd); +bool set_socket_buffers(int fd, int rcvbuf, int sndbuf); diff --git a/nfq/desync.c b/nfq/desync.c index 53aba77..a46071d 100644 --- a/nfq/desync.c +++ b/nfq/desync.c @@ -97,11 +97,11 @@ bool desync_only_first_stage(enum dpi_desync_mode mode) } bool desync_valid_second_stage(enum dpi_desync_mode mode) { - return mode==DESYNC_NONE || mode==DESYNC_FAKEDDISORDER || mode==DESYNC_DISORDER2 || mode==DESYNC_FAKEDSPLIT || mode==DESYNC_SPLIT2 || mode==DESYNC_MULTISPLIT || mode==DESYNC_MULTIDISORDER || mode==DESYNC_IPFRAG2 || mode==DESYNC_UDPLEN || mode==DESYNC_TAMPER; + return mode==DESYNC_NONE || mode==DESYNC_FAKEDDISORDER || mode==DESYNC_FAKEDSPLIT || mode==DESYNC_MULTISPLIT || mode==DESYNC_MULTIDISORDER || mode==DESYNC_IPFRAG2 || mode==DESYNC_UDPLEN || mode==DESYNC_TAMPER; } bool desync_valid_second_stage_tcp(enum dpi_desync_mode mode) { - return mode==DESYNC_NONE || mode==DESYNC_FAKEDDISORDER || mode==DESYNC_DISORDER2 || mode==DESYNC_FAKEDSPLIT || mode==DESYNC_SPLIT2 || mode==DESYNC_MULTISPLIT || mode==DESYNC_MULTIDISORDER || mode==DESYNC_IPFRAG2; + return mode==DESYNC_NONE || mode==DESYNC_FAKEDDISORDER || mode==DESYNC_FAKEDSPLIT || mode==DESYNC_MULTISPLIT || mode==DESYNC_MULTIDISORDER || mode==DESYNC_IPFRAG2; } bool desync_valid_second_stage_udp(enum dpi_desync_mode mode) { @@ -125,15 +125,11 @@ enum dpi_desync_mode desync_mode_from_string(const char *s) return DESYNC_SYNDATA; else if (!strcmp(s,"fakeddisorder") || !strcmp(s,"disorder")) return DESYNC_FAKEDDISORDER; - else if (!strcmp(s,"disorder2")) - return DESYNC_DISORDER2; else if (!strcmp(s,"fakedsplit") || !strcmp(s,"split")) return DESYNC_FAKEDSPLIT; - else if (!strcmp(s,"split2")) - return DESYNC_SPLIT2; - else if (!strcmp(s,"multisplit")) + else if (!strcmp(s,"multisplit") || !strcmp(s,"split2")) return DESYNC_MULTISPLIT; - else if (!strcmp(s,"multidisorder")) + else if (!strcmp(s,"multidisorder") || !strcmp(s,"disorder2")) return DESYNC_MULTIDISORDER; else if (!strcmp(s,"ipfrag2")) return DESYNC_IPFRAG2; @@ -844,7 +840,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint uint8_t *p, *phost=NULL; const uint8_t *rdata_payload = dis->data_payload; size_t rlen_payload = dis->len_payload; - size_t split_pos; + size_t split_pos, seqovl_pos; size_t multisplit_pos[MAX_SPLITS]; int multisplit_count; int i; @@ -1118,23 +1114,19 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint DLOG("dpi desync src=%s dst=%s\n",s1,s2); } - const struct proto_pos *spos; switch(l7proto) { case HTTP: fake = dp->fake_http; fake_size = dp->fake_http_size; - spos = &dp->split_http; break; case TLS: fake = dp->fake_tls; fake_size = dp->fake_tls_size; - spos = &dp->split_tls; break; default: fake = dp->fake_unknown; fake_size = dp->fake_unknown_size; - spos = &dp->split_unknown; break; } if (dp->desync_mode==DESYNC_MULTISPLIT || dp->desync_mode==DESYNC_MULTIDISORDER || dp->desync_mode2==DESYNC_MULTISPLIT || dp->desync_mode2==DESYNC_MULTIDISORDER) @@ -1173,13 +1165,21 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint DLOG("all multisplit pos are outside of this packet\n"); } } + seqovl_pos = ResolvePos(rdata_payload, rlen_payload, l7proto, &dp->seqovl); } - else if (dp->desync_mode==DESYNC_FAKEDSPLIT || dp->desync_mode==DESYNC_SPLIT2 || dp->desync_mode==DESYNC_FAKEDDISORDER || dp->desync_mode==DESYNC_DISORDER2 || - dp->desync_mode2==DESYNC_FAKEDSPLIT || dp->desync_mode2==DESYNC_SPLIT2 || dp->desync_mode2==DESYNC_FAKEDDISORDER || dp->desync_mode2==DESYNC_DISORDER2) + else if (dp->desync_mode==DESYNC_FAKEDSPLIT || dp->desync_mode==DESYNC_FAKEDDISORDER || dp->desync_mode2==DESYNC_FAKEDSPLIT || dp->desync_mode2==DESYNC_FAKEDDISORDER) { multisplit_count=0; - split_pos = ResolvePos(rdata_payload, rlen_payload, l7proto, spos); - if (!split_pos) split_pos = dp->split_unknown.pos; + // first look for non-abs split + for(i=0,split_pos=0;isplit_count && !split_pos;i++) + if (dp->splits[i].marker!=PM_ABS) + split_pos = ResolvePos(rdata_payload, rlen_payload, l7proto, dp->splits+i); + // second look for abs split + if (!split_pos) + for(i=0,split_pos=0;isplit_count && !split_pos;i++) + if (dp->splits[i].marker==PM_ABS) + split_pos = ResolvePos(rdata_payload, rlen_payload, l7proto, dp->splits+i); + if (!split_pos) split_pos = 1; DLOG("regular split pos: %zu\n",split_pos); if (!split_pos || split_pos>rlen_payload) split_pos=1; split_pos=pos_normalize(split_pos,reasm_offset,dis->len_payload); @@ -1187,12 +1187,15 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint DLOG("normalized regular split pos : %zu\n",split_pos); else DLOG("regular split pos is outside of this packet\n"); + seqovl_pos = ResolvePos(rdata_payload, rlen_payload, l7proto, &dp->seqovl); } else { multisplit_count=0; - split_pos = 0; + split_pos = seqovl_pos = 0; } + seqovl_pos = pos_normalize(seqovl_pos,reasm_offset,dis->len_payload); + if (seqovl_pos) DLOG("normalized seqovl pos : %zu\n",seqovl_pos); // we do not need reasm buffer anymore reasm_orig_cancel(ctrack); @@ -1241,7 +1244,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint case DESYNC_IPFRAG1: fooling_orig = (dp->desync_mode==DESYNC_HOPBYHOP) ? FOOL_HOPBYHOP : (dp->desync_mode==DESYNC_DESTOPT) ? FOOL_DESTOPT : FOOL_IPFRAG1; if (dis->ip6 && (dp->desync_mode2==DESYNC_NONE || !desync_valid_second_stage_tcp(dp->desync_mode2) || - (!split_pos && (dp->desync_mode2==DESYNC_FAKEDSPLIT || dp->desync_mode2==DESYNC_SPLIT2 || dp->desync_mode2==DESYNC_FAKEDDISORDER || dp->desync_mode2==DESYNC_DISORDER2)) || + (!split_pos && (dp->desync_mode2==DESYNC_FAKEDSPLIT || dp->desync_mode2==DESYNC_FAKEDDISORDER)) || (!multisplit_count && (dp->desync_mode2==DESYNC_MULTISPLIT || dp->desync_mode2==DESYNC_MULTIDISORDER)))) { if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, dis->tcp->th_seq, dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps, @@ -1284,7 +1287,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint // do seqovl only to the first packet // otherwise it's prone to race condition on server side // what happens first : server pushes socket buffer to process or another packet with seqovl arrives - seqovl = i==0 ? dp->desync_seqovl : 0; + seqovl = i==0 ? seqovl_pos : 0; #ifdef __linux__ // only linux return error if MTU is exceeded for(;;seqovl=0) @@ -1352,13 +1355,16 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint seg = dis->data_payload+from; seg_len = to-from; seqovl = 0; - if (i>=0 && dp->desync_seqovl) + // do seqovl only to the second packet + // otherwise sequence overlap becomes problematic. overlap algorithm is not too obvious. + // real observations revealed that server can receive overlap junk instead of real data + if (i==0) { - if (dp->desync_seqovl>=from) - DLOG("seqovl>=split_pos (%u>=%zu). cancelling seqovl for part %d.\n",dp->desync_seqovl,from,i+2); + if (seqovl_pos>=from) + DLOG("seqovl>=split_pos (%u>=%zu). cancelling seqovl for part %d.\n",seqovl,from,i+2); else { - seqovl = dp->desync_seqovl; + seqovl = seqovl_pos; seg_len = to-from+seqovl; if (seg_len>sizeof(ovlseg)) { @@ -1389,30 +1395,32 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint } break; case DESYNC_FAKEDDISORDER: - case DESYNC_DISORDER2: if (split_pos) { uint8_t fakeseg[DPI_DESYNC_MAX_FAKE_LEN+100], *seg; size_t seg_len; + unsigned int seqovl; - if (dp->desync_seqovl>=split_pos) + if (seqovl_pos>=split_pos) { - DLOG("seqovl>=split_pos. desync is not possible.\n"); - return verdict; + DLOG("seqovl>=split_pos (%u>=%zu). cancelling seqovl.\n",seqovl_pos,split_pos); + seqovl = 0; } + else + seqovl = seqovl_pos; if (split_poslen_payload) { - if (dp->desync_seqovl) + if (seqovl) { - seg_len = dis->len_payload-split_pos+dp->desync_seqovl; + seg_len = dis->len_payload-split_pos+seqovl; if (seg_len>sizeof(fakeseg)) { DLOG("seqovl is too large\n"); return verdict; } - fill_pattern(fakeseg,dp->desync_seqovl,dp->seqovl_pattern,sizeof(dp->seqovl_pattern)); - memcpy(fakeseg+dp->desync_seqovl,dis->data_payload+split_pos,dis->len_payload-split_pos); + fill_pattern(fakeseg,seqovl,dp->seqovl_pattern,sizeof(dp->seqovl_pattern)); + memcpy(fakeseg+seqovl,dis->data_payload+split_pos,dis->len_payload-split_pos); seg = fakeseg; } else @@ -1422,31 +1430,27 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint } pkt1_len = sizeof(pkt1); - if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, net32_add(dis->tcp->th_seq , split_pos - dp->desync_seqovl), dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps, + if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, net32_add(dis->tcp->th_seq , split_pos - seqovl), dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps, ttl_orig,IP4_TOS(dis->ip),IP6_FLOW(dis->ip6), fooling_orig,dp->desync_badseq_increment,dp->desync_badseq_ack_increment, seg, seg_len, pkt1, &pkt1_len)) return verdict; - DLOG("sending 2nd out-of-order tcp segment %zu-%zu len=%zu seqovl=%u : ",split_pos,dis->len_payload-1, dis->len_payload-split_pos, dp->desync_seqovl); + DLOG("sending 2nd out-of-order tcp segment %zu-%zu len=%zu seqovl=%u : ",split_pos,dis->len_payload-1, dis->len_payload-split_pos, seqovl); hexdump_limited_dlog(seg,seg_len,PKTDATA_MAXDUMP); DLOG("\n"); if (!rawsend((struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len)) return verdict; } - - if (desync_mode==DESYNC_FAKEDDISORDER) - { - seg_len = sizeof(fakeseg); - if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, dis->tcp->th_seq, dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps, - ttl_fake,IP4_TOS(dis->ip),IP6_FLOW(dis->ip6), - dp->desync_fooling_mode,dp->desync_badseq_increment,dp->desync_badseq_ack_increment, - zeropkt, split_pos, fakeseg, &seg_len)) - return verdict; - DLOG("sending fake(1) 1st out-of-order tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos); - hexdump_limited_dlog(zeropkt,split_pos,PKTDATA_MAXDUMP); DLOG("\n"); - if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , fakeseg, seg_len)) - return verdict; - } + seg_len = sizeof(fakeseg); + if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, dis->tcp->th_seq, dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps, + ttl_fake,IP4_TOS(dis->ip),IP6_FLOW(dis->ip6), + dp->desync_fooling_mode,dp->desync_badseq_increment,dp->desync_badseq_ack_increment, + zeropkt, split_pos, fakeseg, &seg_len)) + return verdict; + DLOG("sending fake(1) 1st out-of-order tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos); + hexdump_limited_dlog(zeropkt,split_pos,PKTDATA_MAXDUMP); DLOG("\n"); + if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , fakeseg, seg_len)) + return verdict; pkt1_len = sizeof(pkt1); if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, dis->tcp->th_seq, dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps, @@ -1459,39 +1463,32 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint if (!rawsend((struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len)) return verdict; - if (desync_mode==DESYNC_FAKEDDISORDER) - { - DLOG("sending fake(2) 1st out-of-order tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos); - hexdump_limited_dlog(zeropkt,split_pos,PKTDATA_MAXDUMP); DLOG("\n"); - if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , fakeseg, seg_len)) - return verdict; - } + DLOG("sending fake(2) 1st out-of-order tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos); + hexdump_limited_dlog(zeropkt,split_pos,PKTDATA_MAXDUMP); DLOG("\n"); + if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , fakeseg, seg_len)) + return verdict; return VERDICT_DROP; } break; case DESYNC_FAKEDSPLIT: - case DESYNC_SPLIT2: if (split_pos) { uint8_t fakeseg[DPI_DESYNC_MAX_FAKE_LEN+100],ovlseg[DPI_DESYNC_MAX_FAKE_LEN+100], *seg; size_t fakeseg_len,seg_len; - if (desync_mode==DESYNC_FAKEDSPLIT) - { - fakeseg_len = sizeof(fakeseg); - if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, dis->tcp->th_seq, dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps, - ttl_fake,IP4_TOS(dis->ip),IP6_FLOW(dis->ip6), - dp->desync_fooling_mode,dp->desync_badseq_increment,dp->desync_badseq_ack_increment, - zeropkt, split_pos, fakeseg, &fakeseg_len)) - return verdict; - DLOG("sending fake(1) 1st tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos); - hexdump_limited_dlog(zeropkt,split_pos,PKTDATA_MAXDUMP); DLOG("\n"); - if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , fakeseg, fakeseg_len)) - return verdict; - } + fakeseg_len = sizeof(fakeseg); + if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, dis->tcp->th_seq, dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps, + ttl_fake,IP4_TOS(dis->ip),IP6_FLOW(dis->ip6), + dp->desync_fooling_mode,dp->desync_badseq_increment,dp->desync_badseq_ack_increment, + zeropkt, split_pos, fakeseg, &fakeseg_len)) + return verdict; + DLOG("sending fake(1) 1st tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos); + hexdump_limited_dlog(zeropkt,split_pos,PKTDATA_MAXDUMP); DLOG("\n"); + if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , fakeseg, fakeseg_len)) + return verdict; - unsigned int seqovl = dp->desync_seqovl; + unsigned int seqovl = seqovl_pos; #ifdef __linux__ // only linux return error if MTU is exceeded for(;;seqovl=0) @@ -1539,13 +1536,11 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint } #endif - if (desync_mode==DESYNC_FAKEDSPLIT) - { - DLOG("sending fake(2) 1st tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos); - hexdump_limited_dlog(zeropkt,split_pos,PKTDATA_MAXDUMP); DLOG("\n"); - if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , fakeseg, fakeseg_len)) - return verdict; - } + DLOG("sending fake(2) 1st tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos); + hexdump_limited_dlog(zeropkt,split_pos,PKTDATA_MAXDUMP); DLOG("\n"); + if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , fakeseg, fakeseg_len)) + return verdict; + if (split_poslen_payload) { pkt1_len = sizeof(pkt1); @@ -1738,7 +1733,6 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint { const uint8_t *fake; size_t fake_size; - bool b; char host[256]; bool bHaveHost=false; @@ -1990,7 +1984,6 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint ttl_fake = (ctrack_replay && ctrack_replay->autottl) ? ctrack_replay->autottl : (dis->ip6 ? (dp->desync_ttl6 ? dp->desync_ttl6 : ttl_orig) : (dp->desync_ttl ? dp->desync_ttl : ttl_orig)); - enum dpi_desync_mode desync_mode = dp->desync_mode; uint32_t fooling_orig = FOOL_NONE; if (params.debug) @@ -2001,15 +1994,14 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint DLOG("dpi desync src=%s dst=%s\n",s1,s2); } + bool bFake = false; pkt1_len = sizeof(pkt1); - b = false; - switch(desync_mode) + switch(dp->desync_mode) { case DESYNC_FAKE_KNOWN: if (l7proto==UNKNOWN) { DLOG("not applying fake because of unknown protocol\n"); - desync_mode = dp->desync_mode2; break; } case DESYNC_FAKE: @@ -2019,12 +2011,12 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint hexdump_limited_dlog(fake,fake_size,PKTDATA_MAXDUMP); DLOG("\n"); if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len)) return verdict; - b = true; + bFake = true; break; case DESYNC_HOPBYHOP: case DESYNC_DESTOPT: case DESYNC_IPFRAG1: - fooling_orig = (desync_mode==DESYNC_HOPBYHOP) ? FOOL_HOPBYHOP : (desync_mode==DESYNC_DESTOPT) ? FOOL_DESTOPT : FOOL_IPFRAG1; + fooling_orig = (dp->desync_mode==DESYNC_HOPBYHOP) ? FOOL_HOPBYHOP : (dp->desync_mode==DESYNC_DESTOPT) ? FOOL_DESTOPT : FOOL_IPFRAG1; if (dis->ip6 && (dp->desync_mode2==DESYNC_NONE || !desync_valid_second_stage_udp(dp->desync_mode2))) { if (!prepare_udp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, @@ -2040,26 +2032,13 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint // this mode is final, no other options available return ct_new_postnat_fix_udp(ctrack, dis->ip, dis->ip6, dis->udp, &dis->len_pkt); } - desync_mode = dp->desync_mode2; break; default: pkt1_len=0; break; } - if (b) - { - if (dp->desync_mode2==DESYNC_NONE || !desync_valid_second_stage_udp(dp->desync_mode2)) - { - DLOG("reinjecting original packet. len=%zu len_payload=%zu\n", dis->len_pkt, dis->len_payload); - verdict_udp_csum_fix(verdict, dis->udp, dis->transport_len, dis->ip, dis->ip6); - if (!rawsend((struct sockaddr *)&dst, desync_fwmark, ifout , dis->data_pkt, dis->len_pkt)) - return verdict; - return ct_new_postnat_fix_udp(ctrack, dis->ip, dis->ip6, dis->udp, &dis->len_pkt); - } - desync_mode = dp->desync_mode2; - } - + enum dpi_desync_mode desync_mode = dp->desync_mode2==DESYNC_NONE ? dp->desync_mode : dp->desync_mode2; switch(desync_mode) { case DESYNC_UDPLEN: @@ -2067,7 +2046,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint if (!prepare_udp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, ttl_orig,IP4_TOS(dis->ip),IP6_FLOW(dis->ip6), fooling_orig, dp->udplen_pattern, sizeof(dp->udplen_pattern), dp->udplen_increment, dis->data_payload, dis->len_payload, pkt1, &pkt1_len)) { DLOG("could not construct packet with modified length. too large ?\n"); - return verdict; + break; } DLOG("resending original packet with increased by %d length\n", dp->udplen_increment); if (!rawsend((struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len)) @@ -2084,7 +2063,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint if (szcopy>szbuf) { DLOG("packet is too long to tamper"); - return verdict; + break; } memcpy(pkt2+pkt2_len,dis->data_payload+1,szcopy); pkt2_len+=szcopy; @@ -2092,7 +2071,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint if (!prepare_udp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, ttl_orig,IP4_TOS(dis->ip),IP6_FLOW(dis->ip6), fooling_orig, NULL, 0 , 0, pkt2, pkt2_len, pkt1, &pkt1_len)) { DLOG("could not construct packet with modified length. too large ?\n"); - return verdict; + break; } DLOG("resending tampered DHT\n"); if (!rawsend((struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len)) @@ -2102,7 +2081,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint else { DLOG("payload is not tamperable\n"); - return verdict; + break; } case DESYNC_IPFRAG2: { @@ -2150,6 +2129,16 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint break; } + if (bFake) + { + // if we are here original message was not sent in any form + // allowing system to pass the message to queue can result in unpredicted send order + DLOG("reinjecting original packet. len=%zu len_payload=%zu\n", dis->len_pkt, dis->len_payload); + verdict_udp_csum_fix(verdict, dis->udp, dis->transport_len, dis->ip, dis->ip6); + if (!rawsend((struct sockaddr *)&dst, desync_fwmark, ifout , dis->data_pkt, dis->len_pkt)) + return verdict; + return ct_new_postnat_fix_udp(ctrack, dis->ip, dis->ip6, dis->udp, &dis->len_pkt); + } } return verdict; @@ -2178,7 +2167,7 @@ static void packet_debug(bool replay, const struct dissect *dis) char s[80]; str_tcphdr(s,sizeof(s),dis->tcp); DLOG(" %s\n",s); - if (dis->len_payload) { DLOG("TCP: len=%zu : ",dis->len_payload); hexdump_limited_dlog(dis->data_payload, dis->len_payload, 32); DLOG("\n"); } + if (dis->len_payload) { DLOG("TCP: len=%zu : ",dis->len_payload); hexdump_limited_dlog(dis->data_payload, dis->len_payload, PKTDATA_MAXDUMP); DLOG("\n"); } } else if (dis->udp) @@ -2186,7 +2175,7 @@ static void packet_debug(bool replay, const struct dissect *dis) char s[30]; str_udphdr(s,sizeof(s),dis->udp); DLOG(" %s\n",s); - if (dis->len_payload) { DLOG("UDP: len=%zu : ",dis->len_payload); hexdump_limited_dlog(dis->data_payload, dis->len_payload, 32); DLOG("\n"); } + if (dis->len_payload) { DLOG("UDP: len=%zu : ",dis->len_payload); hexdump_limited_dlog(dis->data_payload, dis->len_payload, PKTDATA_MAXDUMP); DLOG("\n"); } } else DLOG("\n"); diff --git a/nfq/desync.h b/nfq/desync.h index 90be218..ad3aaf0 100644 --- a/nfq/desync.h +++ b/nfq/desync.h @@ -28,8 +28,6 @@ enum dpi_desync_mode { DESYNC_RSTACK, DESYNC_SYNACK, DESYNC_SYNDATA, - DESYNC_SPLIT2, - DESYNC_DISORDER2, DESYNC_FAKEDSPLIT, DESYNC_FAKEDDISORDER, DESYNC_MULTISPLIT, diff --git a/nfq/helpers.c b/nfq/helpers.c index 90c0452..aba018c 100644 --- a/nfq/helpers.c +++ b/nfq/helpers.c @@ -5,12 +5,11 @@ #include #include #include +#include #include #include #include -#include "params.h" - int unique_size_t(size_t *pu, int ct) { int i, j, u; @@ -64,22 +63,6 @@ char *strncasestr(const char *s, const char *find, size_t slen) return (char *)s; } -void hexdump_limited_dlog(const uint8_t *data, size_t size, size_t limit) -{ - size_t k; - bool bcut = false; - if (size > limit) - { - size = limit; - bcut = true; - } - if (!size) return; - for (k = 0; k < size; k++) DLOG("%02X ", data[k]); - DLOG(bcut ? "... : " : ": "); - for (k = 0; k < size; k++) DLOG("%c", data[k] >= 0x20 && data[k] <= 0x7F ? (char)data[k] : '.'); - if (bcut) DLOG(" ..."); -} - bool load_file(const char *filename, void *buffer, size_t *buffer_size) { @@ -220,38 +203,6 @@ uint16_t saport(const struct sockaddr *sa) sa->sa_family==AF_INET6 ? ((struct sockaddr_in6*)sa)->sin6_port : 0); } -void dbgprint_socket_buffers(int fd) -{ - if (params.debug) - { - int v; - socklen_t sz; - sz = sizeof(int); - if (!getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &v, &sz)) - DLOG("fd=%d SO_RCVBUF=%d\n", fd, v); - sz = sizeof(int); - if (!getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &v, &sz)) - DLOG("fd=%d SO_SNDBUF=%d\n", fd, v); - } -} -bool set_socket_buffers(int fd, int rcvbuf, int sndbuf) -{ - DLOG("set_socket_buffers fd=%d rcvbuf=%d sndbuf=%d\n", fd, rcvbuf, sndbuf); - if (rcvbuf && setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(int)) < 0) - { - DLOG_PERROR("setsockopt (SO_RCVBUF)"); - close(fd); - return false; - } - if (sndbuf && setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(int)) < 0) - { - DLOG_PERROR("setsockopt (SO_SNDBUF)"); - close(fd); - return false; - } - dbgprint_socket_buffers(fd); - return true; -} uint64_t pntoh64(const void *p) { @@ -416,14 +367,14 @@ void fill_random_az09(uint8_t *p,size_t sz) } } -bool cd_to_exe_dir(const char *argv0) +bool set_env_exedir(const char *argv0) { char *s,*d; bool bOK=false; if ((s = strdup(argv0))) { if ((d = dirname(s))) - bOK = !chdir(d); + setenv("EXEDIR",s,1); free(s); } return bOK; diff --git a/nfq/helpers.h b/nfq/helpers.h index 58051a7..756bab6 100644 --- a/nfq/helpers.h +++ b/nfq/helpers.h @@ -24,7 +24,6 @@ void rtrim(char *s); void replace_char(char *s, char from, char to); char *strncasestr(const char *s,const char *find, size_t slen); -void hexdump_limited_dlog(const uint8_t *data, size_t size, size_t limit); bool load_file(const char *filename,void *buffer,size_t *buffer_size); bool load_file_nonempty(const char *filename,void *buffer,size_t *buffer_size); bool save_file(const char *filename, const void *buffer, size_t buffer_size); @@ -40,9 +39,6 @@ uint16_t saport(const struct sockaddr *sa); bool seq_within(uint32_t s, uint32_t s1, uint32_t s2); -void dbgprint_socket_buffers(int fd); -bool set_socket_buffers(int fd, int rcvbuf, int sndbuf); - uint64_t pntoh64(const void *p); void phton64(uint8_t *p, uint64_t v); @@ -79,7 +75,7 @@ void fill_random_bytes(uint8_t *p,size_t sz); void fill_random_az(uint8_t *p,size_t sz); void fill_random_az09(uint8_t *p,size_t sz); -bool cd_to_exe_dir(const char *argv0); +bool set_env_exedir(const char *argv0); struct cidr4 diff --git a/nfq/nfqws.c b/nfq/nfqws.c index 7307231..f8b74a4 100644 --- a/nfq/nfqws.c +++ b/nfq/nfqws.c @@ -752,12 +752,12 @@ static bool parse_tlspos(const char *s, struct proto_pos *sp) else if (!strcmp(s, "sniext")) { sp->marker = PM_SNI_EXT; - sp->pos=0; + sp->pos=1; } else if (!strcmp(s, "snisld")) { sp->marker = PM_HOST_MIDSLD; - sp->pos=1; + sp->pos=0; } else return false; @@ -838,39 +838,14 @@ static bool parse_split_pos_list(char *opt, struct proto_pos *splits, int splits } static void split_compat(struct desync_profile *dp) { - // make it mostly compatible with old versions - int i; - dp->split_unknown.marker=PM_ABS; - dp->split_unknown.pos=2; - for (i=0;isplit_count;i++) + if (!dp->split_count) { - if (dp->splits[i].marker==PM_ABS) - { - dp->split_unknown.pos=dp->splits[i].pos; - break; - } - } - if (PROTO_POS_EMPTY(&dp->split_http)) - { - dp->split_http=dp->split_unknown; - for (i=0;isplit_count;i++) - if (IsHostMarker(dp->splits[i].marker) || dp->splits[i].marker==PM_HTTP_METHOD) - { - dp->split_http = dp->splits[i]; - break; - } - } - if (PROTO_POS_EMPTY(&dp->split_tls)) - { - dp->split_tls=dp->split_unknown; - for (i=0;isplit_count;i++) - if (IsHostMarker(dp->splits[i].marker) || dp->splits[i].marker==PM_SNI_EXT) - { - dp->split_tls = dp->splits[i]; - break; - } + dp->splits[dp->split_count].marker = PM_ABS; + dp->splits[dp->split_count].pos = 2; + dp->split_count++; } } + static void SplitDebug(void) { struct desync_profile_list *dpl; @@ -878,11 +853,9 @@ static void SplitDebug(void) LIST_FOREACH(dpl, ¶ms.desync_profiles, next) { dp = &dpl->dp; - DLOG("profile %d split_http %s %d\n",dp->n,posmarker_name(dp->split_http.marker),dp->split_http.pos); - DLOG("profile %d split_tls %s %d\n",dp->n,posmarker_name(dp->split_tls.marker),dp->split_tls.pos); - DLOG("profile %d split_unknown %s %d\n",dp->n,posmarker_name(dp->split_unknown.marker),dp->split_unknown.pos); for(int x=0;xsplit_count;x++) DLOG("profile %d multisplit %s %d\n",dp->n,posmarker_name(dp->splits[x].marker),dp->splits[x].pos); + if (!PROTO_POS_EMPTY(&dp->seqovl)) DLOG("profile %d seqovl %s %d\n",dp->n,posmarker_name(dp->seqovl.marker),dp->seqovl.pos); } } @@ -1066,7 +1039,7 @@ static void exithelp(void) " --domcase\t\t\t\t\t; mix domain case : Host: TeSt.cOm\n" " --dpi-desync=[,][,]\t; try to desync dpi state. modes :\n" "\t\t\t\t\t\t; synack syndata fake fakeknown rst rstack hopbyhop destopt ipfrag1\n" - "\t\t\t\t\t\t; disorder2 split2 multisplit multidisorder fakedsplit fakeddisorder ipfrag2 udplen tamper\n" + "\t\t\t\t\t\t; multisplit multidisorder fakedsplit fakeddisorder ipfrag2 udplen tamper\n" #ifdef __linux__ " --dpi-desync-fwmark=\t\t; override fwmark for desync packet. default = 0x%08X (%u)\n" #elif defined(SO_USER_COOKIE) @@ -1078,11 +1051,12 @@ static void exithelp(void) " --dpi-desync-autottl6=[[:[-]]] ; overrides --dpi-desync-autottl for ipv6 only\n" " --dpi-desync-fooling=[,]\t\t; can use multiple comma separated values. modes : none md5sig ts badseq badsum datanoack hopbyhop hopbyhop2\n" " --dpi-desync-repeats=\t\t\t; send every desync packet N times\n" - " --dpi-desync-skip-nosni=0|1\t\t\t; 1(default)=do not act on ClientHello without SNI (ESNI ?)\n" - " --dpi-desync-split-pos=N|-N|marker+N|marker-N\t; comma separated list of split positions. markers: method,host,endhost,sld,endsld,midsld,sniext\n" + " --dpi-desync-skip-nosni=0|1\t\t\t; 1(default)=do not act on ClientHello without SNI\n" + " --dpi-desync-split-pos=N|-N|marker+N|marker-N\t; comma separated list of split positions\n" + "\t\t\t\t\t\t; markers: method,host,endhost,sld,endsld,midsld,sniext\n" "\t\t\t\t\t\t; full list is only used by multisplit and multidisorder\n" - "\t\t\t\t\t\t; single split takes first l7-protocol-compatible parameter if present, first abs value otherwise\n" - " --dpi-desync-split-seqovl=\t\t; use sequence overlap before first sent original split segment\n" + "\t\t\t\t\t\t; fakedsplit/fakeddisorder use first l7-protocol-compatible parameter if present, first abs value otherwise\n" + " --dpi-desync-split-seqovl=N|-N|marker+N|marker-N ; use sequence overlap before first sent original split segment\n" " --dpi-desync-split-seqovl-pattern=|0xHEX ; pattern for the fake part of overlap\n" " --dpi-desync-ipfrag-pos-tcp=<8..%u>\t\t; ip frag position starting from the transport header. multiple of 8, default %u.\n" " --dpi-desync-ipfrag-pos-udp=<8..%u>\t\t; ip frag position starting from the transport header. multiple of 8, default %u.\n" @@ -1149,6 +1123,8 @@ void config_from_file(const char *filename) int main(int argc, char **argv) { + set_env_exedir(argv[0]); + #ifdef __CYGWIN__ if (service_run(argc, argv)) { @@ -1332,10 +1308,6 @@ int main(int argc, char **argv) fprintf(stderr, "cannot create %s\n", params.debug_logfile); exit_clean(1); } -#ifndef __CYGWIN__ - if (params.droproot && chown(params.debug_logfile, params.uid, -1)) - fprintf(stderr, "could not chown %s. log file may not be writable after privilege drop\n", params.debug_logfile); -#endif params.debug = true; params.debug_target = LOG_TARGET_FILE; } @@ -1589,24 +1561,38 @@ int main(int argc, char **argv) break; case 24: /* dpi-desync-split-http-req */ // obsolete arg - if (!parse_httpreqpos(optarg, &dp->split_http)) + DLOG_CONDUP("WARNING ! --dpi-desync-split-http-req is deprecated. use --dpi-desync-split-pos with markers.\n",MAX_SPLITS); + if (dp->split_count>=MAX_SPLITS) + { + DLOG_ERR("Too much splits. max splits: %u\n",MAX_SPLITS); + exit_clean(1); + } + if (!parse_httpreqpos(optarg, dp->splits + dp->split_count)) { DLOG_ERR("Invalid argument for dpi-desync-split-http-req\n"); exit_clean(1); } + dp->split_count++; break; case 25: /* dpi-desync-split-tls */ // obsolete arg - if (!parse_tlspos(optarg, &dp->split_tls)) + DLOG_CONDUP("WARNING ! --dpi-desync-split-tls is deprecated. use --dpi-desync-split-pos with markers.\n",MAX_SPLITS); + if (dp->split_count>=MAX_SPLITS) + { + DLOG_ERR("Too much splits. max splits: %u\n",MAX_SPLITS); + exit_clean(1); + } + if (!parse_tlspos(optarg, dp->splits + dp->split_count)) { DLOG_ERR("Invalid argument for dpi-desync-split-tls\n"); exit_clean(1); } + dp->split_count++; break; case 26: /* dpi-desync-split-seqovl */ - if (sscanf(optarg,"%u",&dp->desync_seqovl)<1) + if (!parse_split_pos(optarg, &dp->seqovl)) { - DLOG_ERR("dpi-desync-split-seqovl is not valid\n"); + DLOG_ERR("Invalid argument for dpi-desync-split-seqovl\n"); exit_clean(1); } break; @@ -1754,10 +1740,6 @@ int main(int argc, char **argv) DLOG_ERR("gzipped auto hostlists are not supported\n"); exit_clean(1); } -#ifndef __CYGWIN__ - if (params.droproot && chown(optarg, params.uid, -1)) - DLOG_ERR("could not chown %s. auto hostlist file may not be writable after privilege drop\n", optarg); -#endif } if (!(dp->hostlist_auto=RegisterHostlist(dp, false, optarg))) { @@ -1798,10 +1780,6 @@ int main(int argc, char **argv) exit_clean(1); } fclose(F); -#ifndef __CYGWIN__ - if (params.droproot && chown(optarg, params.uid, -1)) - DLOG_ERR("could not chown %s. auto hostlist debug log may not be writable after privilege drop\n", optarg); -#endif strncpy(params.hostlist_auto_debuglog, optarg, sizeof(params.hostlist_auto_debuglog)); params.hostlist_auto_debuglog[sizeof(params.hostlist_auto_debuglog) - 1] = '\0'; } @@ -2045,16 +2023,15 @@ int main(int argc, char **argv) DLOG_CONDUP("we have %d user defined desync profile(s) and default low priority profile 0\n",desync_profile_count); +#ifndef __CYGWIN__ + if (params.debug_target == LOG_TARGET_FILE && params.droproot && chown(params.debug_logfile, params.uid, -1)) + fprintf(stderr, "could not chown %s. log file may not be writable after privilege drop\n", params.debug_logfile); + if (params.droproot && *params.hostlist_auto_debuglog && chown(params.hostlist_auto_debuglog, params.uid, -1)) + DLOG_ERR("could not chown %s. auto hostlist debug log may not be writable after privilege drop\n", params.hostlist_auto_debuglog); +#endif LIST_FOREACH(dpl, ¶ms.desync_profiles, next) { dp = &dpl->dp; - - if (!dp->split_count && (dp->desync_mode==DESYNC_MULTISPLIT || dp->desync_mode==DESYNC_MULTIDISORDER || dp->desync_mode2==DESYNC_MULTISPLIT || dp->desync_mode2==DESYNC_MULTIDISORDER)) - { - DLOG_ERR("multisplit requires explicit split pos\n"); - exit_clean(1); - } - // not specified - use desync_ttl value instead if (dp->desync_ttl6 == 0xFF) dp->desync_ttl6=dp->desync_ttl; if (!AUTOTTL_ENABLED(dp->desync_autottl6)) dp->desync_autottl6 = dp->desync_autottl; @@ -2063,6 +2040,10 @@ int main(int argc, char **argv) if (AUTOTTL_ENABLED(dp->desync_autottl6)) DLOG("[profile %d] autottl ipv6 %u:%u-%u\n",dp->n,dp->desync_autottl6.delta,dp->desync_autottl6.min,dp->desync_autottl6.max); split_compat(dp); +#ifndef __CYGWIN__ + if (params.droproot && dp->hostlist_auto && chown(dp->hostlist_auto->filename, params.uid, -1)) + DLOG_ERR("could not chown %s. auto hostlist file may not be writable after privilege drop\n", dp->hostlist_auto->filename); +#endif } if (!LoadAllHostLists()) diff --git a/nfq/params.c b/nfq/params.c index 8a7702c..585d906 100644 --- a/nfq/params.c +++ b/nfq/params.c @@ -154,6 +154,21 @@ int HOSTLIST_DEBUGLOG_APPEND(const char *format, ...) return 0; } +void hexdump_limited_dlog(const uint8_t *data, size_t size, size_t limit) +{ + size_t k; + bool bcut = false; + if (size > limit) + { + size = limit; + bcut = true; + } + if (!size) return; + for (k = 0; k < size; k++) DLOG("%02X ", data[k]); + DLOG(bcut ? "... : " : ": "); + for (k = 0; k < size; k++) DLOG("%c", data[k] >= 0x20 && data[k] <= 0x7F ? (char)data[k] : '.'); + if (bcut) DLOG(" ..."); +} struct desync_profile_list *dp_list_add(struct desync_profile_list_head *head) { diff --git a/nfq/params.h b/nfq/params.h index 8f5151d..630d20d 100644 --- a/nfq/params.h +++ b/nfq/params.h @@ -55,13 +55,12 @@ struct desync_profile char hostspell[4]; enum dpi_desync_mode desync_mode0,desync_mode,desync_mode2; bool desync_retrans,desync_skip_nosni,desync_any_proto; - unsigned int desync_repeats,desync_seqovl,desync_ipfrag_pos_tcp,desync_ipfrag_pos_udp; + unsigned int desync_repeats,desync_ipfrag_pos_tcp,desync_ipfrag_pos_udp; // multisplit struct proto_pos splits[MAX_SPLITS]; int split_count; - // single split pos cache - struct proto_pos split_http,split_tls,split_unknown; + struct proto_pos seqovl; char desync_start_mode, desync_cutoff_mode; // n - packets, d - data packets, s - relative sequence unsigned int desync_start, desync_cutoff; @@ -150,3 +149,4 @@ int DLOG_ERR(const char *format, ...); int DLOG_PERROR(const char *s); int DLOG_CONDUP(const char *format, ...); int HOSTLIST_DEBUGLOG_APPEND(const char *format, ...); +void hexdump_limited_dlog(const uint8_t *data, size_t size, size_t limit); diff --git a/nfq/protocol.c b/nfq/protocol.c index fd280f4..62f5f0e 100644 --- a/nfq/protocol.c +++ b/nfq/protocol.c @@ -511,8 +511,7 @@ size_t TLSPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz) { if (posmarker==PM_SNI_EXT) { - offset = ext-data+1+pos; - return (offset>=0 && offset #include +#include #include #include #include @@ -10,6 +10,30 @@ #include #include #include +#include + +#include "helpers.h" + +int unique_size_t(size_t *pu, int ct) +{ + int i, j, u; + for (i = j = 0; j < ct; i++) + { + u = pu[j++]; + for (; j < ct && pu[j] == u; j++); + pu[i] = u; + } + return i; +} +static int cmp_size_t(const void * a, const void * b) +{ + return *(size_t*)a < *(size_t*)b ? -1 : *(size_t*)a > *(size_t*)b; +} +void qsort_size_t(size_t *array,size_t ct) +{ + qsort(array,ct,sizeof(*array),cmp_size_t); +} + void rtrim(char *s) { @@ -326,6 +350,20 @@ bool pf_is_empty(const port_filter *pf) } +bool set_env_exedir(const char *argv0) +{ + char *s,*d; + bool bOK=false; + if ((s = strdup(argv0))) + { + if ((d = dirname(s))) + setenv("EXEDIR",s,1); + free(s); + } + return bOK; +} + + static void mask_from_preflen6_make(uint8_t plen, struct in6_addr *a) { if (plen >= 128) diff --git a/tpws/helpers.h b/tpws/helpers.h index 9d7d35c..ed84f5b 100644 --- a/tpws/helpers.h +++ b/tpws/helpers.h @@ -15,6 +15,9 @@ typedef union struct sockaddr_in6 sa6; // size 28 } sockaddr_in46; +int unique_size_t(size_t *pu, int ct); +void qsort_size_t(size_t *array,size_t ct); + void rtrim(char *s); void replace_char(char *s, char from, char to); char *strncasestr(const char *s,const char *find, size_t slen); @@ -70,6 +73,8 @@ bool pf_in_range(uint16_t port, const port_filter *pf); bool pf_parse(const char *s, port_filter *pf); bool pf_is_empty(const port_filter *pf); +bool set_env_exedir(const char *argv0); + #ifndef IN_LOOPBACK #define IN_LOOPBACK(a) ((((uint32_t) (a)) & 0xff000000) == 0x7f000000) #endif diff --git a/tpws/params.c b/tpws/params.c index 2aca979..ae57c6d 100644 --- a/tpws/params.c +++ b/tpws/params.c @@ -139,6 +139,22 @@ int HOSTLIST_DEBUGLOG_APPEND(const char *format, ...) return 0; } +void hexdump_limited_dlog(const uint8_t *data, size_t size, size_t limit) +{ + size_t k; + bool bcut = false; + if (size > limit) + { + size = limit; + bcut = true; + } + if (!size) return; + for (k = 0; k < size; k++) VPRINT("%02X ", data[k]); + VPRINT(bcut ? "... : " : ": "); + for (k = 0; k < size; k++) VPRINT("%c", data[k] >= 0x20 && data[k] <= 0x7F ? (char)data[k] : '.'); + if (bcut) VPRINT(" ..."); +} + struct desync_profile_list *dp_list_add(struct desync_profile_list_head *head) { diff --git a/tpws/params.h b/tpws/params.h index d0104dc..3afa694 100644 --- a/tpws/params.h +++ b/tpws/params.h @@ -29,6 +29,8 @@ struct bind_s int bind_wait_ifup,bind_wait_ip,bind_wait_ip_ll; }; +#define MAX_SPLITS 16 + enum log_target { LOG_TARGET_CONSOLE=0, LOG_TARGET_FILE, LOG_TARGET_SYSLOG }; struct desync_profile @@ -38,16 +40,16 @@ struct desync_profile bool hostcase, hostdot, hosttab, hostnospace, methodspace, methodeol, unixeol, domcase; int hostpad; char hostspell[4]; - 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; bool oob, oob_http, oob_tls; uint8_t oob_byte; + // multisplit + struct proto_pos splits[MAX_SPLITS]; + int split_count; + struct proto_pos tlsrec; + int mss; bool tamper_start_n,tamper_cutoff_n; @@ -140,6 +142,7 @@ int DLOG_CONDUP(const char *format, ...); int DLOG_ERR(const char *format, ...); int DLOG_PERROR(const char *s); int HOSTLIST_DEBUGLOG_APPEND(const char *format, ...); +void hexdump_limited_dlog(const uint8_t *data, size_t size, size_t limit); #define VPRINT(format, ...) DLOG(format, 1, ##__VA_ARGS__) #define DBGPRINT(format, ...) DLOG(format, 2, ##__VA_ARGS__) diff --git a/tpws/protocol.c b/tpws/protocol.c index 139ff21..edd2b5a 100644 --- a/tpws/protocol.c +++ b/tpws/protocol.c @@ -24,6 +24,133 @@ static bool FindNLD(const uint8_t *dom, size_t dlen, int level, const uint8_t ** return true; } +const char *l7proto_str(t_l7proto l7) +{ + switch(l7) + { + case HTTP: return "http"; + case TLS: return "tls"; + case QUIC: return "quic"; + case WIREGUARD: return "wireguard"; + case DHT: return "dht"; + default: return "unknown"; + } +} +bool l7_proto_match(t_l7proto l7proto, uint32_t filter_l7) +{ + return (l7proto==UNKNOWN && (filter_l7 & L7_PROTO_UNKNOWN)) || + (l7proto==HTTP && (filter_l7 & L7_PROTO_HTTP)) || + (l7proto==TLS && (filter_l7 & L7_PROTO_TLS)) || + (l7proto==QUIC && (filter_l7 & L7_PROTO_QUIC)) || + (l7proto==WIREGUARD && (filter_l7 & L7_PROTO_WIREGUARD)) || + (l7proto==DHT && (filter_l7 & L7_PROTO_DHT)); +} + +#define PM_ABS 0 +#define PM_HOST 1 +#define PM_HOST_END 2 +#define PM_HOST_SLD 3 +#define PM_HOST_MIDSLD 4 +#define PM_HOST_ENDSLD 5 +#define PM_HTTP_METHOD 6 +#define PM_SNI_EXT 7 +bool IsHostMarker(uint8_t posmarker) +{ + switch(posmarker) + { + case PM_HOST: + case PM_HOST_END: + case PM_HOST_SLD: + case PM_HOST_MIDSLD: + case PM_HOST_ENDSLD: + return true; + default: + return false; + } +} +const char *posmarker_name(uint8_t posmarker) +{ + switch(posmarker) + { + case PM_ABS: return "abs"; + case PM_HOST: return "host"; + case PM_HOST_END: return "endhost"; + case PM_HOST_SLD: return "sld"; + case PM_HOST_MIDSLD: return "midsld"; + case PM_HOST_ENDSLD: return "endsld"; + case PM_HTTP_METHOD: return "method"; + case PM_SNI_EXT: return "sniext"; + default: return "?"; + } +} + +static size_t CheckPos(size_t sz, ssize_t offset) +{ + return (offset>=0 && offsetmarker, sp->pos, data, sz); + case TLS: + return TLSPos(sp->marker, sp->pos, data, sz); + default: + return AnyProtoPos(sp->marker, sp->pos, data, sz); + } +} +void ResolveMultiPos(const uint8_t *data, size_t sz, t_l7proto l7proto, const struct proto_pos *splits, int split_count, size_t *pos, int *pos_count) +{ + int i,j; + for(i=j=0;i='A' && *method<='Z') method++; - if (i<3 || *method!=' ') break; - return method-http-1; - case httpreqpos_host: - if (HttpFindHostConst(&host,http,sz) && (host-http+7)='A' && *p<='Z') p++; + if (i<3 || *p!=' ') break; + return CheckPos(sz,method-data+pos); + case PM_HOST: + case PM_HOST_END: + case PM_HOST_SLD: + case PM_HOST_MIDSLD: + case PM_HOST_ENDSLD: + if (HttpFindHostConst(&host,data,sz) && (host-data+7) #include +typedef enum {UNKNOWN=0, HTTP, TLS, QUIC, WIREGUARD, DHT} t_l7proto; +#define L7_PROTO_HTTP 0x00000001 +#define L7_PROTO_TLS 0x00000002 +#define L7_PROTO_QUIC 0x00000004 +#define L7_PROTO_WIREGUARD 0x00000008 +#define L7_PROTO_DHT 0x00000010 +#define L7_PROTO_UNKNOWN 0x80000000 +const char *l7proto_str(t_l7proto l7); +bool l7_proto_match(t_l7proto l7proto, uint32_t filter_l7); + +// pos markers +#define PM_ABS 0 +#define PM_HOST 1 +#define PM_HOST_END 2 +#define PM_HOST_SLD 3 +#define PM_HOST_MIDSLD 4 +#define PM_HOST_ENDSLD 5 +#define PM_HTTP_METHOD 6 +#define PM_SNI_EXT 7 +struct proto_pos +{ + int16_t pos; + uint8_t marker; +}; +#define PROTO_POS_EMPTY(sp) ((sp)->marker==PM_ABS && (sp)->pos==0) +bool IsHostMarker(uint8_t posmarker); +const char *posmarker_name(uint8_t posmarker); +size_t AnyProtoPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz); +size_t HttpPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz); +size_t TLSPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz); +size_t ResolvePos(const uint8_t *data, size_t sz, t_l7proto l7proto, const struct proto_pos *sp); +void ResolveMultiPos(const uint8_t *data, size_t sz, t_l7proto l7proto, const struct proto_pos *splits, int split_count, size_t *pos, int *pos_count); + + extern const char *http_methods[9]; const char *HttpMethod(const uint8_t *data, size_t len); bool IsHttp(const uint8_t *data, size_t len); @@ -18,8 +52,6 @@ const char *HttpFind2ndLevelDomain(const char *host); int HttpReplyCode(const uint8_t *data, size_t len); // must be pre-checked by IsHttpReply bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *host); -enum httpreqpos { httpreqpos_none = 0, httpreqpos_method, httpreqpos_host, httpreqpos_pos }; -size_t HttpPos(enum httpreqpos tpos_type, size_t hpos_pos, const uint8_t *http, size_t sz); uint16_t TLSRecordDataLen(const uint8_t *data); size_t TLSRecordLen(const uint8_t *data); @@ -29,5 +61,3 @@ bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t ** bool TLSFindExtInHandshake(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext, bool bPartialIsOK); bool TLSHelloExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host, bool bPartialIsOK); bool TLSHelloExtractHostFromHandshake(const uint8_t *data, size_t len, char *host, size_t len_host, bool bPartialIsOK); -enum tlspos { tlspos_none = 0, tlspos_sni, tlspos_sniext, tlspos_snisld, tlspos_pos }; -size_t TLSPos(enum tlspos tpos_type, size_t tpos_pos, const uint8_t *tls, size_t sz, uint8_t type); diff --git a/tpws/resolver.c b/tpws/resolver.c index 6fb25bb..1653756 100644 --- a/tpws/resolver.c +++ b/tpws/resolver.c @@ -221,7 +221,7 @@ bool resolver_init(int threads, int fd_signal_pipe) if (pthread_attr_init(&attr)) goto ex; // set minimum thread stack size - if (pthread_attr_setstacksize(&attr,PTHREAD_STACK_MIN>20480 ? PTHREAD_STACK_MIN : 20480)) + if (pthread_attr_setstacksize(&attr,PTHREAD_STACK_MIN>32768 ? PTHREAD_STACK_MIN : 32768)) { pthread_attr_destroy(&attr); goto ex; diff --git a/tpws/tamper.c b/tpws/tamper.c index a4cf434..553488e 100644 --- a/tpws/tamper.c +++ b/tpws/tamper.c @@ -8,20 +8,11 @@ #include "protocol.h" #include "helpers.h" -const char *l7proto_str(t_l7proto l7) +#define PKTDATA_MAXDUMP 32 + +void packet_debug(const uint8_t *data, size_t sz) { - switch(l7) - { - case HTTP: return "http"; - case TLS: return "tls"; - default: return "unknown"; - } -} -static bool l7_proto_match(t_l7proto l7proto, uint32_t filter_l7) -{ - return (l7proto==UNKNOWN && (filter_l7 & L7_PROTO_UNKNOWN)) || - (l7proto==HTTP && (filter_l7 & L7_PROTO_HTTP)) || - (l7proto==TLS && (filter_l7 & L7_PROTO_TLS)); + hexdump_limited_dlog(data, sz, PKTDATA_MAXDUMP); VPRINT("\n"); } static bool dp_match(struct desync_profile *dp, const struct sockaddr *dest, const char *hostname, t_l7proto l7proto) @@ -87,11 +78,10 @@ void apply_desync_profile(t_ctrack *ctrack, const struct sockaddr *dest) // segment buffer has at least 5 extra bytes to extend data block -void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment,size_t segment_buffer_size,size_t *size, size_t *split_pos, uint8_t *split_flags) +void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment,size_t segment_buffer_size,size_t *size, size_t *multisplit_pos, int *multisplit_count, uint8_t *split_flags) { uint8_t *p, *pp, *pHost = NULL; - size_t method_len = 0, pos; - size_t tpos, spos; + size_t method_len = 0, pos, tpos, orig_size=*size; const char *method; bool bHaveHost = false; char *pc, Host[256]; @@ -116,8 +106,8 @@ void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment, return; } - *split_pos=0; - *split_flags=0; + if (multisplit_count) *multisplit_count=0; + if (split_flags) *split_flags=0; if ((method = HttpMethod(segment,*size))) { @@ -193,7 +183,6 @@ void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment, return; } } - switch(l7proto) { case HTTP: @@ -325,22 +314,27 @@ void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment, pHost = NULL; // invalidate } } - *split_pos = HttpPos(ctrack->dp->split_http_req, ctrack->dp->split_pos, segment, *size); - if (ctrack->dp->disorder_http) *split_flags |= SPLIT_FLAG_DISORDER; - if (ctrack->dp->oob_http) *split_flags |= SPLIT_FLAG_OOB; + if (multisplit_pos) ResolveMultiPos(segment, *size, l7proto, ctrack->dp->splits, ctrack->dp->split_count, multisplit_pos, multisplit_count); + if (split_flags) + { + if (ctrack->dp->disorder_http) *split_flags |= SPLIT_FLAG_DISORDER; + if (ctrack->dp->oob_http) *split_flags |= SPLIT_FLAG_OOB; + } break; case TLS: - spos = TLSPos(ctrack->dp->split_tls, ctrack->dp->split_pos, segment, *size, 0); + if (multisplit_pos) ResolveMultiPos(segment, *size, l7proto, ctrack->dp->splits, ctrack->dp->split_count, multisplit_pos, multisplit_count); if ((5+*size)<=segment_buffer_size) { - tpos = TLSPos(ctrack->dp->tlsrec, ctrack->dp->tlsrec_pos+5, segment, *size, 0); + tpos = ResolvePos(segment, *size, l7proto, &ctrack->dp->tlsrec); if (tpos>5) { // construct 2 TLS records from one uint16_t l = pntoh16(segment+3); // length if (l>=2) { + int i; + size_t dlen; // 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\n",tpos); @@ -351,27 +345,45 @@ void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment, 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; + VPRINT("-2nd TLS record: "); + dlen = tpos<16 ? tpos : 16; + packet_debug(segment+tpos-dlen,dlen); + VPRINT("+2nd TLS record: "); + packet_debug(segment+tpos,*size-tpos); + // fix split positions after tlsrec. increase split pos by tlsrec header size (5 bytes) + if (multisplit_pos) + for(i=0;i<*multisplit_count;i++) + if (multisplit_pos[i]>tpos) multisplit_pos[i]+=5; } } } - - if (spos && spos < *size) - *split_pos = spos; - - if (ctrack->dp->disorder_tls) *split_flags |= SPLIT_FLAG_DISORDER; - if (ctrack->dp->oob_tls) *split_flags |= SPLIT_FLAG_OOB; - + if (split_flags) + { + if (ctrack->dp->disorder_tls) *split_flags |= SPLIT_FLAG_DISORDER; + if (ctrack->dp->oob_tls) *split_flags |= SPLIT_FLAG_OOB; + } break; default: - if (ctrack->dp->split_any_protocol && ctrack->dp->split_pos < *size) - *split_pos = ctrack->dp->split_pos; + if (multisplit_pos && ctrack->dp->split_any_protocol) + ResolveMultiPos(segment, *size, l7proto, ctrack->dp->splits, ctrack->dp->split_count, multisplit_pos, multisplit_count); + } + + if (split_flags) + { + if (ctrack->dp->disorder) *split_flags |= SPLIT_FLAG_DISORDER; + if (ctrack->dp->oob) *split_flags |= SPLIT_FLAG_OOB; + } + if (orig_size!=*size) + { + VPRINT("segment size changed: %zu -> %zu\n", orig_size, *size); + } + if (params.debug && multisplit_count && *multisplit_count) + { + VPRINT("multisplit pos: "); + for (int i=0;i<*multisplit_count;i++) VPRINT("%zu ",multisplit_pos[i]); + VPRINT("\n"); } - - if (ctrack->dp->disorder) *split_flags |= SPLIT_FLAG_DISORDER; - if (ctrack->dp->oob) *split_flags |= SPLIT_FLAG_OOB; } static void auto_hostlist_reset_fail_counter(struct desync_profile *dp, const char *hostname, const char *client_ip_port, t_l7proto l7proto) diff --git a/tpws/tamper.h b/tpws/tamper.h index 0cafee7..65eed6f 100644 --- a/tpws/tamper.h +++ b/tpws/tamper.h @@ -9,12 +9,6 @@ #define SPLIT_FLAG_DISORDER 0x01 #define SPLIT_FLAG_OOB 0x02 -typedef enum {UNKNOWN=0, HTTP, TLS} t_l7proto; -#define L7_PROTO_HTTP 1 -#define L7_PROTO_TLS 2 -#define L7_PROTO_UNKNOWN 0x80000000 -const char *l7proto_str(t_l7proto l7); - typedef struct { // common state @@ -28,9 +22,11 @@ typedef struct void apply_desync_profile(t_ctrack *ctrack, const struct sockaddr *dest); -void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment,size_t segment_buffer_size,size_t *size, size_t *split_pos, uint8_t *split_flags); +void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment,size_t segment_buffer_size,size_t *size, size_t *multisplit_pos, int *multisplit_count, uint8_t *split_flags); void tamper_in(t_ctrack *ctrack, const struct sockaddr *client, uint8_t *segment,size_t segment_buffer_size,size_t *size); // connection reset by remote leg void rst_in(t_ctrack *ctrack, const struct sockaddr *client); // local leg closed connection (timeout waiting response ?) void hup_out(t_ctrack *ctrack, const struct sockaddr *client); + +void packet_debug(const uint8_t *data, size_t sz); diff --git a/tpws/tpws.c b/tpws/tpws.c index ba9031c..8136da3 100644 --- a/tpws/tpws.c +++ b/tpws/tpws.c @@ -182,9 +182,8 @@ static void exithelp(void) " --hostlist-auto-fail-time=\t; all failed attemps must be within these seconds (default : %d)\n" " --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-tls=sni|sniext|snisld\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-pos=N|-N|marker+N|marker-N\t; comma separated list of split positions\n" + "\t\t\t\t\t; markers: method,host,endhost,sld,endsld,midsld,sniext\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" @@ -203,8 +202,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|sniext|snisld\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" + " --tlsrec=N|-N|marker+N|marker-N\t; make 2 TLS records. split records at specified position.\n" #ifdef __linux__ " --mss=\t\t\t\t; set client MSS. forces server to split messages but significantly decreases speed !\n" #endif @@ -276,29 +274,131 @@ void save_default_ttl(void) } } -bool parse_httpreqpos(const char *s, enum httpreqpos *pos) +static bool parse_httpreqpos(const char *s, struct proto_pos *sp) { if (!strcmp(s, "method")) - *pos = httpreqpos_method; + { + sp->marker = PM_HTTP_METHOD; + sp->pos=2; + } else if (!strcmp(s, "host")) - *pos = httpreqpos_host; + { + sp->marker = PM_HOST; + sp->pos=1; + } else return false; return true; } -bool parse_tlspos(const char *s, enum tlspos *pos) +static bool parse_tlspos(const char *s, struct proto_pos *sp) { if (!strcmp(s, "sni")) - *pos = tlspos_sni; + { + sp->marker = PM_HOST; + sp->pos=1; + } else if (!strcmp(s, "sniext")) - *pos = tlspos_sniext; + { + sp->marker = PM_SNI_EXT; + sp->pos=1; + } else if (!strcmp(s, "snisld")) - *pos = tlspos_snisld; + { + sp->marker = PM_HOST_MIDSLD; + sp->pos=0; + } else return false; return true; } +static bool parse_int16(const char *p, int16_t *v) +{ + if (*p=='+' || *p=='-' || *p>='0' && *p<='9') + { + int i = atoi(p); + *v = (int16_t)i; + return *v==i; // check overflow + } + return false; +} +static bool parse_posmarker(const char *opt, uint8_t *posmarker) +{ + if (!strcmp(opt,"host")) + *posmarker = PM_HOST; + else if (!strcmp(opt,"endhost")) + *posmarker = PM_HOST_END; + else if (!strcmp(opt,"sld")) + *posmarker = PM_HOST_SLD; + else if (!strcmp(opt,"midsld")) + *posmarker = PM_HOST_MIDSLD; + else if (!strcmp(opt,"endsld")) + *posmarker = PM_HOST_ENDSLD; + else if (!strcmp(opt,"method")) + *posmarker = PM_HTTP_METHOD; + else if (!strcmp(opt,"sniext")) + *posmarker = PM_SNI_EXT; + else + return false; + return true; +} +static bool parse_split_pos(char *opt, struct proto_pos *split) +{ + if (parse_int16(opt,&split->pos)) + { + split->marker = PM_ABS; + return !!split->pos; + } + else + { + char c,*p=opt; + bool b; + + for (; *opt && *opt!='+' && *opt!='-'; opt++); + c=*opt; *opt=0; + b=parse_posmarker(p,&split->marker); + *opt=c; + if (!b) return false; + if (*opt) + return parse_int16(opt,&split->pos); + else + split->pos = 0; + } + return true; +} +static bool parse_split_pos_list(char *opt, struct proto_pos *splits, int splits_size, int *split_count) +{ + char c,*e,*p; + + for (p=opt, *split_count=0 ; p && *split_countdp; + for(x=0;xsplit_count;x++) + VPRINT("profile %d multisplit %s %d\n",dp->n,posmarker_name(dp->splits[x].marker),dp->splits[x].pos); + if (!PROTO_POS_EMPTY(&dp->tlsrec)) + VPRINT("profile %d tlsrec %s %d\n",dp->n,posmarker_name(dp->tlsrec.marker),dp->tlsrec.pos); + } +} + static bool wf_make_l3(char *opt, bool *ipv4, bool *ipv6) { char *e,*p,c; @@ -399,6 +499,16 @@ void config_from_file(const char *filename) } #endif +#ifndef __linux__ +static bool check_oob_disorder(const struct desync_profile *dp) +{ + return !( + dp->oob && (dp->disorder || dp->disorder_http || dp->disorder_tls) || + dp->oob_http && (dp->disorder || dp->disorder_http) || + dp->oob_tls && (dp->disorder || dp->disorder_tls)); +} +#endif + void parse_params(int argc, char *argv[]) { int option_index = 0; @@ -682,29 +792,45 @@ void parse_params(int argc, char *argv[]) params.tamper = true; break; case 23: /* split-http-req */ - if (!parse_httpreqpos(optarg, &dp->split_http_req)) + DLOG_CONDUP("WARNING ! --split-http-req is deprecated. use --split-pos with markers.\n",MAX_SPLITS); + if (dp->split_count>=MAX_SPLITS) + { + DLOG_ERR("Too much splits. max splits: %u\n",MAX_SPLITS); + exit_clean(1); + } + if (!parse_httpreqpos(optarg, dp->splits + dp->split_count)) { DLOG_ERR("Invalid argument for split-http-req\n"); exit_clean(1); } + dp->split_count++; params.tamper = true; break; case 24: /* split-tls */ - if (!parse_tlspos(optarg, &dp->split_tls)) + // obsolete arg + DLOG_CONDUP("WARNING ! --split-tls is deprecated. use --split-pos with markers.\n",MAX_SPLITS); + if (dp->split_count>=MAX_SPLITS) + { + DLOG_ERR("Too much splits. max splits: %u\n",MAX_SPLITS); + exit_clean(1); + } + if (!parse_tlspos(optarg, dp->splits + dp->split_count)) { DLOG_ERR("Invalid argument for split-tls\n"); exit_clean(1); } + dp->split_count++; params.tamper = true; break; case 25: /* split-pos */ - i = atoi(optarg); - if (i>0) - dp->split_pos = i; - else { - DLOG_ERR("Invalid argument for split-pos\n"); - exit_clean(1); + int ct; + if (!parse_split_pos_list(optarg,dp->splits+dp->split_count,MAX_SPLITS-dp->split_count,&ct)) + { + DLOG_ERR("could not parse split pos list or too much positions (before parsing - %u, max - %u) : %s\n",dp->split_count,MAX_SPLITS,optarg); + exit_clean(1); + } + dp->split_count += ct; } params.tamper = true; break; @@ -724,7 +850,13 @@ void parse_params(int argc, char *argv[]) } else dp->disorder = true; - save_default_ttl(); +#ifndef __linux__ + if (!check_oob_disorder(dp)) + { + DLOG_ERR("--oob and --disorder work simultaneously only in linux. in this system it's guaranteed to fail.\n"); + exit_clean(1); + } +#endif break; case 28: /* oob */ if (optarg) @@ -739,6 +871,13 @@ void parse_params(int argc, char *argv[]) } else dp->oob = true; +#ifndef __linux__ + if (!check_oob_disorder(dp)) + { + DLOG_ERR("--oob and --disorder work simultaneously only in linux. in this system it's guaranteed to fail.\n"); + exit_clean(1); + } +#endif break; case 29: /* oob-data */ { @@ -770,7 +909,7 @@ void parse_params(int argc, char *argv[]) params.tamper = true; break; case 34: /* tlsrec */ - if (!parse_tlspos(optarg, &dp->tlsrec)) + if (!parse_split_pos(optarg, &dp->tlsrec) && !parse_tlspos(optarg, &dp->tlsrec)) { DLOG_ERR("Invalid argument for tlsrec\n"); exit_clean(1); @@ -778,9 +917,11 @@ void parse_params(int argc, char *argv[]) params.tamper = true; break; case 35: /* tlsrec-pos */ - if ((dp->tlsrec_pos = atoi(optarg))>0) - dp->tlsrec = tlspos_pos; - else + // obsolete arg + i = atoi(optarg); + dp->tlsrec.marker = PM_ABS; + dp->tlsrec.pos = (int16_t)i; + if (!dp->tlsrec.pos || i!=dp->tlsrec.pos) { DLOG_ERR("Invalid argument for tlsrec-pos\n"); exit_clean(1); @@ -823,8 +964,6 @@ void parse_params(int argc, char *argv[]) DLOG_ERR("gzipped auto hostlists are not supported\n"); exit_clean(1); } - if (params.droproot && chown(optarg, params.uid, -1)) - DLOG_ERR("could not chown %s. auto hostlist file may not be writable after privilege drop\n", optarg); } if (!(dp->hostlist_auto=RegisterHostlist(dp, false, optarg))) { @@ -858,8 +997,6 @@ void parse_params(int argc, char *argv[]) exit_clean(1); } fclose(F); - if (params.droproot && chown(optarg, params.uid, -1)) - DLOG_ERR("could not chown %s. auto hostlist debug log may not be writable after privilege drop\n", optarg); strncpy(params.hostlist_auto_debuglog, optarg, sizeof(params.hostlist_auto_debuglog)); params.hostlist_auto_debuglog[sizeof(params.hostlist_auto_debuglog) - 1] = '\0'; } @@ -881,8 +1018,6 @@ void parse_params(int argc, char *argv[]) fprintf(stderr, "cannot create %s\n", params.debug_logfile); exit_clean(1); } - if (params.droproot && chown(params.debug_logfile, params.uid, -1)) - fprintf(stderr, "could not chown %s. log file may not be writable after privilege drop\n", params.debug_logfile); if (!params.debug) params.debug = 1; params.debug_target = LOG_TARGET_FILE; } @@ -1054,7 +1189,7 @@ void parse_params(int argc, char *argv[]) } params.tamper = true; break; - + #if defined(__FreeBSD__) case 62: /* enable-pf */ params.pf_enable = true; @@ -1117,16 +1252,21 @@ void parse_params(int argc, char *argv[]) DLOG_CONDUP("we have %d user defined desync profile(s) and default low priority profile 0\n",desync_profile_count); + save_default_ttl(); + if (params.debug_target == LOG_TARGET_FILE && params.droproot && chown(params.debug_logfile, params.uid, -1)) + fprintf(stderr, "could not chown %s. log file may not be writable after privilege drop\n", params.debug_logfile); + if (params.droproot && *params.hostlist_auto_debuglog && chown(params.hostlist_auto_debuglog, params.uid, -1)) + DLOG_ERR("could not chown %s. auto hostlist debug log may not be writable after privilege drop\n", params.hostlist_auto_debuglog); LIST_FOREACH(dpl, ¶ms.desync_profiles, next) { dp = &dpl->dp; - if (dp->split_tls==tlspos_none && dp->split_pos) dp->split_tls=tlspos_pos; - if (dp->split_http_req==httpreqpos_none && dp->split_pos) dp->split_http_req=httpreqpos_pos; - if (params.skip_nodelay && (dp->split_tls || dp->split_http_req || dp->split_pos)) + if (params.skip_nodelay && dp->split_count) { DLOG_ERR("Cannot split with --skip-nodelay\n"); exit_clean(1); } + if (params.droproot && dp->hostlist_auto && chown(dp->hostlist_auto->filename, params.uid, -1)) + DLOG_ERR("could not chown %s. auto hostlist file may not be writable after privilege drop\n", dp->hostlist_auto->filename); } if (!LoadAllHostLists()) @@ -1143,6 +1283,9 @@ void parse_params(int argc, char *argv[]) VPRINT("\nlists summary:\n"); HostlistsDebug(); IpsetsDebug(); + + VPRINT("\nsplits summary:\n"); + SplitDebug(); VPRINT("\n"); #ifndef __OpenBSD__ @@ -1304,6 +1447,7 @@ int main(int argc, char *argv[]) struct salisten_s list[MAX_BINDS]; char ip_port[48]; + set_env_exedir(argv[0]); srand(time(NULL)); mask_from_preflen6_prepare(); diff --git a/tpws/tpws_conn.c b/tpws/tpws_conn.c index c1d335e..a979bf0 100644 --- a/tpws/tpws_conn.c +++ b/tpws/tpws_conn.c @@ -24,7 +24,6 @@ #include "helpers.h" #include "hostlist.h" - // keep separate legs counter. counting every time thousands of legs can consume cpu static int legs_local, legs_remote; /* @@ -93,25 +92,37 @@ static bool socks_send_rep_errno(uint8_t ver, int fd, int errn) } +static bool cork(int fd, int enable) +{ +#ifdef __linux__ + int e = errno; + if (setsockopt(fd, SOL_TCP, TCP_CORK, &enable, sizeof(enable))<0) + { + DLOG_PERROR("setsockopt (TCP_CORK)"); + errno = e; + return false; + } + errno = e; +#endif + return true; +} + ssize_t send_with_ttl(int fd, const void *buf, size_t len, int flags, int ttl) { - ssize_t wr; + ssize_t wr; - if (ttl) + if (!params.skip_nodelay) { + int ttl_apply = ttl ? ttl : params.ttl_default; DBGPRINT("send_with_ttl %d fd=%d\n",ttl,fd); - if (!set_ttl_hl(fd, ttl)) + if (!set_ttl_hl(fd, ttl_apply)) //DLOG_ERR("could not set ttl %d to fd=%d\n",ttl,fd); - DLOG_ERR("could not set ttl %d to fd=%d\n",ttl,fd); + DLOG_ERR("could not set ttl %d to fd=%d\n",ttl_apply,fd); + cork(fd,true); } wr = send(fd, buf, len, flags); - if (ttl) - { - int e=errno; - if (!set_ttl_hl(fd, params.ttl_default)) - DLOG_ERR("could not set ttl %d to fd=%d\n",params.ttl_default,fd); - errno=e; - } + if (!params.skip_nodelay) + cork(fd,false); return wr; } @@ -308,19 +319,18 @@ bool set_socket_buffers(int fd, int rcvbuf, int sndbuf) if (rcvbuf && setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(int)) <0) { DLOG_PERROR("setsockopt (SO_RCVBUF)"); - close(fd); return false; } if (sndbuf && setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(int)) <0) { DLOG_PERROR("setsockopt (SO_SNDBUF)"); - close(fd); return false; } dbgprint_socket_buffers(fd); return true; } + static bool proxy_remote_conn_ack(tproxy_conn_t *conn, int sock_err) { // if proxy mode acknowledge connection request @@ -393,7 +403,10 @@ static int connect_remote(const struct sockaddr *remote_addr, int mss) return -1; } if (!set_socket_buffers(remote_fd, params.remote_rcvbuf, params.remote_sndbuf)) + { + close(remote_fd); return -1; + } if (!set_keepalive(remote_fd)) { DLOG_PERROR("set_keepalive"); @@ -1068,9 +1081,9 @@ static bool in_tamper_out_range(tproxy_conn_t *conn) } -static void tamper(tproxy_conn_t *conn, uint8_t *segment, size_t segment_buffer_size, size_t *segment_size, size_t *split_pos, uint8_t *split_flags) +static void tamper(tproxy_conn_t *conn, uint8_t *segment, size_t segment_buffer_size, size_t *segment_size, size_t *multisplit_pos, int *multisplit_count, uint8_t *split_flags) { - *split_pos=0; + if (multisplit_count) *multisplit_count=0; if (params.tamper) { if (conn->remote) @@ -1081,26 +1094,26 @@ static void tamper(tproxy_conn_t *conn, uint8_t *segment, size_t segment_buffer_ else { if (in_tamper_out_range(conn)) - tamper_out(&conn->track,(struct sockaddr*)&conn->dest,segment,segment_buffer_size,segment_size,split_pos,split_flags); + tamper_out(&conn->track,(struct sockaddr*)&conn->dest,segment,segment_buffer_size,segment_size,multisplit_pos,multisplit_count,split_flags); } } } // buffer must have at least one extra byte for OOB -static ssize_t send_or_buffer_oob(send_buffer_t *sb, int fd, uint8_t *buf, size_t len, int ttl, bool oob, uint8_t oob_byte) +static ssize_t send_oob(int fd, uint8_t *buf, size_t len, int ttl, bool oob, uint8_t oob_byte) { ssize_t wr; if (oob) { - VPRINT("Sending OOB byte %02X\n", oob_byte); uint8_t oob_save; oob_save = buf[len]; buf[len] = oob_byte; - wr = send_or_buffer(sb, fd, buf, len+1, MSG_OOB, ttl); + wr = send_with_ttl(fd, buf, len+1, MSG_OOB, ttl); buf[len] = oob_save; + if (wr<0 && errno==EAGAIN) wr=0; } else - wr = send_or_buffer(sb, fd, buf, len, 0, ttl); + wr = send_with_ttl(fd, buf, len, 0, ttl); return wr; } @@ -1186,36 +1199,53 @@ static bool handle_epoll(tproxy_conn_t *conn, struct tailhead *conn_list, uint32 #endif { // incoming data from local leg - uint8_t buf[RD_BLOCK_SIZE + 5]; + uint8_t buf[RD_BLOCK_SIZE + 6]; rd = recv(conn->fd, buf, RD_BLOCK_SIZE, MSG_DONTWAIT); DBGPRINT("recv fd=%d rd=%zd err=%d\n",conn->fd, rd,errno); if (rd<0 && errno==EAGAIN) rd=0; if (rd>0) { - size_t split_pos; + size_t multisplit_pos[MAX_SPLITS]; + int multisplit_count; + uint8_t split_flags; bs = rd; // tamper needs to know stream position of the block start - tamper(conn, buf, sizeof(buf), &bs, &split_pos, &split_flags); + tamper(conn, buf, sizeof(buf), &bs, multisplit_pos, &multisplit_count, &split_flags); // increase after tamper conn->tnrd++; conn->trd+=rd; - if (split_pos && bspartner->wr_buf, conn->partner->fd, buf, split_pos, !!(split_flags & SPLIT_FLAG_DISORDER), !!(split_flags & SPLIT_FLAG_OOB), conn->track.dp ? conn->track.dp->oob_byte : 0); - DBGPRINT("send_or_buffer(1) fd=%d wr=%zd err=%d\n",conn->partner->fd,wr,errno); - if (wr >= 0) + ssize_t from,to,len; + int i; + bool bApplyDisorder, bApplyOOB; + for (i=0,from=0;i<=multisplit_count;i++) { + to = i==multisplit_count ? bs : multisplit_pos[i]; + + bApplyDisorder = !(i & 1) && ipartner->fd, buf+from, len, bApplyDisorder, bApplyOOB, conn->track.dp ? conn->track.dp->oob_byte : 0); + if (wr<0) break; conn->partner->twr += wr; - wr = send_or_buffer(conn->partner->wr_buf + 1, conn->partner->fd, buf + split_pos, bs - split_pos, 0, 0); - DBGPRINT("send_or_buffer(2) fd=%d wr=%zd err=%d\n",conn->partner->fd,wr,errno); - if (wr>0) conn->partner->twr += wr; + if (wrpartner->wr_buf, conn->partner->fd, buf+from, bs-from, 0, 0); + if (wr>0) conn->partner->twr += wr; + break; + } + + from = to; } } else @@ -1279,7 +1309,7 @@ static bool read_all_and_buffer(tproxy_conn_t *conn, int buffer_number) DBGPRINT("read_all_and_buffer(%d) numbytes=%d\n",buffer_number,numbytes); if (numbytes>0) { - if (send_buffer_create(conn->partner->wr_buf+buffer_number, NULL, numbytes, 5, 0, 0)) + if (send_buffer_create(conn->partner->wr_buf+buffer_number, NULL, numbytes, 6, 0, 0)) { ssize_t rd = recv(conn->fd, conn->partner->wr_buf[buffer_number].data, numbytes, MSG_DONTWAIT); if (rd>0) @@ -1289,10 +1319,7 @@ static bool read_all_and_buffer(tproxy_conn_t *conn, int buffer_number) conn->partner->bFlowOut = true; - size_t split_pos; - uint8_t split_flags; - - tamper(conn, conn->partner->wr_buf[buffer_number].data, numbytes+5, &conn->partner->wr_buf[buffer_number].len, &split_pos, &split_flags); + tamper(conn, conn->partner->wr_buf[buffer_number].data, numbytes+6, &conn->partner->wr_buf[buffer_number].len, NULL, NULL, NULL); if (epoll_update_flow(conn->partner)) return true; @@ -1369,7 +1396,7 @@ static bool handle_resolve_pipe(tproxy_conn_t **conn, struct tailhead *conn_list else if (rd!=sizeof(void*)) { // partial pointer read is FATAL. in any case it will cause pointer corruption and coredump - DLOG_ERR("resolve_pipe not full read %zu\n",rd); + DLOG_ERR("resolve_pipe not full read %zd\n",rd); exit(1000); } b = resolve_complete(ri, conn_list);