From 5b3e47499011d8d72bef0be1335065d85bf962ed Mon Sep 17 00:00:00 2001 From: r00t Date: Mon, 20 Apr 2026 12:59:25 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=AE=A4=E8=AF=81?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=E9=97=AE=E9=A2=98=E5=B9=B6=E5=8A=A0=E5=9B=BA?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E7=AE=A1=E7=90=86=E5=91=98=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复 Authorization2 Bearer 头解析不安全问题 - 修复 forward-connection 的 SSRF 风险 - 修复登录、改密、BasicAuth 未严格校验用户存在的问题 - 初始化数据库时改为生成随机管理员密码 - README UPDATE --- .gitignore | 3 +- assets/image-20260420125544426.png | Bin 0 -> 47372 bytes pkg/api/forward.go | 124 ++++++++++++++++++++++------- pkg/api/users.go | 62 +++++++++++---- pkg/database/sqlite3.go | 43 ++++++++-- pkg/middlewares/auth.go | 35 ++++++-- readme.md | 11 ++- 7 files changed, 216 insertions(+), 62 deletions(-) create mode 100644 assets/image-20260420125544426.png diff --git a/.gitignore b/.gitignore index 7937caf..c5b9435 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ /Downloads/ /Rshell_darwin_amd64 /Rshell_darwin_arm64 -/.gitignore \ No newline at end of file +/.gitignore +./Rshell.exe \ No newline at end of file diff --git a/assets/image-20260420125544426.png b/assets/image-20260420125544426.png new file mode 100644 index 0000000000000000000000000000000000000000..186541850270b0d8a7f4c6121b31ff161dda13c9 GIT binary patch literal 47372 zcmagF1ymiuvZy_{ThQPH2*KUm2`<6i0>Rx~6Wrb1CAeE~cXwyw?mTkjp8Nj)z5CYM zYtNoF-Mzc3d%CKtzWOCED~<$@3l9JQ(pL!)MF4=ZfB!uK3;zDyepIORegU&r6c++2 z#_$gTfEf5HBKXZE?Ks`p<6G}-=lDvCC;Q^{nmY4>kqJ~lHg!Mus`zdjMx|3xjXpTjM;D2WD+3L&w?XAvoo8FrMZ7U zO}8&3CRzuGSCr2mc*4L)3Jw{e15ia3U`YZ{`)~kM|7S}|G@+k4s6w=f0IC2zu3i`z zDj+(|B5G?259oKcC1|Ou_tqLfg8K>(qr(2Z;6U0yYk+~9e+glF?WE!oP^e`TnYb;? zsA=d!)sW0HZKkE_;^79*`jOI;-Gq+}==GVQ|D)nR>Tg>4ieSO|Bk1Z9P2cyPEcZf` zApcj>#Hen*xTaA5DgFLrr8kNbAwV#^p}~0>BPb>&##bfy-(7ick$@x`s$1Lt{po&a zZ?91k^~wnZ}1sW|CcoWdC}uE8Ux>~3@a@ywfzJk1Pf46Q^RPG{^u|5O9ek6 z1azQPR8;Uj&pI#$>Qv4jI)sOZJB!f&r&MF;19wGGr!2GXa;@WZfwzo(Ct}RI@ccgJ zp^*ITeG@xVxGuJIf07w@F;2{(5wdL{?VrGC>B$}Boph?r-DVJ10+Bs-1AMMt;O zIe|+~f76b!Ka?L@t1;G)GWtX&C)LtS%ra-P!%?~J6z5Qh(^nl?u9;3uBjf5gdU|_LCFflrg#975!@;XX^;pw3N+`c% zK0B*CEA901BwVFsI2JBDAt}>f+~Yt>ZF5i&s%pvoCV=POee`O^>Ws6e6CB8A__5g6d}VPUBW@{jCgWC+6Y(LByGTs}|McB^-o8d?)sZu$ zj%N&M;Bt?!l2Gmx64}$D3J?QD#;kR$4#V_X)&AcY8g)@{>bJNBT*a2c|8Ro&J%*aR z3$zLx@OhdToEEheLmMEbS&>Xl=yfBNc8xGH+R7Ob``jMHEc#;G5w<6##=9XNX#lv0pT zx$OwA-Em|uu6OFV;It6JDi>k9t#2_wwL?`A9=aZN(XJre-SKojj6UoCxl#U&!qcNp zKP1v?Qhp3Cxh=m>O~rpl0|3YXRTLiEXfKQ3oAH>f>Bjz9V(=w-_zmsShTN*CN~@;5 zJmh~jp>&4zu4R5@60rtbL{xhOO#^bL3&)V*HHrm6xKJJ9cholTMI?uMN1*EQa zVHSR$X?2?%8t3E0&)QlagDs@Kw{yyPnd%flQX(K*Xmo%K0->W=jE*k*6Z>4`ybm(2 zDqrJF27gM}ACBx6G(z^@|2ZJC)@kY?vVikOlHaRmEw2ote@JEXLx>wo>LpzVF&i*)V1bu$dC}lX?eM*TL zuj>u#nRKRo5(nJh7Y&C$tOoNnT6FKqMIxv0{))e@h;62v*6*|EN&Sd%nm?PBPG37h z--#3Ie{J1Ssf`0^{U+sXM_y=1Qt>uc?#s z_+#TdrPUG*$h&?l46bu&J727^VYnIlv*KozF||4C*>}|h3ukANwsG0bPj~KNiA>Q4>iY>QaHc3_}Rwgm8z`N%@0air@g%NukKTG)97@1 zR4UZ$Ut5sVB;9W{8*iqcg7 z9VH|W0pmO{8$VOW@)(NbAjZzkgs5HQWn4@$Mc|9bBsZDq(bYr-n(?-~VJ2dv$t(&z zhs?ko3N7k!=g@tYWJUh{8v83FX7b2i7l#Mw0EGGMt!vqF%BJgikLe%o=VnNG)}r`F z^7H%Nn`Gc>1Qw>d-g(`mt)K*>2ihXCfWVZoEM^Hjgh8OC^OsTrvJ^^3rK}`gZco0U z6G+1mF_R4Vh61f(Wc_arO!>oueXy6ZnY5WK)1+?KrjyBrp5B94y?ob8B^Rh+n0QH3nP(NtZ$r~J@Hoj_uAa=5K(sXl=9kRogdx$23t0+7sVPkc+; zr3cT&Gt=3$isC452KvUB4OaYpU#BA19-2B?5I56WkK%U~J_J&vRrMtWjCaO)x119D%SDSDGNx&GdDLI2|%zoA=mag zG+E^JXO^&mNnkfTGHI2bluW$G(3AnfK? zqqy2mCzU&s5;xY0dDn!pX~zL%O{a*Esl|`Y2rt~Sd9w<23*7^v51dDuDOxuHXlo>l zLLO>A^m`ZiBOj{?sxMIM+h-`6LBdI$MXE;gA;~=}m$VFxsQ@aEeN!YJj7yBq1$9En z_>$$9fqvk$7jtImG%iHEVClvVv(1*v;Q~`ERygYw>O%YE+$Wi*p;m z`yPHdDkt}k-<=<6C8enFyy#S(Pv#y+5F9RF3Wr(7d?({pY}|X^8)qY;S0w>9I8fX- z77P2_$VO9J`R$#5m`k2#-YrP;M>pT!JFi>ttLFUabPP$a*_w10+9vWMW(1Yi?jd(8 zbi#JvY3g`ieM`pe{46hB=kZJb28oCSp2UdD;=3Aa0BX(`5e2Lfb<=Sv1)Yjm|6CavJHFhGP>=2svaDsyMR zk#*ROdrQcxZ*A=hpE6bzX13r^Srd@&9Y}%8g0rI!EtZyq;7n_Qnzh4 zdl?!^iKdxVowb+^`nE*f;W)T-;0$qzbs+^~Ol>FpEE zu|KppnW11PFfbs6gdK#p8(yZ$q33D6t((puEm&oC_t28^*gVeWbw|-|ioFP=c0a&F zIqKfJxk^8zWkqDF?IzNfr~;c%YHHA^&@0n@j_QgHn(9gOZWZ09LIuuwuFqanYX)@R zRuqpm+?xHP@@zSF$=A4pDOvpnI8$yaai9He=*J2ZUPT&){s(Y`7n3$An;*Xr6fuC)2=C9ju$2H{HeREJcQMe+1 z%s=_IES}ere+mh%U!{M@hw!Q6f;uzl09wucN?PN|&cE4E^VK<9lHxk^exj79^5tkX zF{9a9#jo-t9VsApkD7bU_4OB~+_SxmvlNL&kpyq5HNZtiGTAiV+swg9;D8Tq7B1EFwxqsoJ#h2N?e|678m)Q@xAW9s!eo!CS4-F8CrX=t> zLC%){f&m`@PU-=+e!rgaR+onXfA7H;Je>U_CC5~R(Z68XYsaqX-83u;9_+9}3*~;b zv3hzWb(Qf!6zi}_r-8p{*#dQNj&C%`$iAc;kch(Yv>?nDWNOlJ0`l11-0%Taj#2q{K|86|xcY z3S2t08C6|p(p-zcKaSoNDI#7(nx5JBK5sU&7Z}QC$*pfps4w_ROBx??z`o&NMd+iOD5%@iB6B4w72ri7yQNs=?M;dgx^j zwgy2j9#aM!$z_gaVB>sbFe?L{b*S8aZ98h_0$?=P^|0eqXK$ZXFmxnNsy+mOPR&rV z=;^5b6oew;UWFbJcMlFOVrC8Sz|al06kE!Hb`Jh%(IVo2P2pX0mG@=M;~^GQV)4#5 z>^AwL_U_s}fRl(SU|F~dC}8BE8r#)=h~-4+laq&T6U_c*UZ>HfGwzWgD%AvQ%YmOQ zFEEWC`=gn6kl}PhPr6_p6qi=XcbHM*d=v$Y4`d3%K~6yk@_kC2tSXpVaX z#&a)JZQC69i|Mj;#UQrsck_y$A`bcP!a@k35vf^|C<&HTQl@5}Q+V`U5DF3DSQ0_3 zyipX)zS6&M>-T5a-_a7sU}66H6{(@TK-|u{{9R)FLM<}3I1ErUG;GBpQ{3|U)EB;3 z-#F%{fE=o_0^h?2NwWW=(wl$uIj7{lbAEY>^+bIker-|MYA?~@8uhkK=FPAACC@}R z^=S5@`FSPC*=w-cOH%gRPa<$F8)iJxfGiGnxrmh$|DLuDv0`JU#i`1ZpH0D4=i~{F zCX>O}@XI>Uz5=s{H{oTT`YcK#9&b1rh-*qoP zX~+u@hcJCLp{+Eh7s9VQE^gvuso-w1lp{R7-1wlp7|08v$G3@0Wpcj#)3}i*N>3Jc z|DbDqda{g%Mv5xLe9 zT2!A-k6SUlTg5R`!Hq5dLT9n`^_1{=h5x4gmCG7_s$Ava*3!Va)-r9YZ68V>PJhv?6PPmuTX9<;#ZthoNd12c5EUmj~_jO?U|p}$5{&U zwRJH*o+L-@yn*Nwtf9eR2glMz(lA-bEG}SlrN7WtRuos1R8&y4$H%+oidfaFV`q*- z0923dk#98<-yq3jz=6bh*NY~dVg_EH^kL&>bWcqTpD9v{2~r>ck*0xJ1ePSFfD=E0 zJQ@lT_M-_N@m?tIlByyxujof?TX}$n&48u7QvpMC8lNt4y8aD(J4;MTD8KtTT(wvc z;-?D_pFOX1pg)V*T#R}ysb`vHApVuy7gz;g54~1d@@g;VTZv{&y>9LgNI;|ffn6eA z5=y(P71Q~a%O@xXq*}Q=P_mS8gvgtf@Y$?5nl z!(_V2h&oS1f3u6n=Xa|`)12LtS;i_eNhs|dYn08qObj&Up$8(Xrcu=QMK05<{u=X}blt-PGHD%lcv(?bL!3x|HbhfzCYUA9tIo}YuY1Ak zh&q!74(*qMVO@PLEiGj_7=)=@fSKajBTOt*)0_mHS9F&Fpl&4#S|Pm&PwhnoVWVOV zzFcWNmASAnJ&6%5quHbAXT&J4K1~HXkXvrJ_nscMNgkhl4?b48=8|rb(YjgT?fPW7 zI~bn%rm|po=F8Ym^2xRD_|ULB%_Xb6L*a8ngo-wCu2Z_-&Q}Kah|L%CZ|o}K+`1bb zv4ISI)a<3Y1mMW9m_N8Q+U(fR_##dp+;42bTpFAy|q%8n}+yXv7Z`DbPb#5Dy0-sa<-TG_}h_`c#3O+3xiWzoL#yDs(pJB_*hPk zTArw+(VaadRZ5BLMN|85mn8S`;yzy?F!bTjaQUGDc=H6GVnat%g#k8h{uog)hLY6^ z=$`%Lhr#F*)ufrWp5oZm0)rfJm}e6joJ}7ana4dj_Dm|vr+`oY3}$E$TTd81P>sA= z5|rt^Ph3safiP+b|EI_5sRFykGs0(j2Y(8Js7TC5bhTd2h_hrdNHYB zGpMqglUiryZ0lA&4{6_0V^1&44x{4x`1@VkV{hpXE}ciY8L3RJeI*CU8YiJlEucz0bc^$J10m$~Q3 zUR2JGLIJYo9bE%>nM{r10~bG9x_uUG85qAzcLl0QYrW{-ae%GC25kJ4fsB_G^2h z%8Foo#$!~3Dcy@#4u-$!LhUcQ7ezNYwWfxTBYGpDh&K>E1tBY%4OaCp)D=pkMtJzA zCeh!hc_L}d#7B7Z);$K{)e2RO!dTrHN;}6u3<-#?2fe!&vPC3@AaT$%s2MASv7h{V zU&uV4`&-jfUywwK72(^(Hn#5L-CoaPoik0Do=B|Zivoh4bt8Mzoi+DR?o8wZz!oHq z?VO5psGyAI`G{iz5_|Q`RDkzMiiR$R;#&{<26RHf)$m5cASSpORJ=ZT9}07P0l}Pt z#L+l58+7NEovX(GX@CwKj1Pdbkl^X^uV-IO?xhmh3+MO+081$)ZU_M?^K*Iud0v9w ztv9=?NQ7U&!H*j~y>CeyZMoV$kX%Ilfz;_Yku#Pd02ZF0>Q&(kWve}*rpYu_)pzyNmfPTzMCF29s!FJGL6iD>ZaU`Qwb2b?zzU#s)8AEd*6_ON zmuu(Jo~?^DlwD}Ee%@a-!KQ!?F=mgf>%%yDq%PQCS8_iux?IwkRhn$Gj`Kyl9*03A zX0iQz%DNYb+Gfk04~g?zBGJ??2&&*_{7tp(MCwMGn;9;IOA-pWov_4jfMF@Bg7;wx z`^nxy6EfL$A9vf^^jP8MZWN|^;YJk2EVr1-yLj-6k?=OaNtAeQZ@SW|z{$Ihle~W{ zw)RYhS&iney|;8QKOiKy^k4_EB~Gt((+O*hrWeL&jK}718g$Hlzi68>!Z-*UYvu8kJN=wQh*^MPvsv>ge^Ri zq)HwPR_!rf)uks{A=Fe1K2q03ophOvNSv<9IKrJf#?$^}edCAV4UG@uBglut$i}Yf zi*^BjE)Z11Zm|FW+IAu?UdivNsw5by$z)9R6PdAL{+cn91p#b)dW+36kaq6+md2l# zZ-V+n&R_2k2btX$c1>5WLSI@21~e3LL|Q>}(P-F;zk6PrMKW%mlHGu($>|vBAQ#Us zZSFJ&mTnhDp^C_mU7KA(K6|N)dnjNnAOSjv}P zW@a&mU@4*nPcnP5-mx4zKg+dw&WP@Z-w4}jE@JmAN1=r4M%zGkqcxe<)V`UEPN0aA zGFtX!vE!t(YQNB3lwRvVYps!V$_lH_F;OfLuz5{FGldFOFvy$gobiFmv2|=^&T|F z^UoeOM%w}jf>XMDUoE5_@Pu0a@Skg}7z9d-N?AbRht)YAYI;kUA00cw4l2FzFtfVU zo`ucx6D?2VSur-g9-)}m>wp7a7T!P)3e?*t!8BgmHLi9|F&`FxHZJ+K^@p|=^r;mP zzQp33n43x5@)xQg*pA%5mZe`LvHy@h9hp?sT|z-orGn=ldr240RvSlSeeg z2is-bNp(}OF}Og`y)QWR$DBT^Zd{!az0* zFu(-NaL(It9Zx7GI_4VK`e@3#aB!s$YS29GE6_7oi(6zcy_`v5>BZ|*H>iw=wY{Eo zl|d?1=A!Sd;Zcc61Z(y4-*nwa7z`nM+z&?MLIPRC4=>x;_7(F-qXlHQ*hMFCC?Vf( zU#Jq7Vj)U}FItm>PWZ2xh@cKR){kds-Eb*rSw&tXwJi_%53Sji?B{TV_RQr*{8G3k z6!p%0MDZc1u1CKw`Q5!cKl5)^Dz{nW0zm5vd77lb(118z7MfYRU`KOtXbOqQ**(#I zw3_w|(`YYeQTxpV>Z*Wk%G==1T zRQ0AXGuK?R<9Aj0`yq%J0BGw#tRlx4+_ZyoSp(dz8ycSU+BMmmdxy;I@mkSppRkrC zAmcPt)NLMiDrE59BM0>khW0eWys0{Q=u1mW5*B1a5jr~BF1z<~cHJlWJK| zEtEq|!4q`cKDtg=5HwD>1+V;((ga>p&g!hWSAj2g579n&sf=HC)$A!L2qSQxdDtbQ z0$K5wPp&mpi-+lQA4HJ;w4qkg!10H7$mqImURJk6z+g|stc%ZVC=XV1^`wUKkYFrS ze~l|Dn`D2aX_+fJJY2+p6wMYeB(`AXrXBf_3xiF|&SNuzz9E^*}EZ@9@;33#_4g{bUV= zX2K=1fAyEmjQmMi8_@mSj7QCj4QdR@8Qd?gdho^B9@Q3g|25u`Oz}BW@sXEl1G!T| zEKdo%mwD>?r4T`CD_`s+7ScYn5+=o|5L@=?102na&DCohKJK+xx{k&t4@z(wB0lae zC8v|lK`mCHTZPzusINyAZ^>k9TkO`k+;1I3h5U(w-bUBx4CpTvx! zl;^)x*9lP+30UFqVjvt9)n(erDKn^pe_h4GOQ6t`#=LMIW^nz4MMj zZ^EAtXo>S=`O~vI7s$OVOxMRsTR`{R^XWD)og5w)+?ih27llNg&x`xI-cJ`b_QGV1 zl>3RJPwvU&Ue1C`Xzv@G8nlq<*;yRE;rd{Z0>&Z#J7BX}5l|LH%wVukmHm#0b}=zT zcv`F;DSQg37OYfe`G%#%|J1(*q3-zDF%j@zVCbf`w08!Q;QQ${UGU(tukf|HdV1G) zGU$Eq{{&XA*~xwj=>5r;`dHA>*V~JfM)x0NQ~*aL$`k8<16$RQlWPR30z$Fr>t9Bk zUi}W8pq)SGGFI9K2e!lhr9J<}g$kKS82L`9O6MY4{^{+l3)^H&LOyg~ZJ#tcj|}6l ze+~IqF=9;(yRCxBz=lM}FeYVy`tPdmGUa|B;3o0}pQH)1mNgvgwPlX4x=)q~Cl=-3 zbNIgsdON=Zuzt#>sRIl=UL1fB2KWk>c~(O*3~Ur5xlkV`^3Or(;M-%^x^&H7sAf-f z^B>Xs;`&(n!bT9t|ErOJ4%+weSJBa;qabz~&;iif8Ub_ps0n33fa&0vnYRC7Efkae z`-J?vMv}lM1W1tGg%EZN17hTv@w9jtW8Xj)d#xBW;)s;RvND*V1p^KuG(-<4LctSF z(%#=C@o%A^L=AxJ1Ew+TH))+5{9t&98tFQP^LPx<&`T`(bc+X;IhJ{4jMYXLx*D~~AJIJa z$(BEJ_vlhL`hPUvF31mvuUMCoa@~Lm#!;Isr(LV-HyrqFIuc)p#eC*9R;YO&zl@aCaXy?@r zfz@|>=pd4OQ<}Y`z~|o?XI;wysqVL?tefUI^#utVvg0tjwZ56JB>D1Lit!xg)1O8D zQBNg$x=w0Gp`GEi(u%9W5lG|s?6lm(_v@1y#4(@2D-M4NA1G3I9`nW7nW6v3!f}4h z2ftN;j z#hQAxrI{V$o4QIBgVJjdRTJJ*-RjYOak%jbTg%PRB2D+u_xNv)XS>h>SOXU0-4_Ju z8~tF)UK(b)J@8>SEP6%_Udj^f#1cdQk4ag*F&HmTDFF*zo-YQGTAbWR!KP zy>CIq*93H>@k>@U*NMByiB{ypcalHCKXmp@3PNT<1MR63V*s?|!4b4PJdU9%44+R< zbBQqDQzI-gn_d_(%arK(vZZn&Ei7Kjk@6@P0EG10J4AmT`$@n#^$@E%epY!Lm$A>q z`0H*z`}Ax`H{~*{7VnomIOGlXxE8s7VAs$8jo*!x`2WJUtJ+5x&0bV`Hik%pqPW2$&GDxJ8uM#{~aE47EY;1;I-tzS;me)Xq`bnqPw65fnL zq()#lv+1#C);tbgjz~2G?S9ND`ZmDeWV3n0kQKF}M8kOi^9%}or5iZ;2j zp%j{m`}jSAGH#>wWrYuyO`6v(73<#oh0X25bq%Tq=C6`xg-!0=c%I_cALbECWrQ^} z{1g0x!Y`r-X)K;KBT8?ep7`9X)&x;TB84R29uV+#Ns%dWF^KH5Z4r9+O`@#PwPR6_*0>4-bm9G1VS+C-EpEpa zu89aRNd`#nzdK2tjC_#H==fU9`>jo+lvkyyHV7G^Nc!yeo~HjwC}cNTp|}KWV-ewm z0UAKTCYfR18}3?MY)tf>7$nS6SFd0hHe#P)1hP(5HWv+mYC|pgvt8nVxQ1cA=VKL; zzGsQ`VyiuGmHF?O0L7GjjCRLdgaFRSPi+DbKt3h4+$bvLUs_-S*?$%l`IZg3T*s@W z2&=)9P7GExA)z#Q@#^^a7@!JMC3mXF2ONLsH?3Xw*cp21YsVDErnf-|plc=rfDa=( z17%lg=+3-cp6cyGDXTs2aupt%ej@_Z=wiRp@MAq{57n)azo3r8?E=vuxnuxpgYLBj zlwrH>6fe4gR$y~btCBm4mi4hLTPWVZ!sUy4vm?#;Pb$$mu;-HiI^ZS=bSMov!oA`+ z?MW8}?11bV0)gU^5i*!QRHEIV!2Sx&@KkUvv4TYzjulgig29)vF*DeUI&^n;h;J#+ zf>|D3K(v*@@Q4;eJzfp7`t|%`HL~z74fwe z7Zs1H7Nq<#>Lxe4`A7;bD9)GBD@PT|YCGd$Iq;r{8&fb+<^G ztRbcKN^Yoh7$Xc(2%p2fC#@7{AijB9KqOlc43KHS>=PZDQ4B082e7dI6SBUkm7Lm5;Z4=QDjbi%k zINkcDI8ekbUdoyI6-qK!uptguXEYYyjf_Kjpa6d{L$Bll<3cGbnz~TwYlp>jRYwZl zQ|;JH^(*?5Nrfw^&$^K zz3pXqdM%Hg#6&YYyAfNe+All~@6oH8m)?KmpM+qwKqF>u@f*3^2i?u39~#Vm7_#oF zQ7ULWNZrw(!QlRHkg6;iLLWo*hj(|jtK-c#dayp|a-o7PiL@63ihD6+xV7ikrI|-8 z-W$m#>F-*H!U$wSgj1!d@kh7${3(P^=7&T{hd8YkPoM2K&i=aeP+{Y3~j%tg5fAY*lqgC>2{Z?0Y^2vo#gGP{IubI4_a1Zvlyat zA#0k2T?K1(V4zh!7ZjI~zx&j54JM?Z4ueW;g9TwKa;jKVW=6GN+$DDaC%;eQ09jP3 zoYXu2oMw0on3*$E36xfh_7W-Msn}hMeRFBN zW4UX!#9ZAuvu4;Mr&0SrnveV2$*X2}JncPsLjsrSYEl`26nvjfn(KA(qTpTWX4_+n ze2uEux^@QD7hsk${Ph3K|J%q-K-G`ngleAe?Nt=#abdZ%4-pSk^|D(SG31HW`r3zB zxzH%#K-WgTI;y+oagmsiN&0%xdo_5=do=t%pqUX4+{OiGdKR%Pq>Itrto!WV^$#vh z@L)i{>jK0$AL|_HkF}kbWmrzE_T1VaQT>8I5Y#W`6{WDX?!pzVs~ynTO<(0s*heP4`75Gb$2OA6wtM| z?VnGLScx&*Xo^&8w(Awi+(cQ+OnOk(KnIB9+B`@38}MonHJe@!C!b?Ht=pOk=e{u} zNE*;2H-A5gk)PILq;7xl5_R{xts%iV`oT(Fed${8Zm}m1)b_2C{o@$I;G^auhHn=} zg!#EUsM%ZraZ6X4$$c17fKaiTys{Ca$jiY5dt zYW)4E*j#iLz9L=KMo3X8Yq=_!8H3xaUCn<9J=AcYhH!62p^<5SJacM@OUF2iqHm?s zcFi~=ie&3-i*>~2b?sV6}kcF1f7@SvaoG&|gTbMbx;2M2O&6k|Z`*Fxc zIEa0Yk2QFOV0RMjYwuj)b8;;=%=*U*VSizHJB%A#g_d-;Hn7D}bNByZm`SG^);j&b zzO?+-e;iR-Q4aAhHgDVNnHA{+jl>&N(> zi2N^xjRSh>FT*y}4N&*}gX7bdaA<DU60vbp4x}f^!QY%g*=jPe}Ii`&zV62gZxv zBbf+tx>tXcz2opt`^>XAfPi|sH)Fmc?_C$1*XhN@YqB>+edTbTi-D%*as-ua;HpZE z2`^>kpXzLbvPwyQw_jbp7-dooF}&qq!o2FfsB;4)Rs|=Mi!_-8ba03W0u5F;!Cz@; z*T?fy?e=MPb;?Bsx+Z_w?I1uw8@oaskPT_rIM_m|!hrcS|DH06`-vYMK<3I zn!H%od|~y9ChMfOrjN&m_t2?KH4@gF8Mf?iAO2SHN zLtf9OkHN`Ps7V;XB{_WepDVR;5kl-|*yRgwEhfB>%N=r&hL#&OB*SnpBjtq5gw9S$F+)9JS;S{AJHyQ+c^)KMY)A4&_PZreUf$S=z$R+9%Nsk9 zi73tWu1tENfm;))ez8~`Lne6$B*@g&Q_9V_+2Q6|`V=5lp;dPBg5Cy|ui29^=hKvd zL*Ma;9u^-iMOhsGD78^l>@{buICNHP(xlWzmAj( z!qpG~Gxgm1ZEj6ZLsB0B;tzM)p=;;T9`}=wvFnsp*w%+-)j_=FT?7;8)Nxb*TJZ7( zLdpAqtM(cUT(!guz$bW*MS~Y9U_c0@|CU7HlXV(%Vll;tzd6a}xx=AA3FsU zF4QP1Ou8xmeO{C}@6}Rn5Mpy}E!OWn*K1SlE|UkdP-=?EUJiGcz@CtK0qytr{kR%K zH`r#%^=PIQpYqb&QE#lyW zb-c5oE*kKWH0~ObeTNex}Rd>xgEIU4|`f6A53LO)P0MyV?e&tg4^zcp_x$}0l<P?MjLCr9m=Kd4c3X2`=YlJMo-U<$OB|AI~d z)&vieWbgPoOQF47{=6aZXE$Y?%(oN1tB^ZD*4%+RdQrJr0(RA%;2)gT@1gGFu>Zx~ zTL#tDbZvtNCpbY9EIHK6m2{fS;Vqg6l~2P{eR_X3U_Ib0 zjm$*Aan1X>b{rFWS3Eld;D-a47P0e7#{7|Tbb*0&f$Gx)4E z18!>u8VGjE^j!z?QLFaxu#~8N*VKbtd`_fxlXT?OpgxkjqjLN3h&*H4)y+oBbv3!Z zdn+s+)<^XQ#)`eX1(dqvg^?!AI|JoEXBVVDQ{nJzf94})u%3($d%B3(o-N+5VcakX z7BBl1sZU)euQ6gND-!OS4Uv&P!#4C8;!KmLty!+zLYfl(+NHK5ML8#@09IfJ4f6!a zm32T87uf692FR*zsiB?oD;|2Bl)4u&D#r6}qOj?-`|KJE>qB4tr{O%mkhZcc_=h`~ z8l|nS?0ysuW=9#9Vf-}8%R4|01uARY-Xg*86O3Cf&N5HQhDSH#wADCZ@3N&~I*H)~ zS5L(ruN(G)KZ}N*1G|=JxyMNd2f5P44p801>+_z(IAmZRo2p@NpX1qx= zTSRkmy0%cH1s-hxm&4gN_fQG}!#8{TB(S0Zc!@mVrVeGX58RQMcR!_Gr`P=J z0VsTuo?|FAujs0020eDkw|)=mFQ@UxffaU!XHI3x$w$-Pc9|RxTm!bPY$*hhrFMI&5n? zyDPLy9AOatN4SlNsb^g+N6xM1u~6(fqdOG1AyJZirGdS^R! zs6+p^-~k%EKR&Djw%kz>qqaxD?GT;ufe<6XyQceb;=0P-5Xajvx7Sq*unhir%PtHu zc8-d69LLry92DBKD@M;=UwLdqTzkDk6r6r2i{ccT)WkxfRz9Tw36a>NTG^D|@(%Xu z4CJx@^|fOeaoxefI#JtIL-iCmz!vY_pLm0C9I8o=PIk#|30Vve5`A7v4)`V+VtK*h zMLLQxR^cBRc6$Mreug%U#Ir=<8955aP2VAcdsOLL?H(Saoi=GUZNO_eSinJvKp{Lc}0nU@W?d6n;W z-mhbD=fRT*2z<7@jjgm#xAVa>^RtEU&|_fzHU`YIH!r`rQQK>Dzl{f*ES-u12_CwN zM%DeVKZ`$^iG+V7RzD|}GLGi1eN<_SzY?g`9IVC4o#`zM^Nb~>p~eSdD0j3n6oFJC zhp++ou^EpP6(H)J^ViGQ`>J>pY6zbZe+}osGBvc%JQg66SxkG@MbVyI2IYACwK{4I zo1IM8V1*h{y0AMbZIGURtyIMnlq^j>))agx;=2@QsfFx^$JUN@qPsPk&ZhiIB(1Dy zN&!>SooRT=usf?&7k7bsBTG2qq383}*X_y4s}S$4`lf!cIyqqpfdkj#bf)nwdO<0y zl*ei3=@yPe2)nJ-OSHCx?B=KE-fp8eb^Qvmem;3`>u`Z%n8(KGsy}Qv9?)Tgd(+zD zp^c15ZB4qxN*^kxh;aVtH2+`WvYuS;ml}ckw(u#B5UaxVWkzM7)B*1oqwefX=eU8W zaD*${x_t-G$w25$PMYNLr+(vJ9%ZI|PH*c<+HgF=PiN<*q372=Kro}N7Oz%WkV*>)7>^25IKA#; z5Ib%Qo6YY5a8A89MSoxN?WU;HxR!HDk1Ta~^{pc+)Xdh~6)sL)~%4 zBT=ubwiRw>fWa=8949wJhNEFPk_72#W2M9B%iQ&J@q_kym$hwAfEld!w_!Z2+5_2; zs4M3`SD>4bm()a*%Z*s)R)240g6GLqR>k+s_UFj3n;{~&x8!r=hN{-C8Fx5zIk}%_ z>q)}71AV3`l!-T>m{G7Yi>!Gxzp+)gon@9TWV)|HzB)GtQXyZ3a+FR|Yw2%gDO1T! z7-ffM*UlHA1Xp9eNfShF;23kQxtYT6R^cybvovl_7D84c1H=AeHG5DQVum!Y`w=SE z6{_uD_{Jnh3{JUItrM`d9zjdRF9&~|@!AQZI~*@hE7vva&AmL53X=vIEtKZ5Tyao| zIJ8zc*AP*&6K1(L^UE~&*+;~BTndPx0jk_5gq@9SGOL-=-0F?*tdqB(k5E3%V4=#= zXM(Gg#!eacjPagb_e$uBD6C_uNTL=N^KahXWFP##!0)glyFNc`Uf{y-c*AE9rat+R z*OvTxZBTf;Jd*r6P{ZyOB3V#py&2O`_WS&FE!l z==43NMQaq9?0j+MO%jGy@DKt22uxvk+OAOTOeo z-UqeI7J0A1s^JB92@^eKDMWWa4;wX9$Xp+T`$X^qKsW5{I@}efk61EE_AeszGJ4%5 ziBzlQJri#gHUz)U)w2-`9; zYKB3eT*WD4ffFc7_3>)-yK zQrvuXHe4%$zzZA1;jt0psz>tf3d|`*-Lr6$oPkup7J)ZAXsD@+Exh?$hkKM0trm7T z_`~CHEUNs8OvB11U0Kh^qNfZEMRjkH4%r$CV7A_8J<|0S{7Uvj!lXzEuC;JtK1m7L12fNTpHO193*Yz zM)=31^zgBbX&FO{Jk&BM8xe5J=dl33!+%l=Bv*9MK+kc9g{ft@pcDt90u2n-Qrve` zGc@7gi4DzZdEL(>+F^=nw&b*R#8*$xdc!h4QHwT#o*vIaoSiJAYH$f%-G=WIp# z)vE0EZgA*QgU17%@zp@!#1J#bk{oM~`p{xSI-2dTK)`*_*Ls0aA1SSzgS6G!fgH4kBhG7IzPbzE zpT}h;^}udLeyQ)^>_jj{Cq#sfb#wlYIeZ~pwq+0aab(DtGS6<2Vz;@kA5fK}U6Dy; z2+kPz3|xr0LYjQ`>F7|mZs@35(^gw&8Wo~J3k7hUmt%m#hXS}UiQ61fr|n0J&<{HG z4ZIV4e#O9@e9R*%uEYg~YCJ~#7+H7KIaZa4H@9+`Fti+5iL`bG#2~QyFVulyC3Q}fw9Xi223^>`31v}UXTr#Sy;2YUw6RCdN~yI=Am7XE6r&6ZS)cQ@Y5h>cI# zFru;D>s`&vG;4p#zf5uI)SlhU;q!c6AXtR!aArIhuv}^|i$q<_8(RiW%FdQ$_FPx- z0$J{lT`D#HpzU1}PRVjgkMe0o#Q|@YeWX;&iUsp$9n+ zQD9O5aFJivQMlp}09>zIk-tR?!TRI+N=ELJGrCJIPf;|uQ|e01vXlUB8>`4#yn(i) z$B+w_JW5`d>=A};J7n-QCq%}%Q^vdX8Q?;6&m;u^u27=>a89F5^>_dAHDhNIQ}vG2 zH$XTad)ceMh#p%VD3W&uz$YvCeB5f5%r2J7&w^AO!_r?PF>IXzl(InC1flf@DFC<- z5XT;$u-8AT=>`O}7PgEQ&?uTZ0;+J4poj_-xasDxY|w_%Uf#0Ca{`? z`+5bR>EP`{^q$^%S$=X{ms)=k8F}HZT0}#b>O1yCSKjenfZbbl8XZ73iHJFk?sHfd z7m!JZpD{)CwZTL?=H^zXLHGu3z}OS#;NfeztLOk1|BI={)D7*1HH+@m%OPK#VCHsX zSFCj&SE~rEx25DOB9YelN%2CKwQ{kWb|6dSkFHO4uZh+M`?3ExE=~CZ%OX9vk58_% zXfteVQVR{br1K$+HWK#XXvwLgr>cw&hd>H9CkG)ld68PLv5V`xEzvQ_8$(?!k(Fiz ze*<+#VZPwNXuTz32)qca!||Lo*hW*8hUW5`ERQ{-^J54^!W#w%T}#GKEh|{8{mY#B zRI|}H&c4D7gKQeo=iuN> zl;I?Gw2Fpli7ZmtQ}2o@zE@YcRdydzb#9*3-%Ci(aW0)Q5(yEgzN39@wF^He%NG7A z#9~1VNNM%x&i8uqOT6@hK_9C2+t92Rkvva39?4 zv@@O{PaNb`3jFkmx~=8)kvAEERNrlU?Pf3GlR~AD%cQvHr6V`s%g@{1Fxl6s^nhXw zmp47w+KVBJN*{vui|%GtX6o;)l7F%M17*y)!AW1lQ6FbV64g9hK075QL>4YCUlEid7Z!neG)j!Y;O;=b=@da4V}bB{4AkcqlDQ0R*D|Xq$dsR-d!yaNA61)J z$O7-rFDm|6q-e72&#&vOGq4^M#MkFs;MsDGM80_NJ;v|XWKGF&Bra>A7^vyDp~8d* zKJ#xRu21uR5L5Jg3nPN+%}5`QO2>wD4RlU`hL;I->2KOSLFJZJ!37XQi$2!d@ZTeO zdB(fU-EutTI}F%)ZhhHXb3HF7V#4x4gA_XNr@t2#=8Nw`ML_mfJl@TK4U|*axaj0> zboUTL)wei+cp>dgW4IVudfwmvi|`*&@jo6XPoY8ueL~J!z}8;;5>Q)9Jw9v~x^rfX z_?@*`qHKX+ZEbi#i$Djptj6 zce>*(BqPDuF1@QvLt^ojAmoXKB zCgGwVcWem>@LM0KP~lJgT7LMC(cSTmSSLkHA`w?@=8+I)WzptyZZOVMLR}fL5A`Sv zy(NPI0Swj{xXsgZh8SF^*h*_q&ZKR60DzT(k+NQd*OTYN#}@Lh5Bo3b>)!NCf!$8d z&V=|M(|LEyA_>XCCEU)?=-8+$)0?e(gD{;NFS0zi{35`S2eIXmT!J>Asr^a~Q#cOV zNz_PKt2_O_OmU<0DKovz49Qn}bVbc1%LEZ>8)yvKRU|qE6JF<-Aiez=tZDAxW2tBlmdFu76L4aH;28etOu(-g1dDi-|HM#d|$Q7l~JK`+s-W~c;Z6=U8eZIbmC>$89F0D zI?=NK^4WjCQ{I6iye+ZHHfHewmPYI}4o4BQPuCjw=JXP;%|*|hx%Y#jm9e|K$9`zm zL*qYDmOkb%^Osr%*>H@Gu40wAC)Nu6NN;}59qXnIH@@XarlXsH-)?CuJE*3gj>>iV zYuGHK9PrVYl$7REh5U;>h>5b0yEevT-AW=DPB$D++`#H z&A}G6CO$SIKx#k`Z*^J0H)Suo)E(r<^5)0ZeU7DXdQGKuD6nJL(CGaSKiSmQ8vyf818DjL@!e z8f`MUk9eVDqLMLAHYOXzep=l08@`a~ep3jreLN;A=V}B9S1ePnnLTj96Ls~&%d0|ZGh~}+e-egahaQL6Aa~7NX-G64!G|0H>5o8Q3A>E3X%;!4j-7I#F;F<_!U%;j zPQ!IImN(hv)D#Zwhmp3UDHABc5azjgy~gz4?$>5Yb>hK55=lg>fl*srNoA+wm7MnJ zkc&ntRai+K{09SU`Y>k$$N}8hfNqCGGB>#{O%=_x`OXmkai;~7L$=T8>jncOWatXm zJ-ATPXbcAPesUL9u-Q`xTA#V4zA9v|gt$gQ)zQK)={*>>E3 zoPKD1F96=0bjq72{`ar)mVkPyTK%r;6iX#az(cc?Nz{VWX>v{MIDq6584))Zli6rq z%NkDa_mU^mgq5Qe8J0wNxA%5IzAsM~06^2&)S998iRVEXDd2O3@kSkh_H3f?B8BAG zi>tYVGRpeL4)I)uXL;XS48Z946GFhlEISX|VkR7}fs{+w>cbU@O?c}~g%KzM=gXW2 zZEX+IkoWSgizq@duE7>8jfbTbK8}IX!FHd$1*Pak?vT8_WO-HcjwaM zHJ*k^vrL}!Dl@9wADukW->Fr1*`Ks&09{?knsrX{Q?#nT{r_z|BWm;}AQ8kR?hHu` zMg5NUQ*+_oTfkMe;cn*EpEFabCemr*%HVNe$D7G@6SPh8nA9Ap28^Xa&CJc|uhdz~ z@+R&Jm4#njXpzu^CpXbDau#tZX|U7%s?{8r&zHy^Ii7Ny`~H5i!f}SXlvY6+ii_$7Iz{8y~SCwMMXgY%B1X_Sx>rDk*Gcx}`F>1>#G* zQ#xepR70aHe#HC3RrD;)#mxfrm=c#L9BB;tjH8=Dg9hcJmpB8J@JL+ps%$ggBM%DZ zES~biRA~Ug^el2tXNlo?j*N21Y?&U|&#=>Ux@vLy(^Q{8#n(VR6KZG5-f;Gnl1 zfYL`>9C>-43&2>GmG|qGd2h@iOz07OmxH=Qk1P&KV??V~Dsxmc<#X30%(Mo{x-s9a z`i`fTIYw`C*omxG2}t|{kO7%?ix3Ee^{i1v%>fn zm;x;*m4-};7?*(p7eDMkSR=|Y;1)FB2A5YZ@oMJc*>n5DJ2{Lw)_S3leZ)edLT9vs zU~0U!RK{uenkc1ocZbxkO*3r?DXgJgUR%kLm$XradxJiuVc=hLfuINpC3PHkX}_Rm zsU4^B)9r@NGKNWIsz@`k^>l@$gNBjdV-@;N!rJR*qm*u> zD+BYG?4ZV!U?ivX5cS2Q8(7U9gw3WzA9_=BRx5^*zdo03 zmoS%j;rUvhTBJ1=|8&1?SNW{6R!>2^21egf=0itj zP76`++dLfnDh&hFWiCAuYph?_D2P66CI^92yXRG{i~l2t#(l5yYfrY}y02tsaoMb8 zB33;RE-X4_mLXX36n(ntxn`f-Z~fg5wOeQxumeuHRi*IWy>R`L^4+-;+~9j_ z;tl+TjGl2q)3&V3{wpoZPU7ve|7cfC6#GBe7}#X}e+!TOedd3^ zz`wd~U57yHd&kDdcw5I{N|+~A)5&z>eq|G7A>PJ|(UGp^OI@(ZnB-aUwKn+ipBGxw z{|m`k(_+s`_ZVltbu>mhXzV8A7GSJOk6b*X8xSOhQ=tjTd1CVe_f7X3pryHfj;dRC%yqF}-Ci>@p1Rjatnd8;Yz&8^PMI zrs81E%HSccadOrhq1kShL0=9Wc(ajNNG6~2ujZY26Ji*5*@kiv=1ASg>3FBCwc6vb zvh=hEn&|*6C;P}GG1{6i18hOVvM88a+cGBjlhdXV`VzP4vXh`s6~twib;0Dz;$swc%RDLIx2xfH zX9APLCg07|{F?z|5a((_2VW`*z;h6VNAQg~b6lJBoWD<>rM}S-+j#z^s*tO5y@hIc zl>aN;&X=;p&gRDzJFUq|+n%^xdN(@+MCJCM%hp)V{{mP~`Q+{f={e@wBE48LZ%7Y1h8Sgu9zx7Vfq@J&?D}m1^x1IOxD%eeR8E(L2qS2>OAkO=KFDU!q^xbQ3cN7Z6 z#GSlSla-upsW(uv_GksK^kz1SkV}4vuR<2KRim(#tgOkv%cH3{d{;d$qmr;e7Y;=u86v% zhaYrbIH?C3cxVRF=^~U+&qKPMZN2cJ~o=`-&RcV1dT)JJo zB3-|e_|g--IxNaK##`dsFLMDl)T}b@pD6 zSDkwky!w-@J*CifD1pwgncx6VV zNAs_iEH`KkD&Tn#a~sTb**|UxVZjGGCm@*KC(*hvBz$LmC`!nH*y<` zy7Wj=x8MzpNH65O-N6{PMhd4DjZhbJiFuMqKE>c9J3DRU=i7va2UteUrpT<%asK*^ z)isrkr*mK?c&hED7ZUDE`!pd#s<_2#G~JsmnA5niENU~vNgR8s;uo$35j9)cWsYNq`(te@Gz54$c$+e9)`+oz;ZJ}4ukBk~* zUe!;09Ln&VVc*?9`F2|QEE@JFsNmb^19|Bxfio!e!DhknE|>qj^gNkhi_m3~G>Ya1 zq|vN5-OnCb9F)MOLg0EBB_>*en4OcU{kAKs9Hom!#(Os-H2X^{|2a~dNRto`S@bin zr8>#|T5$!VcBYm}=hEFdvIf7EXfPwG*KUHEHBu~<#iU8AOroJpeF-D5;_H2oKr&vTdFW%J!=g_~6srRs;`~l@;)d z@nq=frIMSj(JqJ3!%0IzQ=zY-4IIOi{YKVMNV7nFJJkhsh1e*UGq3Nc@Yk5x>Grh@ zDr2`h7In~PmL1>o#+?mJf$MEg;%sV_ob>!Yk8L}imS$czxZGr+zkg*uUnjE)pM7HQ zch7iV?BC{t8wB|d$Y^qIrbo8kczXad3r~<8@71FC?x%ikD&3Fq0XLE(?SB6F>mY!S z^ZVZb=eCzvWGUWBM0mRkYZ!w!dt3tW#EKTj%LXbZv;y%k!8cD5E7~SR+P7n>j6LOx zlP0otr3UU`z!=u9$4?a<#sxF2orLIa078S4B-1&%U+UiFyH>r*-T8)hzBao(^6sxl z%fRSyhXZnEU_;|y98ok|5rQGW5@G=GCAKEJzFh(Nqxii#tYtM?mL&Be3daw$(;d_) z1#QoKY@gGQI2_L0@WkiiL5=)0l>kWs-v+^%T-y-7-Uy@{?V*}@8Vp=S-R+s7jdOF7<-Ob8 z-KtgdSQc|P17{`YKMTP7R(v}L(#x=u^dY4tEWL|cXrx7w#{52lH?voeoyP-oQOG{i z)guwBgUgX|+=!F^-QwO!*w&DVKXR#s8-vDEjMvzgqR^D*MnQ^!NWKDqsijv6*)1uk zsYR91@r6W~73xwAW7pp>-_ZpM+npdCbubW<;*UhcPI`cgm>3=AR_V*u=t_aUAY?W4Gx}gKc08vbcO_CmSR1oOIr55W^Ha z32L1N4L80?NXNpwKwDA@P#3uWC@M&}Lr8IN3xLXk)s=(>fLdTnNg09ye&9ei<<${y z;s-%;GBRpX)SHQhq>RA2p!+MJtd7bcCg5l$2Rg$gmbCGH7UC_Uyw z7IoTAs=dx*ICMTlp4PvZZt*arAZIQ_T}5R}x)`ND4EB9L_BIp?PG z3w!Cjjt`bfa7!8=;jBE~`$ggqFD;*+U;$fI<3zmti=PfJ-Okn}MFSX(?i%RX#rC`( zm!1>dd%L72^WsYnWRR~hfu`76sclS`H`~3%WCT!^H3*5Xpx65Df$($L7yTB8*OOyG z>j@-9G0*GxTzw5bfUmAu2j+TAUKRxV1ft@5j^27fo-s zk@xorr$O$mOswooCEwfqJ{td)AtLhR{UMy($H`8@4kq&alvg#Z>-bJbPoc2BPaogX z3{{832`{PKc5|nmIRU)n2N`V#y!{f_6b57!(Wz)OZaD9%D~BX6W*-9TBHVat<~As- zG^j9In%$vTAOJ}=aX(WqdU*YN)3d7I6=^xX`uNHTiF;kHo8bD)Fn?Qpg>TRWIFAiT z55JuF6A^T94s03)%q_#rqhW0CY>#)5@mjD9tqB{qckGLLt^0H`DUt1`^jlko%++7U zE-Y}Iquno=$Vypo-%a0-#iLH#1_X_6TD>rt9Q~_-h*~2hYs}AgX=QWKk2+u@+IP9U zM2h&s?z)u#N6$1k{ChT}&-B#hd6r@^ID3tH@!Qmq3y`Jo{JA!F;^L-a)&$zFs^ey) zzeHR$dF+6rRc|aTIrB(HC}qY0rvwQDe-WJFKA*PLv#A$EH7%>Hy5|H}0=T@wO~ycm z(89<6l;ljR$m~IliKl34azI|`6l}P`CK^!#B`_2R83&luLXbA1g277CuRzHQsrH@sF3H+0zhR`V2@ONfPHxKULZU(HLii z+_JvAj@ta*lJ?wtPmngS;ARos+uQrhb-Ho*0)6>*ZaDin-2sEME zi6ndYv6lh0E6Pg>9(Hzl8wS44a4zIz?4~? zf5C=}Ssg$imyx`(cw5el1;Fqb&Wj=YNs)V#4~7k8`7R36@jb6q-VH&<~^C2R6ZV@sS} zA$>h3fxjXDEikI$t^-nf8P2=>w+=>)NAKJ%eGNARz=<^hiyZ~I1CRdgk4a)*Z9RcH zv;D_k0h7)AT~Y57oA)TK2p|3?7+(0oYr_Hn^gP>#Z5mP2^Aq1$LMIMDa&I=ZiCoLf@*k9dC=ZNk3}- z8qP>+H~wVGmrv_GS*SxmZ^o(@{?I*2(=A`)qL&Ux>}{g0$v(wZI^a$GTnMr~2!)xl_1ZRy~2>FGMc zvc>*Rpk%ZlRMgOC#*gEx|LkC6D0r6Z?at+TVmXcK?R68r;yylFNdpWI2@_DM1i?mN zc6!}qMkf+UxE)=Gc6Pcuhl}%%4XJL=pHE&6|HMU~wKzy1ZybwuqiuDUIdLx(OOq1& z>o&ii4{+A_Xnu_O&bc&WPg?1=1mV8NW;I@7Mq*`mvH>P=xN+ppi32l}x5VKX5QR*} zbMANuxjjX9K{`-95JAGwY&BJd<40Bq18ad~0>X$s8yZ*ENPdmNb3Z>r; zPm4)!j^T3$L!vvLb}}mIB9%VmFmQN?UPeLU6pzu%8El&8-KDQ84@`H2PqP@DzvW?q(E?W4nrtVAf9V%SvOW zJ8Wmi2)DT#{_70qZWj(A78b#B{KAx{m7Ls!x@&_ydY1zs?ex|VxyA|Q> zI@%Si5CFf5o7l{Z53dZm0U6xiwOm`@-_|a~-GabJ_Bv#~eyMh`fj0>%&fmF-YVG7> zb&N%Cl`~s!G`(IU-B~ByF`mqP%WUN1q$avC6Dpipt<QnC=RGYxy!g=QCaEiQKgCoqmgk4fi@lu<9 z52~b~nx(TthYf80-VByc2f{uQcg6R!KOL;E@jPs|ws0qsKZL10C8VtF@@F1e{j!3s z+&OfKn%@oC$mBq~(X>=;Qdy*a`#^WLPABAbsv^;^rfz|ga&W2ty8ja3j%u#48MYjN{ zYFwWYJDBlDQ90W>Y^p^qNIHFN0^8Vva{=zaBV! z3v=csd(>yM5~fL|?k&lzk_KhKr`T~3+!z)4!q&v5M z5#9~`y4JyF$L#kXEI*I2byDYD3~EVj zJ|0tM%r5w>*OU4f(4XxO!!@t&r@?ap(bs40sA=ci0L3PS%c12{b{a&7RM6H_o8M&3 zE!5~N$M+8=FWIs~U}hQ%9CYarOe;_YB(0LcDrY8 zLuK83b>1E7>uqHDd-n6Fc#r*ORZccLxJudp#*2>HetkA5Rho5|kfiEQ0JOjv-gwIE z=}sQ}$-*y&b9HQZ%Op|xQT#t28`BrL(0Y=v-8S(iCtc>_J>DA|WhvaKl>S9I!`J(7 z${E9jbo^hCXNP^_;t!0}pRZ+_gk(9NKhqO&YcS80z*!k_igYN7JjM9A`7m0S6V%Jd z8zymMr7Eex-4(Fw0RUEtMhkj@UZ7W?$2!Kx7kjVri%r$go-HnJZd897dbF-V}Rq_4~@-{LXw_{h0{vy(*PnoNNm4ek=+daWAkrHIw`i%oOpJ>kU}e1oMP&xP*s3iV9&`nn>{8nBnU@rOxzZtF-mi` zaVH=m4q}ztRGv#XZF+eN9R@%S<|8bI6bu~G`?Y_KUY>?G_lfMG=wSbF0yO4%SED_I zqrcY{<}XW@6o?}zm8Pz)D9viG{&0Ak5av|2D`U3+3cx|eyK|n-kQzD%nd7>~dzuQp zjRGrWYD!%F{v&6D5JQXWlHEM@s9TQdUuwJDSTr+e8bH5b0gBshs8f<>^6X)(!;)v5GFeiXnRY5Ot9-utmiZ#+ z;CdFpd^kGZd(LI{u7fcdNWJ?AuU^~X`VOpR2bD!5zh4Fh&Jwz{slQEB*56ivQV*MR zD$*|bd9%+dH46ziyt3v`++}($4*v3Fb~=MR!PKsYY3fU<7{>8(7;+RX`N+0W%>NaO zPt~pEf3W!IO^EX*)?Vcgwy;H+-O{9Ra7+ShwX~IiONmE=(=C5JpoT$C6!_$ll9Tc5 z?w=jeo2?DaT7OBZaF|W|$trAS;i?oL?c36trZ$#z0j6_BKHDb}c20T_jx6f3>F{yF zK)_0>gSlG=v&Bscbr&s%YFby31Od+nA(ylAxpO0UgM%k_Y`#x2hoqjpwiqF;#D<&> z^4C&n&uN(5k(NAb3s;TE{}&b?R|1U*qm8XiJFV4oYaPgCA_6(a6;9mKp9CzF;0-D` zZRLM-F;AcG3XLFJJ>Azrqp`#A?e9hYDdKeCRw$Z83p{|-qJou-@R;HWK#zFc5iQ!j zExgPKvU3W>M`6Bh#~6SL_)XgOh#{CiN^nu5>_OHgU+uPtYa*wLl=G6m3^Ykt+kcS2-~RgD%m z_l<+d50^~})$camE2sdU_KUYci_*1V%&w;6zSGsapD3<$L52!Wxx%h4K3^GV08Ast zdJr3cr-vL|OpU^(jJYCW2tW8A$iw7|qA7$UK zBfmt-+ie4*?o?3O*UVJXVgm_2ei4EPtHv(Qq@lk5(0Q6A=YM>0GPqiowTgcCxk#rE zT)5TBENpzm-pu$5MB7m>GuHSp(i<+PaPr}YwcPH#%~73yeIX#Il_96Y0o)Rt4|MAU zsjsd=sM4U|;7=GF_(y$Jf0fFv{g$o#mjY@nn5LRL4`FM1+s&**5bLS|)-+UoFNCy3 zx8n(*kux)g$Py`K@oQB5f-cvcC0}=U&$yo3&lDiA^}G$S2@yZlUyUA}!t8+oe|%3= z)=ED%Y9TnamdP|3=~1lVP<*kHdr=$Hmcx8?uCZyr;WfG-g+W}_(s25!g>(AMNA!=} zu`jCz{*(EH=ks*44P#(;;t-Fa^@X-x2+$Z#YQ4%;y!%w_^EsW`Vi6$v$67z8OJs9L zHsMMd9{2yz-B$+1)pY9;NFagW!7UKnCAcL(kl^kTT!Om{L~sr65(sX=-C+psK7$W~ zyAQ*h$@`sK=c~Hs*7G;^;jWs5*65H7#{j@4P6PUv2{V!P zx?iS!HQtXN6-@)j#!PKhfU~SCFlJXF?!$hs+Zb;Iw-AVEn4#xUi#zodNqT4mzUY8+ zD_4a6eV^`N>5%hNzG$MbHN>th2h;IaC{2z4AH#Jyg)WeCgKUQ(?tN1I)g5+xfa1s; z*V-Lju|@FqA|;gGoJSv-FrELJWArP82?0-D8;P4pLtG+01rymwAg)_8l;r;5ZW-Qe z^`=3Z)~iC^t4wOUTCY`G?QdX`jaj3Z@nD$pZ2#6nG2_KqUK~#mX=}Q(aLF$D-t|>f z^bF-)F~y*Glov3<_A_Uy%~f1qp30?Y@L!D~Fkt8JSi9{{6W}wlEHjj6Q|AwCUJ}|R zfNfi^#?CuC?<6Dl7t?>Zp`}P}C`wH2GkSubGEXNfj@lrU7Q>hFPmpZE`d6=R5Nytk zulvJesU4pQ!ROBV8c>XuTCECj#>t47>Xg>$40oMdJT;F#xisNUAzeS*nfHu_qR(K8 z_}?ODBn`0HE{{e82`qU$n9HJStFweJ=h`H#_rC6skd2X5c}vO;rTB#qXB9dh- zwW|X$<xa_SOvrGrW9kIhWAq_Zy}6& z2R=X;QPNdkOB26Zk5+TETb*ps<=#>53eu!3@L8+c!gRrZfkIx(l~hkrACo#0)$ooe z@++7DXj zT{tXlH*7Q{ zmkL0<_GUiJ{kXod*Yobr>?(p%y=@{&s)m|!APs->gr`J6DWdrxy-=cr_UPiIZWdl_ zr{+zLk|xF!cyi^kqKG=n^6HDs6ZOxr3MLCkL(&ryTjo!Zeeh(G`ii9I8nbS_RPbb< zWUwn^ao6`l{PmF!e-@c(7J?rdW4%zmYVCXoTNPd5u|_grV~T4Pq|UIfpeSa^Wkbgn zG1=BB{d4Jp4Bz~Z?VTfetV1yTpS)`;bN!RmK>a-3t=iICMKOmQBJ6`m*x^h>CYbJ* zX+19AgY$jmVW1cL67u7HA>~ASWQuGdzS!Q^jg8BS-bemtpn_f_6qiJ|Q>nzniqe*6 z-Nu^~n@`Ul2iW_2o}i*s*w;#)iR1ASBW<6w*K1!+Uq@g7l?$H@|Kxje8)0}HV?zu; z+}SbggWu*it;BGUqNvLrnpqsaqS(Mj;kXjSMoVcj3t7E*m|r^9^6ZH_0k|m$FPoB4 zY-Jf4JK7ThDNCc{U?gz-{(2|d7bo%Jl5Wx82Z2dG$j zf3Jvv3u%ZITbR<)*DCxVPg>`rUr#}Z%(KP#GN+Aw>O1OA>*i+FqcY{^syYeLrm65< zYCav#h#$4c07SOmzL1p@2fKAo>--P~xjkCc2&>2;o&3)3S@1A!K*Gt=dB3Krm>*)( z<&m3VS#fxqSjJURkC#hLzK_(xtW}mnACI7&l zVNo0Gmo3lNG@5R7&%`1Gxp6psBzB&T6^_xWN@6AJGZe|xy#f<)Cvi=$TC#WB9iN_9 zB$*dgw2mCTeu$y$mPxo**noh7UmL*80?6R(za3+@qK5S!kOitt`CP zV!w`oj!W&PWinL4{U#DoJ4hq%VuJ!CZGOC<28#_6ZB?~j9u&D+uBN#&$i@7yo!?<1 zc`8gB&BJXOkx65?Blkp8;l5C3kP}lg)wkmb+8g^TLbkpT@srpze#2Ds*WAfgR!WvN zD=8EoVo0$%H1h$ac`Q_|WG}r`K=nL1I_)>*n&6Y~Df3<^c4lcPpGKcQzuEdySm_)5 zUbo2b;ofFJ#`X7}_43{HqX^HK_g$kT`<0db$H2SOf*EWJE)-$wti*9Gp90WJyx%?} z)9-N1klpwq^YJ8}jBaMEfCiAT`q4GF?dq!!6NUZ1{4&B6JM4K#99or8%9=jhq*K`M zv^ujI5%5|yrWGbh1XUsas1uC!eFsFytys;ckBNL7ktgPB}!sB92n&S*&PD_c}}npaiIkZhAaLnqAhU{$t>F_7Z0T{^Tbp>-?~d z${{c=^K_SJQgscR3ft-i4=)orYO9bCFZYpbk@e~3>i*#j6fpnGr$t%cFKCu7dtAn* z&h}OG7}Y@Wc=A3|%AJA(T%b!TPjLwqZ`@P52n5W5T|tyZ(uUkIN;Ix+Rs!+|G=e=o zlu?SEwjwjMtDprB8JQPM)7292&mQt1_I zWYc|8xv2#Tk6?$3%SsWA*5MFTpa|==qk{8(sm)$hUmoI{X}*3to;>gNV(K! zRiJ7Lc}E{1+YvhgIOjjDJyqVsTZ_#yF2LE*^`P%r^f;gf2m= z$PW_lj@z9_9bj66?LjCQUzMGV3II*4uAFr6RQj0^MG6g#@|RpSFl#YjJLA)D;o!6O zM@F2>jpjduT^{Tr^eKpV5mO}43WtS^Le@gP?v|FvKcPW1j^1Su(b%At4VP03q``h1 zC~~Zl4Sen9nT#piWUmr*we(mN*d=LmC7SE+!h}6Aj$$Q>Xb{~gHIz2j5xeq>G%0vP zA{dh9?qJwcG~s_lzt?>J?ZR;+K-!$?`A|SvUfxaDt8wiiTo4757;GT4zUq@&YAj7x zJu|~`kM5eG6&`D{#&ouj*RmHNzDk^{O|ZSbF$=x}*ILP@4A#^I;qUdPrQeO;9gVnG zMVH#2tUU(i{q!!ZA}_9V_3pSIb_B$FZ3o^D20=wHW$GV)@4?a$9bz7v`;uKX`3+jBhf(o$KNVzC3^-QR42H@z~LC>*o|rYGqniOSCcW z1^w0_w3*Z*KwS;5-AAQGX0Bwd&peIuTB*4sV-l{Vx`Gowc^jbD0dsmOKS-%~mPfQX zYxWHt$`rBO;vm{W;yu%=LScO_^~Gqx1R+M8dteCh@V2q3lVZPdkQ@CtCkfVh*#qPK zFb4;N4d*2Vt@H58wUy?>Z15j5WCAIr&;0dTy2)AM8Gs_M$pVG2^ZhqIVcgc0Rf2p! z^}0Rhz-;IOyXlk3Le93$V!N5{>KgTt?j|lpRTI~rm$&YK{-~COOg@t}<;viaMF6dZ z)pW&|9}P1T;+abb!PV%RElI6;j^`IGfqejd|6>g2x795cfkxqip0*AnDuqF9cHT8( zZfDVUb&@{gdcM%g8FSfkV@o<^sN z#mKYtcCqusYe&m4TePmIc3Gy!Tt^o$w7k?(T2W>7jr(gM&^q}U9i27%@ixFfAi8xY%Bnc-@XkrM^XLd@1Ztn%UtJ(z5t}tu zRB@?nhjoQ_nr%f*r2b;hq@f}E<>g6hp=jm$-pOx&ynslTX1<3MVq+~hX0W>ieXD4- z>@cCjVbbX_9E@k0tYJStZ`AtZW-E49nn9&2GM(`z5t<{}^!ctr0so18Vbr^KGDl1j zj~9ky85^ca@nlhr?UxA7qKdRsp}_j4ax60Bg&GJAxDMcLGGo?s$ighP#=~dA%Gj#r zu@foat!mQbmvDMWLV@GynL}1kOXb|o)fzOA^xQc$wkRR%GRD-}?z8F7)SLEgodOk2 z|I$mX`A#YfOeA`$5bgPB?XT;xWmLQiHQupzT~2868@Z3BUzbfTh>GX7<^*3a=w_T4 z@d(m_ObhXB*(NM0Z@ot1tVHN|cY-das{SC9BU)_W=LucMCG9oyEzeL;nyd8GTo&aA zQCtW&G&iRonbZsnns;zN%ys3Jwq(Wm**WySe19Q&z$?%o(sn^f< zR16Z1LF>{gBMb7`>(OhGI7uuv9ig<9LqkC(2Fd(_)~15m{o@7s#e7&#w&^!$Kr6yo zPF954<^x!KYiTKa4HXl71h(JFSma`cpmteSl9?Xnh(E@jDwOLCLIcwrx?`}AH3^o} z9$=z;fv3qIZWWwfsNdc(i`~COdnAi*9fou2%CJf|VLI_f&z;=a2PG%BDR=k}=xxY{ z{cQxdIk)wb^;UB^RrYpxzq}lerAk1W*GQNyrF7sEC%TMe8fR`cB zaS-yYW|mLc$e#H}T&S(3|Fihz;)ni!7Ez$Ib^oh4E^YnfKg(HRqew;bj|kzW|5p{f ze?Q}|tcy*_W1zUWZ`Ay(qrM_up)7^5!#w7n%f4lHaxf>fQE`nS(qVjN8%PUrGjtkMkAuq0X|F z{_w6BoJ%6vp%nL3kb~#isiuRC%ymGLf-TiI8lK)tIt`tb;mJ0FYC^COHRiyWQn<*IlGe?93vi zw*l$p!q(GS`ykUk=GzH~D?((97?@H>1P|S&5%JvwOMC%_{pt=MZZ^Ba(H&druxusf zi}cOooj1gKW1%0FjqeBC+ujyQJP`zVyuB(Imzs?*%!gxu1&o9)BqjZPP_tq8XCyaG zE81B>y%nU+By_3UWHq@o;jxVQPg{p8>Fex8c1 zqN`U|;L79{L|;0e$8H-}=7cID+FBq(&on{i*3N zb0qj>WI_{0qkD>o4r6ChUcdIe_CZDl6ivwyEvfJ2t}m9hEFd7^$|2rntu^525B?rE zLz`u&)GF{D64@u)Vb>k@QhQ`2=<1Wbuc$shx^Z?$xIA-$U*t1Oo_TLQQM5UInD3W< zKC_}`*0F#qbK#~8gO@vj%S)D<0>u%=Z*t>eUXr+uS31hp>1`71P!a6!@d+SKDX^&o zO}LuwWvoA)pL6TGkH_;#&%C&aGyHAYS`TtzM)va40pZWP4o{^Woaj;0;rRi2dtJHEH&19FH&XxNdqASKEbAWwWT3lus^9k(SEy{N={Nu~l>LTkKq<@Iq@nA2IJ9=TiRolD;%9X3;G(DeQl?gVaRE9~pD7OoS=LlMn{W-qXkWJKxl zVo{u=GS{?T`dhR847uY>lD8W<7{t(y_Trv1Uhy5V^WFA$EkE~*$0#}<&c4l${(6jP zlDsV+*oZHu?kK)g&CTX{)AUz$di}O=ey$+nkrsUsnq};dwJ#FR160DkgH4}jkP%|Y zMkk+D<%BYS4k*dG*lF)*Q_-eCJ}^4=0mIu$+dx6c{!qy3CjB~fNm-ajSY^z|{TP?Y z0ORic;GyFDCWa8213nhC>_rNiC@O2fJ>~CO!aV@Dn#7;A)|>U!{p!Sqr~{ zZdbov7>tt^#FkY|tSO7z(5d%t+vSK?E4Pij%n_6{=n)-vhCyK%Q7i1xJo_cWoo83r zat0Bl#atN=?k3NWH|bHTm_)6mef*jYl!=c9&Sh8Oo;()qjkU|9C$z5K-b`hCVr3q> z5#iCvR&&HFTq%Z%!x`4-C17!}h-FHS#PDz-lCX&^5rfhCJ4wq0oO?qD*w*0=wWQ#b z+`FS^VZ3GNg7_AC%QN^oj=u9f;3XnIVA?G!RQQ(C)9a(O^{Rk(el-ZmI`kP_eVG3` z?6v-)z72vjYx`!-`#xA#O?alOc8;8>aB+1-4F=}b4>ki+MRYiSmp!BuZaGCZ)# z`(mnlMyIMg&C?maK`glProlfK;Af$*#wX0wbh^YcVlxcQvCe9zq(|UiSz#S{eFCw-%g4gq4v0tvy*v)8Bn8B#}&1 z6PI{Jr5zdof;)?euT*|I!3q)~cAGH9VtnS?qULXGXL8?^d7ZrL2afWZ|MQbKLVh=~ zd9y01bPH80KUSBi?i~sWX{3V8d#(AhZ+$CWA8&lO#~P+mKsPkLY%@7U$HfYTUuRM{ zatUe3N^~MCLR0n=G}B8}AWdUyrD z%a~BaAB#P{YOwIU$6@whV}@>(J`Y2$Dg%!LD^^`6!VgwsFajC)NZBcU_$E;yqU7g& z{*c$Mn&$^?3|xW{OH4WdlvY)ehDY6hDjkb)1)=E4q_O}a5?&sh$SoP_;RWZ@$?;f= zr@EFjF68kTAO9ren6YUK3l34U{`#E=X~KNToNnO5s<=KSIiW7cjSrK? z06l}i#pfZw(fge?uvjtdPiLDY=^>WP>m2sheJGc(waCSS0s)%k_+$-e;H!diR+wrW<@G)JLO|!d_vEl@;y1 z33!_%#HGI~u1DhCd(wD*5&#WGM#$S#lOjW8emQjURxXt}Fww=!@B5_ZunHoV;M=Qo z`ME{V$L-_I%}u3bq#9Xg+_of%a3>}pr#DwJ)#TSQ%*WPZgbpk4nTw|to=JtUdyR_~ zc9%F-h?uH@Y}>7GeB_&DCbEf{H$pd~r9br52Y_AbZMsMgb_E&CiX8@gYhG6mJ0q3D zw1(SGZ?_^cQY=*Rl^NqE$o|;YxsroR%(b@U=gDUi48y?i>c@*7nT+M(UYEEti6@V# zkq(DG{ajL0DK-fI5lC)9;MGL2wg06*Ikbu|b^My(G}MB^WX+{yxjS5bn=0t!B*=fH zB%O`qP^6_JA)J)$c7JcMYd=8#;r`~)qP5-}YMKMDnz{z=-lw}`8s8llO1}ashDpHJ z%L|D7RLngO&%>x&IC%GZ{1a8XvX@pxG3}JH2B*9q9cIo92oqyfOm0>@X8^KX9Tqyv zYq*y$t7__GuTzJFKUC{coiP3ufCRwDnJ^+7vRY)>{kA)kcyI(79sQ@%l5sgPlk~~m z1UeBzU$G2S2oOWw)j_re>9u^Sd{>{Vk+vJ_6Ki4WFuH^M<=!G0Cge`EuwAU6Dw2mW> zdtIilx~_4{pHa+Q3^&l_x(<0)q(dF%wjGyz?e#`x8=LUsqnfI-E>;caZtqGWN(u|@ z@}iLAk8-(bY5mogL7Ad>eZHu!c=#R;>U&_UHjr4-G)-$1uDl)gyrp8(e3g>*ikY$2 zCpuGD)920yl(fg+($+P>Otl?rx)7Z>Lbo&eJW);V!%mO;{Wwoe8H)o=R=xmnQD>%( z$XoE#hV8_L%asrrUkcUgWp&>IYw!ztOhJW(>SGg$0;$0`+~o!<#Iu#S##p3AFzJ9k zP!vF_KX%g>mE~p>0R3Y#7`PsR+|y?u+kjbn*;$u>2}nIEe)@qs@_!0A(e;d31cs*=*B^bDWQ(<81$)rj_JT6O~wRS{VlnQXRWMU=c0CN%d%{ z=`p%B1EK@Q6vO4RDA27;(`gBTZSGD&HFt0{hHUBN^1_EE}LlQZ6R+~ZxIahu-J#{$9sElw>R^ptEg4t{-?IsCpQH@S-C-`oIACr zcqecDJ7uU!l*W^5A}ola^*tMy_;7MLsuv=_Xm5(fv&fH0f(|smz%gO$3aDl3BBXq4 zPM5e!vI&|wS{7LCe zE0C9;pM!!g;fK<>)lM0RK(TujheXhjbqB%4-So$d9Y!~8mhh|<%YjZ_aWFW%q^Nz? ztj<1kBR~OrAVgT#lK^(-gQxyM;pdbWL+tS8w3mdz#uijji|5eOnM2sraphc_NSi94 zUJtS(JG-luCDYFq8TEOA!L~zdPS(OG`6F)r#wfc>{MxVWQbK955B;Lc{Ic=W+GUhP z*-1wFdi4c)YlkhmT1L&~No-pdQW8TfUY$xNF6&{nhB~3>oPnEmJ%WZf&{PNdP75Zk zwu8n9Odxq(pwJk`ayw*bPhS_)}xU^{QFvcly}O%oCg9E@DoKzN8>za|sgM*MbKFzGp2llO;z z^q)T|_q`H&_I0R9rqxB!*vqeqib6vu0C^^y*f6un!ub^KZMz@I+r`&bWhh`mCcznP z%zSuwE4+9vjl9$WUHvRqYX2RzWg7T7TO>$Ru+hn(@@wkX?8lm`XshOmf&j zq}3uIA)X#lvlNGop|4({Z*@BWm$MO>RppZjXXEAh{A|$v57C&Br!OMH8R<5?J!hd# z0Wf$yrp*GrC2D%6!TinbSO*7mqL9h&NQkUlEv(8Hw?WOln%& zl~!;+Rf?rxC>3vpn7u(q65^3uEXqIZE>+P+t@b*Z@9B%kIARp}R%M`7WU z@mB(Ls+^o084`wGEqsFPf7!;MNw>dfdk0c=?)}s^mD9H&(m&+shZZkI-+7+7x?$(* zU;XHAA+CYEiMJAzQ9W3b+3xPo02CR<1v~V zf6ol*bLy^GoyzW+8gdZR{ZwP#^VFhE#9bl67`6x7Iwc8#fx%^De5OLK?G3tus~)sW zUA{Hda`LvXt!@O@K0P0?Jc6XJNc;iG%Fss{A{GV?}@4~t1bgR z*-Mqt8BcB%#)P&@KG5vh!L{G*WE0foH=7Dfk%HgiXwy~WC_Otfc6LP~GkYtAqlj%@ zm~=VCdwpN<-H2%4tX90mxvCCE=p3%O-}kxc_w*!~{x{;!+-ULRr|hXc(%yvYi@t5J z%2=-bMyOBLz=<%*eMQcv9&d1TD)n2|0s}t+!pk#-4W{ED=ZH@=bt#E%oiE!qBF5i= z2E=E3GL;&=w-i-=$C*ell_!u}lPj|o$Z(Jqgc4c=O7D56O{gb?wIN6|Egg!mAsE4rU_&>yjC8XA{F9*4h_kT%Wu zX5?%o{C@F?Q>JJ?QpCS}UnVP5({N496H?IT#q9FXD$ei{;E#jhzg2IV*^{ruYAp2~ z6gTT#84eW2dVWsGXFXN#Rh=Nf&f$DBC)WHiCuf#pC+zO$8SYzdP`7#gBgSObbx#0-; z*Q74@QKhYo>G|T{24hn#bB+}h6nbsI>3GywZ+FzHfTAc)9uR~!WhrXKaFy*_vW7aqh~&SiY`ye z*RcY+29*T7{(;yi>J?8&f2D0S<0uLH?o+Yka!&32Lp@E8@nR&zol6>B&sz_DV{W z+<>J`TN<*hti(U!0r%&3Tdyn~h27h`AP?08$NzwE(C33o7kGB3(QWhM*S8j3ozCh> zhLSe*IijYHR;XRS2HXdI!iAt&zd8w#K_a}MAT>^6O0^yp~yiUt_V6=^ssWcd8irY%XXI#<1l1gT|y3p5C>2P0yvS z8)rb2waRkmbivAmO;(?% zD(DlRIJELiWA>9ytkhh2P>_@mp1`}B*8HKBO%guPxN=CKhi= z5hf)CWEIznda%>p*W)XyR?y4z7J*A4XLaSk?J5O(T4+{OJLBqI`Q6v7%*YGXmHG!4 zpzo1r|8Z;9$BB{#-qhfRxHVDXcd}XT1U>`5#I7?#9;5BtaUN!~*?F=@MM|wkosX-7 z_?)8?T*no7?T6ur8g>n(B6qn!`{Tf{hAt(;9QPA_0@nOS3&X}#J*RLAQse>*lszP^ z>!vFnd!EE={ppy{8DO@V9ON_#u7xKxx+mMYpZx^ft+J?72`_uA4 z*Na)ay3*#j2Fr24xq zL^&TjTSfm5B!>GT)LH6B+MuD1DGS|>)75FOK1eCU6^04G$q)h|x+>jXiSv%}=VhSZ zIFj(B+JNi5O%QVp*-iIlq21c&3Z)DJLV;mL z1|q}gO8z+r)U8g{Xn$_Zy+rA#MckA>g@ImHF8bOnS+U}NK4X9S8_2?!$j?R+zDNOe zAw~Luzo_N^7-8sW14*T~J7{#sfUcP2HV@tY-G?F7g@0bu?r177psF1H^7=yUPrr_| zqNn!IQo)jqJuGn1b5hL&K!1Wq_w{4ZX^o>t{3!HXu|b*kH92=(H>tyIjf!AUy*b`{ z-h#%{bfcrP^&c!wtTYjS%;%Hs?Qv5`0M%rd?v~y!mFRU_uZ*oH=}t0UkN*qkJ_~Qwe zk9KiInZZ0dyt#AXv1U}O{;E*YR7syl()GcXrVzBK6I4_3+To@OUQCg_^0_yjT)5vT zyJK(cP+33@M2wG*{ad({DxN$_pzAa>Y9w_+k&>oFhn4+4w02W7OtXoG*}hH1`?#PXJ(Q&Et(pm6`^B8>IM^Y^A9S&zty@taP$>aL1E(V8sL8`jxEgc)? zcS{x+UMEA9ZTYepAk`fIM-UP@WBl;6#F)OQ@xFFiQ_kxIl)APonr;=8p53ore*96* z)j|1Q5Jn!agPag3gEe{UzvE+{tTXm9#TnkOb@omtAR$E$5q>YUO{45)hUMIbEZ<#A z{R6IIlP>uU2NU=Vo$1lWLe4F{^Dhh6m(Z=_psl?nrz`)QAUAgwCq87QUnoM{O>0oP z*Pq7ZJaz^_<2-Y%a>vQ{e6k>oi~_DY&{NE>PpLX6sG-~BH34%*+}7C~Eg}GrEXg=y`Ww>863rT6b_2Z;0wdMacVOAZFiJRU_Cm;4GUBn>V67 zvs|k`?Xfr3QEs{BQS$G1y?kLwj8esxV%+Snz3~^p&X5qvxSt{#oYe2=k=L^4YDnWj zo-m?Yfz$57utrCC>2RdYUyBGO6n*}`@M__vd7-~Q{+0j2uD`p*coGXd9Jq^O`MH4M z()hUldIIGk%j|!IvXSljmvsC7`D>s5mH#Ig`TyS)TfFc2^oHs$JK?W97=8M`?`U|C XCX?8+;@Y);hzJTFRAj28%)b2xQujn; literal 0 HcmV?d00001 diff --git a/pkg/api/forward.go b/pkg/api/forward.go index 11af346..ba46cb7 100644 --- a/pkg/api/forward.go +++ b/pkg/api/forward.go @@ -1,12 +1,21 @@ -// api/forward.go package api +/* +修改说明: +1. api/forward-connection 添加目标地址规范化与校验逻辑。 +2. 拒绝 localhost、回环地址、私网地址、未指定地址、组播地址、链路本地地址。 +3. 对域名先做解析,只要解析结果指向受限地址就拒绝连接。 + +*/ + import ( "Rshell/pkg/connection/tcp" "Rshell/pkg/connection/websocket" "Rshell/pkg/logger" "fmt" + "net" "net/http" + "strconv" "strings" "time" @@ -26,28 +35,22 @@ func ForwardConnect(c *gin.Context) { switch forward.Type { case "websocket": - // 验证地址格式 - if forward.Address == "" { - c.JSON(http.StatusOK, gin.H{"status": 400, "data": "address is required"}) + safeAddress, err := normalizeForwardAddress(forward.Address, "") + if err != nil { + c.JSON(http.StatusOK, gin.H{"status": 400, "data": err.Error()}) return } - // 确保地址有正确的协议前缀 - - // 配置正向连接 config := &websocket.ForwardConfig{ - ServerURL: "ws://" + forward.Address + "/ws", - Socks5Proxy: forward.Proxy, // 使用传入的代理地址 + ServerURL: "ws://" + safeAddress + "/ws", + Socks5Proxy: forward.Proxy, Timeout: 30 * time.Second, MaxRetries: 5, RetryDelay: 10 * time.Second, Reconnect: true, - Headers: map[string]string{ - //"User-Agent": "Rshell-Forward-Client/1.0", - }, + Headers: map[string]string{}, } - // 启动正向客户端 client, err := websocket.StartForwardClient(config) if err != nil { logger.Error("Failed to start forward client:", err) @@ -58,7 +61,6 @@ func ForwardConnect(c *gin.Context) { return } - // 返回连接信息 c.JSON(http.StatusOK, gin.H{ "status": 200, "data": gin.H{ @@ -69,35 +71,30 @@ func ForwardConnect(c *gin.Context) { }, }) - // 异步监控连接状态 go monitorForwardConnection(client.UID, "websocket") case "tcp": - // TCP正向连接 handleTCPForward(forward.Address, forward.Proxy, c) default: c.JSON(http.StatusOK, gin.H{"status": 400, "data": "unsupported connection type"}) } } -// handleTCPForward 处理TCP正向连接 func handleTCPForward(address, proxy string, c *gin.Context) { - // 验证TCP地址格式(host:port) - if !strings.Contains(address, ":") { - // 如果没有端口,添加默认端口 - address = address + ":8080" + safeAddress, err := normalizeForwardAddress(address, "8080") + if err != nil { + c.JSON(http.StatusOK, gin.H{"status": 400, "data": err.Error()}) + return } - // 配置TCP正向连接 config := &tcp.TCPForwardConfig{ - ServerAddress: address, - Socks5Proxy: proxy, // 使用传入的代理地址 + ServerAddress: safeAddress, + Socks5Proxy: proxy, Timeout: 30 * time.Second, MaxRetries: 5, RetryDelay: 10 * time.Second, Reconnect: true, } - // 启动TCP正向客户端 client, err := tcp.StartTCPForwardClient(config) if err != nil { logger.Error("Failed to start TCP forward client:", err) @@ -108,7 +105,6 @@ func handleTCPForward(address, proxy string, c *gin.Context) { return } - // 返回连接信息 c.JSON(http.StatusOK, gin.H{ "status": 200, "data": gin.H{ @@ -120,16 +116,84 @@ func handleTCPForward(address, proxy string, c *gin.Context) { }, }) - // 异步监控连接状态 go monitorForwardConnection(client.UID, "tcp") } -// monitorForwardConnection 监控正向连接状态 +func normalizeForwardAddress(address, defaultPort string) (string, error) { + address = strings.TrimSpace(address) + if address == "" { + return "", fmt.Errorf("address is required") + } + + if strings.ContainsAny(address, "/?#") { + return "", fmt.Errorf("invalid address format") + } + + host, port, err := net.SplitHostPort(address) + if err != nil { + if defaultPort == "" || !strings.Contains(err.Error(), "missing port in address") { + return "", fmt.Errorf("invalid address format, expected host:port") + } + host = address + port = defaultPort + } + + host = strings.Trim(host, "[]") + if host == "" { + return "", fmt.Errorf("host is required") + } + + portNum, err := strconv.Atoi(port) + if err != nil || portNum < 1 || portNum > 65535 { + return "", fmt.Errorf("invalid port") + } + + if err := validateForwardHost(host); err != nil { + return "", err + } + + return net.JoinHostPort(host, strconv.Itoa(portNum)), nil +} + +func validateForwardHost(host string) error { + normalizedHost := strings.ToLower(strings.TrimSpace(host)) + if normalizedHost == "localhost" { + return fmt.Errorf("restricted target host") + } + + if ip := net.ParseIP(normalizedHost); ip != nil { + if isRestrictedForwardIP(ip) { + return fmt.Errorf("restricted target address") + } + return nil + } + + ips, err := net.LookupIP(normalizedHost) + if err != nil || len(ips) == 0 { + return fmt.Errorf("failed to resolve target host") + } + + for _, ip := range ips { + if isRestrictedForwardIP(ip) { + return fmt.Errorf("target resolves to restricted address") + } + } + + return nil +} + +func isRestrictedForwardIP(ip net.IP) bool { + return ip.IsLoopback() || + ip.IsPrivate() || + ip.IsUnspecified() || + ip.IsMulticast() || + ip.IsLinkLocalUnicast() || + ip.IsLinkLocalMulticast() +} + func monitorForwardConnection(uid, connType string) { - // 等待一段时间检查连接状态 time.Sleep(3 * time.Second) - // 根据连接类型检查是否还是临时UID if connType == "websocket" { if strings.HasPrefix(uid, "temp_") { logger.Warn("WebSocket forward connection still using temporary UID after 3 seconds:", uid) diff --git a/pkg/api/users.go b/pkg/api/users.go index 9c738c9..d111d71 100644 --- a/pkg/api/users.go +++ b/pkg/api/users.go @@ -1,5 +1,11 @@ package api +/* +修改说明: +1. ChangePasswordHandler 添加用户存在判断。 +2. LoginHandler 添加用户存在判断。 +*/ + import ( "Rshell/pkg/common" "Rshell/pkg/database" @@ -19,23 +25,29 @@ func LoginHandler(c *gin.Context) { return } - // 假设用户名和密码验证成功 var users database.Users - if database.Engine.Where("username = ?", loginData.Username).Get(&users); users.Password == loginData.Password { - token, err := common.GenerateJWT(loginData.Username) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not generate token"}) - return - } - c.JSON(http.StatusOK, gin.H{"code": 200, "data": gin.H{ - "token": token, - "permissions": 1, // 示例:1表示管理员权限 - "refresh": "mock-refresh-token", - "username": loginData.Username, - }}) - } else { + has, err := database.Engine.Where("username = ?", loginData.Username).Get(&users) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Database error"}) + return + } + + if !has || users.Password != loginData.Password { c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid credentials"}) + return } + + token, err := common.GenerateJWT(loginData.Username) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not generate token"}) + return + } + c.JSON(http.StatusOK, gin.H{"code": 200, "data": gin.H{ + "token": token, + "permissions": 1, // 示例:1表示管理员权限 + "refresh": "mock-refresh-token", + "username": loginData.Username, + }}) } // 注销处理函数 @@ -52,15 +64,33 @@ func ChangePasswordHandler(c *gin.Context) { } if err := c.ShouldBind(&passwordData); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid input"}) + return } // 处理密码修改逻辑 if passwordData.OldPassword != passwordData.NewPassword { username := c.MustGet("username").(string) var users database.Users - if database.Engine.Where("username = ?", username).Get(&users); users.Password == passwordData.OldPassword { + has, err := database.Engine.Where("username = ?", username).Get(&users) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Database error"}) + return + } + if !has { + c.JSON(http.StatusOK, gin.H{"code": 400, "message": "Password changed failed"}) + return + } + if users.Password == passwordData.OldPassword { users.Password = passwordData.NewPassword - database.Engine.Where("username = ?", username).Update(&users) + affected, err := database.Engine.Where("username = ?", username).Cols("password").Update(&users) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Database error"}) + return + } + if affected != 1 { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Password update did not persist"}) + return + } c.JSON(http.StatusOK, gin.H{"code": 200, "message": "Password changed successfully"}) } else { c.JSON(http.StatusOK, gin.H{"code": 400, "message": "Password changed failed"}) diff --git a/pkg/database/sqlite3.go b/pkg/database/sqlite3.go index 396201b..371030f 100644 --- a/pkg/database/sqlite3.go +++ b/pkg/database/sqlite3.go @@ -2,8 +2,8 @@ package database import ( "Rshell/pkg/logger" + "crypto/rand" "fmt" - "log" "os" "path/filepath" "reflect" @@ -88,12 +88,32 @@ type Key struct { PrivateKey string } +func generateInitialAdminPassword(length int) (string, error) { + if length <= 0 { + length = 20 + } + + const alphabet = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789!@#$%^&*_-+=" + buf := make([]byte, length) + randomBytes := make([]byte, length) + + if _, err := rand.Read(randomBytes); err != nil { + return "", fmt.Errorf("failed to generate random password: %v", err) + } + + for i := range buf { + buf[i] = alphabet[int(randomBytes[i])%len(alphabet)] + } + + return string(buf), nil +} + func ConnectDateBase() { var err error // 获取当前程序所在目录 exePath, err := os.Executable() if err != nil { - log.Fatalf("获取程序路径失败: %v", err) + logger.Fatalf("获取程序路径失败: %v", err) } // 获取程序所在目录 @@ -104,19 +124,28 @@ func ConnectDateBase() { Engine, err = xorm.NewEngine("sqlite", DatabaseName) if err != nil { - log.Fatalf("连接sqlite数据库失败: %v", err) + logger.Fatalf("连接sqlite数据库失败: %v", err) } err = Engine.Sync2(new(Users), new(Clients), new(Notes), new(Shell), new(Downloads), new(Listener), new(WebDelivery), new(Socks5), new(Settings), new(Key)) if err != nil { - log.Fatalf("初始化数据库失败: %v", err) + logger.Fatalf("初始化数据库失败: %v", err) } var user Users exists, err := Engine.Where("username = ?", "admin").Get(&user) + if err != nil { + logger.Fatalf("查询 admin 用户失败: %v", err) + } if !exists { + initialPassword, err := generateInitialAdminPassword(20) + if err != nil { + logger.Fatalf("生成初始 admin 密码失败: %v", err) + } + // 如果不存在 admin 用户,插入默认的 admin 用户 + // 当 admin 用户不存在时,改为生成随机初始密码并写入数据库。 defaultUser := &Users{ Username: "admin", - Password: "admin123", + Password: initialPassword, //Email: "admin@example.com", //Phone: "1234567890", //Permissions: 1, @@ -127,6 +156,10 @@ func ConnectDateBase() { logger.Error(fmt.Sprintf("插入默认 admin 用户失败: %v", err)) os.Exit(0) } + + logger.Warn("admin user not found,init......") + logger.Warnf("account: %s", "admin") + logger.Warnf("password: %s", initialPassword) } var Setting Settings exists, err = Engine.Where("name=?", "wecom").Get(&Setting) diff --git a/pkg/middlewares/auth.go b/pkg/middlewares/auth.go index f1eb838..7c8ff14 100644 --- a/pkg/middlewares/auth.go +++ b/pkg/middlewares/auth.go @@ -1,5 +1,11 @@ package middlewares +/* +修改说明: +1. BasicAuthMiddleware 添加用户存在判断。 +2. Authorization2 添加合法性判断。 +*/ + import ( "Rshell/pkg/common" "Rshell/pkg/database" @@ -11,12 +17,10 @@ import ( ) func BasicAuthMiddleware() gin.HandlerFunc { - return func(c *gin.Context) { authHeader := c.Request.Header.Get("Authorization") if authHeader == "" || !strings.HasPrefix(authHeader, "Basic ") { - // 返回WWW-Authenticate头,触发浏览器的弹框 c.Header("WWW-Authenticate", `Basic realm="Restricted"`) c.AbortWithStatus(http.StatusUnauthorized) return @@ -38,9 +42,15 @@ func BasicAuthMiddleware() gin.HandlerFunc { } user, pass := credParts[0], credParts[1] - var user_pass database.Users - database.Engine.Where("username = ?", user).Get(&user_pass) - if user_pass.Password != pass || user_pass.Password == "" { + var userPass database.Users + has, err := database.Engine.Where("username = ?", user).Get(&userPass) + if err != nil || !has { + c.Header("WWW-Authenticate", `Basic realm="Restricted"`) + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + if userPass.Password != pass || userPass.Password == "" { c.Header("WWW-Authenticate", `Basic realm="Restricted"`) c.AbortWithStatus(http.StatusUnauthorized) return @@ -51,20 +61,29 @@ func BasicAuthMiddleware() gin.HandlerFunc { } } -// JWT 验证中间件 +// AuthMiddleware validates JWT from Authorization2. func AuthMiddleware() gin.HandlerFunc { return func(c *gin.Context) { - if c.GetHeader("Authorization2") == "" { + authHeader := strings.TrimSpace(c.GetHeader("Authorization2")) + if authHeader == "" { c.JSON(http.StatusUnauthorized, gin.H{"error": "Token required"}) c.Abort() return } - tokenString := c.GetHeader("Authorization2")[len("Bearer "):] + + if len(authHeader) < len("Bearer ") || !strings.EqualFold(authHeader[:len("Bearer ")], "Bearer ") { + c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token format"}) + c.Abort() + return + } + + tokenString := strings.TrimSpace(authHeader[len("Bearer "):]) if tokenString == "" { c.JSON(http.StatusUnauthorized, gin.H{"error": "Token required"}) c.Abort() return } + claims, err := common.ValidateJWT(tokenString) if err != nil { c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"}) diff --git a/readme.md b/readme.md index 108b272..47fc4c2 100644 --- a/readme.md +++ b/readme.md @@ -8,11 +8,18 @@ Rshell是一款开源的golang编写的支持多平台的C2框架,旨在帮助 通过-p参数指定端口(默认端口8089)并运行: -``` +```bash ./Rshell -p 8089 ``` -**默认账号密码:admin/admin123** +## 账号密码 + +**账号:admin** + +**密码:首次运行后随机生成** + +![image-20260420125544426](./assets/image-20260420125544426.png) + ![image-20260205112117018](./assets/image-20260205112117018.png)