From 353e4dc74dd53718f2b17856be0dd116216efb33 Mon Sep 17 00:00:00 2001 From: ivanjermakov Date: Sun, 22 Oct 2023 22:10:10 +0200 Subject: [PATCH] Peer protocol & handshake --- data/archlinux-2023.10.14-x86_64.iso.torrent | Bin 52638 -> 0 bytes src/hex.rs | 6 +- src/main.rs | 26 ++++++-- src/peer.rs | 64 +++++++++++++++++++ src/tracker.rs | 25 ++++---- 5 files changed, 101 insertions(+), 20 deletions(-) delete mode 100644 data/archlinux-2023.10.14-x86_64.iso.torrent create mode 100644 src/peer.rs diff --git a/data/archlinux-2023.10.14-x86_64.iso.torrent b/data/archlinux-2023.10.14-x86_64.iso.torrent deleted file mode 100644 index 94c137d108d59b58a6f81fd379fb20eaca12d1cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52638 zcmV)5K*_&kH#%c)ZEa<4bTlN6RE^l&YJ~1#lV{&C-bY)~9VtFw$I&EuoZ*p>FZge0qE-^7PI%9HWVRUJ4 zZXje~bY*EVHaRykHa0mnF=aG5X>Mk3WHvf%Wo~D5XlXb!H#ImpGdDJ6G&*i!ZDlht zI$?5SXl!Y2b$Bf@FfubPF)%JMG%a{IHeWV0E@^XbF)})EX=P(&AZ%rBXLM+3H8M0Z zI5=fCI&f)aV`XzQGBY$VIvMCi#AhS4ktyBDKEVSSrt44C9?H%X1r!s9Mt}JSE#^ zwxCc4+#YT~M|rX4xU&uy$u!DQ5H;-lBKd@`aFw?%t|MvPQJJJ&0`d)kOQ5>y^J|>4 zOuD5pm>X$>#P{|*P!z^T@!lsPBfuYQ>ZvD0rTb|7tg}l+m57WH33(ZS$?q#GNLbXP z`)K9YLLehcym4qjv&3d-Xq-#LsoU?WA1$pPhO8py;V z&Jmzb0i~T{)5bNHKep5u zI;Xt{nq6hg>=_nC#E9VMsQI^(^O}n$Vey?km&SNRM^W-7m76pyLleT-czy4MurikW#(XgHSmjO9S2Ovj zEmTw6&af=>7t_fb-s3mT^ls|QNMuE%-%=Fj5&vUBT$v`p^+RTiUBB`ymz_7k4li^m z48-o3$!xP5QyaL$$GP_1(Hyq7=s&7NPJu@=wcO5+n`RrWR6Nl4=Kykronp%7?$8Ii zHw6#_B;e7Bf&3_z>Umddr+#H5p*T~E-s-XeAbPa@0ZbvF46_SL9=yCe5zA zGBl?rOzihsov-C0z73152o8l{Jp5kqWHfsLX>oMV%9<=+R7$4flj~pB7W`lzfEtn+ z9P?}`U2G(U`%+II=qdd6XCAcyTJ%e8r1j@jJl82#;r-h^!uPoyEoZ+M+4N9cG-yQ*K zlpbs*vZsE)Jw5L7YD9+X+I!d;bdwCq&+Xkyd)ymfT0o-OcK|^!0qM;cOt>K^7 zitq>H|EK4N{S4bnJqgVo3iWicgm&ZFn;y;pk%qr4buNE^0jA9tL7o{&$Vbeeiw26B z&gDGKmg&f$fq~qDRLhj|bgg~uzid3-nq8X?uK5*w#3272+Xhk~rqH?EJ<>93IBgd4_%M$NNzzTZp4$MEbPABb3 z2WVXfdWUBX)hLwu*>UAuwDlPaOf-pE4L#ME)tsE$>N<5s{>b{b02~C;7`A2W*Cc&a z1(faC@!wa0@YIelx{p>1m^VnFoyhfLvPp^-d}EI8msjy)?ZbZrI71*j3d?5MF68`J zJ-mq<(~jpU(p72QbKf4_+251yjVQhS8YB&|5Y1=jgPaqhu%}9YfOtrPL$=Jdi#r(G zKmeDpTacb_Z~`3tTyPBawI}DF$n(SVqZHW7A2ZTgT&6yA^=6~>msY9M>xONjal(%5bdwB2?J&0ak=#?VqVJbFb@-tF2K$O`~9Dzd@Gt{oIW4_Y}o zy3vR%qvVq4hwXf)d!e?C@oTQd+`Sr&1#aKLW^eE(*T)iSYs}o+w#xw>(bt!3XCjE; z7RHBnc4)*kY)FKvH_&mG%AOS=;_WfEoQ&imT#^Kzu8Ir85bLz|g#3?%qDtvtzFL8R z&ddcy@ZplU(t&>#$R5NdF8m$$7}EWGkua3?At#%+ zd5~jZbutbq3R~|xZ*DeEb0Q$9etubBM<~k3ZB1gaebd?7*lTpfos~5P%A6nQ{UY3T zT>W2q2PUspP~JUZ)F`P$a_!t2RSrf5nk;eAE(ML%1y@%7K?x`^(BPtuC8?iORlSK} zl&Aryt27Zb15dyi;bJ+d=%BnK0hGV&(H;0|k>lZNd8!#Gfb9AcUrPE1;X5S>tUyC& zW-Xt|Q({xG#6z}7aeywW==I4qgdwskLerB02PdR#e}>oBC?c3O+_XRLfPq1XC>a4{ z;j!dC?S4p!}nM^E? zEvVH~?&jXEa+8iL9{7>mYzl{k8SlAd?dwfU(*P#ZOCKd^&CQ*gG*hE&SANsEi2Bs5 z&xcMC{T^lYj3H$T37B?YvYL=IQ}L4A!uB=~+%iS@=sSrwN9i4PbsF=EXbW}G{!pY` zfF1p|sh^eUG@dvB;-$oF0h9pM{{gv|?LG$(J9dv4&C#wEh@=w4vPc%(Zy`~Cf4&j; z4Oc|EEYp(p0N|FZRh$0^ve8Ub##psipzN}=YD>;Te4F9}8(PwtLX(^iU)>~*!riw# z6bt8F$`7sT2lT_>#iL(s-$FW9lo%s<3GrCnh>9DLi^EqukhNy+U8{p#f<<@yX2=q& zr@JVQCxICF+hUDW2M6KY9EVnn{A-N5r{JyH65^Cx8g@@(TX-JpRwvd@QUPY4N8 z#YK@s`qwffGH@(Og9?FAYK!c6z1M@{A2gnOXLaf7zZ$Sbx_U`#lG7=l^g$|p57Sbt z0_-W{a`@b}8hp0vmK_gog|$(744}{Hv3i%LBy>u6JeVG{j4IkpT^Gt3yN+0fv3#4Y zC^061pwbTZGneo9bBU*216h#<9IOL`30&*7+h=jXq%J)@P~vhK>2}p>$aUPK29k{7 zF_=XKg3)6h^ye`cvd(JWEP<@$2VALv94#qR$GN&c*(jB+92|`+R%{4-QW5T|`cUAe zG!wwF*QhE33fHn6(;oLXF~G9VJ}vGW-XCjS-v0cDSD#VT9v6? zZYaW24(tr`g0p!OYP#r~778mcvNFHSPqc3!4;91iKht_Iki~qhHud2~G6%u_+{H^E z-*i=?U6yNq+;Lffcfp~{I57G0>j0t3eW*W#BSFpFArvk55ol6D(?^DVFco)GO({qo zX2;ue7%2g3N!AKgsiwjM8VtuyH$k=KOfbw$RQp%WvER)zY-J0^@vn?zHEUAtySJ8h zA;#*0FN)NlhXvK~aL<% z6(euv-9rB8WxVTu!@i*fjp>qYg#vI^=A;fM!cD0H?fyj3u;%V zrzn#>&81*%ED;2KQS)OqR)COyFw&!6WiF6?K+28W?2X2dR7E1i;`!OOT`kC}M`&R` ziTL%hE`%%L(a-jmX+$nn(E@~2v5UD~9XFB9`m+77{3CckAV1qr^xlpZ2#h*sRnk1@FrQTmZ4(5-N+x@~6xw7U(wvgd&eq*NIj?T^*?) zXTNtJ_@wD-h+?6ZcCI+pKngWsiqf_(fhFt-c)}_4>_uD|b3#YX9742n*Be=){YG4PVoo+RQjKzDW#NBS`bmyV%-J4g6_>dIW}lpnxAiJy8)2QEha`$z+JExvEX zolUQJY6zcd8&;K;=39I43R!c|D_a;A+9D-pOJ6tcGD+ zXoIA33hhUI^`c2cL<8ecPs##~irlu8s`Yx#?UW#-f*#2~UdJ#|d&6M5glrA_k|2mE zpAl_qYpS0z-T^p78Uul7Yj~8p-oL!LhmK_izMH={SALD5fnBRpE>_j41GM&I(NHmb zZa<^NbK+`WP9tq`?-$L@-59nr(`CcU?-$>snV`|jnDOVfidV#?PNY2(!lY5j#!_X5 z9SdQr~P9f5)Bo$;C*)xj1bn^mdV z3F5YQvg0Y1dMpG#(iX*b8O0jd+3n?f1zls8txEu^YBBxzZeR}gw}Ad53$wvx{lhQe z#$fIk>uWO40VhmxVtlrb83+K*ARkN99se3(t6C>d5VVQ~KQ3r_A@@W6ky5jjT*>MU zf#nMrO3$%Q)Xg%_<;e3j4NrUWUQ^8+Qp}wQBKOW!GPDb!`xLlRD2R`ZrKfm)S5gdJ z#38N|R#%sRM3$gHjFR?PKs=KKFbfM~(2Owmt63)m)Gpuit{b-^-m!gWA7D?KwOj=2k2+?q|RhV|eT zS{KpZ+DYTt(rK(^f%~cdk9*Uq9%x`5P|1iTc?&s*B0Sb~uIw;Mxbo@YhRNVTHg8A% z3VA81AUcb$g~TWECil^_ z`9-JcnC+|pZ3&@cHbfBshL#@sS*k~8-bnGY&?)anB_cj4D}l*Hl$N<332c~h%K1$b z8Y|B2Z02zih@ zTJuUJ+9bO_$hXDVP6i5a6Pk;o{;+bu68X}Gx8U+@e;S2*_Y6HS;{WANY%h@@y_>V2 zxrVhF6739P9fgWwJjR-y>?>GHr|4c16F$n?(i7Ig%)CkDgj1e6D`IEh^tn-Bergb; zZ3nv!;6x9M+Fm{+ZNx!@zwm7x5;YdgsWUZ$3#EOLMcCwfWPks(L@m~!=zt}dFV{Ej z+8dlo7zX4p!^g)^j z$i6&k+5J&)hsK}?xK*1##6P*|dNS%c)ouU`y8WFfsW}kcof!5q6C3;-f|CvgKbjL( zt0wT|E!c*_sdW7k9mmoL36{@GLz5|((4`YAFPMVti0>Qt4af)g?p6a+%tEQv64bhs z0262HEJ5D2B;{seSCBGLu!#S#wnxe8h%0G`Y1g84@p>9ISj@bcnQv&T56q4L^hHSm zcOJv?TuCPNiENctRjIn%|B3%MBAV3GOwD$e!skltHeLy9C{mtXOQ88&+!g3yR4R%H z05FNz&)2-+BgnU9i zjtzLHOic{lTwT387Msd0Z}(uST_!F(o@8b?=;DPKaaI!u-HzAs!BU8Ve$xC=_}gb) zSYOl~`7fnd-&H0n8&QYc6`d8-T{O)#;DVk!GL5+as%?XR(ylgz^tXVNc`syQ;vpa$ z7iT@+s$SD_nOLnSKfwU=Z!ZJ37i6DbXDWPC3DRn@Z=N!H!lRfa62m*_Ah_ET=UKpZ z$?vc@auiG>IyAj-fb-~W&?%Al{=8t3tAvb9#`NB*PP6nCzS3JG(| zO((Pc5|q4wpya<3+D#scE!8_wla1i(>53kyrpl)jEGQ`yK(gf=Gi>s$0bGQG)$b`a zyU3SnEQs^4CI;Q~f>m^sXF}?5Tha(eB~ZKyAfPNXbHRNP<>?mDFtD#)4UnDu{*BNQ z#_{S62>37ZG82ew*B3ON+@HKO`^wsa91%aPL$khd5B(27(U3^!7)@z_6Dgy9=w`o{ z`L@ZB&HLfff^+|NqfV2*F7Y4YATl$kh79{N&X-fO!KR8HzYX4T6{(G2g9}397K9Wi zfyo<%OafC1Qsf?br0s#y_yQ`kuZq$jqI)oruBsXdvfgF@H^*M{PUD--0|ja5rRS%x z-NDa9Un4rEA6D?MgJ-8&9ST75jhT=dja}n7m!$;+p-MFl1-(L23-!TW*IWmpzmD%c z<{Z7z(SQ5rXO_hg5M&LrN~GG&Ee8cEghOv$*U*QBlEjbcDslT8h~kxr2g1Ts%RDG< zaPFFDy}T$o3L1(EJ$wWxnw{89j^P?6tomTdvsK5yz%}3Y+Y?OXMKh} zD>56LMT5kL2)Wj9sp>@(d2<7b#?Q0b#$F;v+{k7tMi(nIBrc9MHi=&k2+u&lzKN$S z5R7JhIh7f*1=Cr7!sX5_pI*6yAi?&1_9UJyyeGeB@GfuWvx%}S+xr%~`p`XOf9J?H zA}49faHO5i<7w-osA14f&k)h7SC%D1!ifYl{be+*;vO&=l{#9xzYq8Y?a-{7M8dJ~ z11Z_M=Spc<30OZe#xQcFa!}T$;!pwespu zTr1Mh9!7*42Rr(#DCrm!(}QmkSvE1)0CM^7MRh9uJjQ?F!@Ia@DVo;xW}i^L%z7!y z=o-ngc`i|2lgm|Qi~2kpLx$A2;H^>%9>IiYF^^YAdz|c(^`*2*VTE0ORoJBLG*j@m zfdA%}Oh$$z`>mFAwhqh_ei^U$4v^RVD-IqgF^Zy++5~5i5576^e@=Ga-Up$prKj?; zLHw#bC^Cv)Gi4t>YI0?VT>gPoocHdQ!4RPXgOyI+a*-orx_K|?KcW-ufeF7SjMc$a zVk^>Acyn39_tsLK*C%4kx>FX`JKYEJZ!i87pjarzP{pVN{r$2wO)fh6)X}u4n+Ko~ zBWd5ken~#r<_3ZV+m{f2r$(|>gxf{ey~Hc2R9z#=k=UiX#3$$MdPRQBGECr2uVUEo z^0Qdo>8{*t+}He{@ODN!ut2|7fhgnpMyd2-B=DGu};a+P!a@8w+()vQmHS1dOCqt6oEBOF;a|0ekDGS2N zVCb@*$h3VjE!tKCt%HBYOGi9O?8LP$vHa!exM(;$uuv1m-#mMkwfU*44e=;*Sl`EB zzxdnKSYnYL94)c$ML04w#eO#hH<3zZRBBLWp#jALeI@O6?Y3hmcpf~~CP6s5@HvB$ zZuc5OJbf#iVOgJKp=fW@DekZffv8)>sp(6qci~P$$v+Qp-D zB^%zL3?7d;f{!i=d7njn-_7=#NK1skzTqSZRZkq7xr;z-*mu&COtwD^0Q+cYbx@>} zh%Kt+=g-3P@pAEk*m|)Rcy-&gN(kO;5CI9*2X4wGKamSGdS2wdBk}nS5Fkc{oP%H`OM8ljNQC;ENm+C6qlqy`5s~D<$w%sv({(2pjITW z6+0=ZBtyTAxgVPV662|?5UchV4twUVGIS)<3LEPmthyn`7*8dd2hTUI>PM@(FMa>G zY5yqjPQbSP4`##bdBewUYF1?*3BT7jWKS~3Ta`ATCNdy-`w3rJ zBJZ*XeNPzfq~SxaziVDq{}iet89MWD=FDOp(~_|?t~xt*f+ejQ&uW>3umlu&rknX5 zU-ekA=ZtfOSOzB!@nevPh8p?5o4}B_gPqw}HW+g#v6{k^twFN87*-w`Un^3}jF&HC znew?S=m2&T`otLa^#=Fz)upU(4GV4|tVQAwZN6{0SAa#&=iU0E z8Y+&^US%7Jxer<~X*JWI@Ygp}f6%oo{pj+L*h~Mg1ANgs6t`XDtOV>9mhN9P78U76 z_HRDF4Hgq$-r3!z(eNWiQT2{Yyw#>_Go+-%$o*qL;LZUTgn+JdL*c-;>Ljf9%Rx7M zOY@W`yvxO5j*NNs65b<0@0769bj(bVH*fqkp>g#~m2gzS-bJ8nmuUr?2cX6dEMM>i9)!yr(BS5T}Lq>_j}N_aF(Nx1zB(Yt8zxI zZ2wCZ$DnN1kcX@8`6vquVH=Iw+H~S!iThB^^TdA)B%xEO(;uE z+zc?*mjwS1P}mawb6{LlHz2s8z8$6)eTq1np|WnJ$tSwm$*202eLxaEg80r4~#)eXF=dbvBO07(+%sKLnA~H@N3SiXaSzA>m(*c!`6CK3 zXozR~9Z58v?U&H$%$mv*)jBlcj>~pEQLe5rj zhHtKk%B!8M4(1oefudhtqBzF+KdrTbH0J|uv*po6cocC0=+xq7=#bXespOi?eztG_ z)r8pZMc1|3ap^qm>5JccBKqY_*Z?h<4>V7p5aAT<*$GHcdfwoae|o2Lq+p}Qql1WZ z_M?~&V2Qy?O^?pTrY%|%8Ry;83QQ8c&R>YJIzXb&nBQ9_%gaYHE}kc}|K4+4Hic7p><31g4H>42w|v zHZjWC?vFNYHYh8rg6aLP`gIQgZeZ&C;Y+Hv_gQHns?pqit7m`+2*t<+i4G+7pd7%hf?p)7hJG z+PgxM$~gbuj4(L=8P*#c-=N>2ZI!D0{%wScxEKz@N{a5KL1uB~X6L zRDtv$XK6-Po(~2@MDw{N_zDXlu!v;wcbNLePSg(Fg^LCAef&1hl(pBtVnH*Owx~n?=4_Ro7P#J+kk9>AP%F zWIYT9l&O>9rOwVeAKAh!Ygt{4+bl1MFEu)f^xNHdx7S~`Ot(#$8*y+1oXQy>2W9~r zoEY*^oaNpoD*=#oAY&9(mn_x(5xSXvV#zoK$jZ>~8vT=>)#RjfHHqm)+e6R6x$3%E zMW_Z`Re$0_%En=$=Vrp&U3}{qRvw=H(4>On?`TS9Q3n_T%ui2ERKZF<%A+a*=6usi zYd}6Z%u&(3qNi>HWt5F>DI#&FZoWt6N{hr5*bOguW(#>Tkr*2~Z#^zUOr{?8)6q4w zsqox<#!@{C>a|)%dyj`s*^Gi>`cd0>{|3dIIF?eI4`ihd*iYDGo&jeXpt6lYzoM@2 z9&pAg1a~%6^g3#4+ux$+HGc6psQ;$QdUZ87ht%RIBXs|A6j+T%1L!|U8WfPoyKZ1o z7VX63mG-Z1sEnWYb6Dat6+{t=wFORTGcH+%W`OTuG=xL@lH06qvJ4wa+}8%!ov9I? zMPYOSe{;7Pm?H7de}U1^Qq*QGmtoAkJtHYk@e|EaC6%ix6yinKA@xh#Yg<kj?s>CyN4|MFWmMxERL^AlqxOlP$&bQ+A z5>llh+$3N?U6GMBgADjuB-5Q>f>)$?ni_I}M%OqtQlRzNjX?ge{1{yRXnn5>v%QRb zo2`N#DZ*0B$Ev47>c)pPVALhz4|(1lMH6);xRk7$W_|yyW1BZ_n4zINv*y6q@j!WR z_Q6Cgw(G?ua9L)UG*I>>H^V4n!&$7hpF zfNU=@lRjmOX5H&f@tsV;A>$52>7%1np%xF^ntb_0wo(en7zxSq>EJ|vI^?5lKaK2n zLQTN!Re_p6dPsb=$eEMD;${oR)_M9((aDnscf8-0CbTyOYNJf`)jT)~<_BK@wU7=3 za;JHBa`1Jbtdh%mY*E9A&?x#yn0s(_0Dg@FEpiKb_=J*mEPlz6DPkQAfoqO|4ThLC zv{Ik%$`5b1P|Km8p0JlocfG~3?>Bk&xG!z+VBd)7EXE)Yd~P3Wu?(`p%jQqR%Cr8l z<}OGVSZhAmPmBBnZZ7DV>Ce0rdIjhfj^KcC?C-bf6XRSkG@u=MrvfqUt%d|8ngz$R zV;dtWJ0+l1v(kGSR>GA|ib)}jx|5VIiN?U7L$#>1@Q$DVPiu#!VzMg`m_bCud%B1x zuKbT`TqXIy4YTS`CAfq6{Y1n8WRayoCCDw=$f<*J*CPZ~u`)gOgdczczY0z>I45xr zDEmP)%po~1WVdbNP8IhQBzYl}A|Es>iW9u5{I1#Lq`|gz4g%!5?(nJrf5am;Xo7s% z?Nh%L!;lRpn~j#g>)djt-l}bMmP$164Zxpmcr{G@vHOm=laILN>0)54P5qZrf7=_ zc~zRFT)K=r>XpdFMi#HXX^x5Ij_o^lsSYro&AAu^F4+y{>&qkBllelfPO`}_Uh&VS zPFPq?T)A6#FFaCEEqt@oyf3?PKmU40A?s@c{5cn(gr={%(*h{;HTl84ScnqJF5-_C zMsWi@vyYv$)DWxaz9dvx4Jmt zbm2kk>`$7c7)H%z6uj1<)zJ9C@_B&f=F0ymT3hQc$#|%i7=_y371x<2U`rXu(P~>J z$aFES{}L6p(Z_a*)o{K+smq(?I2iL^o^%*rd6^qqpGVHBt3p7uD+{ENTq!B-H~mqC z18UBgn*ho5;!e->k_sBlpl6_FMbWv*dUO~QEy@IYuReN^FIu6?`FG9OdbS=@IhXIV z$iNXf_e z>3z=~G;N?}j~Aub3^q_0RwD zbYy$nd%m8+ws9$C>Jr*Dv@4WH<#pd&`!UXoOxrsEkhkvX%9@lb)rtGIg!O@yh%``!@d(r(nKr__h06DNah4)yK;y!>yX=`Ql2MuSM$`-O2RZ@$!VQJ6IA9&bffSz(o} zgzy2-WRuPFwXh|la*+Xr2_Zo28X&@;HEWMZ7s2`ldPjLG*pe(vbsvxIKRguDWa(NX zEjs6k-?p9xU~6?E=wp;AWnr+)hg*sw`|mMdj-sDafL`%vUYGF+h*E6;w{6D2#)J_6 zx{F7hFZt}25*>Is-Pil6-KV^(E}CTUv{`Xsb)yX=+LnYwo=w@ZDYZ=HX%&rtUj#8e zxtA%}VIP>eqJS#HkT^ZZ^n%S2m_o9L^jUdLaI0`lV}t{CXt# zdErZ18PO*>+QU80lZ&iFF>4lRa$727z#9vd~#d(`u5Ib^`<3AWMzcCo8X?UP^-@vP2eg_lS#$rBbN!PtVz>swq*W>N%(>joys-3->sHDvdM zKx<&P$okmk2X$oUIRlEn0oRH)lbn~t{(IL!l=)&0+C(&o z^CNc^k>-hf3csT@8NHO{&AZmoQ%zmZpjp!}q z`t==A2vNS{G8y(tPBLNA+-G_Jk5b}Qj8X<@E-sZ&WX^rmZk-)eB2)}|CrfKyJtp{|6k>gxYa z!SnnS(Ng8f(?muzVlX>MFBK9KD6YAgwC&j_x70A+!+SY5HM!dkCj&g@v%VM_@e%VV5DfTK3Gd#L^v3lC$Fjhd{OeUi(_Cwo~b^ z`C#-}XSVftoyz!1!ABY3bTew_!=@z(^&Cm|4m)cdUKTfzb*%ond94~mn|bdc}T{T6*wSYLB?6hXn9ez|c8N=dp8lF)~o^XpB{;!-He!0JSt%N7IYp zY$h$HgEZ{ibo(0uzYw~LXmk~>XvsPfDOlEoZ|8L04CLJXwB}WgJXaq;i8JdmF2b9l z``|IER5an5IfME{1Zgf2rwc%Zg(Z3r)>YAJ?|17B<^9}s-n^@zJ}BdryF##^1r{4+ z`^_$4QlN2b!aSC?+se`y)Y>;S1Qz=#mZ&M=F6#N^oyGqpK7AL?{Il*T?sxicbr{+gO7MfBh^m5ts`FH;fa%s9F^C;jb*;8vENF zu1X3Z+wd~tIKb<2O1>KbRsd%js-!G7u)BOltwvM9a21DD9w~mV6HBgxwbtNW3Pl2* zCQ-x)mwxLDFx}fDt_%Up7*&nGA@f^xhb9vYK1#j z@2kIgokWp6&F7mjveG(&TG=ly2Wp4R&YpxXCT}>?XBhlTdg@U3K*=rHt&l$aJgS+m z2jv|09>yXw42#{pGlColPc>vriz4(I!UZ`PBeNR`zZ~0Pgud)tCJgpEQ`#9JnlW@W+2e7KK#_s974Or|=1e6Ac`CnXmKwSCM4US;RD zp)1ZgNB4KC@Zjuj(bUAd?yS5Ey)f~VN|7QRR5-+a~%1K$niE>Mn~h&yaq+8ag3S$=IeN!?e|*eyr1 z2o0W|8%uV0IgCm_;2NYpiG4S5Fn4s%LS#|Y(a7jOY>`}q-?JwMOjR$Kb4QY9tx{R# z%6VbnhqJDnSCc{kLV{MkfWI1`h-$H`1O&L7!__9El^?E3xY5?bnRJ zNXEX(xbv0>+VS(o)1+B&dQb+~jWUx>+XlIFog$b5*CnqCl@n{kz{eCDH&^ZbEXed2 zoF!0nNZ2`&qpI@yN7*u}!RhuiaQ>u@j%c0IiV}&eRj_Ue>SN^i(2&23Zg@6_HQhZ4 zU@ATp$exf`f5JHd>GyFSTjs4b3mY3Fr8+lAEhTvg*HvavM3#>~`l4_zsz)VpWNen7 z9CfH6QH(SR{Z87J)aQE(5)HM$X|V+UQg7_Mfv!A(Sb9!e*b$8E!rO#8VuFKO6cvKh_S88~&a`Hw7ib;2~s@?f!7u zWl6hrBuEHE0J0KFj)Q_O1JzFzI3C?k?+q-_!qaN?>@X2Rao{@E2Gwn$YTYa8ab3lhz_f#pd_XLNC zaKg!y%&kBc*!_^|-OepfrADr@+Ubx(6oQt{WtLmj1>rveSX+OwaA+=N;g~x@xoWd4 z){##|%e`my?xfuznyC$>Onjs}d(V5`P+akFTq{#7Nd8f1FSknA8(k_M;VasrLHcyDUAvT2Tl5VPzgBB>!QtLh?< zTApFw)??|BZOig5-P;c&nuxZrAS6&&Y?T~4<)V-m{|vGF{+RkLt_@kgSDSU~XWF?6 z#!Wn)xIG()ig~GEhi;(2U~845`|Hlbt%LDVzQJ?IFlM&Ju}lDvba3?8@h8dW%L&3H zVmVP($MA>fG~`5wa3wGh7y@+9wKNA1Dd@aEH~8C`?(O7}MTn$=R^<8AaIKGZJl}og;~k0`4lp66I-+14xgC; zdorKFy9D49W()ksn24a*e}6{uG#yrnwJO~?!%@IIi_713nj5iZ@GR^CpJ`bX5CdR; z(Rz>ZkQhBjz5g^2voaLp=v1q}oeqpK$F4@|H-tfmC|9x1?%L=!4jS2qHt8gxq7MCb zCkX_*!zbl+YyZ4CN-RXIUDGOUPrs46VLOlOS}!6HVJ7=1zX&@EOS-&kkkp_TlU^gS zm?i}~jjvY;_yY>OnsB^|9nLO(;61!ge{IvqT$PyssOTS>l4ZQZeoqqRs zkIm3IJAClOlX+sk*j2yYc{DiwKIT7Tx$OeE1iibGA<`%Z!-mhXbqF%OKcq>?j#OkN zXQGA_^1kB2!Uj-b;&DX2fMuG(?3_TBxQP8G)Ks#RbSsB^`%za8$l2guX>PI<&VkrW}%B)vI4JQ|1*GENSTUJP|7*^#^2tpI%pzk;t^(b>@{1a>heNlH`ecWg* ztdIH|*5fhU?9TGDo{d)60?@e99~g)BxFfT#{{2WwX~I1%^XaK1~()faAD5BbvmvW|&UL3ovbl zbpcXZJk|nK@DXE%2-PZAyO$jgfQjq|m^Rx%qYRe95X%5K#*X4g z&*EM*PVn`}yzlZwOCk{2$uWPTVpcf8eUqu#%HbkNhcfA-8fa)|0L`*Kg>~%kgXZqt zFho7;i?$l#vix1v6^y-Oly1?IC7QN*(zb2ewr!lWZQHi(JZamuZTp>DRekIAt?oPe z-yYxoLNn&t7&}&k(|zXrP|zVI0qCyw)5cDyY-!4BNIYn5H|}lKNZvfbD^h6eoa#vK z;ai$2NdExye4RBf^r6dvR+se-AQKQhL5WZ6fO6(6v;#Hd6hUuAw^#tF51jgXY(4E; zdLM+dRdV%NziW76;#eY{;7>ldF80^l(ZO|@6q~R0+li;_@CPw^Hq?Y*_QyhP{YOgy z3no9w9ctG_grw<}7UnJuR=9M94wr=1@6gJ2%~%@Od*D3h_wdS%n>2aryCG?BL!qJ{gB{ zoEmBP>yEe?dClz>WC@{ZMjd_!F*ihH?+;R5vb{E^|!Zju?*Fy+z8}Q|L#+d;4pb3w))ZaDrRH7ch$vS+ZGW)w(Z?gO1;MilC3WM!L%Qj{ zR~UswQ-g#N?Oa#e;e&q_GKMh12lhVXn;&utl4&`tzX__@MS}0=v{di-EI}&6a;Wx9 zCSItb<)SWuqMV-i(zNCq(ct5$@=Q&0&+GypKTJi#gRG;sxh&(<&5f^czRT>qkdqIc zy$*j5_i5DT)^OB&e&QZV)qKPp$-O1}l|mGiY5lzCyD@DK4U?tWooy5tr}TS)YV3btNBThqwr~h}%~>;>g~xlZ z#JNC7%}R1~#(tuL;auIZd|)qdcF~*f7&QdEuU%(nQ3=$3P*Xcq3XC1x6`Q_thUg?eTivE&YzGgz>ryR`-41XVUuxP1xX&D*~UTWc(0>#}rRGG4; z9iHDa6?Xcqz?Sz@55nIKp%tz7h^BUckj%XW(iNkwn}16t9_vm10Ow=~9>M(_suiva_$P7IyAn7I z4>U=OZ2>QxjI-M8>qQ@k!eLcEk!Ra&(GeBC!7c3FD)3Uc_E2`MbQqf_K3W z2-06tk0{Ks4zArK6ZqxIf|{R%7|U8({~;TmI;9t70+-`!6BknlRg~YTd5I?7C`*%U zDg7~EG(T-x+X+YUtpsk^c?lHNWFvDUUoH>?P#^oE5*MYnvQ|9>#iDqxIW4wpe#@Cv zJjHZ9DOIA>mLwlM^>0g*30I0)*h{*dLKyCmLs$wYLvJ=qLIfS(7K#tAnmtr|E`hVC zAEyMN57K2oHS|9OE_?XU^@DB_3)%#6;iV(<=@=lEvG`lZuIsdLsJC`$6S+qjU~;B3Vc3R+*@%66{uKk?a9* zlys%$Z|#N;gM4ru&xichnVh5Vd{-%|`FY_8S34)o;sh<-UVJRRUnbc6u^_=u0~4Js zU``ej^=ADc-a49dzuk;jl&V5CjAPKK7VhiU$U`5@OeO_W1fXKz$XVLEiQouWCD?7)I6rq9mkY)iDsSkgK3>)&!aZUIc!Le8bT; zRVLh~+x}gSr+T=g*H`wOeF@^&$D3^U_pjd+-RlBFv`fuUJfO^JLOW{#)bU4wQ+;}~AsZ?C!%N4|YAL#`CQx|Ps zsFw_1bLSM$6Ei$W&ZUr>cA8F3g{6aCoJ#}}GT3$<=sT$heE@m4e?+^S_dnyRyLJVp zO#=8LKzBxeS`I$;*$=@&YF4i)&ejHp+(b=pqqE;zsRg--u3+)+UF%u``8t+5;IVnj z6~E}6m-OZA1OI%Om&qw(_#BUe23G%Al3f1U9;v_y^LwmBd6W6uNd#KYh2F0Ac1nVR`Sx#l$+=b|AyJw!3&dKk z#=MbfY{hxB=W~wV7y0EP*gV`7O5obJ_FS`^yvxs7GjL9{to0VOA#ruG4wuZNhv z_c-WOCL$<^>OzXtSH5J>c@YHFf)}^%C7P-(MA|JI42bm@1L_du&Fj%9KmXY zQ%&h2%8KkLOz(!xWtI(gMGyp8ccB9m6NM2epu9W4eNq85eq|kNvn8(D-A!kd%#Rrd zmX-|rOdsnH?{Q+lfO~6#USS%cbBWee2|qZ?Yw7KkbW7Q=n6X^c!;sNo44)(21rhf1 z=y;|P7-lUAwI56#R@5K!T!GNIGEY@7h3R>)^3_MXRV+uu4#gI*utwCHd%NJBOb}2| zKxZ?Qj|7*&iC;07xi$rUyZu9Y(KLouO&k|Zp^^j8wGD@HFWUNr3><`M?ZPMln#UD z9}g{dH%GcUmTY^WI_k=oU%)y-rKN@MBYcNF5_GxrcoyD`%pS=}NOpNiJ}-QoSY3Kq zhDa;^?wJ#)uEB?tyWR^n?V&5`^N)ST>sjle6!buZc6?OJhio6(#Eq&wk)?3IFj{g7 zuVjWa8?s`Ik|DJ5qi`^uX@REx))y{JGwx<!xpt_jUrZH&1VN-DHHuWu7`zPk2FD^;oBu<5TwUH;Fk`7-|uk>5YO=PTCHisH%-8F|p zhbdNWlG2!*!%nBssK1p)#(}*}$+nDhr0ww6?nvJ(1`bIF68rsj-=9n!b;8x%b0oi; zPyOlc;wc-?+Yh$g86BIl(cTY_-1^Q)`1AZ79?@v=vzO_58Jp#|iG+~dpD3_PN5QOY zt}Z-e&z;CmwYSX~1*+$~b|fY8 zBseV&QR&?Ak*O42W;9$Rk~^dq1Gr-TDU|MwOa8;;{hMZsD*~`5P!)NCef(RJ;)U!?E>*;J z``n2!S5xU|4q>Vr5lX77=|>ZJm^skbQvjoyQ?eq>6ZGv3&p2yUFTk%NopbJdU>U8N6EV)h5b88fmHxU?;HvvDgbQ1 zsr#o_EPDV12ESCF5?RQR6V9ot5*x1$3$%S~cYQK$_NGWEM{3MC?%<-m3l81lLqPW0 z{<6BPVP$}k<8`WoD#Im+P>RU9DzmA!eOnc9B(JfHP#F;4A`1pT65TUBwAVI`h6$Wu zvb%TwWA+PKYg?@G52xY`skf#$Q#Fi=wzf?Y2^}j$3P1uYf$7xGC-dj&SzUr&zKZw? zO{F z-e^ZvvUnkLeZbfUQW2Bjn?M2p*x=k1D6_++-NLSp4Y}0U5aKFO2+UicqSFJ(fgEy> z7L$<<@52n_T5tMpK$K5;vUaYD(Plv%wi3Ek7)pu_h34!vZ{-v)&xtCIoCC0>k$V!a zxA3aM{M~A>^PJ9LWPDgYp4vXgXsG2bNu4N zVH?J4;5!&xe;w2@o`|_pr5J7&j|S04cj=<*dQk$UYr`FwvcSsL!hlFZ$#)gw z&!szijtGO07VE`t`PH+rU$6R=xZx6K`HAn!V)j-}U~U7H1fgk9P*p-k(YNJllK@fJ z(mNb!xUQn#>~b|0%Mng)d~alhXH5F*WEEdPSSiR+PdTtI;<7OF0B0L4-%Kz@;^i+6 zRYXWWl9=+>&0|TZM{y22nxIN`W62A?Vk{I}B_5ALp2q?8g&6JulqC*Z?YDn!1yQQ5 zO9j8kB<2Z+kfC}eL~SUV z1)tYjn^k#QJrYF#&}QD6Y$;H(Gi8>ZCo868K2Hn$+j+^twA>_Wn9C?7>%cRa3iuc? z5H6qelfXSS4~6?_2ACdcv-|bG+4GG8mkQp9ldUq<%x^D33a<;mc7h0KPE)>bjaRpL zzNL4?SZVDia>n2Qv6Zl{k5UCC7F34#CM+PW4`mD<)2r zGBX3^WFdRdmiqVb_9#!asd3-IjdV8xN3|#62A00N5c`%m-F!(2f$iJgS|n5sA=Gy} z8G4IWpO?^GJJdTm^s{+^D?vSz$~&eb^sOkgFLzJNYu)FC#q@kl^Ue6u~9AGPX`NR{}StTd75wfJjl3{5uKOi^VEbp z|0=FcduoPTd)~a=$3EQsKo=PrF}jz|ss}&6$oqigsf&8Bq`Yp_L40x3Oj45q0|5fC z>fj$#o~%~s>n)~7?QbsgH&_^OUa8-R(`uMmTXMkeo$*B#~DkiU86Z?vC4 zyO-V5{epiLTGbp}Sk86xSTDCg99(d8()AJ_q^khahZuTsq!MRhJ0*J2Or#@5UNli#PZ2BPU%ugyz z?x^At1N;>apm-l8H>+U1Ca_J;icL@GHe>5$od*>aF9QL2iAo|A;3DyQ&jMQ!5Pw;! zaHu}SKWS!fRD!Ii7B!sLPL0CFInJ8bJK%`6!ukU?$yJKJD2pbE)60JAH~)Dv}CYK-)(&Zbx)0ERCEPClSU z59q`znEq<(7~~nHjbd1U?}XG^T@w=Q?H5cFKmerP-q?P`!UQgjX8N+3WUYl@RW#Q( zzZ1{_+3Adrre%Q$QEW}XAef&I~V-6Szhxv!gUhS&~hYIveJ&_kW5wQG7% zW*Hia$+V_e!SIHy21OXexBCjbV$@NYkiyu`Fxm@cWPz9`X+68T_ z`db{4fsmTT)S{~pvr@7QMzglJ5bEzfn>sWJ>7>UvpgPm6Bp2kDua9`L6M%>Q5F<>k zGM%pMUA=qFC4Be~1pksOfCLexWw4DC_(6QpyY}efVMvZn2DV+8ID_)IUFk|q1-1~& zlok`?2C?obG^|C$4R>_4YrY?nQwZDZ^`I=QibZlw2;@sV^(^^c<`WY@YA^l+=q$zoY-WBDrOLFJn3_v-gO(o~Z?>DGS3-C}}3}-20;>q@vbQPQV{MDk}#1PrpDpTlm_a z##d)7RS%=KiU>irO}1{OKaM%Kn{GiP%#N>0SDpyJx*5}X-Ig@Zq7(tE>07GeZPzlr zokB&=qGVZ=HARICe)(mXOcDsaEAT14B;e;x&V9U37DFj{hPYarPYm zINr!|6RDjU%g~CIN-2(KDHQXif8J76{1qNe{n*OpfpZ$TdG(pE@+uz8v5uA#oiK=d zddDKp6pVQ|EycWkOTvsyba0M>nwuv}XT^FurT*v?*0fxBRX1j16g*IgVc>_F;HGjH z`%vs95MisGfbKUk6Otw8;{BONF}u8b{T%%689qabj|9B^I;kD}&f{_87R4*90P}#k zX7u|-S}GAFHU$RCVoF58TA2Cg6wl+Nia7dj2P!J#M&j!#2MM4$$5B?r!2NiU`Z#Vt z@LSmuQ?yiZd*!bxUQpYrBpmWY4F+Mvc)-~yFc$is`5efS@kTg*_~U1p&SjvJ7Z?C$ zHXWZW0%`IERi)b^R2;OlE;1x)iOk>|2pm^^snLG?8~#2iFT5= z0&?p15v5N7uq2V;2lM$J!+qoeR@x%l?K=hMwD3$AecxG{HlpMIfR^pXo!k+F;W{yE>`gUTsyff-64RuJp4x|V%tjNal%^r0WIC-AA9hWV4 zaao#u2VP;&4=EEii0Cxgubq{HV;lIPE?3g=$1m;eI-U#NDV6%gjfUHK<|`x+!7p04 zV0)2-7}8m5Oo|a~K%Sc0Nn~)9uvzr-YrlfRy%KT;U)=z6*wDE6asP-n` zv&~@L6{L)P?B=1aIsn4vuhbMY7sIZdEzKoSH`3f~1LfBsxuYhGksya`k`Ki8@Lp}! zP7l4VEDnMkfEp2Zox^KHb;>w#z?csvig9peVkXYk?u!?pV{kZx77w~HzoBO!EYuXz zkgSJwtWSiAqUiVDn^E1de+{aHVrEg}g&lIYle0xiI)@np^bY)9E3pW*Io9D&L`{%D zxSYojza48dMD8&WjT2o3ZgiQF?`}UjPL7_)=j+@2#)3j?ellHCaUZci0uF$M>nzq| z$pMV*m=?`&SzM;`sfA~?!>~>&# zx}<+YXyh~QZ1A<~|q>pE8 z^GGTd@3Pl%{7&6UYr!t8KeCy=iy&v8f446qQp#L@JIx)WWUDhAJn3FSH3NFD`Kgjo zQs&_e2eY|>aPL?|*_1ZRBFpg%APy_ka^3od%E+e2`E6xh0^CNZmBAjQbKb2)jfwMS z-i$OD9LK9^O&SJPvIO{G7X516F1vZb0?UMK9IKdvPZ14~#k<`H42;@P7bA3|T|3nz zr+OM=ls1 zWmk(@l&gx;C&_3XuBa%t(SrI5AzgiI!xZ8Xqq=~13P20jGkW=R}i@! zuz3sP_JkynYH;_GYc{=kx&A(Te$fN(lYeTojn{tK>M{Zv2*-BR<$IpK1Qm?NSUQm` z&zm*sZa#XpDQ~xK;A=o}W=yhocWy@0z)M2M1uxfxgn<`jCLyBgrwVYx))-C(KOeLc zRnIi7A2JA3(R5lR{nA$`NE)U%w8a*kDgVumYmAlZ5**0tVLW;k^-Re|R5t!XjwN(l z?!+^@As%k@(|G1ue)_9jYB}oXil~;jsi0nV-;DUy3u2h{w^4OZylz1@E*5GP;^M?c zgZ#K&0GbF@z7Z8$IGp?2I>B9~XpjS$2i3u5 zk^SGVty8~|)f24lyfRU@Cz+J#zNl3yfAgCp;Z4o#l24zHenEQr$9llT?T?C~R8w1h z*R1D@4bs@st?`lkjzw!k06k}2lzhSZjV0$KG6@vF%-#%}Sdh<*&Y8~975>2zL0TSd zxc#P6+xLe&`{e`1OV^*o;}gtPtlMPI(~AF=Uni9Uu;No)DRd*t08|>z**qA5Iy*c( zFn-Um@p_&)MrFzT1k@gcjoQ&q=0lo-mA1vf#*thKThnD8dlw36P)CMnnoKCw7U?XO z68F=P*ig6T@a4WrCh9o-^Ru+hnr!eKCp0&7b|fENNc~H`lh{HqQU&@9pn{(CPhG#O&b&2f?kj0vZp-vfaEk4zdx_{br}`T-Uw3Y`J5;zT6_ZU@-dFjVvrd zqgSX6O~Le@-pD|~n+$fV=)FWQ6ge)n2|ffVh9;KAh?R00!#u!o$1BL$xMYX2G4upf z14{@Jh`hzAX32GH=h;m{_Sa9@4OQG!Yc<K@!Lv5(I7z9%3B|?;YP&8(z`7?7 z2tc~KUw&&gjqu`@%*An&JuTFmT!xf#YPfNZ9Yr_+@~ZhPCVl(zr5nTS4_PQF6AW_s zla7xMFG8jxox~rT+D6U*5N^<)-#Ue^E=g@}$s2-5zZ{7PE?^@{T)7@JOy~13MdbU; z?mKFr;&X1SBuI^_=y5>x}ey&t|_I{*O7=PRbx7-3)iWNuN5)O#)!my`C__~#3nD`E4HdM`DODVf!%IWnOn z#n&Wj<2S~_DJIxd+){=#X}ZPe?W3CYI{M0(La&JAJfH`J)Apolh{o(c6%FSNE&sN+ z>ejko#p|KVm6nSp;i#(L#A4&s*!1TxP!2D#f(azRt2iw_<;i&je9|9`X!CAh@n$&e zN&3xyYXS*LVKBxW+F4&;XDW_S)OaD2d`9mOct>L@lM0d;3C3X_lY#^8oUbOFOdFL$vKPNYuJ$9{T5f+Wj6|DY&yJzSq|MMuOu ztT38E|2yu2zU!o7EK0SmL1iL6@`& zA|L9`9^fnKwWW(+9~{NxO8-i*j{X-~8z^`j{8{lCiR~r_Rg*k?D&QV=Ed=ljf{lTw zI$?lJQQn8@74h$^j|8#6V%T3)Ygz%#9vPQ+JGo*5ug|8{C3u-5Zw{O@DSB7)Uhs<# zUHc1C8@ebq%&=8<-*M1gykCN*+M(!SyyiN4Oe8PYf)A7Aphrx0zNJlpV~y7Y%p+{!SbJDL zQqLwXEGU1E@JpCF*qmx(hv@|^lRv1$KvL3i7buXQ?bEZV6O4Ak?pRo+k-b%U)E?|=Hs1&-Hc)0e)|@a6L^=UY8ZXYm z>rqWB$4ec@A5TPQ&NKf<+&>zTYP}l+S9=jJWaL(?y~mYz-=v!Het5W_Iq&&%{*}H8 zyu75n=%K+B97jDZLWtnJLvSt19gWc^n$v3R_UJ;HCG^u6!jToJS4@CC$8#0FfJK>D z5cy7KgX_#=SX&6(8h$7YE~ff@UOq;4*zDp^^9CszT}m#yj5xrpUvOuTO9-&R zW>A>6w^tq_ThW$j8D<`-$PfiUxxul_2 z7mpMjd8aGhqrdMnSoq${eqZjt%mtx;b*R+sO9*;p3Y+0)q?xt$&yd67-9DNqK04<& z*0~*268;v|yj#xL(+>DGWlOu-FfdGx)ux&tP%_~RPO2v4n);FHBwxA*wZCBz3Tco< zzUKTfVF99fmN3z<$MOY}dYmVcO~?j$;w;iGv?22{Nlx2=KfO~yeRqwmPmGt;6#|P8 zr=_bRzk!)fL~eiE4Wz)TD!z-aN$PV|%D?-?x!jQy=B++X?8OW|rHZ5w*HIvgo?ht7 zMO+dEf}sH<;<9%>-5PBc`wE#)lx+C7jtgC7Ina8$YifbTE)2PzQ9A-DOr~y6{+G9q zvk2zt%L`BuUbQDlu6~4G@UbT(4i%~VHg4x`Kc8v_x=Fbu`pcQ)$dr}z#Gfh%##?PB zc*B9%TOci4eTne8v%%17cz3rZI*qEl$iwk=`kMFCRv!a3uIgqv+E_N2>roSWVm3j+ z8coCShllVmSDsF|Lfa zk1NqJQd)CF;{E}?4Qe}+KvD3;wm34mFDX@soJVVw&->Z*O3f+IIaRhOlFyk>%nqB)u5w4Xk3X${ ze(RunW8f|Zi4b!{jRMh$9vNlj@NI{`Qej=&Fjh&mibeeV$>v*7LccC+%ohDIY z14n`{7nIz$3&|?mzO?}J#FfruP$S{h*&uxYZ32od?%}V zhcMeA>Vwo@Rf&lzHd=jk3}t+DQ@V0UN?qSg4vv@|f0(q}@;c|fo5|yt^&k62iE+${ zd%NPmtaUjvjP(B8E*TbT4XIV@Dkdfi3cWo;YYGg6x)fhpkMySw`$q~yiqd&91Ts6U zt=R-sF@c=j3aSrcE~@GUJ96f2Bx@wC-jzSt|6HSuJV&7>hz#CLgsc-E+1aUxbQTIu ze#ae(6S3zPdxDZ~)4zcoYAmG}yfrh^rehM$%alsIuz0KY9N`+Pw-*8{iKKHU=%SyG z)PD>=_dtTnCSVu6pk12((geB3e7WNz?-z_zTdc`vgb~#v&<_iL&M$Y??n+omFgC1k zMm0$?=#zUU<%e7NY?-m&w|!W5of>XQ2jExx7}fkHF{0J8_#)KAEBZ=0uLk(QddL~0 z8bbEu&_Q*UX6IEepiUH3$|6WsPA&3tz>RW)tt5Y>%gL4PAQWm<^ga_zNJ?yb{+6x#!PI1Qsw?$9(Sl9ve zJ$8&@cHx}8b#HfFPr%`BSa84%TPez5$@klwfG# zC3^ISJg8@XimYQ;f)=Eh^>ox4fp737GSv>=B!(CFmq`Kow!0vYj#3mgPt3!fBCba> z0I{xox(5`MWosD=^du6&y@z$1y$Wyk^k%v}F(VS`Z4MlRvp2Buxc{na3PUe#IBRBy zk5f>ycBLT;deGTi+nxFmJdMXQIM$9*6{c8B?1aG&7ftD$@|`%y)4j5c=qVl3_|gRt;7FX04I{)I>_1#^iyVhvC#K2 zH<5+D{D+foQa#qN7}ALUa0~?D?d=P^MTFMjm4y_aonGXYUL`|gRHTrsgUo?8(hJzE z-zTDomQWbO_L(E8zmt}hB*9@3=D2Y1AO;O&^SzRtY;}Ukw$reeP7b?aD@SfOW#chF zzq?M!W!v>nF;WIfGduIZ9V5* zbGgkyP0G7I=?^Ac%HPa`AOcGqlZM~$+3qDVFe`)R}~Qas?;6EU)|axo^x%vQWx&1U~--xn9(ZK z5VY#lIUU`?dxA(^`R?e`_%MjmUhMQIk?qrcW#VqN1n|s788-GXVLjW1&kCzB-2z?{?abPN{<3ra!+bPodj+o|?orLB2ukcEWh3E2Wz5ceXp{lw+vn z^)e1!x4W{GA!Ryg1P{grk_WThrV(=o5G{p6Yg#%9!%l*NH{1DkhasubnnjbJ=*a~@ zy+28)WC$OLM zmgkKCSm0=-1Nicwyl#%Td#CKl`MP}-wgXBUhFEr(1$$QI7EJURV-G-!F^b3KALZq& zlyP~6LUgp>x}IS*^UZwu>X>)3a~8i_uxGo$+^Zm43V^e*IES7|L?e!(6g;V4pn&Aq zX)kaL^zv?vf4k#L)THxcFDY283_V!@Cuw%RT%m}L&qbbYgd@vnKMoNjwO(_S<09kL zboE%n0`~wzY6c5KTB?!^(C%$GF8KfbW0DT*D_MVr-1he)P zcsf;qGlm&0)(zK9j(3&REg9WR$?vIqwAm^%8|>c()8aE%enDPHBzoF)ne6VG^Npc9 zgDKM8+N{X{-lM`%eTu2{F4!lG*#p|kCH;tq@orgt_l&UL*g znjm)`A#*tgK(rNTjYS^8^vo{)uw@{;?Y3MZUhk6j%feLresG398rP9~{i{m@v*<7( z6zR?Q<{MxEG=uRJcd5k)4>$rv7%y&r(Y~}{ZdfdqBZyVho(#fG#1US-DFj1o4srz7 zChRH}RMtf_VR0o{kxKN+hwl_Bi*2h@p!=&NGDjUwL1*)n4&6vE4R0P@%GJ|%=h)G1 zA_`}4zsL=rjN;N7Hy(fI{XyE5wrf7}qOUQ;qnU>W#l!s~ehjKlta<&?41H=qL1uhW zlX^aB0&N&omHxBG#Td9^)2hxJ6T92frck-9eN4|4ufhwQ^!<xY|H}&B69}R1jAW-faemu#zbnO)*kix5iIKLhF5GmQPYf}D6CQuL3EM2j$!83 za^`7l(gTXReKa11b-CCO(@@(5^@@q|TfBCHO3W&x2F|R#5Lw|7wpv5kGFFvDf`6uc zQUp!uhUheFde4Gss}R`*zv7hq5ytuG7Lcva-+TD)?E@$a=iUpIgT?SaHKo8{z8I`U zU3V}3y%@P7ewgzT%%`&QD}5XNt-;!#9=hk{*=Egb+F`}}y~ zZ3?_w+{Bb~n=MET=I^^&=H&CmW%2^dJKL6_Gx{6Wf^XoTzueXaQ{slnXzJJr8Z|P0$!A?xYLf8UTav# zSJmu|abh{!`#G3)hVQzw5{_LMNQXE(W4v31CB3@QW?>%BN;=>P+tVP)5yjnnr;<-9 zW;Pef>bvDur8ucpOoS4cPC+avs$~txvB%K)Drp(3%9o39qasif^~ha`=3Eu4W_2M7 z$r(PJ1z`;B2WIFi4No&zm_)!H8|4%{zeVPJBbsX8U1qh33#)AfZt?<;lLPh4L_)+j zWf9rD=t!33E@$t8A>V}T#;j2&G;mpqhx_1dFMXjT0!$2NK~GY=8ogDLOGf+OV-A`U zqxB7k^7oN&JVrn=EI=gHfR=82Tf7BxoudU9-%QNma)tv(1~&r%+_;~oAt-cJImJFi z7!_?e1`AMfaf41fV9Dq7kbl=P=sIV!7r>V#^mlbYYc)&>vR(V zXZO9Q0wX;}6qe#4#)o7%mcDN(CKz#-G%j#8BzAi~1Ahu$ZIA`a@dUsZJW-- zSmR0xw>Md&&sAl8btjKV;z@rHZ*0vl;NbA;b$&uK&191sT+Dg2U(3Xiw)oAW=Nzpq zSE5@J0K2Vhjn`PxR3BqZ$AVkgS<)q$0%YCx9Q7^Dc3rGY_qnK8q6TI#q_;|WF39XP>`<{XB$`GFpD8+<1O|W zQ8FIiHK}idk9G0ycq!4;b58DUWr(O;Vx0RN>_;m$A}up1N1E|pPym$K?}_`XJ3FM3 zJ=~Kd(@pYvcrbzI5iN!Zi*@`Eg&4`Y(C53@hGd$YxZLVk3~e2|bah81DyXYT{I1@G zQu+<@+IBQ1BS=WTIYV(6Moeut&QEFB$PqhAh7|8+iwHT^H^sZA%b*XH&$zQDZoIULR5~GZ45nyr*vOqFDDC&j1-1+RbmEa(C9BPLGH{lLCIckSoN22g`FKaZFJ~c@s zc|JtRM_A2KdAw+(WNu7Up^&9Qtah`z?IkzuWxl>Y!-r)@_k=K;<@4BowoM^Erh0oD z(f`6mV?5s@>o-Y()QBrEzq`y-A7@^F*pOhq_Ir+%bVi_9Y235amlni;COTz1sanL##q8zYhgCK>B^@=i&JJFUfVZ*Ej`HqW%grpL_m_#rZq0Z_WJfd z!Z=H4SL5DK{%=w2IbV$m8If)N7KEQcVHRl6N|a{)4|>+M%{Y5_q3#`A4@<}$;&yJt z2DQ~<86C;`TIs4X@U-8tr6Mj4r4cph5m(^+q!irtOy6liZvYLX+gV>8V4t-76IdvHia=KX{snDRI9SVK1H8V z;VMTEJk2$Al(3@x%n|p=UeL#f#wS`(Y2SL=gQqO?4@2!DYSiG0>H-DZiJ4l0!u?Gy zgw^&mf=5=%Bncq0*DsZmccm{4%7jEF`^wH`{_@FYI2zROlPh)JpH-d?lhTfW(7n6s z$8diw)O!YH_n?Mqbj>`@W;%Qt7OMQVSe@2pVZS*SakNbyNyVrg>tEJ7eu@r9t= z-FE>YB?CY8?9XJ3JO|OUEd}D=6L>Y8NwDHU8CYT;)(}m&iy0L~3*OAXF4UsLK{v_h z-ig2QrjgVE_WJk3YD=^|O!RYJBjMI{MVr{lt%t#fP1rw_nox}1+B-q&ociMf9#^X{ zMAT*3|NPpjzj>gmQ}U(bM^Vy)cuPm0cgu=`9}g>*Zw8!~GCC9Fz=SsYblY=Wl6Vwl zaymCoJU7Z{?c`@1;G)LfDD6|Go`k^)L)0|>hOO%~d>!v%=*~s%Z}5ToX}K7W{f!kA z#0$$ucd9o1oJWvQ%=K9s@cv$)29HX2--Em$&xAG|(ing0`q{!hPk8VYkWcI2kV5d@A#ES>23@-QWt zuyZ;)SkqWrI67IguydL_IoUgM($U#iI5^lj&>9#x*qS)enixCN8aUJ0I~&sdbB;aT z->LO<|2@l|j+x_MgdJ%eEdE3E|1QJI_D>m8Cwp2GD;gsc2U_$0wDg}YIMV&&%s;Kt z+Bum0t7TTE|4oaziNQa7wlMjxApRdTu+aZg!+!;C@9g1WZ9;2g{%@TBA3{csM%K=T zw9c*uP9_f4c6PKz2LE?)=KleSYCWMTZL=s(c6b9AKD>To z79Q}2A80hk2Ws)h9~dQnef@tf!M{fQ40GQ8B)69*^)9&F|XMnekzEhfDXsOo-X z)z^Jhy%#Hn>64k>8^{C8e2W$qcAE|Bsm7t?AWV}Fuo z!d~)6?WITNnZk4pTkOQyV&v3zmOtZm+rH47utqf-)S)~!NmrN4g@xrppJgR{hBR^! zM<7QG=;7)xno13-MfRgI)?1+;vl=LfN#j*hgr*qR>SjMM)4b}<%yH&QjrUcWAi}Ok zTHH|K^NS^~|9tVFre_xy!hQb8O8*~6HtVyCvrM;NxKfi@__TO6Wx2IL+-%ljl*~|3 zKj!0FU8X@nF%v?*L-?i*i21%fyv3y9av?J0XnQUf<()P^U$nfdiQC2@)w_B0Egml{ zX(u(}eDH|;>ruxpbvE46ZWCclJJ@AW)5;JoSar4#?iLS=E%FOENKoy+-U4ras(cAk z@Mqe`iaReaof-ujiCCN9Pj~zE>k5Z97dfZhW;9H4l>cy$!55(D&~5=d z`+Op>t58|5P}!ig7|!=?IfC&k5VMj91*i7@rrG`cE^Op- zK!S8d1~i=CFN$vyhGEsO&6>zO7?(w{1Zmc?m9QzK7uh&}QKMrI@_h@lZ{AQ@bczaJ ziA?vys;Xfq6MKkfeSq*|=5z|-RmJS+f%f*T`%*2n(II~VY@ocw^_oU(C1!EQ9Jm=~ zC;2iD>RijSkC|8D3KW*NvPctc3y)Z?PJ4GB+I^aHEFYiC%&|T^8Oy%rF*ZJNTAIsx0yFH$Z8^qlyT| zF3>=yQ0J53GWtijMbAe$nO-tUCBP`-u*{w`a5serlPctDO-#-&zz&nB7IvN(D%jlp zqGGm<4Ey}pSPw?|{f~G+u7{B=>z9(w3C4Vfx0s;gu@*1@12WCU?n;_v-O9;D$?MWwk_nUq)ZtM6V{Rx0W9O!WPj0=Y8Hgk^?x(B7Ao+Bz zPpr{|*5b9C@OP|hepv|qMSuIY_|TYJY~fL#3o;7?$T|us#h4P*wP-7lyy3<@U7eOL zGJP(;iT)-AEadXi^*{`Dtz4N&pq|o5j;u=5?R0n1UoKjZ1$3FumQEm_X|8F-MKH1W5(8V$5ax^s_6}^ zw;J0affZMaCURBI9y#Yqd@iW{(Rvt&bwOd(3cL*KLe)>zd+|;hg$I)y1sJQ1H>>-? zOaPzP8u0GtER@ToH@rH*^TnkG`OC`Ml|3%RJ2%J@!p~T1PwLDW(#N#ean~Q(h`}f5 zV3?@?Prneo(AXJ~;!nR5FBGL=`(4XGPRLn9jjk#g6cV|_&wTM`KU4M(2YJ#0R$!~G z0=a&@&`j=zFV~+kG$yo_<04!lMG1AtWL>%z{0z~O1a#CxF!jsgs`Td~FP18we97PVF=j~2m+`Y5qkr-*IsD^u074!1C2VzXf%q?aB9^CY1#pwQLTak3^1rz|+V zvYoM#Hj^0pj0uB1N$Cn3&Q{kJbOJg1QCO^KTyd|TArqW)JQ&Hzs&)+Hu!h*8C|Lj@ zJFOIbu%e-fgNfk;lGl^vWRQO^1hJrM98h<9jk}OTAkn0Kw^@K;GlnP->W#}x7Z8c? z$)sZ+6jgXw&bEQs(Q5YI_YBtD6vO;;lkzjac)RaJ^BoR>feQ#X@%QrN)cINGPQO3g$kADqc=|Ghq z6jLG7$Ky&V!Ku61w8B#XqDH2{A&F%3<9Arc=1zu!A_KIgVerX8NfIC2xKYiq=B(5QHXoUxn;p?Bspn5p)`vdGhgWy7lZ4jsveTV}IP z)h$XH`~yfxb#8L_%4*f}`T+}DWQJ;xVp`6aw;<1%^t!%2t8-H$7VHc1uv1E2`s_Lq zDJw~mj<#<^!$w7zYq^J57Qs%Epi`4j1Fr{$GUfzVBm-LX{g}I%T>f&^CdCd1J23Ks z2}>Fv<~C4gWsq6}}-S}>Y-dv_`Hg^FQ9n|2*0RQzfB8B1R5gX}9H}@q{ zX8FpGs|3$K?o!US*jND+*!E`o_-8zs=Q+T?h~CYBw)(G{FWi?in<(73ZVKf`{D$A} z19*P?%wZh z7zsgcB!L+cLkyA_l}W1fTyh2N59ssh$1BaO*R4u^&=Se9)7mZKi+!$N`CbJKV&7}w zd>I=MiK|>TD-;3EfaFC;BJmU#?FU=eDBzP>0s_1idS)Mpx;^6rlrKUDsui;yz^!?) z1b^f;3Y|j;D(SfdeIvPZ^zfbhZ$_S&1b0`#D30xSRg;+|v94>NW#|!Uj)Lq~2RjB2 zr-Ru{sHBIdC$#~t5rv%@ll6DP`Gl)^oPyCez0$xp21Xy_#8hu%JR?E+6+UX%SvEtZ zIYv}n-{UHw+z%2dhI2}5(9o{B({i~*Nku7czegbpVLtnofStImQG!1xVxu{t&*ToZ z(`LOecnp9CM@w;`xCZk82iN?bkGB--X6o>)#H0AE@JbB*Dp&?K!jIwg@*?v?rPQM% zRs3VAW>Dy%f^VjY<55y}=y7BS9vC;7m;Ah2m(Q<=FJ*~Gim<|V+C z&JzCd{C&PIat6^&g5aIqr@Yxi!+>2}FgrBSqW@2{C-*o87K_?-Gp7GyT>k>IVS!`p749PxG%gPCMLPI~A2A#t@ZD^~0gaY?0Wpe^9Llfrc5 z$eQZWdz~cnS4s;vt8#E*+*N>cvXgo_srLK9#nIQG`94sh2iL~>rZoeqFg!*$bki8K zq#hQfh8}dup`}TP4C*)_*hKTwS%I5Av{jNTrb)OrddHSUPn?uf`mt5aqQz zI=(PzdDIuWD57?HpeQ=fvK zBSIKPr|df2VBkv@Da1=>cN6r#vh7Vl0Pxg*;Z*ZgeP*n!riS z%#C-3E#}#k?^noJ>=i>#STI6!M`mz}g@ZT=luRr;uU6`ae@4`tuN$%cgQC;uEq^>@J+6f&d-^+82)KVb1hA{#3C0wVR>k(Wf z^@<=F$qc%d%NrWDk2$>k5i9y;FyKYB+iR}-uAJb1ceSt`S?eTJYWTU2xgGvA#f&i$ zTHUec4QtJU-b)AMy7hY8Aqv}y7-C;!p=$K;DR%xxq5wAhx*A6Q^~A`xmIemGKy0lp zZ01H@_#j3*@%JCb8Lmibw3Hd@L;MvHg;j2zjCy2{S>KKWE_j;2u!eH$CQF47#5YR8z7zSRzzC36{m5k84bW#24XvNjaNVA9;&_Uvl&^*H= z`5q6~pf!nhLneF=hIV}9CC|pt1NoOjlk`wRHNXIL8_#n*;#bUeX6+eloZ82hx2cmy4got;TokwSmrnztI zNq7!w=;y|g={}iGncZV=G9MOFoL2YOf}RH7hW$FCv?BVW34xBi>t0-qGdJMG1HCu7 z-W!G}@z*f!uSv?8-KeJ4t^$e%Pz@pT?v5p@t0XKx String { - str.iter().map(|c| format!("{:x?}", c)).collect::() +pub fn hex(str: &[u8]) -> String { + str.iter().map(|c| format!("{:02x}", c)).collect::() } diff --git a/src/main.rs b/src/main.rs index 2ae6113..1cc27be 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +use peer::handshake; use rand::{distributions::Alphanumeric, thread_rng, Rng}; use std::{fs, path::PathBuf}; @@ -6,12 +7,13 @@ use types::ByteString; use crate::{ metainfo::Metainfo, - tracker::{tracker_request, TrackerRequest}, + tracker::{tracker_request, TrackerRequest, TrackerResponse}, }; mod bencode; mod hex; mod metainfo; +mod peer; mod sha1; mod tracker; mod types; @@ -23,12 +25,12 @@ fn main() { (Some(metadata), left) if left.is_empty() => metadata, _ => panic!("metadata file parsing error"), }; - println!("{metainfo_dict:#?}"); + println!("{metainfo_dict:?}"); let metainfo = match Metainfo::try_from(metainfo_dict.clone()) { Ok(info) => info, Err(e) => panic!("metadata file structure error: {e}"), }; - println!("{metainfo:#?}"); + println!("{metainfo:?}"); let info_dict_str = match metainfo_dict { bencode::BencodeValue::Dict(d) => d.get("info").unwrap().encode(), _ => unreachable!(), @@ -38,10 +40,26 @@ fn main() { println!("peer id {}", String::from_utf8_lossy(peer_id.as_slice())); let tracker_response = tracker_request( metainfo.announce, - TrackerRequest::new(info_hash, peer_id, tracker::TrackerEvent::Started, None), + TrackerRequest::new( + info_hash.clone(), + peer_id.clone(), + tracker::TrackerEvent::Started, + None, + ), ) .expect("request failed"); println!("tracker response: {tracker_response:?}"); + if let TrackerResponse::Success(resp) = tracker_response { + for p in resp.peers { + match handshake(&p, &info_hash, &peer_id) { + Ok(_) => { + println!("successfull handshake with peer {:?}", p); + break; + } + Err(e) => eprintln!("{}", e), + } + } + } } /// Generate random 20 byte string, starting with -<2 byte client name><4 byte client version>- diff --git a/src/peer.rs b/src/peer.rs new file mode 100644 index 0000000..a01d674 --- /dev/null +++ b/src/peer.rs @@ -0,0 +1,64 @@ +use std::io::{self, Read, Write}; +use std::net::{IpAddr, SocketAddr, TcpStream}; +use std::str::FromStr; +use std::thread::sleep; +use std::time::Duration; + +use crate::hex::hex; +use crate::tracker::TrackerPeer; +use crate::types::ByteString; + +pub fn handshake( + peer: &TrackerPeer, + info_hash: &ByteString, + peer_id: &ByteString, +) -> io::Result<()> { + println!("connecting to peer {peer:?}"); + let timeout = Duration::new(2, 0); + let mut stream = TcpStream::connect_timeout( + &SocketAddr::new(IpAddr::from_str(&peer.ip).unwrap(), peer.port as u16), + timeout, + )?; + stream.set_read_timeout(Some(timeout))?; + stream.set_write_timeout(Some(timeout))?; + let handshake = handshake_packet(info_hash, peer_id); + println!("writing handshake {}", hex(&handshake.to_vec())); + match stream.write_all(&handshake) { + Err(e) => { + eprintln!("write error: {}", e); + return Err(e); + } + _ => println!("write ok"), + }; + stream.flush()?; + let mut reader = stream; + let mut read_packet = vec![]; + println!("reading response"); + let mut retry = 0; + loop { + if retry > 3 { + return Err(io::Error::new(io::ErrorKind::Other, "read timeout")); + }; + match reader.read_to_end(&mut read_packet) { + Err(e) => { + eprintln!("read error: {}", e); + return Err(e); + } + Ok(n) if n > 0 => { + println!("peer response: {}", hex(&read_packet.to_vec())); + } + _ => { + println!("no data"); + retry += 1; + sleep(Duration::new(1, 0)); + } + }; + } +} + +pub fn handshake_packet(info_hash: &ByteString, peer_id: &ByteString) -> Vec { + let pstr = "BitTorrent protocol"; + let pstrlen = &[pstr.len() as u8]; + let reserved = &[0u8; 8]; + [pstrlen, pstr.as_bytes(), reserved, &info_hash, &peer_id].concat() +} diff --git a/src/tracker.rs b/src/tracker.rs index d11e81b..585d23b 100644 --- a/src/tracker.rs +++ b/src/tracker.rs @@ -168,19 +168,19 @@ impl TryFrom for TrackerResponse { #[allow(dead_code)] #[derive(Debug)] pub struct TrackerResponseSuccess { - peers: Vec, - interval: i64, - warning_message: Option, - min_interval: Option, - tracker_id: Option, - complete: Option, - incomplete: Option, + pub peers: Vec, + pub interval: i64, + pub warning_message: Option, + pub min_interval: Option, + pub tracker_id: Option, + pub complete: Option, + pub incomplete: Option, } pub struct TrackerPeer { - peer_id: ByteString, - ip: String, - port: i64, + pub peer_id: ByteString, + pub ip: String, + pub port: i64, } impl fmt::Debug for TrackerPeer { @@ -217,9 +217,10 @@ pub fn tracker_request( let resp = Client::new() .get(format!("{announce}{query}")) .send() - .map_err(|e| e.to_string())? + .map_err(|e| format!("request error: {}", e))? .bytes() - .map_err(|e| e.to_string())?; + .map_err(|e| format!("request body error: {}", e))?; + println!("{}", String::from_utf8_lossy(&resp)); let resp_dict = parse_bencoded(resp.to_vec()) .0 .ok_or("Malformed response")?;