From 5235a586551be2e0671b9d7a3a53214061084abc Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 23 May 2024 15:23:35 +0200 Subject: [PATCH] import --- TODO | 7 + build.xml | 73 + config | 15 + img/add.png | Bin 0 -> 3738 bytes img/app.png | Bin 0 -> 2736 bytes img/arrow.png | Bin 0 -> 3269 bytes img/browser.png | Bin 0 -> 1135 bytes img/connect.png | Bin 0 -> 2932 bytes img/cursor_connection.png | Bin 0 -> 454 bytes img/device/dasan_olt.png | Bin 0 -> 4296 bytes img/device/dasan_ont.png | Bin 0 -> 4610 bytes img/device/mikrotik.png | Bin 0 -> 4146 bytes img/device/racom_ray.png | Bin 0 -> 5176 bytes img/device/racom_ray_2.png | Bin 0 -> 5732 bytes img/device/summit_bt.png | Bin 0 -> 5528 bytes img/device/summit_sdv.png | Bin 0 -> 9499 bytes img/device/sw_hp.png | Bin 0 -> 4273 bytes img/device/sw_tp_link.png | Bin 0 -> 4387 bytes img/device/tinycontrol.png | Bin 0 -> 5232 bytes img/device/ubnt.png | Bin 0 -> 2758 bytes img/flag_gray.png | Bin 0 -> 725 bytes img/flag_green.png | Bin 0 -> 644 bytes img/flag_orange.png | Bin 0 -> 634 bytes img/flag_red.png | Bin 0 -> 679 bytes img/lock.png | Bin 0 -> 769 bytes img/log.png | Bin 0 -> 4347 bytes img/online_clients.png | Bin 0 -> 4000 bytes img/ping.png | Bin 0 -> 3467 bytes img/platform_android.png | Bin 0 -> 1147 bytes img/platform_pc.png | Bin 0 -> 3940 bytes img/server.png | Bin 0 -> 4550 bytes img/settings.png | Bin 0 -> 4773 bytes img/ssh.png | Bin 0 -> 397 bytes img/trash.png | Bin 0 -> 313 bytes img/unlock.png | Bin 0 -> 802 bytes img/winbox.png | Bin 0 -> 1023 bytes log.txt | 39 + manifest.mf | 3 + nbproject/build-impl.xml | 1436 +++++++++++++++++ nbproject/genfiles.properties | 8 + nbproject/project.properties | 83 + nbproject/project.xml | 25 + src/cucky/jguard/server/Config.java | 134 ++ src/cucky/jguard/server/Database.java | 547 +++++++ src/cucky/jguard/server/Main.java | 96 ++ .../jguard/server/ServerMessageParser.java | 263 +++ .../jguard/server/probe/PacketCaptor.java | 156 ++ .../jguard/server/probe/ProbeThread.java | 223 +++ .../jguard/server/probe/SendedProbe.java | 56 + src/cucky/jquard/server/network/Server.java | 45 + .../server/network/ServerAdapterHandler.java | 48 + .../network/ServerAdapterInitializer.java | 34 + 52 files changed, 3291 insertions(+) create mode 100644 TODO create mode 100644 build.xml create mode 100644 config create mode 100644 img/add.png create mode 100644 img/app.png create mode 100644 img/arrow.png create mode 100644 img/browser.png create mode 100644 img/connect.png create mode 100644 img/cursor_connection.png create mode 100644 img/device/dasan_olt.png create mode 100644 img/device/dasan_ont.png create mode 100644 img/device/mikrotik.png create mode 100644 img/device/racom_ray.png create mode 100644 img/device/racom_ray_2.png create mode 100644 img/device/summit_bt.png create mode 100644 img/device/summit_sdv.png create mode 100644 img/device/sw_hp.png create mode 100644 img/device/sw_tp_link.png create mode 100644 img/device/tinycontrol.png create mode 100644 img/device/ubnt.png create mode 100644 img/flag_gray.png create mode 100644 img/flag_green.png create mode 100644 img/flag_orange.png create mode 100644 img/flag_red.png create mode 100644 img/lock.png create mode 100644 img/log.png create mode 100644 img/online_clients.png create mode 100644 img/ping.png create mode 100644 img/platform_android.png create mode 100644 img/platform_pc.png create mode 100644 img/server.png create mode 100644 img/settings.png create mode 100644 img/ssh.png create mode 100644 img/trash.png create mode 100644 img/unlock.png create mode 100644 img/winbox.png create mode 100644 log.txt create mode 100644 manifest.mf create mode 100644 nbproject/build-impl.xml create mode 100644 nbproject/genfiles.properties create mode 100644 nbproject/project.properties create mode 100644 nbproject/project.xml create mode 100644 src/cucky/jguard/server/Config.java create mode 100644 src/cucky/jguard/server/Database.java create mode 100644 src/cucky/jguard/server/Main.java create mode 100644 src/cucky/jguard/server/ServerMessageParser.java create mode 100644 src/cucky/jguard/server/probe/PacketCaptor.java create mode 100644 src/cucky/jguard/server/probe/ProbeThread.java create mode 100644 src/cucky/jguard/server/probe/SendedProbe.java create mode 100644 src/cucky/jquard/server/network/Server.java create mode 100644 src/cucky/jquard/server/network/ServerAdapterHandler.java create mode 100644 src/cucky/jquard/server/network/ServerAdapterInitializer.java diff --git a/TODO b/TODO new file mode 100644 index 0000000..c0cf5e5 --- /dev/null +++ b/TODO @@ -0,0 +1,7 @@ ++ pridavani typu zarizení vcetne ikon + ++ ruzne type objektu + obecny objekt s zadnou funkcí + odkaz na jinou mapu + zarizeni site + diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..0772a7b --- /dev/null +++ b/build.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + Builds, tests, and runs the project JGuardServer. + + + diff --git a/config b/config new file mode 100644 index 0000000..6a4efb6 --- /dev/null +++ b/config @@ -0,0 +1,15 @@ +#Sat Jan 19 23:43:37 CET 2019 +mysql_server=89.103.77.58 +mysql_port=18000 +#mysql_server=10.10.0.51 +#mysql_port=3306 +mysql_user=root +mysql_password=Michal2407 +mysql_database=jGuard +server_port=1225 +client_version=25 +debug=false + +interface_mac=30-52-CB-20-B2-6F +gateway_mac=D0-96-FB-43-CE-1F +#D4-CA-6D-21-F1-6F diff --git a/img/add.png b/img/add.png new file mode 100644 index 0000000000000000000000000000000000000000..4a5bf9fe6f616a4a9e3f88849578c4d95d98403f GIT binary patch literal 3738 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 literal 0 HcmV?d00001 diff --git a/img/app.png b/img/app.png new file mode 100644 index 0000000000000000000000000000000000000000..8de69c333e2a77fae07ef7beafcbd7f748cf6649 GIT binary patch literal 2736 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 DBCTluK+>WfaGMci!&~b{N_Y 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 R(>4IGZ@1sBhdxZ+{{S@{ksbg5 literal 0 HcmV?d00001 diff --git a/img/cursor_connection.png b/img/cursor_connection.png new file mode 100644 index 0000000000000000000000000000000000000000..6492f7f421350c81ae47e7e88faf09f6c78d8c0c GIT binary patch literal 454 zcmV;%0XhDOP))__UjimkB!xi` zf|Xz@TZ1pHg;<#?dYz4}jS%&FsHos~NE)@Uu+mPDV9?^gPDOkvLk=y@MLq6r$Gh1L zf`535S(y1hGwd+Dk}s{8#7y4eL9F5$xA>Nav>Wp{x|b%oz`LxZttem%r3!J9#Vo`F zSiw~d>EnEjCH;&!{0WdIIc!$?0pGEy_7T_1k4DA)Si)rk(j+D2QwydGv-sVF^l`RA zq%9c7FZ_!nPEtsdPvJDOG|3Ix(hBTnB)`R4njFS(mQjJBTGBTaJwmsebUv!2}j&TCTit$gJ=UHH{ wV!Rq*&1cc!;U*Nt+0?Tc$1Xz>%07*qoM6N<$f=d#_S^xk5 literal 0 HcmV?d00001 diff --git a/img/device/dasan_olt.png b/img/device/dasan_olt.png new file mode 100644 index 0000000000000000000000000000000000000000..781e884e77e52a587ec23cae1c329e6088a06bf7 GIT binary patch literal 4296 zcmV;(5I66MP)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000H^Nkl7-k6~}+)&dmF?<>yZ|Zek}bA!%`w5VfRDL7-450v1pqArL|! z+CmkiZeYQN9jJ)H22=?o6jelm1!@BeK3Z2GXjN$1Bzy$wkTg!)*okqR_wxMW@yy(N zSj@5I7FcAJ1s&7{b-rZ$vp>JqGl{JsTMlReHvq#xKQIVX zfQH%Iz!~5L;MhiIQUC5wU26ite*1yTz#?!Jr~}KuS)j1M9bgog0`3M50(Sxm{1XWA z{|FLUmHUAn@TWDzfIYxT;5Fbz;ARW_%!b5%_hzJlF93G{4+8fA=YWH6=D>%516JkF zfFA;n0pBzGv_be`;FG{_fG-1IG5aIH6+43=^L-2WAn-+?YJMhx!)pml07rm#0WaH2 z9|7J6JYn_+tlDYdCg9V+X5a+ydEnia%u{y0&)TvJ_y%y^Vr9T7yMH(a_5#lVUEpJ8 zcmT+O*MZLfzX!ewJZ;rK4SWuG7oMl_JsuHWJYVsVgK{H?h)mj)?@Ni9&@c;f>aNQG# zpFR5L4UbCXgjdfmJi3}@qfzV-1ON~CB9Qxl*lZOXfdHbQpbA1j)q@I(;84;mr(Ub& z_kHxh{e8^_?JLWrUx9dJXpl7)#F5bMbcga>?^kgH;7}w(d-%)Rk%(yiA5Ss7*v!6O#jif(ke-9|8&@WLbv8=kM9KXYvQK1x80&%wM?ltvp1xH)~Z?)u7k)LRBHCL)WX7_2A{bA|iy62<46dPVBr=t5op8 zC&&LbGo2RMT|)z{o;>->-{OOOZEn#u|`z-62VQ474 zu~DFqL_*;U(%#DPyYIMdugsi2_x@97=BA4P^+pXuNV8R97nP2sY(p8?cxNrJCgFtj z6jWi2t|bIowgg3kmVl_zTS=K1A3G#JI`os3Za1ynG&#)W_GJ)Zba;d!ctTJR#ksPt zA%Jt>a3}#SqZ#yp5P~75I2R!*gy2iEA&3)vuya6~6~sr6ouG4Ng^|`C4nKZ~fbh^m zU!rqmnMx%gcyBNZd6APB1%1svVsUr^SzZtdPg0E#gviBQUFqSAK)ul@&j!zGx{6C8 zk|ZgK9h6SH-HY#h=k|ppi6>UGl&#xtre3S#Rp>5v0VT_F3SUr368ai_WQFJNug);g z-;XLJm4x{Vb1W@grqO6{wYS3L#AcF8Om^WSwQ7|(iAmE{UO9V)O0|m53u?6*J8#+P zywdiy@?7wRtv$L}-EiU1r2p2i_?%l)EQk!nKi+WG9ugTkYY;VYSzWvC* z7B4N1OifKOb83bV0=M2e#q7CP=yjJ#l9)U%5D}WqJ}T7;bFZJLS+5gC5vzGdYiJnn zJ+X_3lY~6an43FK62%lgkmng&CblpB! qr(52mtY%r1=h}Pg|G--b{CfbK`NqF*|F^aP0000KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000LqNkl;tntbybts&-poip^xXXd2-P(W+ySP61;7U8fEz%d63+rzU@fp2*avI? zBH%g@icd&lD3+UmdEjql#ej!_K42JV0#>NRZ!ZFN_Fkod?*Sda9^e_^Ltx*%9{39I zm|}Stcm?=9@LLr>qa=I+coygaegOPP#a{;UY7I)6->HOqfbRpA+GiPXpbVe|I0mc- z2Gm1e1GWNhsQ3$t?HQmI_!iI%TmrreY*AoNtMw^WmUiG*z^KYq0qVuwsEqh`6&qA4tX6<+rQB*@CvaK-KL_H# z4DeZ?U)9k8&MC9BC8FK)v&;19~B z04IPy1HB4;&&QZF12ig`-&gIH0mjt5oa%x>MW$B8f<;~QU+2JMKqs(Uxq1+o1g4bZ zr_~1IDqc|cA~k1K?|b*t(~Hl68%o~y6k1EMG8HRJwdWFb&@532BUM5}B@gfGB>bO$ z7d&tP;@uA}Gc$IRY&Hvk<2bm1*;w>S<6r*b+Gu!qm|QMro;`bZQU zTDfxNa7RbSdCRhT5{X1#dwcufkt0XuJ3BkEZJU!PPqKUWZVntcKu1Rhw+n{uj9(A8 zZ{JQ=R~PHnt)u;s4n$o|H8W#3D~Pg9%eC9RJ!8#{jf{+ptnKOPdGg%3bDcv&L+y^^ znDKZVAq2g>y)E6{-J5k?C!5W>_4W1RRaI5pJv}|$wY9YuULTm?+iB#}rkFwoyHH8r*K#EBE%92gk*lH)i@Aq4SwoMbYIuHUVi zZQEFu#lpe@6B85ZLZOg04C6BwFJ62u9*w+S~75pqQVEJC3vC{Q2{pXU?2?e0+SoK}uOkqG=k2VW8_eWk$NLqiGtN zrU4KH!Ch0S`%x6(d0yq2bUN)dH#c8fy?RZ*=M^sO+O_M{=RW^%pLXC+Z*uC`!Q)r1 zTzRTcC}?3AB7{InNuf}{_kE%$Len&KU9W(+cb1g_2*a?lru-j3N=X=o1VKP9m+L!p z_}E%4iXygd-8wWnI@+4cqjLQ?Fi{i{MNvgH z-}iAG2gh-!udn~-!9zz^2!LhFmJJLK54YaBbqgsaLI@Ix1g2?XnkKewlS-w~G>uZJ zM6p<8VPSz{v51tiqRhQ2>AH>(f-nrZbLS5Ed>#PHvS?~*8e6|^E#lQb9@46+s)mIS z2q6f9fFKB%pPwfTLo`hz7K>Hn)O8)(wyCYHMb~wtl+4Y|v9Pd!@B9Bg;}rlYC7$OI zh9S0X6N|<0JP*S#J_0a~A3IEce}DYy)vHgIN+ol#;mWL}lmtP5@B0KnKt7*mc6OG0 zK9BGF*tShJo29zCnpiA`VHo(n&)nP`rBVsYvPdSAn5Ib-MI@8SnJrtk{Nmd6e_R&; zO-)UQH*MNlxe<&DW?GGwz^T-U`g z4C3)PrfF8l)YR0xnakyVBE!;6L&R(WFgH7+ZQA&SUU&9(@A%~OONByVL!nT3dVYTX zt5V7pK@h~s^({NR%qEdYkV>U+T^At)uIn-}F+nn!B$-T7?s?m`$I|KaFPAP|`bwct z2tU=loPB$Lh*6cYwbAi0a=9Eq7%(_^L+;(X=T<{w?)2ouN3ZI-{uj%#W}+xcg<-h# zUZV*iDhE_cOAFQ2)l5!Kl1`@)LSULEnM~$LV`JmD!g(&cVLXqrK( zBpDx@BoR-rbH_IA=8X@D$8Dbd#xq_J1n)$e{?6;KA4&-!HoLC7JqUs)N~O}8D2fPz zfMT(T=Xn*!o2E%D79*8PjpTBmVHgJe{r%CZRjXdBsj2x{ zu~-}_C-*#$WHL#qRASq0a=DjW*InPx(D3z4CiAFm s+qY|KYF>__=*Z$z@c&;#pEUk80N1c8hiP2~#{d8T07*qoM6N<$f*%8+I{*Lx literal 0 HcmV?d00001 diff --git a/img/device/mikrotik.png b/img/device/mikrotik.png new file mode 100644 index 0000000000000000000000000000000000000000..3c05f5fb12743a468764ac07d793172808c656ea GIT binary patch literal 4146 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 literal 0 HcmV?d00001 diff --git a/img/device/sw_hp.png b/img/device/sw_hp.png new file mode 100644 index 0000000000000000000000000000000000000000..a2d74d2c24039ff5199f4cdd202252bba96ccdbc GIT binary patch literal 4273 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 T%mECh00000NkvXXu0mjf9CY~T literal 0 HcmV?d00001 diff --git a/img/device/sw_tp_link.png b/img/device/sw_tp_link.png new file mode 100644 index 0000000000000000000000000000000000000000..76879b29d10fba74060d4724cd0b457d17154f47 GIT binary patch literal 4387 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 literal 0 HcmV?d00001 diff --git a/img/device/tinycontrol.png b/img/device/tinycontrol.png new file mode 100644 index 0000000000000000000000000000000000000000..077a2af1c38c56cf31ac8f01779a9cf7449648ba GIT binary patch literal 5232 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 M07*qoM6N<$f}cAu)Bpeg literal 0 HcmV?d00001 diff --git a/img/flag_gray.png b/img/flag_gray.png new file mode 100644 index 0000000000000000000000000000000000000000..a7e38dba54248eef11b1dad84335bd05cdda732e GIT binary patch literal 725 zcmV;`0xJE9P)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ;TS-JgRCwBykUeM=Q51#0`{wP;5)CM(+;oYlEYwD^()5ZpAxpqY?6lI@tzsi7 zj9q$Bu~0#=@FyBbVh9qaOGE;TE+Mjlf9y^}xX+RPKLfWMqV)p&^Ec zhbfgxU4!-Yb!KK}=VDvyh>ThQ$=W#Qn4FwsVPSzr2Dk{eX3)$Try;~s;~ z9gRk#TU@VE?>tv-YI-BYag67Ac%DbQ-6o16RFx!2{vpfbb|Z)gOG`^Mo6Rm>uGQ65 zj4^-EZEbCJA}G+vnux&W=H`=nz5Z-@dHHRAJU%|YlBOxvT0GCgT1%_dVs37ZBuO4w zPy$=v^l;4B^`{qsuj6OWG^AAopjNA07#tkD3E0KOMO2kEO6M{gk&CGyblu%;Kc)&XaHJZ1rTEZp-o29Y;);Ho5|3WJ- zGu@39V|X8ih!K!ZoNZ}iRLBHW|77o#1`z@r?wP&gKlN(>7-t$A7+>bd00000NkvXX Hu0mjf)&D={ literal 0 HcmV?d00001 diff --git a/img/flag_green.png b/img/flag_green.png new file mode 100644 index 0000000000000000000000000000000000000000..def17cc5aee8234c36099db99db4f7a276bb7acc GIT binary patch literal 644 zcmV-~0(6o#MgyLVr*ZQ8X6glg3fWQ8D7N{eV+E!rgoX{|Ea*kl?+1g#>ng{Wl^ zRHI3fi_(-Zb)lpXspB;2Fuy-{?rH9Pi&<6lzzc`(p8N0~HE`?5knQT`pLVQwaNFFN zE^^6rIp7M2fKp&vr!-}qb1CzA3a2}qkLcLqP7}}lIQ5|QF4Vg zGP$(u*}0D@yM_z(A+Gk;V*?;2OB){*oSR_X-8R>cB19k79inf2xwTC<5hf-H`CAgjDgeL zOeRxM6)?>L0mx?I^?V=s1cNpR0o%uw_%WJpe`p0_%{?R(hLx{e-c?688vD*;)2NW- zU1Wx5vqOy80u*t{sRD01PArFl?{Ney0S!z%;VCc#Ko#)!taoV>Ua1;oCM|(fdPfejbl``k#l|$Z}{w+WO e2dVId|Kv9?#{VCWSX8wD0000>6CXyz4v^Z%Uqa(BzoZ4oWps}N4hTTei)*MD#PgRmidLA z$hl+cq86M36cEI-i{krN#F(@EiU`tqewQ}1v{2psl!~nltghcis(w4NVKewX7!Nnx z&*=5b_~~kig6s}e*NAddtbf3wigS)ATxQ*oYw+$CWcp#U3(C{rTTIg#bg3PeP6 zgh{QU_V6htI$E(Lr~;Gy(AN%C8zEf_RD$op>tWGv1J&|BmOe#qEY}c5PStM zRj>?<4-#u2p8@r-*-jX`57XnY|0ZBCzXzs3JV25tVB)|mDi*#CfGG!WO?>vJz%&?1 z9!g@ez}Prm)K~~+K#~9=h%u0x25Z0+(su?g|G2n$EYJ|7rb&vWcess5We|Sy8=hMw^a)H55g-~V+ z3el3!T<=T1EUbNC0mNEfRaek@weKX58EtF+PQrh~b0-cqR&^-Wj@%dmgqgtp|70Y* zbLsi}o?<{1Tkzh+J!5+_SwaOBP~iCEvD-i|Ae5K|rV1Yg#Qb16b$isY%CTRQVe=e*AH8Fb*J4)G2B}6huH-83A@uVt{|-Ck)2l UZWfq~9RL6T07*qoM6N<$f>7fcg8%>k literal 0 HcmV?d00001 diff --git a/img/flag_red.png b/img/flag_red.png new file mode 100644 index 0000000000000000000000000000000000000000..e99a587698006cf4fc89cfec93096d3014b15d8a GIT binary patch literal 679 zcmV;Y0$BZtP)Ta;{y=*o6%#L`kwa4(|W6uK!`#Eqq0rHeqR-PnZ)B5qt1 z(T%1J?WQ43Xq%=Dh>nJms0%eD5-mw4nM|BX=DqjbzJ5%J8?0h2VStfL-emRic?w&%p|8D;rFF8$ zvt%<5@M*%HZ~JlW?MTufQt)Yt5)cq2t1LkrBd!3EYhHw3S;ix@-WVae@HMr*qa-ua zAkr)lg7HgbL?aM35XJIhoWt%;V1*XQ&+<}KQ33?xD~mxz6jencMZtU88f4t_@Q6Rl}l&u&5b$$yrGG4 z<(v1XyT4n(doTqA`X;9@0)GUzK^gdaY=5!l0K`BtkcKgPq>JwU3g#qS9;yH(o`Fj9 z10sTY(1z#X;Z9~h=tMFDHPBYGKT@h{?*K6GXE)x@+uSx?F56tY&aawM5ED9kw_$&N~NO17&}d) zh`3d&)n-ej5|+#5k9&K2g<`P?ySuwHbjh1sE_ZP#V|unq@~m2|o;^A`dZ+99BxCHV zVHnq1t=0r-u23k9(M#f_8n`XW^7`iHrV)?FzpbsUo%y>zKR+Li#bT9gHrvkU^HakZ zKU!a3my3&wFZ<0bEG#@+@yMLf~sR$KR(P+}_R?Q}>^|l_we#j4SQA$^SQ^^>PXJ57@LPh|^wXLU6 zqKy!QWd@RzIZS0w30KCp4QB#4nxqH{0+|AE3-VyyNN9${)$)2vbLK5=>1538r4h~) z`bWVC{_yFBmWbcj(s_HQ`BU;%Atb6Qz=#G?*Fhd2m`zm*&C@h%b>r*Y ziw}Co%OnIkE>H>W1k_HSJAT+E45D=c2GRZvwd*dI83o9400000NkvXXu0mjf@Ni%$ literal 0 HcmV?d00001 diff --git a/img/log.png b/img/log.png new file mode 100644 index 0000000000000000000000000000000000000000..7d600210bea50607d0829c2468debbc53412cf3a GIT binary patch literal 4347 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 literal 0 HcmV?d00001 diff --git a/img/online_clients.png b/img/online_clients.png new file mode 100644 index 0000000000000000000000000000000000000000..becc247b2e8797b92ead5abf799f6208d31b75ce GIT binary patch literal 4000 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 N002ovPDHLkV1i<=BJltK literal 0 HcmV?d00001 diff --git a/img/platform_pc.png b/img/platform_pc.png new file mode 100644 index 0000000000000000000000000000000000000000..f5bad407419c6544dc3403f1e194ea0c0756d7e1 GIT binary patch literal 3940 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 literal 0 HcmV?d00001 diff --git a/img/settings.png b/img/settings.png new file mode 100644 index 0000000000000000000000000000000000000000..f45c23f4837cb525ad189352fdc184565b213b97 GIT binary patch literal 4773 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 literal 0 HcmV?d00001 diff --git a/img/trash.png b/img/trash.png new file mode 100644 index 0000000000000000000000000000000000000000..0e634f47011d17f7ca550a9f3840ec694d1dc6c5 GIT binary patch literal 313 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VORy^j(EZKTiH}_bv0LiP<~cSFd=G!OS_8nr@(_#{@ zIHq#GK->0HY2fkVHNpv}b5336`M~}DsOJ9+8MYIwhcp-~9`rKa7iwU?_jta+GPZqF z?|m_X?|UD}I!Te|2c=&q|Qvev>-2N6o`g%<_m zT9!qqjis7^HqOEehXz-x+EApJlsJ*(I2n!R-QF3TL6j&N^}vrayf>W3oIAtdZQ9w{ zQRQ;^_3UUo9;yeX_i~Y~d|Zc!R3d>ZEPkzmAWOH-uU~pRZ0&PyabNIa#h$ zDpUU}xK^u8DW!JX?e=`L*`!vh^~dq?@q@#|LmV9)&B-CZ*X#AGBL$aAC5M@RIz2tT z;(6YEt@Vr?GUNOH^|Sq9OF0+GMNm9X>Dz-DJG2gR%or`;DijX;_l+NH2t}md-U}0YwY`=^CwqJ zh52vJx?Oa;9WursolavclSL+*K{lJi`yal`XZ-B>!@a}Gcen2Guqu>g_X{(h-JR;R znt1kH&AK_7O{)O8OFoohxR-2PPFr~A1K_<03uB*tjmAUz3&3xJ|Hh6j<-^tgz)ySV zdjtmrbD5wWKx-hCVq~(6u`w66W+WaS5eo(i3`HT)A}n!57yI#jBLp)Gh5#lQgP4E; zD_j$p872swk_Urc!B9$!2ucu(P+&x^LJvtAjHn8bc%;bb;_(NGx0AldG;n`ZN$Bw& zbG#X!koe1ErU+vM5Ev}320}S@Az3hiVJQQP0&#*(fB|5IFrZ#lq71|m4KE(rVBZ|s zMaL={nlRYHkSd5sX|1gve%#f+6u;PY9n*NxCac>~s@)-Veol%F$Z>#_2B$RhOPd2P zMMkH+pwsQ0++J&>M35grz|4j&f^AuJY}?i$N+~^X?O2v&i(3#GV**iM-0%1My%1Rl giLO1*^O?E-FUtW`TBCR{9{>OV07*qoM6N<$f*whGkpKVy literal 0 HcmV?d00001 diff --git a/img/winbox.png b/img/winbox.png new file mode 100644 index 0000000000000000000000000000000000000000..492d23e41a86216cf1542d9437463fe7cd1bbeb0 GIT binary patch literal 1023 zcmVqE|D>JL4$%W|fd@5?}C4AdzsDwZK1{p-?$lb?`zn(Z)1}$eYM5(U%F5 zHTh)L&m^6`4oUqGB<~F)rScX+6;1Q2!DWZt z+1n^HSh(sQaA$hK*7Sj`>jzic3r_jT7&h#zG?8m!B5xpa-^F_!V)%QypTpK7W5)ZV zP-)HPx7v+ddDkq8wGZ&N?q8JYmvPEq=FaxPd`kZSo~dWp}Q)cmpsR9fr2$SF2(eRB0o zbj8oOHZsK>Z8M_q(_b+*^ANI@X&n0EZyeDq;8@p6nkAgzRrehbQ`ojdwo%bUo6;p- z(iQ}k{>a@JnLte|XR$D!v6PZTJb~mt8^> zBt79FzXA_cA6u&##a|0%1eXq=s^b^198jP(V#oP=P_#}Vp9iVFoI|N@nN?mIhu!n4 zM!tX9>Gj!{c)%?-vc!Ppr6rux8{vB3Dz5bW1^|4LS?vDc9#R@6klj3u0?i^T|M)JR zh%V#-ewl~Yr8Zz=!A;gt+KKA+A*)H=Gzwv^9%br5Oq=HLhv^~oBPNt<#*n9;VRzNu z20#3hM4m}uYvRi1U6gvb?C<8B_qIdR|LElk?Ks@>ufsE6hm3c>M^WoA6b%EATr?sq zPi-NmxOk$q0RlnTzK*9dTj7#tFgq!_Ae6S@oMsRJi^+L2>aR^Ar|}N9RShAw=8k21 z)gbJ*7Tq8s8zPTnB46>dHqpg%B1NYa;+*?69v6R(@FSf#uN_AJs1ZZI|B1^zcP+^^ zKd??aDp~SMPax6DL==$8kb4}Fe`c!f3&r=J&iZTx!nq&3$~r2#+0|K{ti4prx+!(s zhJ9bbA->!~K4LjJL=t(684_$hz?-uea*iePdHp#iB2QF&h2C$yoT%o_|?zh*EyF~^dCC(2c{3XZh`;+002ovPDHLkV1oD==@kF~ literal 0 HcmV?d00001 diff --git a/log.txt b/log.txt new file mode 100644 index 0000000..716a009 --- /dev/null +++ b/log.txt @@ -0,0 +1,39 @@ +01.11.20 18:28:27 I ------------------------- +01.11.20 18:28:27 I jGuard Server +01.11.20 18:28:27 I ------------------------- +01.11.20 18:28:27 I Version: 1 (Sun Oct 25 16:22:20 CET 2020) +01.11.20 18:28:27 I OS detect ... +01.11.20 18:28:27 I windows +01.11.20 18:28:27 I Loading config ... +01.11.20 18:28:27 I successfully +01.11.20 18:28:27 I Connecting to a selected database ... +01.11.20 18:28:29 I successfully +01.11.20 18:28:29 I Loading map and object list ... +01.11.20 18:28:33 I load 5 maps +01.11.20 18:28:33 I Probe thread run +01.11.20 18:28:33 D 7 network interface available +01.11.20 18:28:33 I Local adress: 192.168.122.107 +01.11.20 18:28:33 D Capture Thread run +01.11.20 18:28:34 I Server started on port 1225 +01.11.20 18:28:36 I Pozadavek na autorizaci odeslan (127.0.0.1) +01.11.20 18:28:37 I Uživatel: michal přihlášen (127.0.0.1) +01.11.20 18:28:38 I Spusten ping na: 10.10.0.2 +01.11.20 18:28:38 I Spusten ping na: 89.103.77.58 +01.11.20 18:28:38 I Spusten ping na: 10.10.0.51 +01.11.20 18:28:38 I Spusten ping na: 10.0.0.47 +01.11.20 18:28:38 I Spusten ping na: 10.10.0.4 +01.11.20 18:28:38 I Spusten ping na: 10.10.0.41 +01.11.20 18:28:38 I Spusten ping na: 10.0.0.54 +01.11.20 18:28:38 I Spusten ping na: 10.10.0.50 +01.11.20 18:28:38 I Spusten ping na: 10.10.0.40 +01.11.20 18:28:38 I Spusten ping na: 10.0.0.16 +01.11.20 18:28:38 I Spusten ping na: 10.10.0.3 +01.11.20 18:28:38 I Spusten ping na: 10.0.60.195 +01.11.20 18:28:38 I Spusten ping na: 10.10.0.42 +01.11.20 18:28:38 I Spusten ping na: 10.0.0.54 +01.11.20 18:28:38 I Spusten ping na: 10.0.0.147 +01.11.20 18:28:38 I Spusten ping na: 10.0.0.30 +01.11.20 18:28:38 I Spusten ping na: 10.0.0.17 +01.11.20 18:28:38 I Spusten ping na: 10.0.0.10 +01.11.20 18:28:38 I Spusten ping na: 10.0.0.31 +01.11.20 18:28:38 I Spusten ping na: 10.10.0.52 diff --git a/manifest.mf b/manifest.mf new file mode 100644 index 0000000..328e8e5 --- /dev/null +++ b/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml new file mode 100644 index 0000000..bcb9eda --- /dev/null +++ b/nbproject/build-impl.xml @@ -0,0 +1,1436 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties new file mode 100644 index 0000000..83606af --- /dev/null +++ b/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=24a327c4 +build.xml.script.CRC32=4e5395cd +build.xml.stylesheet.CRC32=8064a381@1.80.1.48 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=24a327c4 +nbproject/build-impl.xml.script.CRC32=a4c12d70 +nbproject/build-impl.xml.stylesheet.CRC32=830a3534@1.80.1.48 diff --git a/nbproject/project.properties b/nbproject/project.properties new file mode 100644 index 0000000..2be9c08 --- /dev/null +++ b/nbproject/project.properties @@ -0,0 +1,83 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=JGuardServer +application.vendor=Michal +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# Files in build.classes.dir which should be excluded from distribution jar +dist.archive.excludes= +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/JGuardServer.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +file.reference.mysql-connector-java-8.0.15.jar=C:\\Users\\Michal\\Documents\\NetBeansProjects\\JAR lib\\mysql-connector-java-8.0.15.jar +file.reference.netty-all-4.1.33.Final.jar=C:\\Users\\Michal\\Documents\\NetBeansProjects\\JAR lib\\netty-all-4.1.33.Final.jar +includes=** +jar.compress=false +javac.classpath=\ + ${reference.jGuard.jar}:\ + ${file.reference.netty-all-4.1.33.Final.jar}:\ + ${file.reference.mysql-connector-java-8.0.15.jar} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.external.vm=true +javac.processorpath=\ + ${javac.classpath} +javac.source=1.8 +javac.target=1.8 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +main.class=cucky.jguard.server.Main +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=default_platform +project.jGuard=../jGuard +reference.jGuard.jar=${project.jGuard}/dist/jGuard.jar +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project. +# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. +# To set system properties for unit tests define test-sys-prop.name=value: +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test diff --git a/nbproject/project.xml b/nbproject/project.xml new file mode 100644 index 0000000..cdee5ef --- /dev/null +++ b/nbproject/project.xml @@ -0,0 +1,25 @@ + + + org.netbeans.modules.java.j2seproject + + + JGuardServer + + + + + + + + + + jGuard + jar + + jar + clean + jar + + + + diff --git a/src/cucky/jguard/server/Config.java b/src/cucky/jguard/server/Config.java new file mode 100644 index 0000000..2a51516 --- /dev/null +++ b/src/cucky/jguard/server/Config.java @@ -0,0 +1,134 @@ +package cucky.jguard.server; + +import cucky.jGuard.lib.LogFile; +import cucky.jGuard.lib.OSValidator; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +public class Config { + + private Properties prop = new Properties(); + private String configFile = "config"; + private OutputStream output = null; + private InputStream input = null; + + public static Map conf = new HashMap(); + + public Config() { + + } + + private InputStream getFile(String configFile) throws Exception{ + if (OSValidator.isWindows()) { + return new FileInputStream(configFile); + } else if (OSValidator.isUnix()) { + return new FileInputStream("/jGuardServer/"+configFile); + } + return null; + } + + public String get(String name) { + + try { + + input = getFile(configFile); + + // load a properties file + prop.load(input); + + return prop.getProperty(name); + + } catch (Exception ex) { + LogFile.printErr("Config file exception: " + ex.getLocalizedMessage()); + } finally { + if (input != null) { + try { + input.close(); + } catch (IOException e) { + LogFile.printErr("Config file exception: " + e.getLocalizedMessage()); + } + } + } + return ""; + } + + public void set(String name, String value) { + try { + + output = new FileOutputStream(configFile); + + // set the properties value + prop.setProperty(name, value); + + // save properties to project root folder + prop.store(output, null); + + } catch (IOException io) { + LogFile.printErr("Config file exception: " + io.getLocalizedMessage()); + } finally { + if (output != null) { + try { + output.close(); + } catch (IOException e) { + LogFile.printErr("Config file exception: " + e.getLocalizedMessage()); + } + } + } + } + + public Properties loadAll(){ + try { + + input = getFile(configFile); + + if (input == null) { + LogFile.printErr("Config file error: input is null"); + return null; + } + prop.load(input); + + return prop; + } catch (Exception ex) { + LogFile.printErr("Config file exception1: " + ex.getMessage()); + return null; + } finally { + if (input != null) { + try { + input.close(); + } catch (IOException e) { + LogFile.printErr("Config file exception: " + e.getLocalizedMessage()); + } + } + } + } + + public void store(Properties prop){ + try { + + output = new FileOutputStream(configFile); + + // save properties to project root folder + prop.store(output, null); + + } catch (IOException io) { + LogFile.printErr("Config file exception: " + io.getLocalizedMessage()); + } finally { + if (output != null) { + try { + output.close(); + } catch (IOException e) { + LogFile.printErr("Config file exception: " + e.getLocalizedMessage()); + } + } + + } + } + +} diff --git a/src/cucky/jguard/server/Database.java b/src/cucky/jguard/server/Database.java new file mode 100644 index 0000000..001ab82 --- /dev/null +++ b/src/cucky/jguard/server/Database.java @@ -0,0 +1,547 @@ +package cucky.jguard.server; + +import cucky.jGuard.lib.LogFile; +import cucky.jGuard.lib.object.ObjectType; +import cucky.jGuard.lib.object.Map; +import cucky.jGuard.lib.object.MapObject; +import cucky.jGuard.lib.object.ObjectConnection; +import cucky.jGuard.lib.object.ObjectServices; +import cucky.jGuard.lib.object.ServerSettings; +import cucky.jGuard.lib.object.SnmpProbe; +import cucky.jGuard.lib.object.SnmpProfile; +import cucky.jGuard.lib.object.User; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; + +public class Database { + + private static Connection connection = null; + private static String host; + private static String port; + private static String database; + private static String user; + private static String password; + + + public void connect(String host, String port, String database, String user, String password) { + this.host = host; + this.port = port; + this.database = database; + this.user = user; + this.password = password; + + connection = connect(); + } + + private static Connection connect() { + try { + LogFile.printInfo("Connecting to a selected database ..."); + Connection connection = DriverManager.getConnection("jdbc:mysql://" + host + ":" + port + "/" + database + "" + + "?useUnicode=true" + + "&characterEncoding=UTF-8" + + "&autoReconnect=true" + + "&serverTimezone=UTC", user, password); + LogFile.printInfo(" successfully"); + return connection; + } catch (SQLException ex) { + LogFile.printErr("MySQL connection exception: " + ex.getMessage()); + System.exit(0); + } + return null; + } + + /** + * vrati aktualni spojeni + * @return + * @throws SQLException + */ + public static Connection getCurrentConnection() throws SQLException { + if (connection != null || connection.isClosed()) { + return connection; + } else { + connection = connect(); + } + return null; + } + + /** + * obecny dotaz + * @param sqlQuery + */ + public static void query(String sqlQuery) { + try { + Statement statement = getCurrentConnection().createStatement(); + statement.executeUpdate(sqlQuery); + } catch (SQLException ex) { + LogFile.printErr("MySQL exception: " + ex.getMessage()); + } + } + + /** + * insert + * @param sql dotaz + * @return pri uspesnem vlozeni vrati posledni vlozeny id + */ + public static int insert(String sqlQuery) { + long id = -1; + try { + Statement statement = getCurrentConnection().createStatement(); + statement.executeUpdate(sqlQuery, Statement.RETURN_GENERATED_KEYS); + + PreparedStatement getLastInsertId = getCurrentConnection().prepareStatement("SELECT LAST_INSERT_ID()"); + ResultSet rs = getLastInsertId.executeQuery(); + if (rs.next()) { + id = rs.getLong("last_insert_id()"); + } + } catch (SQLException ex) { + LogFile.printErr("MySQL exception: " + ex.getMessage() + " SQL: " + sqlQuery); + } + return (int) id; + } + + /** + * update + * @param sql sql dotaz + */ + public static void update(String sql) { + try { + Statement statement = getCurrentConnection().createStatement(); + statement.executeUpdate(sql); + statement.close(); + } catch (SQLException ex) { + LogFile.printErr("MySQL exception: " + ex.getMessage() + " SQL: " + sql); + } + } + + /** + * overi zda se v databazi nachazi uzivatel s odpovidajicim heslem + * @param username + * @param password + * @return + */ + public static boolean verifyUser(String username, String password) { + try { + String sql = "SELECT count(*) FROM users WHERE username = '" + username + "' && password = '" + password + "'"; + Statement statement = getCurrentConnection().createStatement(); + ResultSet rs = statement.executeQuery(sql); + rs.next(); + int rowCount = rs.getInt(1); + rs.close(); + if (rowCount == 1) { + return true; + } + } catch (SQLException ex) { + LogFile.printErr("MySQL exception: " + ex.getMessage()); + } + return false; + } + + /** + * vráti seznam vsech map + * @return ArrayList + */ + public static ArrayList getMapList() { + String sql = "SELECT * FROM maps"; + ArrayList list = new ArrayList(); + try { + Statement statement = getCurrentConnection().createStatement(); + ResultSet rs = statement.executeQuery(sql); + while (rs.next()) { + list.add(new Map( + rs.getInt("id"), + rs.getString("name"), + rs.getBoolean("locked"), + getMapObject(rs.getInt("id")), + getObjectConnection(rs.getInt("id")) + )); + } + rs.close(); + } catch (SQLException ex) { + LogFile.printErr("MySQL exception: " + ex.getMessage() + "query: " + sql); + } + return list; + } + + /** + * vrati seznam objektu odpovidajici mape + * @param mapId id mapy + * @return ArrayList + */ + public static ArrayList getMapObject(int mapId) { + ArrayList list = new ArrayList<>(); + String sql = "SELECT * FROM object WHERE map_id = " + mapId; + try { + Statement statement = getCurrentConnection().createStatement(); + ResultSet r = statement.executeQuery(sql); + while (r.next()) { + ObjectServices service = new ObjectServices( + r.getBoolean("winbox"), + r.getString("portWinbox"), + r.getBoolean("ssh"), + r.getString("portSsh"), + r.getBoolean("web"), + r.getString("portWeb"), + r.getInt("verzeWeb"), + r.getBoolean("telnet"), + r.getString("portTelnet"), + r.getBoolean("sms"), + r.getString("portSms"), + r.getInt("verzeSms") + ); + + list.add(new MapObject( + r.getInt("id"), + mapId, + r.getString("name"), + r.getInt("type"), + r.getInt("x"), + r.getInt("y"), + r.getString("ip"), + r.getString("user"), + r.getString("password"), + r.getBoolean("active"), + r.getBoolean("notification_sms"), + r.getBoolean("notification_sound"), + service, + r.getString("location"), + r.getString("description"), + r.getInt("snmpProfile"), + getSnmpProbe(r.getInt("id")) + )); + } + r.close(); + statement.close(); + } catch (SQLException ex) { + LogFile.printErr("MySQL exception: " + ex.getMessage() + "query: " + sql); + } + return list; + } + + /** + * vrati seznam propojeni na zaklade mapy + * @param mapId + * @return + */ + private static ArrayList getObjectConnection(int mapId) { + ArrayList list = new ArrayList(); + try { + String sql = "SELECT * FROM objectConnection WHERE map_id = " + mapId; + Statement statement = getCurrentConnection().createStatement(); + ResultSet r_con = statement.executeQuery(sql); + while (r_con.next()) { + list.add(new ObjectConnection( + r_con.getInt("id"), + r_con.getInt("from_obj"), + r_con.getInt("to_obj"), + r_con.getInt("type"), + r_con.getBoolean("snmp_read"), + r_con.getInt("sourceObjectId"), + r_con.getInt("iface") + )); + } + r_con.close(); + } catch (SQLException ex) { + System.out.println("MySQL exception: " + ex.getMessage()); + } + return list; + } +/* + private static ArrayList getSnmpProbe(int objId) { + ArrayList list = new ArrayList(); + try { + String sql = "SELECT * FROM snmpProbe WHERE mapObject = " + objId; + Statement statement = getCurrentConnection().createStatement(); + ResultSet r_con = statement.executeQuery(sql); + while (r_con.next()) { + list.add(new SnmpProbe( + r_con.getInt("id"), + r_con.getString("desc"), + r_con.getString("oid"), + r_con.getBoolean("view") + )); + } + r_con.close(); + } catch (SQLException ex) { + System.out.println("MySQL exception: " + ex.getMessage()); + } + return list; + } + */ + + /** + * vrati informace o uzivateli podle jmena + * @param username + * @return User + */ + static User getUserInfo(String username) { + User u = null; + try { + Statement statement = getCurrentConnection().createStatement(); + ResultSet r_con = statement.executeQuery("SELECT * FROM users WHERE username = '" + username + "'"); + while (r_con.next()) { + u = new User(r_con.getInt("id"), + username, + r_con.getBoolean("setServer"), + r_con.getBoolean("addMap"), + r_con.getBoolean("removeMap"), + r_con.getBoolean("addObject"), + r_con.getBoolean("removeObject"), + r_con.getBoolean("disableObject")); + } + r_con.close(); + } catch (SQLException ex) { + LogFile.printErr("MySQL exception: " + ex.getMessage()); + } + return u; + } + + /** + * vrati nastaveni serveru + * @return ServerSettings + */ + static ServerSettings getServerSettings() { + ArrayList snmp = new ArrayList<>(); + ArrayList user = new ArrayList<>(); + try { + Statement statement = getCurrentConnection().createStatement(); + ResultSet rs = statement.executeQuery("SELECT * FROM snmpProfile"); + while (rs.next()) { + snmp.add(new SnmpProfile( + rs.getInt("id"), + rs.getString("name"), + rs.getInt("version"), + rs.getString("port"), + rs.getString("community") + )); + } + rs.close(); + ResultSet su = statement.executeQuery("SELECT * FROM users"); + while (su.next()) { + user.add(new User( + su.getInt("id"), + su.getString("username"), + su.getBoolean("setServer"), + su.getBoolean("addMap"), + su.getBoolean("removeMap"), + su.getBoolean("addObject"), + su.getBoolean("removeObject"), + su.getBoolean("disableObject") + )); + } + rs.close(); + } catch (SQLException ex) { + System.out.println("MySQL exception: " + ex.getMessage()); + } + + return new ServerSettings(snmp, user); + } + + /** + * vrati seznam SNMP sond podle objektu + * @param objId + * @return + */ + private static ArrayList getSnmpProbe(int objId) { + ArrayList list = new ArrayList(); + try { + String sql = "SELECT * FROM snmpProbe WHERE mapObject = " + objId; + Statement statement = getCurrentConnection().createStatement(); + ResultSet r_con = statement.executeQuery(sql); + while (r_con.next()) { + list.add(new SnmpProbe( + r_con.getInt("id"), + r_con.getString("desc"), + r_con.getString("oid"), + r_con.getBoolean("view") + )); + } + r_con.close(); + } catch (SQLException ex) { + System.out.println("MySQL exception: " + ex.getMessage()); + } + return list; + } + + /** + * vklada typ zarizeni do databaze + * @param name nazev + * @param img_str obrazek v textove podobe + */ + public static int addDeviceType(String name, String img_str) { + String sql = "INSERT INTO object_type (name, img_str) VALUES('" + name + "', '" + img_str + "')"; + long id = -1; + try { + Statement statement = getCurrentConnection().createStatement(); + statement.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS); + + PreparedStatement getLastInsertId = getCurrentConnection().prepareStatement("SELECT LAST_INSERT_ID()"); + ResultSet rs = getLastInsertId.executeQuery(); + if (rs.next()) { + id = rs.getLong("last_insert_id()"); + } + } catch (SQLException ex) { + LogFile.printErr("Exception add device type: " + ex.getMessage()); + } + return (int) id; + } + + /** + * prida propojeni dvou objektu + * @param ObjectConnection + * @return + */ + public static int addObjectConnection(ObjectConnection c) { + String sql = "INSERT INTO objectConnection (" + + "map_id, " + + "from_obj, " + + "to_obj, " + + "type, " + + "snmp_read," + + "sourceObjectId, " + + "iface" + + ") VALUES(" + + "'" + c.getMap() + "', " + + "'" + c.getFrom() + "', " + + "'" + c.getTo() + "', " + + "'" + c.getType() + "', " + + "'" + (c.isRead()? "1" : "0") + "', " + + "'" + c.getSourceObjectId() + "', " + + "'" + c.getIface() + "'" + + ")"; + long id = -1; + try { + Statement statement = getCurrentConnection().createStatement(); + statement.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS); + + PreparedStatement getLastInsertId = getCurrentConnection().prepareStatement("SELECT LAST_INSERT_ID()"); + ResultSet rs = getLastInsertId.executeQuery(); + if (rs.next()) { + id = rs.getLong("last_insert_id()"); + } + } catch (SQLException ex) { + LogFile.printErr("Exception add device type: " + ex.getMessage() + "(SQL: " + sql + ")"); + } + return (int) id; + } + + /** + * vraci seznam typu zarizeni nacteny z databaze + * @return + */ + public static ArrayList listObjectType(){ + ArrayList dt = new ArrayList<>(); + try { + String sql = "SELECT * FROM object_type"; + Statement statement = getCurrentConnection().createStatement(); + ResultSet r_con = statement.executeQuery(sql); + while (r_con.next()) { + dt.add(new ObjectType(r_con.getInt("id"), r_con.getString("name"), r_con.getString("img_str"))); + } + r_con.close(); + } catch (SQLException ex) { + LogFile.printErr("MySQL exception list device type: " + ex.getMessage()); + } + return dt; + } + + /** + * prida map object + * @param mo + */ + public static int addObject(MapObject mo) { + int id = -1; + String query = "INSERT INTO object (" + + "name, " + + "ip, " + + "type, " + + "user, " + + "password, " + + "snmpProfile, " + + "winbox, " + + "portWinbox, " + + "ssh, " + + "portSsh, " + + "web, " + + "portWeb, " + + "verzeWeb, " + + "telnet, " + + "portTelnet, " + + "sms, " + + "portSms, " + + "verzeSms, " + + "map_id, " + + "x, " + + "y, " + + "active, " + + "description, " + + "location" + + ") VALUES (" + + "'" + mo.getName() + "', " + + "'" + mo.getIp() + "', " + + "'" + mo.getType() + "', " + + "'" + mo.getUser() + "', " + + "'" + mo.getPassword() + "', " + + "'" + mo.getSnmpProfile() + "', " + + "'" + (mo.getService().isWinbox() ? "1" : "0") + "', " + + "'" + mo.getService().getPortWinbox() + "', " + + "'" + (mo.getService().isSsh()? "1" : "0") + "', " + + "'" + mo.getService().getPortSsh() + "', " + + "'" + (mo.getService().isWeb()? "1" : "0") + "', " + + "'" + mo.getService().getPortWeb() + "', " + + "'" + mo.getService().getVerzeWeb() + "', " + + "'" + (mo.getService().isTelnet()? "1" : "0") + "', " + + "'" + mo.getService().getPortTelnet() + "', " + + "'" + (mo.getService().isSms()? "1" : "0") + "', " + + "'" + mo.getService().getPortSms() + "', " + + "'" + mo.getService().getVerzeSms() + "', " + + "'" + mo.getMap() + "', " + + "'" + mo.getX() + "', " + + "'" + mo.getY() + "'," + + "'" + (mo.isActive()? "1" : "0") + "', " + + "'" + mo.getDescription() + "', " + + "'" + mo.getLocation() + "'" + + ")"; + id = insert(query); + return id; + } + + /** + * aktualizuje objekt + * @param mo MapObject + */ + static void updateObject(MapObject mo) { + String sql = "UPDATE object SET " + + "name = '" + mo.getName() + "', " + + "ip = '" + mo.getIp() + "', " + + "type = '" + mo.getType() + "', " + + "user = '" + mo.getUser() + "', " + + "password = '" + mo.getPassword() + "', " + + "snmpProfile = '" + mo.getSnmpProfile() + "', " + + "winbox = '" + (mo.getService().isWinbox() ? "1" : "0") + "', " + + "portWinbox = '" + mo.getService().getPortWinbox() + "', " + + "ssh = '" + (mo.getService().isSsh()? "1" : "0") + "', " + + "portSsh = '" + mo.getService().getPortSsh() + "', " + + "web = '" + (mo.getService().isWeb()? "1" : "0") + "', " + + "portWeb = '" + mo.getService().getPortWeb() + "', " + + "verzeWeb = '" + mo.getService().getVerzeWeb() + "', " + + "telnet = '" + (mo.getService().isTelnet()? "1" : "0") + "', " + + "portTelnet = '" + mo.getService().getPortTelnet() + "', " + + "sms = '" + (mo.getService().isSms()? "1" : "0") + "', " + + "portSms = '" + mo.getService().getPortSms() + "', " + + "verzeSms = '" + mo.getService().getVerzeSms() + "', " + + "map_id = '" + mo.getMap() + "', " + + "x = '" + mo.getX() + "', " + + "y = '" + mo.getY() + "', " + + "active = '" + (mo.isActive()? "1" : "0") + "', " + + "description = '" + mo.getDescription() + "', " + + "location = '" + mo.getLocation() + "'" + + "WHERE id = " + mo.getId(); + update(sql); + } + +} diff --git a/src/cucky/jguard/server/Main.java b/src/cucky/jguard/server/Main.java new file mode 100644 index 0000000..d7bb299 --- /dev/null +++ b/src/cucky/jguard/server/Main.java @@ -0,0 +1,96 @@ +package cucky.jguard.server; + +import cucky.jGuard.lib.BuilddDate; +import cucky.jGuard.lib.LogFile; +import cucky.jGuard.lib.OSValidator; +import cucky.jGuard.lib.object.Map; +import cucky.jGuard.lib.object.MapObject; +import cucky.jGuard.lib.object.OnlineClients; +import java.util.ArrayList; +import java.util.Properties; +import cucky.jquard.server.network.*; +import cucky.jguard.server.probe.ProbeThread; + +public class Main { + + public static final int VERSION = 1; + public static final int CLIENT_MIN_VERSION = 26; + public static Properties config; + public static ArrayList maps; + public static ArrayList onlineClients = new ArrayList(); + + + public static void main(String[] args) { + + LogFile.clear(); + LogFile.printInfo("-------------------------"); + LogFile.printInfo(" jGuard Server "); + LogFile.printInfo("-------------------------"); + + LogFile.printInfo("Version: " + VERSION + " (" + BuilddDate.get() + ")"); + + /// + /// detekce operacniho systemu + /// + LogFile.printInfo("OS detect ..."); + if (OSValidator.isWindows()) { + LogFile.printInfo(" windows"); + } else if (OSValidator.isUnix()) { + LogFile.printInfo(" linux"); + } else { + LogFile.printInfo(" other"); + } + + /// + /// nahrani konfigurace + /// + LogFile.printInfo("Loading config ..."); + config = new Config().loadAll(); + if (config != null) { + LogFile.printInfo(" successfully"); + } else { + LogFile.printInfo(" fail"); + System.exit(0); + } + + /// + /// pripojeni k databazi + /// + new Database().connect(config.getProperty("mysql_server"), + config.getProperty("mysql_port"), + config.getProperty("mysql_database"), + config.getProperty("mysql_user"), + config.getProperty("mysql_password")); + + /// + /// nacteni map + /// + LogFile.printInfo("Loading map and object list ..."); + maps = Database.getMapList(); + LogFile.printInfo(" load " + maps.size() + " maps"); + + + /// + /// spusteni sond + /// + ProbeThread pt = new ProbeThread(); + pt.start(); + + for (Map m : maps) { + for (MapObject obj : m.getObjects()) { + if (obj.isActive()) { + ProbeThread.addPingProbe(obj.getIp(), obj.getId()); + } + } + } + + /// + /// spusteni serveru + /// + new Server().start(Integer.valueOf(config.getProperty("server_port"))); + + + + } + +} diff --git a/src/cucky/jguard/server/ServerMessageParser.java b/src/cucky/jguard/server/ServerMessageParser.java new file mode 100644 index 0000000..0312844 --- /dev/null +++ b/src/cucky/jguard/server/ServerMessageParser.java @@ -0,0 +1,263 @@ +package cucky.jguard.server; + +import io.netty.channel.Channel; +import io.netty.channel.group.ChannelGroup; +import io.netty.channel.group.DefaultChannelGroup; +import io.netty.util.concurrent.GlobalEventExecutor; +import cucky.jGuard.lib.LogFile; +import cucky.jGuard.lib.Message; +import cucky.jGuard.lib.Status; +import cucky.jGuard.lib.object.ObjectType; +import cucky.jGuard.lib.object.Map; +import cucky.jGuard.lib.object.MapObject; +import cucky.jGuard.lib.object.ObjectConnection; +import cucky.jGuard.lib.object.OnlineClients; +import cucky.jguard.server.probe.ProbeThread; +import java.net.InetSocketAddress; + +public class ServerMessageParser { + + MapObject mo; + + private Channel currentClient; + private static final ChannelGroup channels = new DefaultChannelGroup( + "containers", GlobalEventExecutor.INSTANCE); + + public ServerMessageParser(Channel currentClient) { + this.currentClient = currentClient; + } + + public void parse(Message msg) { + + System.out.println("-> Message type: " + msg.getType()); + + switch (msg.getType()) { + + case Message.CLIENT_VERSION: + + int client_version = (int) msg.getMsg(); + if (client_version < Main.CLIENT_MIN_VERSION) { + LogFile.printInfo("Zastaralý klient. Odeslán požadavek na aktualizaci (" + ((InetSocketAddress) currentClient.remoteAddress()).getHostName() + ")"); + currentClient.writeAndFlush(new Message(Message.UPDATE, null)); + } else { + LogFile.printInfo("Pozadavek na autorizaci odeslan (" + ((InetSocketAddress) currentClient.remoteAddress()).getHostName() + ")"); + // odeslat autorizacni udaje + currentClient.writeAndFlush(new Message(Message.AUTH_REQUEST, null)); + + } + break; + + + case Message.AUTH: + String[] userData = (String[]) msg.getMsg(); + String username = userData[0]; + String password = userData[1]; + int platform = Integer.parseInt(userData[2]); + if (username.equals("michal") && password.equals("123555")) { + channels.add(currentClient); + + currentClient.writeAndFlush(new Message(Message.AUTH_SUCEFULL, null)); + + // pridat do seznamu klientu + Main.onlineClients.add(new OnlineClients( + String.valueOf(currentClient.id()), + username, // username + ((InetSocketAddress) currentClient.remoteAddress()).getAddress().getHostName(), // ip adresa + String.valueOf(((InetSocketAddress) currentClient.remoteAddress()).getPort()), // port + platform)); // platforma + // odeslat vsem nový seznam pripojenych klientu + sendAll(new Message(Message.ONLINE_CLIENTS, Main.onlineClients)); + // odeslat nastaveni serveru + currentClient.writeAndFlush(new Message(Message.SERVER_SETTINGS, Database.getServerSettings())); + // odeslat informace o prihlasenem uzivateli, opravneni atd + currentClient.writeAndFlush(new Message(Message.USER_INFO, Database.getUserInfo(username))); + // odeslat seznam map + for (Map i : Main.maps) { + currentClient.writeAndFlush(new Message(Message.MAP, i)); + } + // odesle seznam zarizeni + currentClient.writeAndFlush(new Message(Message.LIST_OBJECT_TYPE, Database.listObjectType())); + // zapise do logu + LogFile.printInfo("Uživatel: " + username + " přihlášen (" + ((InetSocketAddress) currentClient.remoteAddress()).getAddress().getHostName() + ")"); + } else { + LogFile.printErr("Uživatel: " + username + " nebyl přihlášen - chybné údaje (" + ((InetSocketAddress) currentClient.remoteAddress()).getAddress().getHostName() + ")"); + currentClient.writeAndFlush(new Message(Message.AUTH_FAIL, null)); + currentClient.disconnect(); + } + break; + + + case Message.ADD_MAP: + String mapName = (String) msg.getMsg(); + // vlozeni do databaze + int id = Database.insert("INSERT INTO maps (name) VALUES ('" + mapName + "')"); + // vlozeni do seznamu + Main.maps.add(new Map(id, mapName)); + // odeslani infomace o nove mape klientum + sendAll(new Message(Message.MAP, new Map(id, mapName))); + break; + + + case Message.REMOVE_MAP: + int map_deleted = (int) msg.getMsg(); + // smazání z databáze + Database.query("DELETE FROM maps WHERE id = " + map_deleted); + // odebrani z lokalniho seznamu + for (Map m : Main.maps) { + if (m.getId() == map_deleted) { + Main.maps.remove(m); + break; + } + } + // odeslani zmeny vsem + sendAll(new Message(Message.REMOVE_MAP, map_deleted)); + break; + + + case Message.SET_MAP_LOCK: + int[] data_lock = (int[]) msg.getMsg(); + for (Map ma : Main.maps) { + if (ma.getId() == data_lock[0]) { + ma.setLock((data_lock[1] == 1)); + } + } + // upravit informaci v databazi + Database.update("UPDATE maps SET locked='" + data_lock[1] + "' WHERE id=" + data_lock[0]); + // odeslani informace ostatnim + sendAll(new Message(Message.SET_MAP_LOCK, data_lock)); + break; + + + case Message.MOVE_OBJECT: + int[] data_move = (int[]) msg.getMsg(); + // upravit pozici v databazi + Database.update("UPDATE object SET x='" + data_move[2] + "', y='" + data_move[3] + "' WHERE id=" + data_move[1]); + // prepsat v lokalnim arraylistu + for (Map map : Main.maps) { + for (MapObject object : map.getObjects()) { + if (object.getId() == data_move[1]) { + object.setX(data_move[2]); + object.setY(data_move[3]); + // odeslat + sendAll(new Message(Message.UPDATE_OBJECT, object)); + break; + } + } + } + break; + + + case Message.ADD_OBJECT: + mo = (MapObject) msg.getMsg(); + // vlozeni do databaze + int id_new_obj = Database.addObject(mo); + if (id_new_obj != -1) { + + // nastavi id objektu + mo.setId(id_new_obj); + // prida do lokalniho seznamu + for (Map map : Main.maps) { + if (map.getId() == mo.getMap()) { + map.addObject(mo); + break; + } + } + // odeslani objektu klientum + sendAll(new Message(Message.ADD_OBJECT, mo)); + // spusteni sondy + if (mo.isActive() && !mo.getIp().equals("")) { + ProbeThread.addPingProbe(mo.getIp(), mo.getId()); + } + } else { + // odeslat klientovi zpravu že doslo k chybe + currentClient.writeAndFlush(new Message(Message.SERVER_MESSAGE, "Došlo k chybě")); + } + break; + + + case Message.UPDATE_OBJECT: + mo = (MapObject) msg.getMsg(); + // aktualizace v databázi + Database.updateObject(mo); + // aktualizace v lokalnim listě + for (Map map : Main.maps) { + if (map.getId() == mo.getMap()) { + map.updateObject(mo); + break; + } + } + // odeslání neznameho stavu + int[] data = { + mo.getMap(), // id mapy + mo.getId(), // id objektu + Status.NA // status + }; + sendAll(new Message(Message.STATE_UPDATE, data)); + // odeslani aktualizovaného objektu klientům + sendAll(new Message(Message.UPDATE_OBJECT, mo)); + // zastavit sondu a spustit znova + ProbeThread.removePingProbe(mo.getId()); + if (mo.isActive() && !mo.getIp().equals("")) { + ProbeThread.addPingProbe(mo.getIp(), mo.getId()); + } + break; + + + case Message.ADD_OBJECT_TYPE: + ObjectType objtype = (ObjectType) msg.getMsg(); + // pridani do databaze + int id_add_obj_type = Database.addDeviceType(objtype.getName(), objtype.getImgStr()); + // pridani id do objektu + objtype.setId(id_add_obj_type); + // odeslat novy objekt vsem + sendAll(new Message(Message.ADD_OBJECT_TYPE, objtype)); + break; + + + case Message.NEW_CONNECTION: + ObjectConnection oc = (ObjectConnection) msg.getMsg(); + // pridani do databaze + int id_add_con = Database.addObjectConnection(oc); + if (id_add_con != -1) { + for (Map map : Main.maps) { + if (map.getId() == oc.getMap()) { + // pridani do arraylistu + map.addObjectConnection(oc); + // odeslat nové propojeni klientum + sendAll(new Message(Message.NEW_CONNECTION, oc)); + break; + } + } + } else { + // odeslat klientovi zpravu že doslo k chybe + currentClient.writeAndFlush(new Message(Message.SERVER_MESSAGE, "Došlo k chybě")); + } + break; + + + + + default: + LogFile.printErr("Neznamy typ zpravy"); + + } + + } + + public static void sendAll(Message message) { + if (channels.isEmpty()) { + return; + } + channels.writeAndFlush(message); + } + + public void removeClient() { + // odebrani ze seznamu klientu + for (int i = 0; i < Main.onlineClients.size(); i++) { + if (Main.onlineClients.get(i).getId() == String.valueOf(currentClient.id())) { + Main.onlineClients.remove(i); + } + } + sendAll(new Message(Message.ONLINE_CLIENTS, Main.onlineClients)); + } +} diff --git a/src/cucky/jguard/server/probe/PacketCaptor.java b/src/cucky/jguard/server/probe/PacketCaptor.java new file mode 100644 index 0000000..280e723 --- /dev/null +++ b/src/cucky/jguard/server/probe/PacketCaptor.java @@ -0,0 +1,156 @@ +package cucky.jguard.server.probe; + +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.URL; +import java.net.UnknownHostException; +import java.util.Arrays; +import cucky.jguard.server.Main; +import cucky.jGuard.lib.LogFile; +import jpcap.JpcapCaptor; +import jpcap.JpcapSender; +import jpcap.NetworkInterface; +import jpcap.NetworkInterfaceAddress; +import jpcap.packet.EthernetPacket; +import jpcap.packet.ICMPPacket; +import jpcap.packet.IPPacket; +import jpcap.packet.Packet; + +class PacketCaptor { + + private static final int DEFAULT_TTL = 32; + public static final short SEQ = 100; + public static final String DATA = "data"; + + private NetworkInterface device; + private InetAddress thisIP; + private byte[] gwmac; + private JpcapSender sender; + private JpcapCaptor captor; + + public PacketCaptor() throws Exception { + + //initialize Jpcap + NetworkInterface[] ifaces = JpcapCaptor.getDeviceList(); + LogFile.printDebug(ifaces.length + " network interface available"); + + for (NetworkInterface iface : ifaces) { + if (macAsString(iface.mac_address).equals(Main.config.get("interface_mac"))) { + device = iface; + break; + } + } + + captor = JpcapCaptor.openDevice(device, 1000, false, 10); + + for (NetworkInterfaceAddress addr : device.addresses) { + if (addr.address instanceof Inet4Address) { + thisIP = addr.address; + LogFile.printInfo("Local adress: " + thisIP.getHostAddress()); + break; + } + } + if (!((String) Main.config.get("gateway_mac")).isEmpty()) { + gwmac = macToByte((String) Main.config.get("gateway_mac")); + } else { + gwmac = getMacGateway(); + } + + + captor.setFilter("icmp and dst host " + thisIP.getHostAddress(), false); + sender = captor.getJpcapSenderInstance(); + + } + + /** + * odesle icmp packet na zadanou adresu + * + * @param address - cilova adresa + * @param seq - cislo sekvence + * @throws UnknownHostException + */ + public void sendPacket(String address, short seq) throws UnknownHostException { + //create ICMP packet + ICMPPacket icmp = new ICMPPacket(); + icmp.type = ICMPPacket.ICMP_ECHO; + icmp.seq = seq; + icmp.id = 0; + icmp.setIPv4Parameter(0, false, false, false, 0, false, false, false, 0, 0, DEFAULT_TTL, IPPacket.IPPROTO_ICMP, + thisIP, InetAddress.getByName(address)); + icmp.data = DATA.getBytes(); + EthernetPacket ether = new EthernetPacket(); + ether.frametype = EthernetPacket.ETHERTYPE_IP; + ether.src_mac = device.mac_address; + ether.dst_mac = gwmac; + icmp.datalink = ether; + sender.sendPacket(icmp); + } + + public Packet getPacket() { + return captor.getPacket(); + } + + /** + * prevede mac adresu ve tvaru xx-xx-xx-xx-xx-xx na byte[] + * + * @param macAddress - string ve tvaru xx-xx-xx-xx-xx-xx + * @return byte[] + */ + private byte[] macToByte(String macAddress) { + String[] macAddressParts = macAddress.split("-"); + byte[] macAddressBytes = new byte[6]; + for (int i = 0; i < 6; i++) { + Integer hex = Integer.parseInt(macAddressParts[i], 16); + macAddressBytes[i] = hex.byteValue(); + } + return macAddressBytes; + } + + /** + * prevede mac adresu ve formatu byte[] na retezec + * + * @param bytes + * @return + */ + private String macAsString(byte[] bytes) { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (byte b : bytes) { + if (!first) { + sb.append(String.format("-%02X", b)); + } else { + sb.append(String.format("%02X", b)); + first = false; + } + + } + return sb.toString(); + } + + /** + * vrátí mac adresu výchozí brány + * + * @return byte[] + * @throws Exception + */ + private byte[] getMacGateway() throws Exception { + byte[] mac = null; + LogFile.printDebug("Dynamic GW search in progress..."); + InetAddress pingAddr = InetAddress.getByName("www.google.com"); + captor.setFilter("tcp and dst host " + pingAddr.getHostAddress(), true); + while (true) { + new URL("http://www.google.com").openStream().close(); + Packet ping = captor.getPacket(); + if (ping == null) { + LogFile.printDebug("cannot obtain MAC address of default gateway."); + System.exit(-1); + } else if (Arrays.equals(((EthernetPacket) ping.datalink).dst_mac, device.mac_address)) { + continue; + } + mac = ((EthernetPacket) ping.datalink).dst_mac; + break; + } + LogFile.printDebug("Default gateway found -- " + Arrays.toString(mac)); + return mac; + } +} diff --git a/src/cucky/jguard/server/probe/ProbeThread.java b/src/cucky/jguard/server/probe/ProbeThread.java new file mode 100644 index 0000000..7e78eb1 --- /dev/null +++ b/src/cucky/jguard/server/probe/ProbeThread.java @@ -0,0 +1,223 @@ +package cucky.jguard.server.probe; + +import cucky.jGuard.lib.LogFile; +import cucky.jGuard.lib.Message; +import cucky.jGuard.lib.object.Map; +import cucky.jGuard.lib.object.MapObject; +import cucky.jGuard.lib.Status; +import cucky.jguard.server.Main; +import cucky.jguard.server.ServerMessageParser; +import java.net.UnknownHostException; +import java.util.ArrayList; +import jpcap.packet.ICMPPacket; +import jpcap.packet.Packet; + +public class ProbeThread extends Thread { + + private static final int RUN_DELAY = 5000; + + protected static ArrayList sended = new ArrayList(); + private static PacketCaptor pc; + private static boolean stop = false; + + public ProbeThread() { + super("probe-thread"); + } + + @Override + public void run() { + super.run(); + + LogFile.printInfo("Probe thread run"); + + try { + pc = new PacketCaptor(); + } catch (Exception ex) { + LogFile.printErr("[ProbeThread] PacketCaptor exception: " + ex.getMessage()); + } + + CaptureThread ct = new CaptureThread(); + ct.start(); + + TimeoutThread to = new TimeoutThread(); + to.start(); + } + + /** + * Přidá do fronty zařízení se sondou + * + * @param address + */ + public static void addPingProbe(String address, int deviceId) { + Thread th = new Thread() { + @Override + public void run() { + try { + super.run(); + Thread.sleep(RUN_DELAY); + // odesle packet + pc.sendPacket(address, PacketCaptor.SEQ); + // zaradi zarizeni mezi odeslane + sended.add(new SendedProbe(SendedProbe.TYPE_PING, deviceId, address, System.currentTimeMillis())); + } catch (InterruptedException | UnknownHostException ex) { + LogFile.printErr("addProbe exception: " + ex.getMessage()); + } + LogFile.printInfo("Spusten ping na: " + address); + } + + }; + th.start(); + + } + + /** + * odebere ping sondu zarizeni + * + * @param deviceId + */ + public static void removePingProbe(int deviceId) { + for (SendedProbe sp : sended) { + if (sp.getDeviceId() == deviceId) { + sended.remove(sp); + break; + } + } + } + + /** + * Aktualizuje ve frontě informace o sonde + * + * @param address + */ + private static void updateProbe(String address) { + try { + for (SendedProbe probeData : sended) { + if (probeData.getAddress().equals(address)) { + // odesle packet + pc.sendPacket(address, PacketCaptor.SEQ); + // zaradi zarizeni mezi odeslane + probeData.setTime(System.currentTimeMillis()); + break; + } + } + } catch (UnknownHostException ex) { + LogFile.printErr("addProbe exception: " + ex.getMessage()); + } + } + + /** + * Odchytavani packetu + */ + private static void capturePacket() { + Packet packet = pc.getPacket(); + + if (packet != null) { + long packetRecTime = ((long) packet.sec) * 1000 + packet.usec / 1000; // TODO + if (packet instanceof ICMPPacket) { + ICMPPacket p = (ICMPPacket) packet; + + if (p.seq == PacketCaptor.SEQ) { + if (p.type == ICMPPacket.ICMP_ECHOREPLY) { + for (SendedProbe pd : sended) { + + // POKUD JDE O SONDU TYPU PING A ZAROVEN JDE O STEJNOU ADRESU + if (pd.getAddress().equals(p.src_ip.getHostAddress()) && pd.getType() == SendedProbe.TYPE_PING) { + + long latency = System.currentTimeMillis() - pd.getTime(); // TODO oveřit správný výpočet + LogFile.printDebug("Odpoved od: " + p.src_ip.getHostAddress() + " time: " + latency + " ms"); + + // pripadna aktualizace stavu v seznamu + updateStatus(pd, Status.OK); + + // nové odeslani icmp packetu + updateProbe(pd.getAddress()); + break; + } + } + } else { + LogFile.printDebug("ICMP packet type: " + p.type); + //fail("ping", p.src_ip.getHostAddress()); + } + } + + } + + } + } + + private static void updateStatus(SendedProbe pd, int currentStatus) { + for (Map map : Main.maps) { + for (MapObject obj : map.getObjects()) { + if (pd.getDeviceId() == obj.getId()) { + if (obj.getStatus() != currentStatus) { + // ulozit do arraylistu + obj.setStatus(currentStatus); + // odeslat novy status vsem + int[] data = { + obj.getMap(), // id mapy + obj.getId(), // id objektu + currentStatus // status + }; + ServerMessageParser.sendAll(new Message(Message.STATE_UPDATE, data)); + } + break; + + } + } + } + } + + /** + * Vlakno pro nonstop odchyt packetu + */ + private static class CaptureThread extends Thread { + + public CaptureThread() { + super("packet-capture"); + } + + @Override + public void run() { + super.run(); //To change body of generated methods, choose Tools | Templates. + LogFile.printDebug("Capture Thread run"); + while (!stop) { + capturePacket(); + } + } + } + + /** + * Vlakno pro timeout sondy ve fronte + */ + private static class TimeoutThread extends Thread { + + // testovat se bude 1x za sekundu + private static final int CHECK_INTERVAL = 1000; + + public TimeoutThread() { + super("timeout-checker"); + } + + @Override + public void run() { + + while (!stop) { + try { + Thread.sleep(CHECK_INTERVAL); + for (SendedProbe pd : sended) { + if (System.currentTimeMillis() > (pd.getTime() + CHECK_INTERVAL) && pd.getType() == SendedProbe.TYPE_PING) { + // pripadna aktualizace stavu v seznamu + updateStatus(pd, Status.OFFLINE); + // nové odeslani icmp packetu + updateProbe(pd.getAddress()); + } + } + } catch (InterruptedException ex) { + LogFile.printErr("TimeoutThread exception: " + ex.getMessage()); + } + } + + } + } + +} diff --git a/src/cucky/jguard/server/probe/SendedProbe.java b/src/cucky/jguard/server/probe/SendedProbe.java new file mode 100644 index 0000000..b78aac5 --- /dev/null +++ b/src/cucky/jguard/server/probe/SendedProbe.java @@ -0,0 +1,56 @@ +package cucky.jguard.server.probe; + + +public class SendedProbe { + + public static final int TYPE_PING = 1; + public static final int TYPE_SNMP = 2; + + private int type; + private int deviceId; + private String address; + private long time; + + public SendedProbe(int type, int deviceId, String address, long time) { + this.type = type; + this.deviceId = deviceId; + this.address = address; + this.time = time; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public long getTime() { + return time; + } + + public void setTime(long time) { + this.time = time; + } + + public int getDeviceId() { + return deviceId; + } + + public void setDeviceId(int deviceId) { + this.deviceId = deviceId; + } + + + + +} diff --git a/src/cucky/jquard/server/network/Server.java b/src/cucky/jquard/server/network/Server.java new file mode 100644 index 0000000..3b51bc4 --- /dev/null +++ b/src/cucky/jquard/server/network/Server.java @@ -0,0 +1,45 @@ +package cucky.jquard.server.network; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; +import io.netty.handler.ssl.util.SelfSignedCertificate; +import cucky.jGuard.lib.LogFile; +import cucky.jguard.server.Config; + +public class Server { + + public void start(int port) { + EventLoopGroup producer = new NioEventLoopGroup(); + EventLoopGroup consumer = new NioEventLoopGroup(); + + try { + SelfSignedCertificate ssc = new SelfSignedCertificate(); + SslContext sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) + .build(); + + ServerBootstrap bootstrap = new ServerBootstrap() + .group(producer, consumer) + .channel(NioServerSocketChannel.class) + .childHandler(new ServerAdapterInitializer(sslCtx)); + + LogFile.printInfo("Server started on port " + port); + + ChannelFuture f = bootstrap.bind(port).sync(); + f.channel().closeFuture().sync(); + + + } catch (Exception e) { + LogFile.printErr("Server error: " + e.getMessage()); + } finally { + producer.shutdownGracefully(); + consumer.shutdownGracefully(); + } + + } + +} diff --git a/src/cucky/jquard/server/network/ServerAdapterHandler.java b/src/cucky/jquard/server/network/ServerAdapterHandler.java new file mode 100644 index 0000000..f064f42 --- /dev/null +++ b/src/cucky/jquard/server/network/ServerAdapterHandler.java @@ -0,0 +1,48 @@ +package cucky.jquard.server.network; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.group.ChannelGroup; +import io.netty.channel.group.DefaultChannelGroup; +import io.netty.util.concurrent.GlobalEventExecutor; +import cucky.jGuard.lib.LogFile; +import cucky.jGuard.lib.Message; +import cucky.jguard.server.ServerMessageParser; + +public class ServerAdapterHandler extends SimpleChannelInboundHandler { + + private static final ChannelGroup channels = new DefaultChannelGroup( + "containers", GlobalEventExecutor.INSTANCE); + + @Override + protected void channelRead0(ChannelHandlerContext chc, Message msg) throws Exception { + Channel currentClient = chc.channel(); + // predani prichozi zpravy ke zpracovani + new ServerMessageParser(currentClient).parse(msg); + } + + @Override + public void channelRegistered(ChannelHandlerContext ctx) throws Exception { + super.channelRegistered(ctx); + Channel currentClient = ctx.channel(); + currentClient.writeAndFlush(new Message(Message.CLIENT_VERSION, null)); + } + + @Override + public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { + super.channelUnregistered(ctx); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + new ServerMessageParser(ctx.channel()).removeClient(); + } + + public static void sendAll(Message message) { + if (channels.isEmpty()) { + return; + } + channels.writeAndFlush(message); + } +} diff --git a/src/cucky/jquard/server/network/ServerAdapterInitializer.java b/src/cucky/jquard/server/network/ServerAdapterInitializer.java new file mode 100644 index 0000000..c4c1f18 --- /dev/null +++ b/src/cucky/jquard/server/network/ServerAdapterInitializer.java @@ -0,0 +1,34 @@ +package cucky.jquard.server.network; + + + +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.socket.SocketChannel; +import io.netty.handler.codec.serialization.ClassResolvers; +import io.netty.handler.codec.serialization.ObjectDecoder; +import io.netty.handler.codec.serialization.ObjectEncoder; +import io.netty.handler.ssl.SslContext; + +public class ServerAdapterInitializer extends ChannelInitializer { + + private final SslContext sslCtx; + + public ServerAdapterInitializer(SslContext sslCtx) { + this.sslCtx = sslCtx; + } + + + + @Override + protected void initChannel(SocketChannel channel) throws Exception { + ChannelPipeline pipeline = channel.pipeline(); + + pipeline.addLast(sslCtx.newHandler(channel.alloc())); + pipeline.addLast("decoder", new ObjectDecoder(ClassResolvers.cacheDisabled(getClass().getClassLoader()))); + pipeline.addLast("encoder", new ObjectEncoder()); + + pipeline.addLast("handler", new ServerAdapterHandler()); + } + +} \ No newline at end of file