From 1d1e804a1ca72d521f9d9dc9ed1883fb02201f1b Mon Sep 17 00:00:00 2001 From: Laxmikant Rashinkar Date: Mon, 22 Apr 2013 12:54:57 -0700 Subject: [PATCH] first checkin for gtcp-proxy, a man-in-the-middle program to monitor network traffice --- tests/gtcp_proxy/README.txt | 4 + tests/gtcp_proxy/gtcp-proxy | Bin 0 -> 25665 bytes tests/gtcp_proxy/gtcp-proxy.c | 678 ++++++++++++++++++++++++++++++++++ tests/gtcp_proxy/gtcp.c | 394 ++++++++++++++++++++ tests/gtcp_proxy/gtcp.h | 48 +++ 5 files changed, 1124 insertions(+) create mode 100644 tests/gtcp_proxy/README.txt create mode 100755 tests/gtcp_proxy/gtcp-proxy create mode 100644 tests/gtcp_proxy/gtcp-proxy.c create mode 100644 tests/gtcp_proxy/gtcp.c create mode 100644 tests/gtcp_proxy/gtcp.h diff --git a/tests/gtcp_proxy/README.txt b/tests/gtcp_proxy/README.txt new file mode 100644 index 00000000..41907e2e --- /dev/null +++ b/tests/gtcp_proxy/README.txt @@ -0,0 +1,4 @@ + +gtcp-proxy is a 'man in the middle' program to monitor data flowing between +two network connections. + diff --git a/tests/gtcp_proxy/gtcp-proxy b/tests/gtcp_proxy/gtcp-proxy new file mode 100755 index 0000000000000000000000000000000000000000..6896b4379bbca80af7c154fe8cd1f8dd778c6b3d GIT binary patch literal 25665 zcmcJ14S1B*weHS@@FygJ`V-OWh=GDiOb{^GA~OlWL4!v5DHe6|H%Ug4Oq`h@ctm4E zC|^f3*IF;v+tc#&w$@{Nd$>JnF(QN_1p0W|h^?j8t5G@j8wrpqVpMGIyVl-&X3sF6 zwLZ`N@?_Rt?_O)|wg1-szHj#I`ztCJyRx!`zFgv3L0t1x4@2@(i0X1?6;>+736Ho) zTp&gR$-%#ebBM~6ro+m#rel>{Hs~n)m%1cS>hf?x(+edmG}RswrSg)OM5;F8RsP`x)r@O;8##mPfTCKD+#@|Si@dI_PN4(JF->*-$PE22SM^)&@_-}W zLqsg%&m`5wR{dABp3>V&rNFjQ4<|G|DPf_h*1H*c?5E2AM@0L1iz?5lU1li%HC5FW zS2hQ0=gqmYxqeo2u(f^NtaYXHX3d*Z6bTj0=6dJD0g}D$#ue;}ZpQ*}(O66e&DXog{zPK=>6|~kMoeP`)zZtJ4=l6jJpol68ztQ|DJaqxwpLH zkAsKrT6N)bTmF`N^pz92fHPh}7@C|R=R0sF6aH7oX2N$l;5Rtf`40#D9N2N=Km95M z;7$j@f8gN%HyrRQM) z(I$U$O>LlA=7iRzYDQ~nn*;uuXtbuTi6vTUf~}lDrJ_g&GL}xxMg!|)1*8opA0dTy zXl;y2`_g;usdAjmg#(e`J>(6SF2x8B)Cai$>0K~V$1PRUjHYja6vNVk3`V2Ds4|$W zPECD1D{-qN+D!FAN-YfU>H`fm?ak<=;62j8mOvy@(-`pA2Wy%`jp=40P3VrAX4&Y; zex&*|)kI`n1OCQvu#H_&1vwSo7NSuRlKoHw*JG%l6;(fzXj@Gkk7CRq)qa>X7}%<<>jRN! zIJBOpUwzv2GiQ;UDLeuEbxi@-47S4m*1CYdt|k%{{su^}l~C=Sn1{(p%oCgEViIdq zvr|q$NVLh>DyMxU&{{9Tfx5K_0D*A0HRNv&)zxt0*VNSo+M=SNt~J^$l#;&=6FnLb zDgubQ=1?R+uGEpac_I?3TN8+iCQQc&EW&GeU)Qw8-%t~5h8)64tB3@e;k9T9w4fzL zE#|U7Cy;6g8s7aCutCLoq?P8ksSv96+?crfzva!zPb&Zo;CHg)xgiy zAm}y&KhMCQG;n%$*VlFfKVO5OI}Ch^f%h8t1qOb=!1cQx%I-JtuNv})4g4YlA29HX z4csM(KEuF^ z4Sc47mm2tG2JSKN%MILT;8z%Um4VMP@Kpw0WZ=~Xex-po8F;aQw;6befv+?0*#_QW z;8z*=W&>AYn{&Gje6AtC)xhT&_%;K-+Q6SQaC)}W*LDM+uR+iq27Zl!_Zs-u4g7$C zf5X804P3pldnvfsZxtMFyU4 z;1vd5VBm`l+}Alc)*gchY=4(NaxjN|oiC2vJ2LnBnQk#MtrDMk7kiK*zKJyB10%Sm zl@Xssd_VKo5bq+sm-*SmXA{4j`OAo>r6<0P`KiRyk`wP{ehTrl)WkcOpG-VeJKo0p zc;d$rU(LLWcv^zuRm^`f13WD~aS!wF6HiM{yqNj-h^M6{?q>dN;%SMA=QIBr@wBwW z1@ni9rzIsm^mhQ$eoH(pCGi2~e?vSiA@P3Zdx)o{Bi_sWuZX85Bfg#ar--MeBEF6J z9}`bYM7*2%$BCzP6KBgE5^5N~7tyTsE{5U*zb+r-ln5U*nX9^xtV$34u4iKmbs zFJ}Hu;wjX}-OSe#Pa!^@&-}NDr_df3%-=#hh4lE)XH@^|iFXq}z`8$cHP#1SIUrRiN zxOhJE-y)tuTU;=I3-J`v;zOTu{}b;aet`Kh;=RQ8Gk*>76uRQQ%+DsCLRNe`c<(p8 zE4?ec%U3Kz#93wCTUc2#; z823J_56PT7B<*Xdcmxf`iF|;mqC^Ifv@g&iBlZBiBP;z$k%J^M3{_w3-a8X?s$n@W* zA$_Y%e?sYrFzkyhco%6^^~-^joI;K4|AX#D-XYF=!=9YT*outZe?cSK4*}bKl$$f> zS7dJk0VAq?i|tm@8QDeAo0_>)l{ae>WS3Yk+Q&bpR{yIidOH2bgdbzy8UMDzO9}rY@RCEjXc^f}vESavf_v@PQG(BUlS|lV-w2(GlHQU--sfmV z;*Ux$v2Rf!-#&xple?f}M-d%WNW|W#eS6a$ha_2`HO#xiutTl(kL-LSX=e}WNOs6+ z?+&u2{Q3JAuyc|d2yOMIy`GY>XrY^ok+a?%WRi`20ZUIMEnTTB%^*wE3f>)L?~7Wh zhI(ae)}BjgFtKZIpciCQqt&^=dZ@j=Sd&kZ=I%_Idzj4`?M}&m!s2e|!@>Nd#RY89 z=z)LIVvDr+#MDHUk7B^3bi+Ss@d9b_wxq@X!4{3~_$MtM@3YGwTN#_S7cy!z^dsB*98FK} z4)TFZFF90ceUYNGVXkDve(6J+&-?7NKO#!;ngae>Dp*FDK08QGE;908v{xd(X8@kT zv0WXgH0yK0C+zv8oPV{4y&gP8wShw?>`MsOwIO>79|v4U$P)~rWlz{Axx6>1;$)AY zn_&vHqU0cjoP#}8mwLpm=_tqknRG6Z(?&U0QO*|1i7jYwyG0^*`*h@fopM8*dv9TJ zB2P@ydCOE@X(F$htg_zK$g{(6%btklv&WKyoiDB`>0N!h_crhC-aEYhz1`j2CC8Rn zFW=#_-u87K8LC>oq_4MoE?T8;k6_X_GzC6QV5Nly^n=rISWZ;{^9W3`*SWwGK-2>ef!>qhK8cNi#K8fk=}y-DJX}^jZWUd z5d0Z?`|e>BG%|2IRm`|bdPzO0AO93BV)92*`DI>f$ollTL9{?m2dv8h@xWj=()GUB zsA+Vk>9aN#ZU!ys^RZi) zLzPy2;jqv8&}ZFNXv1Pb7XsiP^1AMaJN9d^OZTXJ<9H0+vDA9YyW<1~46mC#w75{% zp-@}WYoA4KSQIGe2IvJE2=bOEOfB)k-x9&}$DGw?_kdD-ja^vii!~RHt&G(d=A*U+ z@oEU${}UeDD@`AQ|Iml;Kr!jV{G<=Z$>RK^4;LvP=1Cvy<3mt9(~(~zJMZESR8TCl zd#Hl31^9JbuHL+j4?_?l-7NBky+h?!$^7VQ-JTDKFr zB?n7R^$dN@BX)O}RSUZS4tMpoFSi%6dkbdBqU~zvtma4^|08mEeyr|kLnPhxKDwu5 zBz_SFkc>a8dwd2y+Q54W7r3Q`{BhjRYWb~({HqCkna}#v9)tF<-WJ4Ej3K0?$|g7+3e>POYog z+MBoe<18Vt46VU>XwRZjV+)F;6Exy(U_bio@0}PK;rn9Sf?!u~kGmB0oQitpbuA$a zZ`vPoMqXDj*xl=pZC`}>6Gd0l$5m ztFl_cwCZ=LoCCdU5DDQ#`&!8GTh^qc?RF(983W^o} zhakKCR74Jnu)A@?dUU!RA3Z*5&RNLY4I{#S8hJ3#3=O*pX)G17M+>R%5dY8SRV1(L zX}Ii*{h+V_urKzDLN{n->?egYnEs-$nCa7nrA&7gdO)#e(2&@x=3mbc4a>=ia=(GH z?2m9yd2|c6_ilRgWiLjH#d7-3qZWzB5n4B|YY&7f=6{yAWd})5T!Y-MgLxaD0_WQ@s&J9@ z$&$Jc7tMUH%M1;4|%ipbsdU66Tb_J$#{olx~!`&cEGlZBEhx{l>NR6qsK9iMdb$fB&{*4&TJg`a~7KQDz;#r(m%EmKH5 zXD@PTwEjDamwvvtr0(RRnQxaIL{pHX<UTWOSP02NwSS(37E2Zc+f$hGAM%T}H@H`KG#J&t_@%?y; ze1X{bPJS3I>5X5?9+3JWj3TPZ2(r98>rtP)i^VRYl>6~0&h2^&xw{Fp=MO@zb1d8wO()gZw7K>f3>0guMgx_igoA6^P$UhXo#he6bb7Bt`YtQjb6?VW!yH z@Vd`x?>BBSD~1s>wp0xG`p775%RTfYB^vrF23#;$fmK772cPnO-HWOnw=s9DinseJ zjBIleJ7AvOh&VzwD45 z2VIQSFEM1_gj-)|$UcF7MG;?a$gW2z=8&zjHoSzf*6XuY>`3d#&l_($664JWZ{&Ep z?_D|GFdSh{4af7*IabAySj7>9`SvQ6XHSJJ=EPxN=Z3=~Iv?<1UyL6nXUVjufSxSf z2}Wk-8+=T_E)SuKmyAJufW}wapst6Hvb(*@m*F;QI|ifmLM4Kc^^6anu`Tr3^UBiN zeCxnR@9p_yv~^(5@Ws~4Gxs30(B$opb&*K$00s1(&NnV7mx7a};43rtT8D$Zp0Ye) z{eEVjHOxwfc^pdLwmee>eFQk4_0j1Md_T87^;$12vI0k}+wD$vMclV8)eS?f&0eEBw-Z; znfsAr6g^7Jc!wuc=Y}KVz82MT$(r15j!@j!y*wR5?tbIsE-Tt@r9Fz;? z=Sv^LXg#<$`6%SuGHdTxJQ8e~wv$NOgAqSAbqx*-NZH+2wCx z&UGD&URd%M`hL37`lQl2wa7Z*9Xb1TU*}%DgF4#&o-cM=Azo)S7v|H`bwMRk?n=B; zpHYdgX^Qc%U8?V|KS+C@lz88TH(WE|mk%*6dj9kDvn9UY#%r#zcsCZq`z{2(zo|D^ z^8K0pdo1~c?4>ibn|vWl4@_RcejQT znuc^GwjVaT+d|=}xFygMiU!J( zW$Urovc(;0kGew*sB$FK9uOq6A7ZSu2TVCRlH6!IIfBjJ%(@`^>nFkB z+fgCjz)c_S39B&Ax8o*$I~M*@@V%f_p#7k<6J!9i19Tg1_MQZ-#x&?B9@M6MP)rAr zkEc}nU0Xh08&rcfH@AY4c(wspdQdagI0rn2-*P}#B0%Qpg#ib1>Frg z0J;;e!)a^hFY)l=2K_U>Fz|s+$73?>JQ;;Yqi)dIpxZ&|d-Q(LAy9r~K~>MD`r^vE z=N6H*E118=7! zxu@|z^bh2yoY(Qc8GXe2yB6i<2Xn`=zbAoHpVDRp0sKzjWOoAUeHb|XMolbz(6uRh zNv^wdl&foe?%0a6TpJ-K<{)bh^)34R6Qre|u5O6jbe0#N-%R}^9lEG3o1yn4#srtk z^=r}D;tJ$Hi2MV{?->OqklWwkdf4?)_Lfl(=4=|>IVSrnE|~XmD+o~uk)4p=`sT>U zdU}v6%guk-RhC=uPuH{h!gw2k9Gd5!zHr;emsU*YHBMEfE1h=0M-$(l#=n-vpHAax4?0~FBuV@} zi8i{?rGJ<7;onC>#BX(Zh+wC>yoSbkI6>bc)79_t5E1lxk*=>PK{f3}frQ05ifW?V z686GshICt2E5p>zbkS6$>pV%sjC>ELG@h?`6*dy(jZ^Zve7tGk`fSgW`e=9Nb-6=| zFHreP{r^9?wmM@O&z|dHe7~ZvD*CRXrxYE1o|HRR(dmlLQ*^PSD-~@}bgiPB6n#w5 zClq~F(fx|Ps_46lo>Fvlfhu3o>59%%bg`l<6>U&-t)iP0eN53O6n$3F{ffS-=(~!Z zQgrnBs(eMKD>_fn#fq*}v_a9eif&T$F-4zH^jSssEBdOU?<#sq(a}>>`HD_gbe^J% z6$*gEiqv9G{+ly-3 zgE(9uSTBl*!w<=tL{a_vR@jp?8kRYD%Aniy6vmIdaG<${6jZ9MIVy_y$5loATfQRx z9aT{{#J>An2uYcrI- zUI$5^*6MWqhk#L==yh2C&Uo{=0CDMZBqx2&Z5y&(AQ1f^v(0-$wAG zY5KHBh_0b2OoXkdPq zi*9deE!qE??@QCCol|t#3z#JONpffE{|*^u`Lt__u3@F0?r%E(3S?>QY5#@3kGYnt z2r)p$aq01?^>w{oN1j=}{(a*B-Y(Fku9w_l6$ifx_8R>1@NI3vl1 zbg9d8Mjri!cX>uUTSPPBqY}?ArZM~~H}Tw(ijNk09ZJQ=2)(|f;$wwgH&XF&Lazs@ z`1r*0ODcZmIT4hfjmWR#a;p49#95CCaXMRMI9CEW*&+{dF_U~g&a=vdPZBzwv0?R* zjT2OKoJz&Ng1A#{LY&UV+an!U%*r_ul9?h>&IPW6`1Vi_ASsB`a=4+NXvmHtqKeHVwfV;#*v3;rs zspPd9$t;odU(ee+749jkW??-a*MTED?n^z4>klXY>Ar*fA4u!3|Hk+vv3-F@n_&7Q z#?SQs=fJ7lZE53b5AaOwM(Yj9(`IP8bnJKoX~JpKGF|$9={VAa(-vvEH2yKtgm))91?| z@JSeNRb|ywP$JHsR`{wkd;%7Y$t;=rF~tEdVtk^Y9p!ZCHRoCf`I{W@JCvQ~sU9+u zxB|c@p9S8hV2YfOH4EfWRR_?{XGqvYz2mD61lU!%P@^w4QPZU3y<6*u?!lJ>! z&OHveJl}1ixUodq;dkn=war2P*TC~L%=Z@^XlB+<>P~~bnnnOOk-YCS$?7rDz4J=JYBOL>@0G? zZ&h|~EB3Jd`}~Qo(;VdQcEDo}_>Ud%oeubG4!G@rf9ilw8bjm%%=6m53OrLg&vU?U zcEDSJ)A;O88*dSopC}Hc&G&m9?8F@Ke^qw;YMg(j`sGOn`DYyPJ_kIm?C+l~`<=gA zLm4L>45h;;O{u#ryTHe@Z3!8=1Xh0Lg1O|JC|`h z532hd9S5#;kgsyU8?_zvT*=>&qm7ywKlAxC>|jS=r%awDrgpbUSo zejRaF3HdESeS1sGdRe{FWuB*JM%vb&+;?ejXjnM~C*Mod0JWESV^H4bGbL z(J-}eXAOb8tu3`Ql9Pwyr5s(CD)dFp+=Q2@2RF*I9}`~lQG2Oo z<@tK47}Zv`4|$(B%2FPY$KQh)2Nfoa8<^hlcE$;NspZg?yi}wyIoxo9DD|QqrF=GG zrc)S`$6F?k08BHA>SQ{RGGSjGG%2gh_0fk%Ivv#LUvd+=ydLL2wnwm|k2;XgMKszV z<&?{e$Cz@r*I>@j5rd?O36_4UsGrYGRl_UwT*cH%%1v~sSaKflW=QEAABM{PnmGQ` z=_pTfP#x@O=q5YdA6*w++s+e>3m|zM|B7QtY4@l9ZrZsi2Re=i+8O ziq|~k(&$|p8OCX|pL4J%b&GLeXVR%T)UEg;o?0d`wvA(02}?@TnVKvqPXi@a9F9`e zXVWHIn4C>NGFFz#hlY|Z86M0td{Yj9{=aI_ON6ph+ +#include +#include + +#include "gtcp.h" + +#define WINDOW_TITLE "Tcp Proxy Version 1.0" +#define CONTEXT_ID 1 +#define MSG_INFO GTK_MESSAGE_INFO +#define MSG_WARN GTK_MESSAGE_WARNING +#define MSG_ERROR GTK_MESSAGE_ERROR +#define MAIN_THREAD_YES 0 +#define MAIN_THREAD_NO 1 + +/* globals */ +pthread_t g_tid; +int g_keep_running = 1; +int g_loc_io_count = 0; /* bytes read from local port */ +int g_rem_io_count = 0; /* bytes read from remote port */ + +GtkWidget *g_btn_start; +GtkWidget *g_tbx_loc_port; +GtkWidget *g_tbx_rem_ip; +GtkWidget *g_tbx_rem_port; +GtkWidget *g_tbx_loc_stats; +GtkWidget *g_tbx_rem_stats; +GtkWidget *g_statusbar; +GtkWidget *g_txtvu_loc_port; +GtkWidget *g_txtvu_rem_port; + +/* forward declarations */ +static void *tcp_proxy(void *arg); + +static void show_msg(int not_main_thread, int style, + const gchar *title, const gchar *msg); + +static void show_status(int not_main_thread, char *msg); +static void clear_status(int not_main_thread); +static void enable_btn_start(int main_thread); +static void disable_btn_start(int main_thread); + +/* getters */ +static char *get_local_port(); +static char *get_remote_ip(); +static char *get_remote_port(); + +/* setters */ +static void show_loc_port_stats(int main_thread, int count); +static void show_rem_port_stats(int main_thread, int count); + +/* handlers */ +static gboolean on_delete_event(GtkWidget *widget, GdkEvent *ev, gpointer data); +static void on_destroy(GtkWidget *widget, gpointer data); +static void on_start_clicked(GtkWidget *widget, gpointer data); +static void on_clear_clicked(GtkWidget *widget, gpointer data); +static void on_quit_clicked(GtkWidget *widget, gpointer data); + +int main(int argc, char **argv) +{ + /* init threads */ + g_thread_init(NULL); + gdk_threads_init(); + + /* setup GTK */ + gtk_init(&argc, &argv); + + /* create labels and right justify them */ + GtkWidget *lbl_loc_port = gtk_label_new("Local port"); + GtkWidget *lbl_rem_ip = gtk_label_new("Remote IP"); + GtkWidget *lbl_rem_port = gtk_label_new("Remote port"); + GtkWidget *lbl_loc_stats = gtk_label_new("Local port recv stats"); + GtkWidget *lbl_rem_stats = gtk_label_new("Remote port recv stats"); + + gtk_misc_set_alignment(GTK_MISC(lbl_loc_port), 1.0, 0.5); + gtk_misc_set_alignment(GTK_MISC(lbl_rem_ip), 1.0, 0.5); + gtk_misc_set_alignment(GTK_MISC(lbl_rem_port), 1.0, 0.5); + gtk_misc_set_alignment(GTK_MISC(lbl_loc_stats), 1.0, 0.5); + gtk_misc_set_alignment(GTK_MISC(lbl_rem_stats), 1.0, 0.5); + + /* create text boxes */ + g_tbx_loc_port = gtk_entry_new(); + g_tbx_rem_ip = gtk_entry_new(); + g_tbx_rem_port = gtk_entry_new(); + g_tbx_loc_stats = gtk_entry_new(); + g_tbx_rem_stats = gtk_entry_new(); + + /* stat text boxes are read only */ + gtk_entry_set_editable(GTK_ENTRY(g_tbx_loc_stats), FALSE); + gtk_entry_set_editable(GTK_ENTRY(g_tbx_rem_stats), FALSE); + + /* enable when debugging */ +#if 0 + gtk_entry_set_text(GTK_ENTRY(g_tbx_loc_port), "1234"); + gtk_entry_set_text(GTK_ENTRY(g_tbx_rem_ip), "192.168.2.20"); + gtk_entry_set_text(GTK_ENTRY(g_tbx_rem_port), "43222"); +#endif + + /* setup buttons inside a HBox */ + g_btn_start = gtk_button_new_with_label("Start listening"); + GtkWidget *btn_clear = gtk_button_new_with_label("Clear receive stats"); + GtkWidget *btn_quit = gtk_button_new_with_label("Quit application"); + + GtkWidget *hbox = gtk_hbox_new(FALSE, 5); + gtk_box_pack_start_defaults(GTK_BOX(hbox), g_btn_start); + gtk_box_pack_start_defaults(GTK_BOX(hbox), btn_clear); + gtk_box_pack_start_defaults(GTK_BOX(hbox), btn_quit); + +#if 0 + g_txtvu_loc_port = gtk_text_view_new(); + g_txtvu_rem_port = gtk_text_view_new(); +#endif + + /* create table */ + GtkWidget *table = gtk_table_new(8, 3, FALSE); + + int row = 0; + + /* insert labels into table */ + gtk_table_attach(GTK_TABLE(table), lbl_loc_port, 0, 1, row, row + 1, + GTK_FILL, GTK_FILL, 5, 0); + row++; + + gtk_table_attach(GTK_TABLE(table), lbl_rem_ip, 0, 1, row, row + 1, + GTK_FILL, GTK_FILL, 5, 0); + row++; + + gtk_table_attach(GTK_TABLE(table), lbl_rem_port, 0, 1, row, row + 1, + GTK_FILL, GTK_FILL, 5, 0); + row++; + + gtk_table_attach(GTK_TABLE(table), lbl_loc_stats, 0, 1, row, row + 1, + GTK_FILL, GTK_FILL, 5, 0); + row++; + + gtk_table_attach(GTK_TABLE(table), lbl_rem_stats, 0, 1, row, row + 1, + GTK_FILL, GTK_FILL, 5, 0); + row++; + + row = 0; + + /* insert text boxes into table */ + gtk_table_attach(GTK_TABLE(table), g_tbx_loc_port, 1, 2, row, row + 1, + GTK_FILL, GTK_FILL, 5, 0); + row++; + + gtk_table_attach(GTK_TABLE(table), g_tbx_rem_ip, 1, 2, row, row + 1, + GTK_FILL, GTK_FILL, 5, 0); + row++; + + gtk_table_attach(GTK_TABLE(table), g_tbx_rem_port, 1, 2, row, row + 1, + GTK_FILL, GTK_FILL, 5, 0); + row++; + + gtk_table_attach(GTK_TABLE(table), g_tbx_loc_stats, 1, 2, row, row + 1, + GTK_FILL, GTK_FILL, 5, 0); + row++; + + gtk_table_attach(GTK_TABLE(table), g_tbx_rem_stats, 1, 2, row, row + 1, + GTK_FILL, GTK_FILL, 5, 0); + row++; + row++; + + /* insert hbox with buttons into table */ + gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, row, row + 1, + GTK_FILL, GTK_FILL, 5, 0); + row++; + +#if 0 + /* text view to display hexdumps */ + gtk_table_attach(GTK_TABLE(table), g_txtvu_loc_port, 0, 2, row, row + 1, + GTK_FILL, GTK_FILL, 5, 0); + row++; +#endif + + /* status bar to display messages */ + g_statusbar = gtk_statusbar_new(); + gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(g_statusbar), TRUE); + gtk_table_attach(GTK_TABLE(table), g_statusbar, 0, 2, row, row + 1, + GTK_FILL, GTK_FILL, 5, 0); + + /* setup main window */ + GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width(GTK_CONTAINER(window), 5); + gtk_window_set_default_size(GTK_WINDOW(window), 640, 480); + gtk_window_set_title(GTK_WINDOW(window), WINDOW_TITLE); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_window_set_resizable(GTK_WINDOW(window), FALSE); + + gtk_container_add(GTK_CONTAINER(window), table); + gtk_widget_show_all(window); + + /* setup callbacks */ + g_signal_connect(window, "delete-event", G_CALLBACK(on_delete_event), NULL); + g_signal_connect(window, "destroy", G_CALLBACK(on_destroy), NULL); + + g_signal_connect(g_btn_start, "clicked", G_CALLBACK(on_start_clicked), NULL); + g_signal_connect(btn_clear, "clicked", G_CALLBACK(on_clear_clicked), NULL); + g_signal_connect(btn_quit, "clicked", G_CALLBACK(on_quit_clicked), NULL); + + gdk_threads_enter(); + gtk_main(); + gdk_threads_leave(); + + return 0; +} + +/** + * Start listening on specifed local socket; when we get a connection, + * connect to specified remote server and transfer data between local + * and remote server + *****************************************************************************/ + +static void *tcp_proxy(void *arg) +{ + char buf[1024 * 32]; + + int lis_skt = -1; + int acc_skt = -1; + int con_skt = -1; + int sel = 0; + int rv = 0; + int i = 0; + int count = 0; + int sent = 0; + + /* create listener socket */ + if ((lis_skt = tcp_socket_create()) < 0) + { + show_msg(MAIN_THREAD_NO, MSG_ERROR, "Creating socket", + "\nOperation failed. System out of resources"); + enable_btn_start(MAIN_THREAD_NO); + return NULL; + } + + /* place it in non blocking mode */ + tcp_set_non_blocking(lis_skt); + + if ((rv = tcp_bind(lis_skt, get_local_port())) != 0) + { + show_msg(MAIN_THREAD_NO, MSG_ERROR, "Bind error", + "\nUnable to bind socket, cannot continue"); + tcp_close(lis_skt); + enable_btn_start(MAIN_THREAD_NO); + return NULL; + } + + /* listen for incoming connection */ + if (tcp_listen(lis_skt)) + { + show_msg(MAIN_THREAD_NO, MSG_ERROR, "Listen error", + "\nUnable to listen on socket, cannot continue"); + tcp_close(lis_skt); + enable_btn_start(MAIN_THREAD_NO); + return NULL; + } + + show_status(MAIN_THREAD_NO, "Waiting for client connections"); + + /* accept incoming connection */ + while (g_keep_running) + { + acc_skt = tcp_accept(lis_skt); + if (acc_skt > 0) + { + /* client connected */ + show_status(MAIN_THREAD_NO, "Client connected"); + tcp_close(lis_skt); + lis_skt = -1; + break; + } + if ((acc_skt < 0) && (tcp_last_error_would_block())) + { + /* no connection, try again */ + usleep(250); + continue; + } + else + { + tcp_close(lis_skt); + lis_skt = -1; + enable_btn_start(MAIN_THREAD_NO); + return NULL; + } + } + + /* we have a client connection, try connecting to server */ + if ((con_skt = tcp_socket()) < 0) + { + show_msg(MAIN_THREAD_NO, MSG_ERROR, "Creating socket", + "\nOperation failed. System out of resources"); + tcp_close(lis_skt); + tcp_close(acc_skt); + enable_btn_start(MAIN_THREAD_NO); + return NULL; + } + + /* place it in non blocking mode */ + tcp_set_non_blocking(con_skt); + + rv = tcp_connect(con_skt, get_remote_ip(), get_remote_port()); +#if 0 + if (rv < 0) + { + show_status(MAIN_THREAD_NO, "Could not connect to server"); + tcp_close(lis_skt); + tcp_close(acc_skt); + enable_btn_start(MAIN_THREAD_NO); + return NULL; + } +#endif + + if ((rv < 0) && (tcp_last_error_would_block(con_skt))) + { + for (i = 0; i < 100; i++) + { + if (tcp_can_send(con_skt, 100)) + break; + + usleep(100); + } + + if (i == 100) + { + show_status(MAIN_THREAD_NO, "Could not connect to server"); + tcp_close(lis_skt); + tcp_close(acc_skt); + enable_btn_start(MAIN_THREAD_NO); + return NULL; + } + } + + show_status(MAIN_THREAD_NO, "Connected to server"); + rv = 0; + + while (g_keep_running && rv == 0) + { + if ((sel = tcp_select(con_skt, acc_skt)) == 0) + { + usleep(10); + continue; + } + + if (sel & 1) + { + /* can read from con_skt without blocking */ + count = tcp_recv(con_skt, buf, 1024 * 16, 0); + rv = count < 1; + + if (rv == 0) + { + g_loc_io_count += count; + show_loc_port_stats(MAIN_THREAD_NO, g_loc_io_count); + + /* TODO: hexdump data here */ + + sent = 0; + + while ((sent < count) && (rv == 0) && (g_keep_running)) + { + i = tcp_send(acc_skt, buf + sent, count - sent, 0); + + if ((i == -1) && tcp_last_error_would_block(acc_skt)) + { + tcp_can_send(acc_skt, 1000); + } + else if (i < 1) + { + rv = 1; + } + else + { + sent += i; + } + } + } + } + + if (sel & 2) + { + /* can read from acc_skt without blocking */ + count = tcp_recv(acc_skt, buf, 1024 * 16, 0); + rv = count < 1; + + if (rv == 0) + { + g_rem_io_count += count; + show_rem_port_stats(MAIN_THREAD_NO, g_rem_io_count); + + /* TODO: hexdump data here */ + + sent = 0; + + while ((sent < count) && (rv == 0) && (g_keep_running)) + { + i = tcp_send(con_skt, buf + sent, count - sent, 0); + + if ((i == -1) && tcp_last_error_would_block(con_skt)) + { + tcp_can_send(con_skt, 1000); + } + else if (i < 1) + { + rv = 1; + } + else + { + sent += i; + } + } + } + } + } + + tcp_close(lis_skt); + tcp_close(con_skt); + tcp_close(acc_skt); + + show_status(MAIN_THREAD_NO, "Connection closed"); + enable_btn_start(MAIN_THREAD_NO); + return NULL; +} + +/** + * Display a message using specified style dialog + * + * @param style information, warning or error + * @param title text to be displayed in title bar + * @param msg message to be displayed + *****************************************************************************/ + +static void show_msg(int not_main_window, int style, + const gchar *title, const gchar *msg) +{ + GtkWidget *dialog; + + if (not_main_window) + gdk_threads_enter(); + + dialog = gtk_message_dialog_new(GTK_WINDOW(NULL), + GTK_DIALOG_DESTROY_WITH_PARENT, + style, + GTK_BUTTONS_OK, + "%s", msg); + + gtk_window_set_title(GTK_WINDOW(dialog), title); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + + if (not_main_window) + gdk_threads_leave(); +} + +/** + * Write message to status bar + *****************************************************************************/ + +static void show_status(int not_main_thread, char *msg) +{ + if (not_main_thread) + gdk_threads_enter(); + + gtk_statusbar_push(GTK_STATUSBAR(g_statusbar), CONTEXT_ID, msg); + + if (not_main_thread) + gdk_threads_leave(); +} + +/** + * Clear status bar + *****************************************************************************/ + +static void clear_status(int not_main_thread) +{ + if (not_main_thread) + gdk_threads_enter(); + + gtk_statusbar_remove_all(GTK_STATUSBAR(g_statusbar), CONTEXT_ID); + + if (not_main_thread) + gdk_threads_leave(); +} + +/** + * Enable 'Start Listening' button + *****************************************************************************/ + +static void enable_btn_start(int not_main_thread) +{ + if (not_main_thread) + gdk_threads_enter(); + + gtk_widget_set_sensitive(GTK_WIDGET(g_btn_start), TRUE); + + if (not_main_thread) + gdk_threads_leave(); +} + +/** + * Disable 'Start Listening' button + *****************************************************************************/ + +static void disable_btn_start(int not_main_thread) +{ + if (not_main_thread) + gdk_threads_enter(); + + gtk_widget_set_sensitive(GTK_WIDGET(g_btn_start), FALSE); + + if (not_main_thread) + gdk_threads_leave(); +} + +/** + * Return local port setting + *****************************************************************************/ + +static char *get_local_port() +{ + const char *cptr = gtk_entry_get_text(GTK_ENTRY(g_tbx_loc_port)); + return (char *) cptr; +} + +/** + * Return remote IP setting + *****************************************************************************/ + +static char *get_remote_ip() +{ + const char *cptr = gtk_entry_get_text(GTK_ENTRY(g_tbx_rem_ip)); + return (char *) cptr; +} + +/** + * Return remote port setting + *****************************************************************************/ + +static char *get_remote_port() +{ + const char *cptr = gtk_entry_get_text(GTK_ENTRY(g_tbx_rem_port)); + return (char *) cptr; +} + +/** + * Update local port stat counter + *****************************************************************************/ + +static void show_loc_port_stats(int not_main_thread, int count) +{ + char buf[128]; + + sprintf(buf, "%d", count); + + if (not_main_thread) + gdk_threads_enter(); + + gtk_entry_set_text(GTK_ENTRY(g_tbx_loc_stats), buf); + + if (not_main_thread) + gdk_threads_leave(); +} + +/** + * Update remote port stat counter + *****************************************************************************/ + +static void show_rem_port_stats(int not_main_thread, int count) +{ + char buf[128]; + + sprintf(buf, "%d", count); + + if (not_main_thread) + gdk_threads_enter(); + + gtk_entry_set_text(GTK_ENTRY(g_tbx_rem_stats), buf); + + if (not_main_thread) + gdk_threads_leave(); +} + +/** + * User clicked on window close button + *****************************************************************************/ + +static gboolean on_delete_event(GtkWidget *widget, GdkEvent *ev, gpointer data) +{ + return FALSE; +} + +/** + * Close application + *****************************************************************************/ + +static void on_destroy(GtkWidget *widget, gpointer data) +{ + /* this will destory all windows and return control to gtk_main() */ + gtk_main_quit(); +} + +/** + * Start a thread that listens for incoming connections + *****************************************************************************/ + +static void on_start_clicked(GtkWidget *widget, gpointer data) +{ + /* local port must be specified */ + if (gtk_entry_get_text_length(GTK_ENTRY(g_tbx_loc_port)) == 0) + { + show_msg(MAIN_THREAD_YES, MSG_ERROR, "Invalid entry", + "\nYou must enter a value for Local Port"); + return; + } + + /* remote IP must be specified */ + if (gtk_entry_get_text_length(GTK_ENTRY(g_tbx_rem_ip)) == 0) + { + show_msg(MAIN_THREAD_YES, MSG_ERROR, "Invalid entry", + "\nYou must enter a value for Remote IP"); + return; + } + + /* remote port must be specified */ + if (gtk_entry_get_text_length(GTK_ENTRY(g_tbx_rem_port)) == 0) + { + show_msg(MAIN_THREAD_YES, MSG_ERROR, "Invalid entry", + "\nYou must enter a value for Remote Port"); + return; + } + + if (pthread_create(&g_tid, NULL, tcp_proxy, NULL)) + { + show_msg(MAIN_THREAD_YES, MSG_ERROR, "Starting listener", + "\nThread create error. System out of resources"); + return; + } + + disable_btn_start(MAIN_THREAD_YES); +} + +/** + * Clear stat counters + *****************************************************************************/ + +static void on_clear_clicked(GtkWidget *widget, gpointer data) +{ + g_loc_io_count = 0; + g_rem_io_count = 0; + show_loc_port_stats(MAIN_THREAD_YES, g_loc_io_count); + show_rem_port_stats(MAIN_THREAD_YES, g_rem_io_count); +} + +/** + * Quit application + *****************************************************************************/ + +static void on_quit_clicked(GtkWidget *widget, gpointer data) +{ + /* this will destory all windows and return control to gtk_main() */ + gtk_main_quit(); +} diff --git a/tests/gtcp_proxy/gtcp.c b/tests/gtcp_proxy/gtcp.c new file mode 100644 index 00000000..9f0fcf88 --- /dev/null +++ b/tests/gtcp_proxy/gtcp.c @@ -0,0 +1,394 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Jay Sorg 2013 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtcp.h" + +/** + * Return a newly created socket or -1 on error + *****************************************************************************/ + +int tcp_socket_create(void) +{ + int rv; + int option_value; + +#if defined(_WIN32) + int option_len; +#else + unsigned int option_len; +#endif + + /* in win32 a socket is an unsigned int, in linux, its an int */ + if ((rv = (int) socket(PF_INET, SOCK_STREAM, 0)) < 0) + return -1; + + option_len = sizeof(option_value); + + if (getsockopt(rv, SOL_SOCKET, SO_REUSEADDR, (char *) &option_value, + &option_len) == 0) + { + if (option_value == 0) + { + option_value = 1; + option_len = sizeof(option_value); + setsockopt(rv, SOL_SOCKET, SO_REUSEADDR, (char *) &option_value, + option_len); + } + } + + option_len = sizeof(option_value); + + if (getsockopt(rv, SOL_SOCKET, SO_SNDBUF, (char *) &option_value, + &option_len) == 0) + { + if (option_value < (1024 * 32)) + { + option_value = 1024 * 32; + option_len = sizeof(option_value); + setsockopt(rv, SOL_SOCKET, SO_SNDBUF, (char *) &option_value, + option_len); + } + } + + return rv; +} + +/** + * Place specifed socket in non blocking mode + *****************************************************************************/ + +void tcp_set_non_blocking(int skt) +{ + unsigned long i; + +#if defined(_WIN32) + i = 1; + ioctlsocket(skt, FIONBIO, &i); +#else + i = fcntl(skt, F_GETFL); + i = i | O_NONBLOCK; + fcntl(skt, F_SETFL, i); +#endif +} + +/** + * Assign name to socket + * + * @param skt the socket to bind + * @param port the port to bind to + * + * @return 0 on success, -1 on error + *****************************************************************************/ + +int tcp_bind(int skt, char *port) +{ + struct sockaddr_in s; + + memset(&s, 0, sizeof(struct sockaddr_in)); + s.sin_family = AF_INET; + s.sin_port = htons((uint16_t) atoi(port)); + s.sin_addr.s_addr = INADDR_ANY; + + return bind(skt, (struct sockaddr *) &s, sizeof(struct sockaddr_in)); +} + +/** + * Listen for incoming connections + * + * @param skt the socket to listen on + * + * @return 0 on success, -1 on error + *****************************************************************************/ + +int tcp_listen(int skt) +{ + return listen(skt, 2); +} + +/** + * Accept incoming connection + * + * @param skt socket to accept incoming connection on + * + * @return 0 on success, -1 on error + *****************************************************************************/ + +int tcp_accept(int skt) +{ + int ret ; + char ipAddr[256] ; + struct sockaddr_in s; + +#if defined(_WIN32) + int i; +#else + unsigned int i; +#endif + + i = sizeof(struct sockaddr_in); + memset(&s, 0, i); + return accept(skt, (struct sockaddr *)&s, &i); +} + +/** + * Check if the socket would block + * + * @return TRUE if would block, else FALSE + *****************************************************************************/ + +int tcp_last_error_would_block() +{ +#if defined(_WIN32) + return WSAGetLastError() == WSAEWOULDBLOCK; +#else + return (errno == EWOULDBLOCK) || (errno == EAGAIN) || (errno == EINPROGRESS); +#endif +} + +/** + * Close specified socket + *****************************************************************************/ + +void tcp_close(int skt) +{ + if (skt <= 0) + return; + +#if defined(_WIN32) + closesocket(skt); +#else + close(skt); +#endif +} + +/** + * Create a new socket + * + * @return new socket or -1 on error + *****************************************************************************/ + +int tcp_socket(void) +{ + int rv; + int option_value; + +#if defined(_WIN32) + int option_len; +#else + unsigned int option_len; +#endif + + /* in win32 a socket is an unsigned int, in linux, its an int */ + if ((rv = (int) socket(PF_INET, SOCK_STREAM, 0)) < 0) + return -1; + + option_len = sizeof(option_value); + + if (getsockopt(rv, SOL_SOCKET, SO_REUSEADDR, (char *) &option_value, + &option_len) == 0) + { + if (option_value == 0) + { + option_value = 1; + option_len = sizeof(option_value); + setsockopt(rv, SOL_SOCKET, SO_REUSEADDR, (char *) &option_value, + option_len); + } + } + + option_len = sizeof(option_value); + + if (getsockopt(rv, SOL_SOCKET, SO_SNDBUF, (char *) &option_value, + &option_len) == 0) + { + if (option_value < (1024 * 32)) + { + option_value = 1024 * 32; + option_len = sizeof(option_value); + setsockopt(rv, SOL_SOCKET, SO_SNDBUF, (char *) &option_value, + option_len); + } + } + + return rv; +} + +/** + * Connect to a server + * + * @param skt opaque socket obj + * @param address connect to this server + * @param port using this port + * + * @return 0 on success, -1 on error + *****************************************************************************/ + +int tcp_connect(int skt, const char *hostname, const char *port) +{ + struct sockaddr_in s; + struct hostent *h; + + memset(&s, 0, sizeof(struct sockaddr_in)); + s.sin_family = AF_INET; + s.sin_port = htons((uint16_t) atoi(port)); + s.sin_addr.s_addr = inet_addr(hostname); + + if (s.sin_addr.s_addr == INADDR_NONE) + { + h = gethostbyname(hostname); + + if (h != 0) + { + if (h->h_name != 0) + { + if (h->h_addr_list != 0) + { + if ((*(h->h_addr_list)) != 0) + { + s.sin_addr.s_addr = *((int *)(*(h->h_addr_list))); + } + } + } + } + } + + return connect(skt, (struct sockaddr *) &s, sizeof(struct sockaddr_in)); +} + +/** + * Return 1 if we can write to the socket, 0 otherwise + *****************************************************************************/ + +int tcp_can_send(int skt, int millis) +{ + fd_set wfds; + struct timeval time; + int rv; + + time.tv_sec = millis / 1000; + time.tv_usec = (millis * 1000) % 1000000; + FD_ZERO(&wfds); + + if (skt > 0) + { + FD_SET(((unsigned int) skt), &wfds); + rv = select(skt + 1, 0, &wfds, 0, &time); + + if (rv > 0) + { + return tcp_socket_ok(skt); + } + } + + return 0; +} + +/** + * Return 1 if socket is OK, 0 otherwise + *****************************************************************************/ + +int tcp_socket_ok(int skt) +{ + int opt; + +#if defined(_WIN32) + int opt_len; +#else + unsigned int opt_len; +#endif + + opt_len = sizeof(opt); + + if (getsockopt(skt, SOL_SOCKET, SO_ERROR, (char *) (&opt), &opt_len) == 0) + { + if (opt == 0) + return 1; + } + + return 0; +} + +/** + * Check if specified sockets can be operated on without blocking + * + * @return 1 if they can be operated on or 0 if blocking would occur + *****************************************************************************/ + +int tcp_select(int sck1, int sck2) +{ + fd_set rfds; + struct timeval time; + + int max = 0; + int rv = 0; + + memset(&rfds, 0, sizeof(fd_set)); + memset(&time, 0, sizeof(struct timeval)); + + time.tv_sec = 0; + time.tv_usec = 0; + FD_ZERO(&rfds); + + if (sck1 > 0) + FD_SET(((unsigned int) sck1), &rfds); + + if (sck2 > 0) + FD_SET(((unsigned int) sck2), &rfds); + + max = sck1; + + if (sck2 > max) + max = sck2; + + rv = select(max + 1, &rfds, 0, 0, &time); + + if (rv > 0) + { + rv = 0; + + if (FD_ISSET(((unsigned int) sck1), &rfds)) + rv = rv | 1; + + if (FD_ISSET(((unsigned int)sck2), &rfds)) + rv = rv | 2; + } + else + { + rv = 0; + } + + return rv; +} + +int tcp_recv(int skt, void *ptr, int len, int flags) +{ +#if defined(_WIN32) + return recv(skt, (char *) ptr, len, flags); +#else + return recv(skt, ptr, len, flags); +#endif +} + +int tcp_send(int skt, const void *ptr, int len, int flags) +{ +#if defined(_WIN32) + return send(skt, (const char *)ptr, len, flags); +#else + return send(skt, ptr, len, flags); +#endif +} diff --git a/tests/gtcp_proxy/gtcp.h b/tests/gtcp_proxy/gtcp.h new file mode 100644 index 00000000..970540ef --- /dev/null +++ b/tests/gtcp_proxy/gtcp.h @@ -0,0 +1,48 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Jay Sorg 2013 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _GTCP_H +#define _GTCP_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int tcp_socket_create(void); +void tcp_set_non_blocking(int skt); +int tcp_bind(int skt, char *port); +int tcp_listen(int skt); +int tcp_accept(int skt); +int tcp_last_error_would_block(); +void tcp_close(int skt); +int tcp_socket(void); +int tcp_connect(int skt, const char *hostname, const char *port); +int tcp_can_send(int skt, int millis); +int tcp_socket_ok(int skt); +int tcp_select(int sck1, int sck2); +int tcp_recv(int skt, void *ptr, int len, int flags); +int tcp_send(int skt, const void *ptr, int len, int flags); + +#endif