From 75cde6a1ea4b7dbe12f0be8361566a0cc2ac574d Mon Sep 17 00:00:00 2001 From: bolvan Date: Mon, 8 May 2017 18:56:43 +0300 Subject: [PATCH] do not touch non-http looking data blocks --- binaries/armhf/tpws | Bin 54676 -> 54692 bytes binaries/mips32r1-lsb/tpws | Bin 96624 -> 96640 bytes binaries/mips32r1-msb/tpws | Bin 87776 -> 91888 bytes binaries/x86_64/tpws | Bin 72640 -> 72928 bytes tpws/tpws.c | 1209 ++++++++++++++++++------------------ 5 files changed, 612 insertions(+), 597 deletions(-) diff --git a/binaries/armhf/tpws b/binaries/armhf/tpws index 4ef43e5c7f8207a669c56d5261eeba3d226fc30f..886ae12fa6efe54f5f1bad3decfdee200d21fe01 100755 GIT binary patch delta 5543 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 diff --git a/binaries/mips32r1-lsb/tpws b/binaries/mips32r1-lsb/tpws index 32e6ac2c332118969caccacfe88e8814556e8b48..c2f21daf7702b3671529c9b348b5711a2e7a4c79 100755 GIT binary patch delta 14712 zcmb_jdt6mj_TT$l9`_>XMUl5~k;_X#P*FiqE>B6(do@%{Q&cP;D3q93He4N=Aya|2 zc64$Yde~S)LR|};G_kbI$)d6uD=YNpm^qGNIgb7&HOZa-oi^7qH;a$oL#UtEDQBK>B-oEhDYa{Qa;LN1{R)?+i)eBr-&#X zopgMp*%<#pdK|-F77NLE9C*R|Djvl;44nCU&>WE+5%=+N{C)8f5A(E5ud(Kc6MT%) zvk8-w$<5*pX`+9?Ov9F;=KGn6+02|RvIdB!r7#{K4ocQwdySPd$(qOx)x|Scot3>( z7tUJh%;JWW=!?m4cBmP`e1pYs!z^Ad9yPdlix}?_)^sIsCTvjx+-9*XQugp+uunBo=9o#H;P0oA?dt(YfC#NEP< z)OB<3tEwN*+Md;A9FuHj;u+806x23qxDE8VfC4}Xpb$V_D+O-{`jr~*sCe5XXyj$J zF>mY67*#+b76B9^HcF}yI-ZKTxlkaRNf{PLwzU}eb#6A2@M3Xz#DMCn;VPG0%;d7S z>Oi4z8e-020n8SltklNHiC9-x$&SQD#g{O0VtJTKkG)sclG=0Go6piZqW!Te+e*BD z)TL_U9!$|j{MO`O{nsv)7IcbR25D}Awh9l%vNvThL&ol;QY&1*D*aWNIk2jJL$=ky zvg=9KRKy>(Sr|W+lxq#w){*-%paVeRmxlf%N%aEyV!3w$uN2$7 zSB_Pu>BAN(_GQ3F#4p;s5ONF|oxOCryN9pBgXEG=E5)bY(UN7D_|ZFb#v#pM-GHf@ z0fXHPuny}nKzAmG(<<3paZwOXM!1hJ5&otDAxkvXqs&=kb$#ZAAUklbN8)}5Lml>7AgDwEY z;Q5*jHaACkL+?VjHAdj1f16nA6GpE0vE%u7pU8o_Yhg!ox3+@!9S~DEEZcg?O(zF% z+JK^AYEUd;Z^Oyc`UJ(?)(2XstCno;6C@c8;`u&9VDyK5$nD3SLw6o=c3(|ys?yw405_Ggck1*rRL0%{u4+gz zT>zZU5QP}=`HgtmZ)5^_WwI7D)4DfUr9#juCqwm2clc;_{7m?0Gu`16;vPPU8h4f3 z`o06IN%nM>?Nqc1wlQS}V|QXKhj}0a)L4sHjXIy=WHz!687kUxj!CS>YG%(j2e9Xw zv#@sWuo|R(WUjQHETRXlgrLlUr|LH>0>9E)MP9s94Cog!gLJTK_Gf7tl4Tukhg8=~ z&qg8Ix>R_V_KUMp_3&z$(<;{Y3(LcvJqtf8K9A8TbexVm?ZIFVH5PSF(oZ;@=@+(w zIQcNiy4sCnpI#ivMt6>6!w+sY=#rz|Y)nbc1gMXr-^q5L?%?hUJvc&=<1TJxnA8j1uibq!=FZ8m%#aCM)xntuww1A9 z_?fP`b%KY4#n_;e`wsqA^NF$wUhsq8PK&INF#n?7E5(YCAg*xnFZs#b%OPX@D>16q z6(rpuZiK`wsNayQ=ut{>q3NdQ1IoVpMl4c_$s(4sdm78xr_V9TU@c)eO|&m5$XO|= zIaV{5x0+#cf&QF)YnZY`V z<=j@zY4KrLX$c3_dsIyV+)mnfwU}Dk8yT}$4J%5alatA zw?NfQ-4U|~MOXivYK6a`8pT`U$UN>t7go5QI}{1B>CPKiwu!h<@LQZu)B0n}PH* z40KN|3jCP;Ttu0D=ro1$IkMz@k;yPM)vU_WG3 zbC^!w%@_0~KSTfAsNBBY8D3!^)#bp4?hMr6Yd2kYtkEhTgjKD&BC&f#H|I&>(U`Ch{S>5)Rx@MwSrrFkg3@)l>Uq&} zApAtAWe(9b#$hMEL3D*aG9| zJS*J`<$}lM@b^Ss+{l6YXAc}P<>iMXOnwxb<7AUQGeI=O4-mHa0{W;GKarmmAI47{ zc-EwbmVPve?jDhpu!wII4GBa2H~Oe2fr45IMR6iwT*4rWPL-v6nxm5HgtjtVJM1|0 z&2Nd}L&iv6K4RIBP`}p)t3)K?glHHthQBV}9Wq}!7AuA&Mfle%{#Tcfv^X()X!LOX z6I&mqN}`Z$a$8AXxQg_=pisRuG&-SQA64sch@urK+H0vr`$XlhacdVhhK7g#BD>*< zv-kw@XksL<5HBQ-#Pf^9$l-cST-Ek%jJ|JL)mwxpTmr#mn&7!2Dam3U(~};_V}BM` zk_L#1q(r|t(W>1_5Q*`_g7{2vI4L|}%wj8jMxcn$tK6?IuBV!$y!1rBuDa<;*R8=c-^N_=IVC;Eak^yS8`RUnEES>C(cHzfy%?O3(TLZGD3K`hh*`=`Dsadf%8J9!|;O8^qz10e9#- zW7DXkb!u&A!m%s#lEv^xVW#tI)T||*>f6+y;mLTLnld~*JgsNnenKuD86K@H3mdfV z7uTH`ekXrPj2iJD{<#=FvK)c*^vH0t8wrI(l{hgn3~xF)o9JDZbi{YbO?n<_O4Hv_ zOsSC^#c`_TrwnGN4wa7ii3y`Z{ZIac`^%|8h~{@w$3bP!QDc<1#arxCl<@00^HpN? zs4*t<==3=H{B9Aij+!i;d{RiGr{gVn#^`cW{1|o0=sGbvi2D?zspy3r;_{e$({>Hr zF2<)#G+ofJ0Q(U`j-%8vjw&g^cf^$`-!QxIE|thL`O+3e!1eHpKHO>wkwp6=>9{SX@Q{+$!} zNta)dk;VdN78)x1H9&5$*Qvyk*{;x45P(w{wZD5Koo8X*#k( zr7v?WT`^0-H`c@Dt4!M~)ImgP#ZDOUS;buv-!4Y2%H*fS>Q&8fKuqN!*fmF-Up+x; znImE!AoUkM@CQ7lHP7IetX*p|rOw%|^J@Y*!sKREoYYe6ie2l)IYRNlhj^lR;larv z9W&JpsqH!#SHxa!zTj-At6*>72OYnMCPU-d4?WB2oA)|=WO9W({0^6{P8Vl?HJvw# zQPuzBv(Bk1x_6puu=BVCz5co31CHik%~u9AzpwqCo33tD7iEYY8^gH8_3Fk&Tsn{^ zLN{IWsobp29mo}-n>R@2$6QZsew?EfBkCseJn^f#_f0MJ>KdHy`bGE*zQ2Cmpu8HZ z>AJ;$4=ba6agmtWfjZhS*3h3_&lA%dp2E5-4d43|2$lB21abNC^FC=?RrLCJ@uk4w z)KGY2>(@S2+tkT@86sj^zUi2T;*f8fWr}}7oivIQ+a~%{Y1qDTBI1c_QrlQ?pCF3l zMxWyC>bg_u;z#+K)R88BHDRy_-jPA+X~!JNu|vGF;}z4f->J*M{C8mfWHFopD=sL>bltU zSB@VU5AVqgwKb)qC?|g%jLX6quWVLX9vJKjZT<|$rAb_CSxJZV<)OSyG`u`!kaLZ+ z1$T6SbB$pORY<`1Y+b*jj@;fR{{C{Lscl+rEIA&r@(O)}OnAkfK&uUSwRNt^Y4T%T zOvNRu7rt-VoEz#qFn0`GoZ{(MqD}FxZo6NJlR6^AwO1mfwg^|C>wRSISJlZ2;o|b% zxBV^QYNXn*&5djBu{MgMul^53@oOUyj9oe zVDgjJF?jmz3zN*Ri6Q$21DUdKXb7#xZ?g42bd}(@*=&4)%(hyYw5L%t?6WxXcUk+x z3COy2q&{c=j%{2stI5I)Td-)0QTt-y&1RVkkkibsrN@zja6w8EGA+?rSkN9T3xram z!IbY0gWy5?bMRcSKbbeW8ukkgB|y}oR8qTy|=z1Uu=1uj@y~n(|EoJ zJ@5xS|9oJuAK8ea0lH_y7vCIMZfacf78G0r?CygZrir_(#KSsKJacdoS-Hi+_9N+r zDBdBeP>5<2o^M$BL^0%zvHp!~N}KST!=7+(jWXM$MzQ*hDoDHhMzjT*kzHn_2C}ID zz9{Qr=Cb^3ya|VAJST=98iP!~^w2nyu}$5#c=6JqiBgBZxOON962`Vg<2kSGF+4A} z4fUKjJ+^kD2tE88o+_R`Y~vGMXAcVrlMftuTynGtpQ90ITtki?=RU>ns4P$UxPE*) z-++XacYGqpt$qBr`06|GFLuv%#9g~s`j@}Jp`+fbQ&R4UIO&2xTt4xdDaD}1&MtA{ zL?p)%e*YblO;X36Q^s&Ve(;XT!PT*6l`$C5-pVUni$5&Kr6E4~D3?zbp&zg0h2qhV z=SZD5#rcn)=LN3KC!gg~%Ezt|e;q7I&Kt^suR4`Mm*J^MB0Lo-bzB$MPDPm7uB!qT ziVxG0Md7DL$_k&_DP4bBEw%hx#C{fGs{gmT=#pp{JyclIjKcX@0Nv-G*-f3-)Vcq3 z&pAHzkXo*Z*w3G$Yvyz1nmOH%6XP#F=h&Dp|A@D=PrrgW{LI%sD?&*UR#E;zG_?#DCV8i?E9=SwaB5V7YcP-ZxPNNTw#V!yWYsp5gJ z->1BD!LHO97wuB#d2#;Ym)z_+`j1R5wV!i&{qreKrO4$jiJB_`+|!=Bsnjms`0gQ3 zd;X>}yO{9(a&8wdejhh*YPPirH4R180k*5zhLyQ?asK=081(uhObhLKoBZ)=+|g^L z1M@|8G3;s(ai42)v;X>=1pa@%UWezT8*zBvf8$PWcfEaM4hJ&$KOadQ zpSZ65XC9p8nsGCkQx)^$d|ay^y3Tj?m86ajTCZ`w#lRh{k9hJ-1eJW$i+{ya%L$a-WcwrMIUB)&vSus{A-|*oR`Bpir56|Ha`H?>S zG3m%rxxX(T!RzHbUp}9-^5u4^HydWCASUahqzOo5y-o!2?BqM{Av&;F&E6| zM|A8{wLXQy!NOj_-aTH|xnW0LfjxgqpdpcAImFR7f0}BNos3p4UFPHa;a^P92m{_NM@yNmRDOb z-O{8CawdNg0~@eMvHTBQYI&hGHl8o@;AwJ0B9A7u6Z!mwju}OkWaQP{yu!SqJf+KW z>{F-a<*4ID)3b7D*w3e+d{yOG3+3S41(gdd3(LwEEw_}FmoHysDO zVU{~9%Zaq2Y{8;)ta!Z_d@;0M6o2#iHy3@{f^ z*}68B`|;L|qjgm0C>RvzJLHgIKT=>1f&8o06M>1^a}w+fVqGrfXb_Im=m2%fUN+1aTEG` z0S5pn@bK+`*57WEZ{H zSFSEAYdtfSd+^qV$vn)!+vJy~;vTM*-<`_GJmKJ$YDZEQD+ZJRmH;XN>j1TYtpNJl zskPKcVb97O+3W~_+VE*TeT$Y~D`sW0ihBXnx&hHt04!5%EDhk8Vw2cjxxvAQCMoOJ zXmmuY0r5X}D~(EGmHnpiXnzal-S}ZHYZ_1H74m)4c)cY*iyV?S z^Pp;v!8Ue@*_au2UN{N8M%!3HCjQywhc;IGk&U%1x3PBM__tN8Gl$wN)_B3jwy&}= z=hwiqY^?r-jTJ4nv0~U;1gOM6d2^ucT!=OKHij>uEDbGw+GUny;I{+-ADr50t7h>Z z*jW4+Fseej?r{#=OHKkoDKSOcR*3maNb686xE=k|Q1pG!eQRU89T4(QFoaSJ#@LGS z>)LF<94Lpjr4xMsy8`$QN}UHipkj437C(RicryIg*^I5g#Pn=-bwf65gr-i+ts9%g zY*6mlI4C4#F*5|W?0|<}1_K%YSoV)F;7RQ2RvXIzzp8Bziq`RvjWs?GPgUSYZuDC` zvsmXvj2Yl*tUvIvjWt!{(FozW5T5%I7M+67&mj!souA>dIt&GYUmp*q?_rxh#TtOa z1>t}W(AKZXW;SeH$0T?M%6kBq5n=6>#qjSQ7^AUP(42u6&O$KSM(}2UY9}5=UqMI* z24UH05S#)8JPZ#Uhh=DY_JP%KKf^Xyfm;A6fJ8uXZ46t}- zu>iCwP`cn%%#-o}=9k5s8?vieBN#Y@;*%xG_7!4F{gBU}vP7LP2}0oS!*zuIAy z4FPEBkM7~A3CS*(2k#_(?*E205WU>t+>5g5mqBLwuZFaWqY82o~;4Fj{-5o|S^ zX@_fOX0uD=CYHl4pzQ>70L}q^ARIzFLAbjw7zBVpU=~XO!wNeXTux@l|kW9g+Jdbzl&MP zBU!#!48ENsRB9L#0w!q>b-JOj1wZ=uHQJF>i&GpOWvG1O1Vw6t$ix;U=$4nrCvqc|25p0kaaz zk}oboo1-b^B;9j+D0r@XaUnKj>0B&I%a0yX9~0hNmclksjrD@eprHW%yqQ3O57tnYtIw)nGM8UN#S`b}yh6nXuQrbD|m<(5-u?yZU$sv`LR_n3;9hKVL+OvP>7ILs3BpfM@QZAyI>BT70m-i z%4e|6DW}Zj!3Nv|+Pb^pUJZdyOj4z;Crzx@5ZiKY@EyI+Yjp%RcO#9ZZb%6jd#xqLaA*k+G1fjv2vgNGQUX?FI%e_^{*Zip0lY@p zTKB6rrEEedHu*X^0ij{Ix=~jf5$63EE%0{AE%WfoTDeIH7+xvAfR)CUdYXj0TS|Da zcYz>G-8}>D1vN_H6uwm+LgBVew`UQLH^e`oEub)df+!>9x;Z@53zv(|#k+XBI*~k| zhw>fr#ia<69W>Mhh34>Jc}Xd^7;q1%vcO8x`(-_SSvj-%1fAJ{ow_OrTS-SO*fTj{ zF=WsUruYbv<&B$8F|r*-;?uDbu|~SKy0_C9j=R4b^QKBbKegU;VG>fl0B6hPF18z? zP|*-NSKf4E=$m3Nzor?786#b2iZ1n}%ceuhZb(Pe*4x-dvrPVWCA3!h)#|<(=|W|= z_LN=pE`Eowesc0G91L7{YABPF_;Gn7&XN4hy>J2*IEoYIU>S-IC2JZ`xu_kKBWee= ziPAAr^@Kk)_@vs8ps7Jw)NRH{_bmQYuHJOz z>+JXi`CUYW)N)FmGzX{0{He}^4h#@V^9J0zY6q9FDxzeeI<1`aj%B=`;xXwnZCW{D zI^v|w(GeWCctfL(H4=t$MP*87T>4y>pdpkV4zOn10RDQ9J$6}T@8R^UE6U5PjP^Xe&Wlb5al z59inNi?|xRQNOB(wG6otB^mzYesr+@AzuW&WVxhfXsRhGJo-@>q0a11?meIDJ?hq6=w7+$$ za9kv9qLEIiK|_??^KeHzb%at0iY7gM7*Gpp`a&x&lvTQW4XClSZKD~~I{F1|K((dK zVCTJ+nJ#sM@W~M`4}o&mY?!!H>(AjnYHxlC_wh!_*yAk}i7KbI`w%E7{Zx!@O716L zybrm!9VH-H+wWc^K2%zIWP3aN^&oh)S$aSAkLo`%fG&G4)PDGr?f0XP@{CNTYfu`P zCI1Omx)i8MBX7xftimps<)y25jD)L)ZUZkAh~3^`p`e<>2T7HKWS>fG6{<#3rF
    A8lBxlZ~E#iciW7RXmQLl=o8; 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 PD5tN)@Ief3Rfhi;ZUGSh diff --git a/binaries/mips32r1-msb/tpws b/binaries/mips32r1-msb/tpws index 10c20baa587db43dc0c0f5c88a7f1f98db4defe4..7bbe3909fd082713e12add175d73f063848db8f6 100755 GIT binary patch delta 22994 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%0DM&ONpP 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 UOM3^NFEoiUgH%OZLh zn>+G@-kZhe+dJJ0gazCnNJ2Na3)x9$n8#z(OEK@zIK8x@UAtyC@p@r{m@vf82sUG{ zrKy1WCq7Ocb2&xkvNYv-Ew0={ToRYfRBye!TwHU5W2o;nmf3flW%NDIEO836^!2k; zr{o{yvg@sqJ(5*;%uMp^BT80jeB3!76tA~Owr+ErR;Q{+Tq3zyw8h1QW9Z`-MOSpT z*CI0Epvf`B<^8F=A|iRN;|yj?)nkaIDcj5IBeEbUKN{f@Q~$v)MJ!wPe0$#@llo3G zx$P8_iL|AdMOtPuJ*FcAiOb z{Qa5NQg8W;yM(2DaK=4?#O*V`q%L{ZeZoQBHES6)hG!Xs4SaOgWqeUydi4!HIGsUrb^TmvH5hb@>sGUbqaQN0I!YY+Uv=q zwnk~m-UHD$3c0*0+8|hYeY9a|Y3Sgze_mzqMt(YaJ#i|{UM{$J>FjLbNnSs@Sa_5V z&Mu5yPy<@!lar;@aJ}9pY*9t(o-$7>-=*IISo@q+Lg|4yri2B(B2!geHA!LN^*n#h zjY5Ws6|Z8Q3xj3gy1A-BO6G2fi40|8;mf>#Zi;XnKRb6{OvV&gn+h}YhIzNdCe}^? z?&g=~r3eft$>O5_+Sw{jMIQ z|CC%l5Whs&!!N{}WAdhqGV#QOVxdB%STY4RgSR9&gc?4SuwK-E$n^`>3TITr%233V zw|LWn>q&}p3swtH@WjOZ)bJ**6<*~R6Kz5lmy%M1@AAx~VkoN?_qQHzNm?!3)ZF)) zT-W!iTpQsXO?RfUVq3Smm=pU>%k_P4$PIl%h%-aXj&DudDY>4!s>Q{a?R8ee-1c2&gbM28H7VxC09{=$gzjlB`T*NPJlIZQ zUPYVNzc7R6Jwdp%xDO4n38FWSaeY%346JMooD1nvWrj#NdCMw5$ z1t!|X5@RyIxX>Z&;;Cu2IZXuDa4ikIA}wV`9(25m52vMxNoV+IntADagb_6D25Mv4 zophxjZ*k?wAYBF0)x04!Woa`(G@|M2>6RD(Ml}Rc9kitwrUXN6Z(&5W+1BV(v0i8S z)XTO>W?;J~uwjh0H<_C$8R>p8&w`&88l%mUyh|OASum!W?k%qI@ix$-d(bn2Uj7uA zdlI%xolHfxlrRAU^MD*cFq7rm-T;BUo?v~=T}X{ROUcw#^j92`cMv%rBIzG$A#)V>F25dn$EAHI%_!H znePn@Ra5DhOT# zmN|qIv9v9n4q=@mrvOwSU zS|6NZ5^s~%mml1xDnJeKp?)s*9b|ERJxtOf!Q$k)P;DfhA!4NKfJ|5%qPm!NrJEZA z(qBhdkX{W>8mVM2D0!$~3o40!r(2?=Uj6I_#a-3AXbDm!i>#nVPUf+vGj~S1KRX4* z3)3y|jA-k^&AY&vLNqbIMWyX;6P-TX2RzqLTl^Pj7x4zGVaG1`ZuFs3>{~%Is7pN1 zCSA}Bfi^hER5^HqDSj;sPSK}Zg_AzvO~et;usLEB$`WjwY(y``N`>#X*%lfDxR}EZ zDORd&V5sBzZ3ZEh58D<9_57l3dCY` z;Te85v(TF21W;R->ZTwekQOCq>90f)@CI&QnvF`-z0|ZgrFwcn7hj_yLLv+rnHbc& z)FBjbABG;~QdU-MN`2^D@&DizS&L&+I?%1^MAJdSKHidb7xn;SS%nEHN2gC0_qXE} z*`EtS63@?ZQDN7RV}>HUIW{3rQwL??Aa%r7@+&!6;(Vq-eN&)LUX|+*A~fKXf;isH z2XoiV)OK_SYzbogb6j?$2z}h?Nd8u!HWW(x-Vmrv%ETrQ%0OUg@-$!?g_NO*W%wrD z2CrIY1S@LUo>oHrM~m6UfnrvPF%6aok@XfcH^pvvv)$sd^Vb}*@DcYp%)$@2F3*~5 z7^K)4s0OiWBZ-P$(*J;$gRdO91B%(RECwriRep8Q8Aj9EsIU_giba8BbqAe^aNL<%WQL+ zspZ$fLT*{UGB&M!%CHw`nDME=FiQjzyv3{xScB9DZkak~?{XWK`N(q9;h8FPrJs{x5g0Oc6fj4J(%=XbT1i=ZLhXBk4(gW~C|CFotgXWcU2}d<^3T zc;YHs{4weW{QM-`@O|FBYRSSMQID1iWQvHsAdFRnk)*{CZM&Zr1kK~VRVg?;8C#W- zZl!*hNwUmfqMsUhP%QHPYK4cDql1whNR5s`_&NJYk!5eI3vr|Jq-iJDQM zD;?qLrMVCC^Q&z_4%aEBSeepKkk~P&X+q&!yi!>${DXU4X|ZzY6qJAG!>$zJvWmEx zk15&0Z#Bf4DToiKi0`S0NxY*d8+%ukqGt-?W)%@h%9RqO<~(pY#`9cDgbwN>mv)(j zmD*tMl)-80;BOP+LN#K*v!apNEx;Qik^MG#SQ~ycbhz{nb$Eq3w;F^H!?Sru@yb~F z1W<$4m-Iswbt$24tS7>Nm(n#5qv*cF#ne998mj(%jXW|vh0dFZKg6kCir&w6l^Af~ z(>^DjKV6a(D_@#2`Br|WWQnnerocv>Lb6Ii6Yf7(Y5k}p_1CPP6NgjS5z1b@6;3a& zy2dINc5u%%E3J;6wnMB-g%mrEwjE^l1ewh~CK3x-f3o$kM7@+<%6-?Eq8&aJZKLA? z*RM$vamieBi)cg&U$cLXo#6x)V{nd}W9NpoyP{+^)J*5foxFYR^)u{@`afT57y5Z( z>Ek)~TXa$bR-SsQ9CwJtS|W5aZAX|LBYd!zWQiF6b6hc-Au>Br@$r$;ToISZb*9)D zpQy-2j83Ji3U#i_Dnz#|8+Ei*RXj%IeSty>fl*--`b?3@Mk0ufhB}P!38t4~>-o`j z`-J;>=6VO}O85Fz!fD>U{*EZstdUY|GndM!;>;}DB(!i(*^Tk_wpUQ)zA}EU&x@mF zRr=T-eyMDAObdh?Zo~OE(Z<+%?kq2k$+I>&t~h7l1U$mST6!Fpd3(84bd2(W@+F%G z`+mw&k*H?0Zg-68rKMGb43_DiQGy;|rc}}1Y(D04Ag*|f)Mmw!5y#@At?0A%IIggb z%|;xlL!6~m$2MYu{A<^U#!T+HHkmHn*De+d2l$z5vmx7+YnPD^*f(rPR675Hg?k>G z!C%=BKQ~XZG_gocp84CjZ^LiX_d<^Xi%7H~W<)VD!v^1)M^6BT_U1|K8p-0rm7l-P zl#nM4?L}DGMqNxNDdS_79)RL1?l$E~t+kBh;s6U`VxJ^|&G~k+h-QKL`}xrdgK*1i#T ziNsEl_Ehsu0m}Bu>7U>#dB(MXhknV+qRD22V+G7_sPurLsE=TTb1aVOEKT`X`}3bU zR>_#v1pA9I%z96rB%;c-6pvpy7QrH0iusyKIjwL2i%rLL8D>Z@Pt?H*56c$unhXSl z!~xz^xe9xmp~^B8EBei=g|)nVGXl^6uiNYpPVu9gUF4mYHfIZadFl3NdH#U!gPkJP*kX2?Ul8bsX60}g_R zs>o*u4&k6H$p~e-TE3mfq>XH5+hLYx*@$3sczm>v?&0UX{FNJ`a`SYeDZ`it2kYID z$EaBAJve@)_t5y5)jobj)r)C-ES9m|e&-wIS8jM**w348d_=HweN|b(Gh{~0It#P7 ztu=m^J57D#-+`tX_3Neh*~BIAW?DrSDXNia{vhwDDns?+tJ)Wz#=I)rjfj%RJniHf zYkB=mg$acn$TJr4b@d@xBvXz86raY=-n1HdLx1xYVIkjj^Xi=0J<%ibtijX|wUibwfb4v+hG`#nS;{@9~U=$A` zz@p9NZzcpL0uNmIW;Py()PB>IoTEUI%Q3b{^yV8ypPdw)VmWNYvFbOibHNE93S-4k)D6?C6I!=Fz>SMh(Uu zai7{7|)5l%2Uayrr_Q=ojsZVc#B98x_#o8-10#J&Pn=-rl65;Md^ z#t+}RLd;RP@783OahEfJ6*MH6r<0(BO6%5JMI^kI(hkR?*ch-XITstQKhu1 z7}TKF8Ff3e7gF`HF1VLg87YoFT-j+>$E^>>;Fov)2LhjW7wQod2)o|J`B2BV4xuam zb_0H^ZeNAp4*ESleEWPMzE|`jxy7?M1bz!nSD>ZLIDX}h#bVMqZrQyA*cH3C3UR!D z_XczwxN|jPzj7y@)DKAeSOn&-L7d*ocip)bfc`s|C&l&k3l`^0rZ6TOZO^hq%bUAW z_0qzY1A9`0LY}&3ty)%MorM_ zU1>8-C^Z*%bI)DRMH??6@Pi>h@@lt5n{*RRZ*8(@vhcxLlbCsh`)Y5zbx2FjnUrG` zwn8*`d8j?lqHh-MeSP1agUL0`I+d2$5WWpBYDWcJ;UNzOBMpe&W8hi^#z$rcUWE zdE+L_-h~$lJD#vBr($!@9-C^T&A|{pP+ykRg0K%G#MYDh;$4Rvtun%nGxfVvqLzPm z6;^`RzY^NNd%r2a5^s?t_TgO*$%98=NMCx%aDa3P;;F}2ekISmKMiPzAu}m1AQl!` z4x3;cxyI6DDmfoB0)bDeb7`aDE=|xIZ^7-wqdaF;9jOJ|kH^ z&#wSV9wtPJvpe~P`xhr`C}F`F4_vqI4D)%7qHH&61*frtLKK9X2cKFAZbwufp*;#d z0iS@6h%M?P5Cqv0>PHeDdcd4ije<%*8GV7CHF^~!1(ix>H6MH6nWXAtIvnsS<1bEw zo@g+~pP_Zxc950E%18zk+HCw%Ls^szEp~yI3a&gzdz$J8vyuxtdOdCZ_@3xhm>$5J z;ov9$4KnX}aLrxxxt*Ii=M+(DMyIT8{J z1k0LC1B@AORd0DJFpFa^A3)Qo_-_ zbiRLX?^dCQ=kIeQ4KfclcSvy}r8=+%PY|faI^MMJE+J`{c|1lO)s@gfAZFUIP$EeO z`iJ*n&)xp;CdA|m4?hLcp8Y8?GP!50mlhEfl)&rVUsh0!a2`#}VU7E!j)3PQ2rtdU zW}ucRQ!ci+^6MjfEE3^ICY(s@HVMx29$9QP?IttRdtV4H4^cfu6SRt~9JS&sP43)! z-t-8zyd=fcicwWM{8leH&cNGNYW1ReFHdIY&?=KwSOAafn#~KJqR1G6tzzVZDr(Y_ zW;0$=+12ctYpR8OmcD0c)3B(A4>aFM-ty>mIQeuxnmuQL^r*!o=%@S9T+vj^haXKz zs`on_9Jn1`Gg%XJ-D9g4s>4*D5*M68l_10_P_x)j>oPuCeVsOA!N7a zjE@IoIB3QipT(lFzk8DF9{-&nbKm1u(NxDHTRxzy=4&Zi7bjxTWQhVjgPm!aDKO+8 zn91}uX%FuDzj&99<=)1|gZtYK38^?J7?8G6{uOASw3XL=zp&6TfM7bbhx=ZPV10+!Zoqb_uouKJz=i-rP4oQ(F@1nXwx)^cgFK`4m%`h} zue8oX3geOOHwwqNyZtTvX8gb;nlJM59~>8cec(VRUYzPWut79+^NR zbKKdHCYlF$b;kkGe2$NHd}cNM$P$kbjU;cq-t;sc8yT^YDZ+g>ws##!@a-^66CeAx zv{+L&fi(2-z_j4mjqOG8g2W;|vz-t>4Wxy@5(0HZP_MD=1Zz*A#D|YF z4zqNaj>7s8(DY~}W@G*S7GS9E2qG@Zt-yT~e;&IB;= z@Z&?~%~ww$kZHBsY1F`KJ`G1>R51;%n25KPZ>1E zMJPtw6o7H5I|aCK9^m1$#}0bvb#|v<_g&SUWr9|UJ+qPi=Cr-YTF8e)Fwn9#{cOvc z^fT(@N4uBZQ1Un8_1HEF|JU50DitcAK9Yk3C3S%3-Qa0gfNxFtr3gPz7qc4$nal%5J7qp*hT9 z(y;!>J^1w;$;NO05qi*k?uY>tt{hoUakAuD2WsE?XAKLT$VL`qp8^Uj{!HXA9gvAm zJbRzuwMVl~2g#feQ|u;;|D zf{$UJjY5^r7*cBe@s<~7i#MsYF?CVKWgf|h%sh;&h59Cox{+Q{t9nrsd-1Lde9@wt zcZ7TbS^IvnG0xG)Sg~wH_RhqKY&7|ex>Xzhj98)`LiuKG9?%&A`=|t^&MeI(aU0yPc5)pCLEv2p}a&Sep0?a9Ps?D)90WdQ7tdTagcO#%^ zu}`f+oLE9xFOfXBcJc04iWl`-b;uVKE%9-RMQ>Uo90L~DhC6GwN#ZfMMT2;8RV(^< zq6k+sh8+E>kU)EblEky3UI_rCggjF+fTPm8N<2)$nJe9ac4ukDdVs*k`U zk}hi6y4N5^2k&}ql`)XW@F0ZUNXd-$V=U5cwlu-_%mpL-@@p$+7L2rJ;5Kh)e^$W= z6D2cy9S6N7*s|aZj7qBBy#*u3AOxr+F*n{SfUmV~EEwh&hn6oq#o(vXCOj%AE*L(B zlG%TlA|6AVJAZCVswBn(A9bgMMx>PA%o~2L?(lnlUWWJn@d>v_;*qBe*#BpovI$;Z zamp+l;B}{HF&#a%Jw}F)*->`j5lcVxPJ6Z2+k|x9`?|rXqdWvy6ylT<#r24K3ZsmF zKr^Yhmw@Z#4X4dT2MC6R20vb^Zd9;Ip=TTx!Xx}%Y=@`~oEln`S|xzP)Yn*xx*&b0 zTgig8Z>$yudH)+JSbt~VNZXJVY5kupy(Nu_vxx*CWu~IaL3CPRToiY6;>Orj(-;o${Z$9&@ zHSZ4L83vucq5%2@!@WP~o%P~AL+gLH8Ta&;6s{ImVHX5ZS1GM|^q1?UZ|J%A?X@6y z@oigtHI-e)d-g1%)fWd_ZXDXPedC-dEJ`EyH(V)Y$N91R3Z1Hh6f=``2Or zcH(_Ae$Tz%2x!%>Y*A_&P%YH_t4)a0=YRD`d^+uxAeS0fv<-SfIZi=NY@DYN>ocp!3A(Srg5g>sJa?Hr=GcuomGLFT5}3J&<6$J^zSgKV=3+bgjN&Np?-87V~|Z@cOaK15;MJ(8dJTS z3od})dDuJ%YJc(i0-xG3lX)yN)Ot7$llGHdK^PO~Uj|{5rq_8VNVjxPQ2?R02oW*L z(v*cb6kC4a!-TmSHRsI6ajR6|wx!Ls9w8qp{)09?OKd~CHKyPSf*(>O?dMmFSuOOWXk|W^ zf0t~{zf8&!J!g!heL8e))j<)SacuPwgWAbbE5^9{cgwXX3bj>fDpk*KGTRQ~ea1sk z)h*h_FSG5ns3tXfp8lN;e;9P>cfT@a(eW06rt?I79#r8PI+WYaedlvU zT;hJ8MVFG_b1ao}zkdV~*Zt80@p-g}TXpzr1(Y9J>9NwuM?cCIs<`}dvRK%2-1#wH z4)k)%AMV3%_a7e1LTQZi3K(=Qg*tMMmbF>cEo-u>s4S*K6ADM7LN#iBQc{R9D|Qne!NGkh9na7iR3!Sj{qtv& zU`p+y9amIQ^wfj{kwx(yEva7eZC z1TXpXbu%i|yRJX4#owU${+zscCGC*t9V-FS!Q87h((^*Z=>Nk*E(_fDBbr_n&GMI* zF^%sp55PmJFWrNeB8D$z3-=%Rq7mmNo-dMPHCo8bNJ_NCm!9FhUpR0~?fW7Hzhhsl z*4{t$OT?T>tjMqE1M1NVUm7J&LQ{dnYm|8_={ zqBRF9j$iz{HA;-A?UZyvUzDtMAFDliMkm}NidpWH8MA~NBBG;w8supI$>wO`#wbDQ z^vxH3)cMhT!PM!E6CR3=^Jo}9k~%Zug%6_R-CFx;duL<0S5Kt&~ZK>vz=dzIFSz_Ux4HCH5k@?k?G0ME!H38C!GLclOlCyXxv{+Zf)zNhxq?K`rfJLjheFTI#5h%*Wu zT})BGy|^NZDqctC^u8%LjT007+c~CmfVsfr0S2fqc+eA(c!eASbu{4{Hwk+}*6#nU zWU`MK=m0)_IaJD_HVy@SzbDXlGBI6vEPCbUxbc6gUkO)P``%E8tG}(@Q$MvMC%=`K zPM*&eOhWRPwOIY?c{lt5VUPmUWjFfHbQ*Jnj3*Q!w$UJ?fAUjg$e3*4uE>yyUtyxJ z$dEs!txUk^rpQ1i;lcO_LLu!cLYw$B78z>L2p#~YgFZ!u`YG_{DeaCRY+}GWrQJKF zeImHsxim+JSzDCkVMR&J__pKQr6Lq1^#majgOg0U;MCej-L$K>%Dc^-F;)Sm5x-ze zIqPMv{N~QCBH{J~c>h-;zlXyu6}hW3-zD79=trHrc<5HS&&QPKYMHVJ4s~Y+Q+C5) z>T$o{%aj2)(oWp3m9PP-0NlrlOuL!#Fjm@~fbYQ+dp%64tOqWx>+6`Zk?;Y(4>Y#{ z=RW*pb|LT@9|fR~G3EJDraXdgnVTu|fw&$7%bZNP4ajrhM{$Epc@XdiJxmcQz+69b zIeM9L9QZ%>G3EX$Fi{ISfN#UKp&65-|0KTa?6`O1JH!;cuBv<+?c2~_*Un}u>%e`M zmlY*QCwq&9GEx02$291`fLf+z2sh*76K@cqrM?#Khc8Z?4p*(_hua6I!F`D-2O)G#M-WOU;VUjrhX*sbYBUU7o*ZAmhQM!#fb$qTGl@`A+quyyEQnf&E-0j= zUTuCW(DZnK9x|LUA5i`9HPs$$gQmbUg5dpO#@i?1Yo(Cc*UF*IjLr*dg+g(yCxofB z-5%8SQzj*y`I%a0_e_-;6|Q#2lAv2C?s;W$_Ed&o9# zScFNqPgPi?AALKu5*GzpkzvFy1w(##jkZ_<=rtK(7Q7OKuFVx#JvA4v?rl4itFwMv zIMITf={8@O@qkYlXQvzHIKnXJUo01l!2q65zqg(B>|+xz11LK~EUAWHur9 zgI^Q=AJQfZ_~S#RVd6!QmKrOjFAuf;@w3y|9|j&Y{(Exxba#7TeSEJyJVF4zH=Mjd z{f`r?Ryg}Yb-k}iRc@f%YWkX7)D*593zGKX?l8qdkWcKdP^Sw7>;3Ml+j4)i ziXO1le(%+7^~h)#azUzUwqIRUkA8)%)(33yXt*^SG{Ixi)hBptK$R!T?>moMLYw6w z(zb-jWI~WGTSlixx(I1)djq5Z$@SDkKCnHW2B7OkbT<2i)pB0rL$Eg*N?Ue~15Es_`q$g6+$ zHEHc<7s6znpa|r8xhKp7K?twNS9gwA8bV}yr9HHHC8YVP6x#HJHns2-(AujSsdav* zf7Kg?QJGC7vR6mLEWaSv8wxeT(6BmpAXW~Ai4OD_GGK?(p6Stp5kr3N-l_=W*g;2j==B${6k9cf~a$dk{NPG>%3 zG4QG8JOO9_G+frHo#f|_)nUlVLGW<-G-z#ori3vV#!r+6pLxS01myiZPj!IKEn9`u zYl8@X9&VKdG4}cB)#+aJg}CL#a0OqG+Ww?@aX@WwI9Fut>S8%ti6;t2zYE z)KyX^Q??6t2x5F~XLF5^HZx%ef8H?E*;^x+qVb=O3iz_b{?2m*%l0xM+uJz?*bIm$ z*gMTT1oI61H>tQS!)>%3FAC$!#P-hm9T<^|5xL&Z?j3?@rUUm5Z|7jMb>u6^UT{lUcE$UV7Jb`P%CK;;w5x#*BDn!QqKZybnx&QzG diff --git a/binaries/x86_64/tpws b/binaries/x86_64/tpws index c8d471a9d3bcc242325457aaa048afe7fae86a4f..ce3d3d5fef218b91f5a57cd14bcd34bea3f84fe0 100755 GIT binary patch delta 16063 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 L+ZgF+015kl*hVkO diff --git a/tpws/tpws.c b/tpws/tpws.c index 3602d6c..3b2fc49 100644 --- a/tpws/tpws.c +++ b/tpws/tpws.c @@ -24,690 +24,705 @@ #include "tpws.h" #include "tpws_conn.h" -enum splithttpreq {split_none=0,split_method,split_host}; +enum splithttpreq { split_none = 0, split_method, split_host }; struct params_s { - char bindaddr[64]; - uid_t uid; - gid_t gid; - uint16_t port; - bool daemon; - bool hostcase,hostdot,hosttab,methodspace,methodeol,unixeol; - char hostspell[4]; - enum splithttpreq split_http_req; - int split_pos; - int maxconn; + char bindaddr[64]; + uid_t uid; + gid_t gid; + uint16_t port; + bool daemon; + bool hostcase, hostdot, hosttab, methodspace, methodeol, unixeol; + char hostspell[4]; + enum splithttpreq split_http_req; + int split_pos; + int maxconn; }; struct params_s params; -unsigned char *find_bin(void *data,ssize_t len,const void *blk,ssize_t blk_len) +unsigned char *find_bin(void *data, ssize_t len, const void *blk, ssize_t blk_len) { - while (len>=blk_len) - { - if (!memcmp(data,blk,blk_len)) - return data; - data=(char*)data+1; - len--; - } - return NULL; + while (len >= blk_len) + { + if (!memcmp(data, blk, blk_len)) + return data; + data = (char*)data + 1; + len--; + } + return NULL; } ssize_t send_with_flush(int sockfd, const void *buf, size_t len, int flags) { - int flag,err; - ssize_t wr; + int flag, err; + ssize_t wr; - flag=1; - setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)); - wr=send(sockfd,buf,len,flags); - err=errno; - flag=0; - setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)); - errno=err; - return wr; + flag = 1; + setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int)); + wr = send(sockfd, buf, len, flags); + err = errno; + flag = 0; + setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int)); + errno = err; + return wr; } -void close_tcp_conn(tproxy_conn_t *conn, struct tailhead *conn_list, - struct tailhead *close_list){ - conn->state = CONN_CLOSED; - TAILQ_REMOVE(conn_list, conn, conn_ptrs); - TAILQ_INSERT_TAIL(close_list, conn, conn_ptrs); +void close_tcp_conn(tproxy_conn_t *conn, struct tailhead *conn_list, + struct tailhead *close_list) { + conn->state = CONN_CLOSED; + TAILQ_REMOVE(conn_list, conn, conn_ptrs); + TAILQ_INSERT_TAIL(close_list, conn, conn_ptrs); } -static const char *http_split_methods[]={"GET /","POST /","HEAD /","OPTIONS /",NULL}; -static const char *http_split_host[]={"\r\nHost: ",NULL}; +static const char *http_methods[] = { "GET /","POST /","HEAD /","OPTIONS /","PUT /","DELETE /","CONNECT /","TRACE /",NULL }; +static const char *http_split_host[] = { "\r\nHost: ",NULL }; #define RD_BLOCK_SIZE 8192 -bool handle_epollin(tproxy_conn_t *conn,int *data_transferred){ - int numbytes; - int fd_in, fd_out; - bool bOutgoing; - ssize_t rd=0,wr=0,bs; +bool handle_epollin(tproxy_conn_t *conn, int *data_transferred) { + int numbytes; + int fd_in, fd_out; + bool bOutgoing; + ssize_t rd = 0, wr = 0, bs; - //Easy way to determin which socket is ready for reading - //TODO: Optimize. This one allows me quick lookup for conn, but - //I need to make a system call to determin which socket - numbytes=0; - if(ioctl(conn->local_fd, FIONREAD, &numbytes) != -1 - && numbytes > 0){ - fd_in = conn->local_fd; - fd_out = conn->remote_fd; - bOutgoing = true; - } else { - fd_in = conn->remote_fd; - fd_out = conn->local_fd; - numbytes=0; - ioctl(fd_in, FIONREAD, &numbytes); - bOutgoing = false; - } + //Easy way to determin which socket is ready for reading + //TODO: Optimize. This one allows me quick lookup for conn, but + //I need to make a system call to determin which socket + numbytes = 0; + if (ioctl(conn->local_fd, FIONREAD, &numbytes) != -1 + && numbytes > 0) { + fd_in = conn->local_fd; + fd_out = conn->remote_fd; + bOutgoing = true; + } + else { + fd_in = conn->remote_fd; + fd_out = conn->local_fd; + numbytes = 0; + ioctl(fd_in, FIONREAD, &numbytes); + bOutgoing = false; + } - if (numbytes) - { - if (bOutgoing) - { - char buf[RD_BLOCK_SIZE+4],*p,*phost=NULL; - ssize_t l,split_pos=0,method_split_pos=0,host_split_pos=0,split_array_pos_offset=1,pos; - const char **split_array=NULL, **split_item, **item; - - rd = recv(fd_in,buf,RD_BLOCK_SIZE,MSG_DONTWAIT); - if (rd>0) - { - bs = rd; - - if (params.unixeol) + if (numbytes) + { + if (bOutgoing) { - printf("Replacing 0D0A to 0A\n"); - p = buf; - while (p=find_bin(p,buf+bs-p,"\r\n",2)) - { - *p = '\n'; p++; - memmove(p,p+1,buf+bs-p-1); - bs--; - } - } + char buf[RD_BLOCK_SIZE + 4], *p, *pp, *phost = NULL; + ssize_t l, method_len=0, split_pos = 0, method_split_pos = 0, host_split_pos = 0, split_array_pos_offset = 1, pos; + const char **split_array = NULL, **split_item, **method; + bool bIsHttp; - if (params.methodspace) - { - for(item=http_split_methods;*item;item++) - { - l = strlen(*item); - if (p=find_bin(buf,bs,*item,l)) - { - pos = p-buf; - printf("Found http method '%s' at pos %zd. Adding extra space.\n",*item,pos); - p += l-1; - pos += l-1; - memmove(p+1,p,bs-pos); - *p = ' '; // insert extra space - bs++; // block will grow by 1 byte - method_split_pos = pos-2; // remember split position and use it if required - break; - } - } - } - if (params.hostdot || params.hosttab) - { - if (phost=find_bin(buf,bs,params.unixeol ? "\nHost: " : "\r\nHost: ",params.unixeol ? 7 : 8)) - { - host_split_pos = phost-buf+7; - p = phost+8; - while(p<(buf+bs) && *p!='\r' && *p!='\n') p++; - if (p<(buf+bs)) - { - pos = p-buf; - printf("Adding %s to host name at pos %zd\n",params.hostdot ? "dot" : "tab",pos); - memmove(p+1,p,bs-pos); - *p = params.hostdot ? '.' : '\t'; // insert dot or tab - bs++; // block will grow by 1 byte - } - } - } - if (params.split_pos) - { - split_pos = params.split_pos 0) { - case split_method: - // do we have already split position ? if so use it without another search - if (method_split_pos) - split_pos = method_split_pos; + bs = rd; + + bIsHttp = false; + for (method = http_methods; *method; method++) + { + method_len = strlen(*method); + if (method_len <= bs && !memcmp(buf, *method, method_len)) + { + bIsHttp = true; + method_len-=2; // "GET /" => "GET" + break; + } + } + if (bIsHttp) + { + printf("Data block looks like http request start : %s\n", *method); + + if (params.unixeol) + { + printf("Replacing 0D0A to 0A\n"); + p = pp = buf; + while (p = find_bin(p, buf + bs - p, "\r\n", 2)) + { + *p = '\n'; p++; + memmove(p, p + 1, buf + bs - p - 1); + bs--; + if (pp == (p - 1)) + { + // probably end of http headers + printf("Found double EOL at pos %zd. Stop replacing.\n", pp - buf); + break; + } + pp = p; + } + } + + if (params.methodspace) + { + // we only work with data blocks looking as HTTP query, so method is at the beginning + printf("Adding extra space after method\n"); + p = buf + method_len + 1; + pos = method_len + 1; + memmove(p + 1, p, bs - pos); + *p = ' '; // insert extra space + bs++; // block will grow by 1 byte + } + if (params.hostdot || params.hosttab) + { + if (phost = find_bin(buf, bs, params.unixeol ? "\nHost: " : "\r\nHost: ", params.unixeol ? 7 : 8)) + { + host_split_pos = phost - buf + 7; + p = phost + 8; + while (p < (buf + bs) && *p != '\r' && *p != '\n') p++; + if (p < (buf + bs)) + { + pos = p - buf; + printf("Adding %s to host name at pos %zd\n", params.hostdot ? "dot" : "tab", pos); + memmove(p + 1, p, bs - pos); + *p = params.hostdot ? '.' : '\t'; // insert dot or tab + bs++; // block will grow by 1 byte + } + } + } + if (params.split_pos) + { + split_pos = params.split_pos < bs ? params.split_pos : 0; + } + else + { + switch (params.split_http_req) + { + case split_method: + split_pos = method_len - 1; + break; + case split_host: + if (host_split_pos) + split_pos = host_split_pos; + else + split_array = http_split_host; + break; + } + } + if (split_array) + { + // we havent found split post yet. need to search. + for (split_item = split_array; *split_item; split_item++) + { + l = strlen(*split_item); + if (p = find_bin(buf, bs, *split_item, l)) + { + split_pos = p - buf; + printf("Found split item '%s' at pos %zd. Split offset is -%zd.\n", *split_item, split_pos, split_array_pos_offset); + split_pos += l - split_array_pos_offset; + break; + } + } + } + if (params.hostcase) + { + if (phost || (phost = find_bin(buf, bs, params.unixeol ? "\nHost: " : "\r\nHost: ", params.unixeol ? 7 : 8))) + { + printf("Changing 'Host:' => '%c%c%c%c:' at pos %zd\n", params.hostspell[0], params.hostspell[1], params.hostspell[2], params.hostspell[3], phost - buf + 2); + memcpy(phost + 2, params.hostspell, 4); + } + } + if (params.methodeol) + { + printf("Adding EOL before method\n"); + if (params.unixeol) + { + memmove(buf + 1, buf, bs); + bs++;; + buf[0] = '\n'; + if (split_pos) split_pos++; + } + else + { + memmove(buf + 2, buf, bs); + bs += 2; + buf[0] = '\r'; + buf[1] = '\n'; + if (split_pos) split_pos += 2; + } + } + } else { - split_array = http_split_methods; - split_array_pos_offset = 3; + printf("Data block does not look like http request start\n"); + } + + if (split_pos) + { + printf("Splitting at pos %zd\n", split_pos); + wr = send_with_flush(fd_out, buf, split_pos, 0); + if (wr >= 0) + wr = send(fd_out, buf + split_pos, bs - split_pos, 0); + } + else + { + wr = send(fd_out, buf, bs, 0); } - break; - case split_host: - if (host_split_pos) - split_pos = host_split_pos; - else - split_array = http_split_host; - break; } } - if (split_array) - { - // we havent found split post yet. need to search. - for(split_item=split_array;*split_item;split_item++) - { - l = strlen(*split_item); - if (p=find_bin(buf,bs,*split_item,l)) - { - split_pos = p-buf; - printf("Found split item '%s' at pos %zd. Split offset is -%zd.\n",*split_item,split_pos,split_array_pos_offset); - split_pos += l-split_array_pos_offset; - break; - } - } - } - if (params.hostcase) - { - if (phost || (phost=find_bin(buf,bs,params.unixeol ? "\nHost: " : "\r\nHost: ",params.unixeol ? 7 : 8))) - { - printf("Changing 'Host:' => '%c%c%c%c:' at pos %zd\n",params.hostspell[0],params.hostspell[1],params.hostspell[2],params.hostspell[3],phost-buf+2); - memcpy(phost+2,params.hostspell,4); - } - } - if (params.methodeol) - { - printf("Adding EOL before method\n"); - if (params.unixeol) - { - memmove(buf+1,buf,bs); - bs++;; - buf[0]='\n'; - if (split_pos) split_pos++; - } - else - { - memmove(buf+2,buf,bs); - bs+=2; - buf[0]='\r'; - buf[1]='\n'; - if (split_pos) split_pos+=2; - } - } - if (split_pos) - { - printf("Splitting at pos %zd\n",split_pos); - wr=send_with_flush(fd_out,buf,split_pos,0); - if (wr>=0) - wr=send(fd_out,buf+split_pos,bs-split_pos,0); - } else { - wr=send(fd_out,buf,bs,0); + // *** we are not interested in incoming traffic + // splice it without processing + + //printf("splicing numbytes=%d\n",numbytes); + rd = numbytes = splice(fd_in, NULL, conn->splice_pipe[1], NULL, + SPLICE_LEN, SPLICE_F_MOVE | SPLICE_F_NONBLOCK); + //printf("spliced rd=%d\n",rd); + if (rd > 0) + { + wr = splice(conn->splice_pipe[0], NULL, fd_out, NULL, + rd, SPLICE_F_MOVE); + } + //printf("splice rd=%d wr=%d\n",rd,wr); } - } } - else - { - // *** we are not interested in incoming traffic - // splice it without processing + if (data_transferred) *data_transferred = rd < 0 ? 0 : rd; + return rd != -1 && wr != -1; +} - //printf("splicing numbytes=%d\n",numbytes); - rd = numbytes = splice(fd_in, NULL, conn->splice_pipe[1], NULL, - SPLICE_LEN, SPLICE_F_MOVE | SPLICE_F_NONBLOCK); - //printf("spliced rd=%d\n",rd); - if (rd>0) - { - wr = splice(conn->splice_pipe[0], NULL, fd_out, NULL, - rd, SPLICE_F_MOVE); - } - //printf("splice rd=%d wr=%d\n",rd,wr); +void remove_closed_connections(struct tailhead *close_list) { + tproxy_conn_t *conn = NULL; + + while (close_list->tqh_first != NULL) { + conn = (tproxy_conn_t*)close_list->tqh_first; + TAILQ_REMOVE(close_list, close_list->tqh_first, conn_ptrs); + + int rd = 0; + while (handle_epollin(conn, &rd) && rd); + + printf("Socket %d and %d closed, connection removed\n", + conn->local_fd, conn->remote_fd); + free_conn(conn); } - } - if (data_transferred) *data_transferred = rd<0 ? 0 : rd; - return rd!=-1 && wr!=-1; } -void remove_closed_connections(struct tailhead *close_list){ - tproxy_conn_t *conn = NULL; +int event_loop(int listen_fd) { + int retval = 0, num_events = 0; + int tmp_fd = 0; //Used to temporarily hold the accepted file descriptor + tproxy_conn_t *conn = NULL; + int efd, i; + struct epoll_event ev, events[MAX_EPOLL_EVENTS]; + struct tailhead conn_list, close_list; + uint8_t check_close = 0; + int conncount = 0; - while(close_list->tqh_first != NULL){ - conn = (tproxy_conn_t*) close_list->tqh_first; - TAILQ_REMOVE(close_list, close_list->tqh_first, conn_ptrs); + //Initialize queue (remember that TAILQ_HEAD just defines the struct) + TAILQ_INIT(&conn_list); + TAILQ_INIT(&close_list); - int rd=0; - while(handle_epollin(conn,&rd) && rd); + if ((efd = epoll_create(1)) == -1) { + perror("epoll_create"); + return -1; + } - printf("Socket %d and %d closed, connection removed\n", - conn->local_fd, conn->remote_fd); - free_conn(conn); - } + //Start monitoring listen socket + memset(&ev, 0, sizeof(ev)); + ev.events = EPOLLIN; + //There is only one listen socket, and I want to use ptr in order to have + //easy access to the connections. So if ptr is NULL that means an event on + //listen socket. + ev.data.ptr = NULL; + if (epoll_ctl(efd, EPOLL_CTL_ADD, listen_fd, &ev) == -1) { + perror("epoll_ctl (listen socket)"); + return -1; + } + + while (1) { + if ((num_events = epoll_wait(efd, events, MAX_EPOLL_EVENTS, -1)) == -1) { + perror("epoll_wait"); + retval = -1; + break; + } + + for (i = 0; i < num_events; i++) { + if (events[i].data.ptr == NULL) { + //Accept new connection + tmp_fd = accept(listen_fd, NULL, 0); + if (tmp_fd < 0) + { + fprintf(stderr, "Failed to accept connection\n"); + } + else if (conncount >= params.maxconn) + { + close(tmp_fd); + fprintf(stderr, "Too much connections : %d\n", conncount); + } + else if ((conn = add_tcp_connection(efd, &conn_list, tmp_fd, params.port)) == NULL) + { + close(tmp_fd); + fprintf(stderr, "Failed to add connection\n"); + } + else + { + conncount++; + printf("Connections : %d\n", conncount); + } + } + else { + conn = (tproxy_conn_t*)events[i].data.ptr; + + //Only applies to remote_fd, connection attempt has + //succeeded/failed + if (events[i].events & EPOLLOUT) { + if (check_connection_attempt(conn, efd) == -1) { + fprintf(stderr, "Connection attempt failed for %d\n", + conn->remote_fd); + check_close = 1; + close_tcp_conn(conn, &conn_list, &close_list); + conncount--; + } + continue; + } + else if (conn->state != CONN_CLOSED && + (events[i].events & EPOLLRDHUP || + events[i].events & EPOLLHUP || + events[i].events & EPOLLERR)) { + check_close = 1; + close_tcp_conn(conn, &conn_list, &close_list); + conncount--; + continue; + } + + //Since I use an event cache, earlier events might cause for + //example this connection to be closed. No need to process fd if + //that is the case + if (conn->state == CONN_CLOSED) { + continue; + } + + if (!handle_epollin(conn, NULL)) { + close_tcp_conn(conn, &conn_list, &close_list); + conncount--; + check_close = 1; + } + } + } + + //Remove connections + if (check_close) + remove_closed_connections(&close_list); + + check_close = 0; + } + + //Add cleanup + return retval; } -int event_loop(int listen_fd){ - int retval = 0, num_events = 0; - int tmp_fd = 0; //Used to temporarily hold the accepted file descriptor - tproxy_conn_t *conn = NULL; - int efd, i; - struct epoll_event ev, events[MAX_EPOLL_EVENTS]; - struct tailhead conn_list, close_list; - uint8_t check_close = 0; - int conncount = 0; +int8_t block_sigpipe() { + sigset_t sigset; + memset(&sigset, 0, sizeof(sigset)); - //Initialize queue (remember that TAILQ_HEAD just defines the struct) - TAILQ_INIT(&conn_list); - TAILQ_INIT(&close_list); - - if((efd = epoll_create(1)) == -1){ - perror("epoll_create"); - return -1; - } + //Get the old sigset, add SIGPIPE and update sigset + if (sigprocmask(SIG_BLOCK, NULL, &sigset) == -1) { + perror("sigprocmask (get)"); + return -1; + } - //Start monitoring listen socket - memset(&ev, 0, sizeof(ev)); - ev.events = EPOLLIN; - //There is only one listen socket, and I want to use ptr in order to have - //easy access to the connections. So if ptr is NULL that means an event on - //listen socket. - ev.data.ptr = NULL; - if(epoll_ctl(efd, EPOLL_CTL_ADD, listen_fd, &ev) == -1){ - perror("epoll_ctl (listen socket)"); - return -1; - } - - while(1){ - if((num_events = epoll_wait(efd, events, MAX_EPOLL_EVENTS, -1)) == -1){ - perror("epoll_wait"); - retval = -1; - break; - } + if (sigaddset(&sigset, SIGPIPE) == -1) { + perror("sigaddset"); + return -1; + } - for(i=0; i=params.maxconn) - { - close(tmp_fd); - fprintf(stderr, "Too much connections : %d\n",conncount); - } - else if((conn = add_tcp_connection(efd, &conn_list, tmp_fd, params.port)) == NULL) - { - close(tmp_fd); - fprintf(stderr, "Failed to add connection\n"); - } - else - { - conncount++; - printf("Connections : %d\n",conncount); - } - } else { - conn = (tproxy_conn_t*) events[i].data.ptr; + if (sigprocmask(SIG_BLOCK, &sigset, NULL) == -1) { + perror("sigprocmask (set)"); + return -1; + } - //Only applies to remote_fd, connection attempt has - //succeeded/failed - if(events[i].events & EPOLLOUT){ - if(check_connection_attempt(conn, efd) == -1){ - fprintf(stderr, "Connection attempt failed for %d\n", - conn->remote_fd); - check_close = 1; - close_tcp_conn(conn, &conn_list, &close_list); - conncount--; - } - continue; - } else if(conn->state != CONN_CLOSED && - (events[i].events & EPOLLRDHUP || - events[i].events & EPOLLHUP || - events[i].events & EPOLLERR)){ - check_close = 1; - close_tcp_conn(conn, &conn_list, &close_list); - conncount--; - continue; - } - - //Since I use an event cache, earlier events might cause for - //example this connection to be closed. No need to process fd if - //that is the case - if(conn->state == CONN_CLOSED){ - continue; - } - - if (!handle_epollin(conn,NULL)){ - close_tcp_conn(conn, &conn_list, &close_list); - conncount--; - check_close = 1; - } - } - } - - //Remove connections - if(check_close) - remove_closed_connections(&close_list); - - check_close = 0; - } - - //Add cleanup - return retval; -} - -int8_t block_sigpipe(){ - sigset_t sigset; - memset(&sigset, 0, sizeof(sigset)); - - //Get the old sigset, add SIGPIPE and update sigset - if(sigprocmask(SIG_BLOCK, NULL, &sigset) == -1){ - perror("sigprocmask (get)"); - return -1; - } - - if(sigaddset(&sigset, SIGPIPE) == -1){ - perror("sigaddset"); - return -1; - } - - if(sigprocmask(SIG_BLOCK, &sigset, NULL) == -1){ - perror("sigprocmask (set)"); - return -1; - } - - return 0; + return 0; } void exithelp() { - printf(" --bind-addr=|\n --port=\n --maxconn=\n --split-http-req=method|host\n --split-pos=\t; split at specified pos. invalidates split-http-req.\n --hostcase\t\t; change Host: => host:\n --hostspell\t\t; exact spelling of \"Host\" header. must be 4 chars. default is \"host\"\n --hostdot\t\t; add \".\" after Host: name\n --hosttab\t\t; add tab after Host: name\n --methodspace\t\t; add extra space after method\n --methodeol\t\t; add end-of-line before method\n --unixeol\t\t; replace 0D0A to 0A\n --daemon\t\t; daemonize\n --user=\t; drop root privs\n"); - exit(1); + printf(" --bind-addr=|\n --port=\n --maxconn=\n --split-http-req=method|host\n --split-pos=\t; split at specified pos. invalidates split-http-req.\n --hostcase\t\t; change Host: => host:\n --hostspell\t\t; exact spelling of \"Host\" header. must be 4 chars. default is \"host\"\n --hostdot\t\t; add \".\" after Host: name\n --hosttab\t\t; add tab after Host: name\n --methodspace\t\t; add extra space after method\n --methodeol\t\t; add end-of-line before method\n --unixeol\t\t; replace 0D0A to 0A\n --daemon\t\t; daemonize\n --user=\t; drop root privs\n"); + exit(1); } void parse_params(int argc, char *argv[]) { - int option_index=0; - int v,i; + int option_index = 0; + int v, i; - memset(¶ms,0,sizeof(params)); - memcpy(params.hostspell,"host",4); // default hostspell - params.maxconn = DEFAULT_MAX_CONN; - - const struct option long_options[] = { - {"help",no_argument,0,0},// optidx=0 - {"h",no_argument,0,0},// optidx=1 - {"bind-addr",required_argument,0,0},// optidx=2 - {"port",required_argument,0,0},// optidx=3 - {"daemon",no_argument,0,0},// optidx=4 - {"user",required_argument,0,0},// optidx=5 - {"maxconn",required_argument,0,0},// optidx=6 - {"hostcase",no_argument,0,0},// optidx=7 - {"hostspell",required_argument,0,0},// optidx=8 - {"hostdot",no_argument,0,0},// optidx=9 - {"split-http-req",required_argument,0,0},// optidx=10 - {"split-pos",required_argument,0,0},// optidx=11 - {"methodspace",no_argument,0,0},// optidx=12 - {"methodeol",no_argument,0,0},// optidx=13 - {"hosttab",no_argument,0,0},// optidx=14 - {"unixeol",no_argument,0,0},// optidx=15 - {NULL,0,NULL,0} - }; - while ((v=getopt_long_only(argc,argv,"",long_options,&option_index))!=-1) - { - if (v) exithelp(); - switch(option_index) + memset(¶ms, 0, sizeof(params)); + memcpy(params.hostspell, "host", 4); // default hostspell + params.maxconn = DEFAULT_MAX_CONN; + + const struct option long_options[] = { + { "help",no_argument,0,0 },// optidx=0 + { "h",no_argument,0,0 },// optidx=1 + { "bind-addr",required_argument,0,0 },// optidx=2 + { "port",required_argument,0,0 },// optidx=3 + { "daemon",no_argument,0,0 },// optidx=4 + { "user",required_argument,0,0 },// optidx=5 + { "maxconn",required_argument,0,0 },// optidx=6 + { "hostcase",no_argument,0,0 },// optidx=7 + { "hostspell",required_argument,0,0 },// optidx=8 + { "hostdot",no_argument,0,0 },// optidx=9 + { "split-http-req",required_argument,0,0 },// optidx=10 + { "split-pos",required_argument,0,0 },// optidx=11 + { "methodspace",no_argument,0,0 },// optidx=12 + { "methodeol",no_argument,0,0 },// optidx=13 + { "hosttab",no_argument,0,0 },// optidx=14 + { "unixeol",no_argument,0,0 },// optidx=15 + { NULL,0,NULL,0 } + }; + while ((v = getopt_long_only(argc, argv, "", long_options, &option_index)) != -1) { - case 0: - case 1: - exithelp(); - break; - case 2: /* bind-addr */ - strncpy(params.bindaddr,optarg,sizeof(params.bindaddr)); - params.bindaddr[sizeof(params.bindaddr)-1] = 0; - break; - case 3: /* qnum */ - i=atoi(optarg); - if (i<=0 || i>65535) - { - fprintf(stderr,"bad port number\n"); - exit(1); - } - params.port=(uint16_t)i; - break; - case 4: /* daemon */ - params.daemon = true; - break; - case 5: /* user */ - { - struct passwd *pwd = getpwnam(optarg); - if (!pwd) + if (v) exithelp(); + switch (option_index) { - fprintf(stderr,"non-existent username supplied\n"); - exit(1); + case 0: + case 1: + exithelp(); + break; + case 2: /* bind-addr */ + strncpy(params.bindaddr, optarg, sizeof(params.bindaddr)); + params.bindaddr[sizeof(params.bindaddr) - 1] = 0; + break; + case 3: /* qnum */ + i = atoi(optarg); + if (i <= 0 || i > 65535) + { + fprintf(stderr, "bad port number\n"); + exit(1); + } + params.port = (uint16_t)i; + break; + case 4: /* daemon */ + params.daemon = true; + break; + case 5: /* user */ + { + struct passwd *pwd = getpwnam(optarg); + if (!pwd) + { + fprintf(stderr, "non-existent username supplied\n"); + exit(1); + } + params.uid = pwd->pw_uid; + params.gid = pwd->pw_gid; + break; + } + case 6: /* maxconn */ + params.maxconn = atoi(optarg); + if (params.maxconn <= 0) + { + fprintf(stderr, "bad maxconn\n"); + exit(1); + } + break; + case 7: /* hostcase */ + params.hostcase = true; + break; + case 8: /* hostspell */ + if (strlen(optarg) != 4) + { + fprintf(stdout, "hostspell must be exactly 4 chars long\n"); + exit(1); + } + params.hostcase = true; + memcpy(params.hostspell, optarg, 4); + break; + case 9: /* hostdot */ + params.hostdot = true; + break; + case 10: /* 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 + { + fprintf(stderr, "Invalid argument for split-http-req\n"); + exit(1); + } + break; + case 11: /* split-pos */ + i = atoi(optarg); + if (i) + params.split_pos = i; + else + { + fprintf(stderr, "Invalid argument for split-pos\n"); + exit(1); + } + break; + case 12: /* methodspace */ + params.methodspace = true; + break; + case 13: /* methodeol */ + params.methodeol = true; + break; + case 14: /* hosttab */ + params.hosttab = true; + break; + case 15: /* unixeol */ + params.unixeol = true; + break; } - params.uid = pwd->pw_uid; - params.gid = pwd->pw_gid; - break; - } - case 6: /* maxconn */ - params.maxconn=atoi(optarg); - if (params.maxconn<=0) - { - fprintf(stderr,"bad maxconn\n"); - exit(1); - } - break; - case 7: /* hostcase */ - params.hostcase = true; - break; - case 8: /* hostspell */ - if (strlen(optarg)!=4) - { - fprintf(stdout,"hostspell must be exactly 4 chars long\n"); - exit(1); - } - params.hostcase = true; - memcpy(params.hostspell,optarg,4); - break; - case 9: /* hostdot */ - params.hostdot = true; - break; - case 10: /* 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 - { - fprintf(stderr,"Invalid argument for split-http-req\n"); - exit(1); - } - break; - case 11: /* split-pos */ - i = atoi(optarg); - if (i) - params.split_pos = i; - else - { - fprintf(stderr,"Invalid argument for split-pos\n"); - exit(1); - } - break; - case 12: /* methodspace */ - params.methodspace = true; - break; - case 13: /* methodeol */ - params.methodeol = true; - break; - case 14: /* hosttab */ - params.hosttab = true; - break; - case 15: /* unixeol */ - params.unixeol = true; - break; } - } - if (!params.port) - { - fprintf(stderr,"Need port number\n"); - exit(1); - } + if (!params.port) + { + fprintf(stderr, "Need port number\n"); + exit(1); + } } void daemonize() { - int pid; + int pid; - pid = fork(); - if (pid == -1) - { - perror("fork: "); - exit(2); - } - else if (pid != 0) - exit(0); + pid = fork(); + if (pid == -1) + { + perror("fork: "); + exit(2); + } + else if (pid != 0) + exit(0); - if (setsid() == -1) - exit(2); - if (chdir ("/") == -1) - exit(2); - close(STDIN_FILENO); - close(STDOUT_FILENO); - close(STDERR_FILENO); - /* redirect fd's 0,1,2 to /dev/null */ - open ("/dev/null", O_RDWR); - /* stdin */ - dup(0); - /* stdout */ - dup(0); - /* stderror */ + if (setsid() == -1) + exit(2); + if (chdir("/") == -1) + exit(2); + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + /* redirect fd's 0,1,2 to /dev/null */ + open("/dev/null", O_RDWR); + /* stdin */ + dup(0); + /* stdout */ + dup(0); + /* stderror */ } bool droproot() { - if (params.uid) - { - if (setgid(params.gid)) - { - perror("setgid: "); - return false; - } - if (setuid(params.uid)) - { - perror("setuid: "); - return false; - } - } - return true; + if (params.uid) + { + if (setgid(params.gid)) + { + perror("setgid: "); + return false; + } + if (setuid(params.uid)) + { + perror("setuid: "); + return false; + } + } + return true; } -int main(int argc, char *argv[]){ - int listen_fd = 0; - int yes = 1, retval = 0; - int r; - struct sockaddr_storage salisten; - socklen_t salisten_len; - int ipv6_only; +int main(int argc, char *argv[]) { + int listen_fd = 0; + int yes = 1, retval = 0; + int r; + struct sockaddr_storage salisten; + socklen_t salisten_len; + int ipv6_only; - parse_params(argc,argv); + parse_params(argc, argv); - memset(&salisten,0,sizeof(salisten)); - if (*params.bindaddr) - { - if (inet_pton(AF_INET,params.bindaddr, &((struct sockaddr_in*)&salisten)->sin_addr)) + memset(&salisten, 0, sizeof(salisten)); + if (*params.bindaddr) { - salisten.ss_family = AF_INET; - ((struct sockaddr_in*)&salisten)->sin_port = htons(params.port); - salisten_len = sizeof(struct sockaddr_in); - } - else if (inet_pton(AF_INET6,params.bindaddr, &((struct sockaddr_in6*)&salisten)->sin6_addr)) - { - salisten.ss_family = AF_INET6; - ((struct sockaddr_in6*)&salisten)->sin6_port = htons(params.port); - salisten_len = sizeof(struct sockaddr_in6); - ipv6_only=1; + if (inet_pton(AF_INET, params.bindaddr, &((struct sockaddr_in*)&salisten)->sin_addr)) + { + salisten.ss_family = AF_INET; + ((struct sockaddr_in*)&salisten)->sin_port = htons(params.port); + salisten_len = sizeof(struct sockaddr_in); + } + else if (inet_pton(AF_INET6, params.bindaddr, &((struct sockaddr_in6*)&salisten)->sin6_addr)) + { + salisten.ss_family = AF_INET6; + ((struct sockaddr_in6*)&salisten)->sin6_port = htons(params.port); + salisten_len = sizeof(struct sockaddr_in6); + ipv6_only = 1; + } + else + { + printf("bad bind addr\n"); + exit(1); + } } else { - printf("bad bind addr\n"); - exit(1); + salisten.ss_family = AF_INET6; + ((struct sockaddr_in6*)&salisten)->sin6_port = htons(params.port); + salisten_len = sizeof(struct sockaddr_in6); + ipv6_only = 0; + // leave sin6_addr zero } - } - else - { - salisten.ss_family = AF_INET6; - ((struct sockaddr_in6*)&salisten)->sin6_port = htons(params.port); - salisten_len = sizeof(struct sockaddr_in6); - ipv6_only=0; - // leave sin6_addr zero - } - - if (params.daemon) daemonize(); - - if((listen_fd = socket(salisten.ss_family, SOCK_STREAM, 0)) == -1){ - perror("socket: "); - exit(EXIT_FAILURE); - } - - if ((salisten.ss_family==AF_INET6) && setsockopt(listen_fd, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only, sizeof(ipv6_only)) == -1) - { - perror("setsockopt (IPV6_ONLY): "); - close(listen_fd); - exit(EXIT_FAILURE); - } - if(setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) - { - perror("setsockopt (SO_REUSEADDR): "); - close(listen_fd); - exit(EXIT_FAILURE); - } + if (params.daemon) daemonize(); - //Mark that this socket can be used for transparent proxying - //This allows the socket to accept connections for non-local IPs - if(setsockopt(listen_fd, SOL_IP, IP_TRANSPARENT, &yes, sizeof(yes)) == -1) - { - perror("setsockopt (IP_TRANSPARENT): "); - close(listen_fd); - exit(EXIT_FAILURE); - } + if ((listen_fd = socket(salisten.ss_family, SOCK_STREAM, 0)) == -1) { + perror("socket: "); + exit(EXIT_FAILURE); + } - if (!droproot()) - { - close(listen_fd); - exit(EXIT_FAILURE); - } + if ((salisten.ss_family == AF_INET6) && setsockopt(listen_fd, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only, sizeof(ipv6_only)) == -1) + { + perror("setsockopt (IPV6_ONLY): "); + close(listen_fd); + exit(EXIT_FAILURE); + } - if(bind(listen_fd, (struct sockaddr *)&salisten, salisten_len) == -1){ - perror("bind: "); - close(listen_fd); - exit(EXIT_FAILURE); - } + if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) + { + perror("setsockopt (SO_REUSEADDR): "); + close(listen_fd); + exit(EXIT_FAILURE); + } - if(listen(listen_fd, BACKLOG) == -1){ - perror("listen: "); - close(listen_fd); - exit(EXIT_FAILURE); - } + //Mark that this socket can be used for transparent proxying + //This allows the socket to accept connections for non-local IPs + if (setsockopt(listen_fd, SOL_IP, IP_TRANSPARENT, &yes, sizeof(yes)) == -1) + { + perror("setsockopt (IP_TRANSPARENT): "); + close(listen_fd); + exit(EXIT_FAILURE); + } - //splice() causes the process to receive the SIGPIPE-signal if one part (for - //example a socket) is closed during splice(). I would rather have splice() - //fail and return -1, so blocking SIGPIPE. - if(block_sigpipe() == -1){ - fprintf(stderr, "Could not block SIGPIPE signal\n"); - close(listen_fd); - exit(EXIT_FAILURE); - } + if (!droproot()) + { + close(listen_fd); + exit(EXIT_FAILURE); + } - fprintf(stderr, "Will listen to port %d\n", params.port); + if (bind(listen_fd, (struct sockaddr *)&salisten, salisten_len) == -1) { + perror("bind: "); + close(listen_fd); + exit(EXIT_FAILURE); + } - retval = event_loop(listen_fd); - close(listen_fd); + if (listen(listen_fd, BACKLOG) == -1) { + perror("listen: "); + close(listen_fd); + exit(EXIT_FAILURE); + } - fprintf(stderr, "Will exit\n"); - - if(retval < 0) - exit(EXIT_FAILURE); - else - exit(EXIT_SUCCESS); + //splice() causes the process to receive the SIGPIPE-signal if one part (for + //example a socket) is closed during splice(). I would rather have splice() + //fail and return -1, so blocking SIGPIPE. + if (block_sigpipe() == -1) { + fprintf(stderr, "Could not block SIGPIPE signal\n"); + close(listen_fd); + exit(EXIT_FAILURE); + } + fprintf(stderr, "Will listen to port %d\n", params.port); + + retval = event_loop(listen_fd); + close(listen_fd); + + fprintf(stderr, "Will exit\n"); + + if (retval < 0) + exit(EXIT_FAILURE); + else + exit(EXIT_SUCCESS); }