From 9c45eafa8600ea714e80265e8b645beaad70609f Mon Sep 17 00:00:00 2001 From: Nardi Ivan Date: Sat, 9 Sep 2023 18:28:01 +0200 Subject: [PATCH] Fix some errors found by fuzzers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix compilation on Windows. "dirent.h" file has been taken from https://github.com/tronkko/dirent/ Fix Python bindings Fix some warnings with x86_64-w64-mingw32-gcc: ``` protocols/dns.c: In function ‘ndpi_search_dns’: protocols/dns.c:775:41: error: cast from pointer to integer of different size [-Werror=pointer-to-int-cast] 775 | unsigned long first_element_len = (unsigned long)dot - (unsigned long)_hostname; | ^ protocols/dns.c:775:62: error: cast from pointer to integer of different size [-Werror=pointer-to-int-cast] 775 | unsigned long first_element_len = (unsigned long)dot - (unsigned long)_hostname; | ``` ``` In file included from ndpi_bitmap64.c:31: third_party/include/binaryfusefilter.h: In function ‘binary_fuse8_hash’: third_party/include/binaryfusefilter.h:160:32: error: left shift count >= width of type [-Werror=shift-count-overflow] 160 | uint64_t hh = hash & ((1UL << 36) - 1); ``` ``` In function ‘ndpi_match_custom_category’, inlined from ‘ndpi_fill_protocol_category.part.0’ at ndpi_main.c:7056:16: ndpi_main.c:3419:3: error: ‘strncpy’ specified bound depends on the length of the source argument [-Werror=stringop-overflow=] 3419 | strncpy(buf, name, name_len); ``` --- Makefile.am | 2 +- src/include/ndpi_typedefs.h | 26 +- src/lib/ndpi_bitmap64.c | 15 + src/lib/ndpi_domain_classify.c | 7 + src/lib/ndpi_main.c | 16 +- src/lib/protocols/dns.c | 2 +- .../third_party/include/binaryfusefilter.h | 6 +- tests/cfgs/default/pcap/opera-vpn.pcapng | Bin 1507204 -> 1450106 bytes .../default/result/dns2tcp_tunnel.pcap.out | 30 + windows/nDPI.vcxproj | 6 +- windows/nDPI.vcxproj.filters | 5 + windows/src/dirent.h | 1239 +++++++++++++++++ 12 files changed, 1333 insertions(+), 21 deletions(-) create mode 100644 tests/cfgs/default/result/dns2tcp_tunnel.pcap.out create mode 100644 windows/src/dirent.h diff --git a/Makefile.am b/Makefile.am index 9745ec7af34..8efab1d074a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -20,7 +20,7 @@ EXTRA_DIST = README.md README.fuzzer.md CHANGELOG.md CONTRIBUTING.md \ python/requirements.txt python/setup.py python/tests.py \ lists/107_gambling.list \ lists/107_gambling_custom.list \ - lists/README.md + lists/README.md \ sonar-project.properties .github .ci-ignore doc: diff --git a/src/include/ndpi_typedefs.h b/src/include/ndpi_typedefs.h index 47e2b989767..d440a9e7483 100644 --- a/src/include/ndpi_typedefs.h +++ b/src/include/ndpi_typedefs.h @@ -625,6 +625,19 @@ struct ndpi_flow_input_info { unsigned char seen_flow_beginning; }; +/* Save memory limiting the key to 56 bit */ +//#define SAVE_BINARY_BITMAP_MEMORY + +PACK_ON +struct ndpi_binary_bitmap_entry { +#ifdef SAVE_BINARY_BITMAP_MEMORY + u_int64_t value:56, category:8; +#else + u_int64_t value; + u_int8_t category; +#endif +} PACK_OFF; + /* ******************* ********************* ****************** */ /* ************************************************************ */ @@ -1180,19 +1193,6 @@ typedef void ndpi_bitmap; typedef void ndpi_bitmap64; typedef void ndpi_bitmap_iterator; typedef void ndpi_filter; - -/* Save memory limiting the key to 56 bit */ -//#define SAVE_BINARY_BITMAP_MEMORY - -PACK_ON -struct ndpi_binary_bitmap_entry { -#ifdef SAVE_BINARY_BITMAP_MEMORY - u_int64_t value:56, category:8; -#else - u_int64_t value; - u_int8_t category; -#endif -} PACK_OFF; typedef struct { u_int32_t num_allocated_entries, num_used_entries; diff --git a/src/lib/ndpi_bitmap64.c b/src/lib/ndpi_bitmap64.c index 1c8368b294f..f254c148362 100644 --- a/src/lib/ndpi_bitmap64.c +++ b/src/lib/ndpi_bitmap64.c @@ -76,6 +76,9 @@ bool ndpi_bitmap64_compress(ndpi_bitmap64 *_b) { ndpi_bitmap64_t *b = (ndpi_bitmap64_t*)_b; u_int32_t i; + if(!b) + return(false); + if(b->num_used_entries > 0) { if(b->num_used_entries > 1) qsort(b->entries, b->num_used_entries, @@ -122,6 +125,9 @@ bool ndpi_bitmap64_compress(ndpi_bitmap64 *_b) { bool ndpi_bitmap64_set(ndpi_bitmap64 *_b, u_int64_t value) { ndpi_bitmap64_t *b = (ndpi_bitmap64_t*)_b; + if(!b) + return(false); + if(b->is_compressed) { /* We need to discard the filter and start over as this @@ -155,6 +161,9 @@ bool ndpi_bitmap64_set(ndpi_bitmap64 *_b, u_int64_t value) { bool ndpi_bitmap64_isset(ndpi_bitmap64 *_b, u_int64_t value) { ndpi_bitmap64_t *b = (ndpi_bitmap64_t*)_b; + if(!b) + return(false); + if(!b->is_compressed) ndpi_bitmap64_compress(b); return(binary_fuse16_contain(value, &b->bitmap)); @@ -165,6 +174,9 @@ bool ndpi_bitmap64_isset(ndpi_bitmap64 *_b, u_int64_t value) { void ndpi_bitmap64_free(ndpi_bitmap64 *_b) { ndpi_bitmap64_t *b = (ndpi_bitmap64_t*)_b; + if(!b) + return; + if(b->entries) ndpi_free(b->entries); if(b->is_compressed) @@ -178,5 +190,8 @@ void ndpi_bitmap64_free(ndpi_bitmap64 *_b) { u_int32_t ndpi_bitmap64_size(ndpi_bitmap64 *_b) { ndpi_bitmap64_t *b = (ndpi_bitmap64_t*)_b; + if(!b) + return(0); + return(sizeof(ndpi_bitmap64) + binary_fuse16_size_in_bytes(&b->bitmap)); } diff --git a/src/lib/ndpi_domain_classify.c b/src/lib/ndpi_domain_classify.c index 0c997992296..fb4affb7561 100644 --- a/src/lib/ndpi_domain_classify.c +++ b/src/lib/ndpi_domain_classify.c @@ -43,6 +43,9 @@ ndpi_domain_classify* ndpi_domain_classify_alloc() { void ndpi_domain_classify_free(ndpi_domain_classify *s) { u_int32_t i; + if(!s) + return; + for(i=0; iclasses[i].domains != NULL) { ndpi_bitmap64_free(s->classes[i].domains); @@ -86,6 +89,8 @@ bool ndpi_domain_classify_add(ndpi_domain_classify *s, } else if(s->classes[i].class_id == 0) { s->classes[i].class_id = class_id; s->classes[i].domains = ndpi_bitmap64_alloc(); + if(!s->classes[i].domains) + s->classes[i].class_id = 0; break; } } @@ -113,6 +118,8 @@ u_int32_t ndpi_domain_classify_add_domains(ndpi_domain_classify *s, } else if(s->classes[i].class_id == 0) { s->classes[i].class_id = class_id; s->classes[i].domains = ndpi_bitmap64_alloc(); + if(!s->classes[i].domains) + s->classes[i].class_id = 0; break; } } diff --git a/src/lib/ndpi_main.c b/src/lib/ndpi_main.c index ae3c4c20063..c2a5b2f2fce 100644 --- a/src/lib/ndpi_main.c +++ b/src/lib/ndpi_main.c @@ -2669,7 +2669,8 @@ void ndpi_debug_printf(unsigned int proto, struct ndpi_detection_module_struct * void set_ndpi_debug_function(struct ndpi_detection_module_struct *ndpi_str, ndpi_debug_function_ptr ndpi_debug_printf) { #ifdef NDPI_ENABLE_DEBUG_MESSAGES - ndpi_str->ndpi_debug_printf = ndpi_debug_printf; + if(ndpi_str) + ndpi_str->ndpi_debug_printf = ndpi_debug_printf; #endif } @@ -3002,7 +3003,15 @@ struct ndpi_detection_module_struct *ndpi_init_detection_module(ndpi_init_prefs ac_automata_name(ndpi_str->custom_categories.hostnames_shadow.ac_automa, "ccat_sh", 0); #else ndpi_str->custom_categories.sc_hostnames = ndpi_domain_classify_alloc(); + if(!ndpi_str->custom_categories.sc_hostnames) { + ndpi_exit_detection_module(ndpi_str); + return(NULL); + } ndpi_str->custom_categories.sc_hostnames_shadow = ndpi_domain_classify_alloc(); + if(!ndpi_str->custom_categories.sc_hostnames_shadow) { + ndpi_exit_detection_module(ndpi_str); + return(NULL); + } #endif ndpi_str->custom_categories.ipAddresses = ndpi_patricia_new(32 /* IPv4 */); @@ -3407,7 +3416,7 @@ int ndpi_match_custom_category(struct ndpi_detection_module_struct *ndpi_str, u_int max_len = sizeof(buf)-1; if(name_len > max_len) name_len = max_len; - strncpy(buf, name, name_len); + memcpy(buf, name, name_len); buf[name_len] = '\0'; if(ndpi_domain_classify_contains(ndpi_str->custom_categories.sc_hostnames, @@ -6887,6 +6896,9 @@ int ndpi_load_hostname_category(struct ndpi_detection_module_struct *ndpi_str, (AC_AUTOMATA_t *)ndpi_str->custom_categories.hostnames_shadow.ac_automa, name_to_add,category,category, 0, 0, 1); /* at_end */ #else + if(ndpi_str->custom_categories.sc_hostnames_shadow == NULL) + return(-1); + return(ndpi_domain_classify_add(ndpi_str->custom_categories.sc_hostnames_shadow, (u_int16_t)category, (char*)name_to_add) ? 0 : -1); #endif diff --git a/src/lib/protocols/dns.c b/src/lib/protocols/dns.c index 1a318aa028a..b518ba0f2b6 100644 --- a/src/lib/protocols/dns.c +++ b/src/lib/protocols/dns.c @@ -772,7 +772,7 @@ static void ndpi_search_dns(struct ndpi_detection_module_struct *ndpi_struct, st dot = strchr(_hostname, '.'); if(dot) { - unsigned long first_element_len = (unsigned long)dot - (unsigned long)_hostname; + uintptr_t first_element_len = dot - _hostname; if(first_element_len > 32) { /* diff --git a/src/lib/third_party/include/binaryfusefilter.h b/src/lib/third_party/include/binaryfusefilter.h index 991e28c67d9..6e2498baa6f 100644 --- a/src/lib/third_party/include/binaryfusefilter.h +++ b/src/lib/third_party/include/binaryfusefilter.h @@ -157,7 +157,7 @@ static inline uint32_t binary_fuse8_hash(int index, uint64_t hash, uint64_t h = binary_fuse_mulhi(hash, filter->SegmentCountLength); h += index * filter->SegmentLength; // keep the lower 36 bits - uint64_t hh = hash & ((1UL << 36) - 1); + uint64_t hh = hash & ((1ULL << 36) - 1); // index 0: right shift by 36; index 1: right shift by 18; index 2: no shift h ^= (size_t)((hh >> (36 - 18 * index)) & filter->SegmentLengthMask); return h; @@ -477,7 +477,7 @@ static inline uint32_t binary_fuse16_hash(int index, uint64_t hash, uint64_t h = binary_fuse_mulhi(hash, filter->SegmentCountLength); h += index * filter->SegmentLength; // keep the lower 36 bits - uint64_t hh = hash & ((1UL << 36) - 1); + uint64_t hh = hash & ((1ULL << 36) - 1); // index 0: right shift by 36; index 1: right shift by 18; index 2: no shift h ^= (size_t)((hh >> (36 - 18 * index)) & filter->SegmentLengthMask); return h; @@ -522,7 +522,7 @@ static inline bool binary_fuse16_allocate(uint32_t size, filter->ArrayLength = (filter->SegmentCount + arity - 1) * filter->SegmentLength; filter->SegmentCountLength = filter->SegmentCount * filter->SegmentLength; - filter->Fingerprints = (uint16_t*)ndpi_malloc(filter->ArrayLength * sizeof(uint16_t)); + filter->Fingerprints = (uint16_t*)ndpi_calloc(filter->ArrayLength, sizeof(uint16_t)); return filter->Fingerprints != NULL; } diff --git a/tests/cfgs/default/pcap/opera-vpn.pcapng b/tests/cfgs/default/pcap/opera-vpn.pcapng index b6bcfa24e5b4876ed457bc70ab59b7684a58f38f..cb05c25442d2df11539167f38c05b54674bf0411 100644 GIT binary patch delta 61472 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�Bj4fF7Uh`%Js$JW%IPd%?Kig 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 delta 121307 zcmaH!1$b0f)5o)#>}CVOLxOAY1cF0xEfSFT;NlRZxVyWw zXo?s6X6EkQ%-N6g?(_6*H~07cXU?2Cde6Q4-`-|%Ut5lJ1_e21`T63YRDxkq=Ws52tQMgpL@B#GiLJ@_! zl!~lXqI8M&?Mp_LDqa%*pYTCv(2;DOfBS(WW&1caKwYw{IDG@yBiMyJl5M0`C|O zMn^ks?ur%_IK~UEPYoqmpagT=H7qa4ZFhgRs7exc&#)Zd#$5`1TX?mcDf?(h9oW$y9FusrfNf17)i zzskjAaN#VdA$x!dyk{u7+XNI)ByP*c9;@zbwe9Zx0V?l)G`QInAS~G&Dx2pF&OO+{ zZN^e&=)FV7^xdM}k&%VmR~%>#FJYo|WXN(rM5@f@2zLo*C@^>pnXZ&ty3F1TAk!`l z^)U$(omtHp=`v#6TFaV%CAKQ$zV1?4I|Ya^bpdOogH+b)*Ve63SCue1NM-IoU5~k2 zDOKk3)dM85U;#JwX48WG-Bp6qD9V^ITDOX5!n9zO(Kih|M(?Ip8Lie(H!7%WtdK@k z&fUNzv`bT85zUcJPzLJmK{}Ns4G?kWUQmR)a(b1Oh^BhE5~iomrLfearN`3a5S69* zgg_uGX3VMxVEP|*Lx8vv7Gw-j7+coXW9&(o%Gd!yz!>)2H8b&&@pP-y&Z8tUsgzm~ zQm}xazI}dHfA_~sssy-zEG9WvO{kukCjf5?21b$z^Rse}dpD%B$GfELD(`*+MBK@5 zig4GexSf(GJxIs6kE<>Zyb3pvHZCM?LU zN}5!?Kn@%GySysLiX(!K)el!C%^E~7==H;032Vc-KYW%m{wIhO&T>S>QqU=W&d)nV z9`$McJzg~{pz`W4Ai~tZ65-xlKo#{|13WU;f-0Gch(s2wt!NE&84I?)fGc5pL0&K( z=^9;IUJi4f{Lol zwL%Unw_TK~{J*2gq|4Bg2N(BuAB<9&M2<}2ydfdDxXR>LS1S@z?8COE)vb=(MLjE|`W2}35 zl`%IWU`){T=1_T6Qu@Vtq|$%KrFc3y5Wx_jR6@V+R3Ye(90vPPMJ~nX1!Djai&H`U z=BmU!;^vUiBMvj-gaMVfIj;2|nS&D2G%9OVfA{$+s(c|9i-2rG)~Z~S2R8dSPZ9c6 zR~6wWLclCd^G;V+6@xf5>EZkn^3_n)=U=Fa$uppq%Iv>@h|wjXgE?1AWwPgZJ#q=z zYO6Zfu!)`m4z8oJR22}h;U8%B7wf1j?LryM(>4M2gnV_guwj{3(S|ALbZb+XyLEbbEO{@NpgLXXV+(lON zOyx1?eMpaid*mv6!j9FyD?%_DF$}?kb;=MtM+O^G#f&!h^v&FP9);$IA>lV{9ambl%Q&co573IK&@!xO*Mq9SV;_y%S)R8urRV%0^ZnASz^`3Zw}^=+ zjX1;~ce$S*=gGiJcL+GDEG3R*o@Wk>v1=T$D+%=X^y!030vR?X0 zSCpkkh)N%PQ&swRhJ1{!c!HGo zfig&~k-<92|Af~G55NOJ5*j>FH-p+|JsPH$T!R;?6KsQv$FDc zFfDP*Wa45x2;W`TgD{Crs6Me?eiK~30^hITgi2JbY)XyZY5UEl4HOP!eMgy!w<#Q< zVorcbm3iX~=CRE18D^0?7>~$8qOQZFn(j9OMnv9pMTp-WQX%=8>=+8+Vg`3gv`-Eb zpJMV!;nRDdMw7z$k~W)YB;V}e^_lZQw`gs&%OuS_cutfBfVLk*?+}w!tW0fEb!8iX z#DPt_mD;2p*^Y5Ne=$0PiO)Z{<(H@jUAx$lK^gueg9!+c7@iv1Lp?PL2;3RLV(?ba zTM3agVM9_gXEdp2Y^fe&U5pCV4rVbaTS7QMv>zo-=( zedmqoZ@iAHA+2r!lWI9s<%up5C7u_Ns*#6lCqg(=qs>H7EA25dA7i=6D%5 zl22I1y_HOS4aU0+guGpb23^Kssi)wPxP^KY{rokR(J%1K1x(it0RC^EqCra88jnt`@<^@2J1tY}x=8~Vhz@1(i1>VPc6kx-k~k`Qm?zo8K9P4K+tHPtc62ng(=+C|&e@ITHrCO&uJc|eG1Gyv$*l;h`(_#t0p5&tfrD{oXnxsr4bL?dzj#eX(ynp_?#J9lEgy;rPF&j7?03 z{sAW3=sb?EJa7kr(;#poR14a_PzXnW24j?w@G?u)%m?mJ;8@8a-aPic^OUZ}Sas>{ zB7}2I*u->*Mn6k=!Q+ph=<~BlH6^(R*RR2};~Gv$v`mvsMOYfNV*H&qDa`aYqO!j;Wo{; zrpq%C#nE#4uX1F0IX9wZDyLYiF6SGR!uaDpxG1(lS(|M>du;YvA=~5;82}J%=!$`B zBqbh_fe6u%NH&kGky;Wj!a^izAgH!h*)Klx+Pu3#vdM#s5N%7KNiAxliSJAB5;Xh* zAl9jAtFlfz1EcQ)WUFlx-L^`j3bjk9H{7y9(7W&G?$s_e9B8mNi52|y3DBRH&^N*PLQiTU*GD;EjLJi%J z9DC8x93WaBHKk@f7ME;n0?8TUY+=HERIgG z9+Na*vgOMegm7W7?)UkLfWce9&g>(dnZZC+nmz0$;5tVR8|M#W;F{8ik zm5RGAN%5{Vr`6-s-s@_o8UsXk8N}hck`fQ^4+!buy$=0T_Ipxf-?N~d1X3Gz&vXm= zRs6f}_~Uk93~>HB{BL!x?Es3}m*u(CzIYwJLM6OCB4VCPj`I-gcX~pw^0_($e>H)B(|9)O@spBIxc0jX>UyOik~;PnTSycntW7;zzr0I;QCi( z2<{=HJI+TRUURKoU<_*x3g?P&~-2T*^H6NU0{PGg5d8DBu{OUF1q_Zmy^vXG$Qc zdVrLxhwmIrN`*m%orB3CbQlt^VGwxk?EpqB{=*g_9*3CuREzH%lt_({f}I1rNu-s@ zXy!WyQ$P_vH~`$r3~KGd%zbbq#|ysZie>=-&Xh%o6q%%Oa+UH3B{njLit3rn%`SJ=XEAbOwXp&NfYkTxvkdeS&==Nb)3VfGCIx@?%X)2J^)W_#gdbQJFE~}Z4q8^?6Ih8v73}%>` z*<>&$)y(7)9$VRRDQ!JAn7(Qzq@+jZfSRdgFzItEZT(>|UDb@GtjE@NHFKo`F<~Mo zk22FPSM@M$)J(N%9$V}4D19DgFy_1x!&|kjHe?a>4WXiOUUO64L2#-NQq=ZOQbJDE4!3mPz~qHP4Y=4<^J83 zEC%O?8Qj5qX1-C$JE4Mgz-%h$gy^n<2DrKxS_CXv?@~~*&L;}v>H&j`rELY}VJ#N> zPWWK=IRPLsqo`SRZecZErw!$>V$!g{%bC4CK-nMKiip@^<}en1K1WDGh{nd%#nfYC znrOq`^D+>wI;Es=@v#U=8jEOex<@gyzqX*1nIFnorWcdSIky4m(sU0ty88hGe z;nRjx4Pm+mbL0!OR59iYyttJbdxBr1k`#QNzTJV*lYCw!Imx+F94f(V2k&x<@XAtv zx!ML)qsGv&GH(o=dG57%tFqdn?FVMDXBEjJ_srl#kD6wuI9Nq$RorYO2c-y?3R(r$>X*i^)PPN>q~LtOv;uXrf{^9sn80{(PDjcv~sZfvAqGEQ$w==B9C%4 zP+GfUFay*~x(*(l!)m69!Gtzc+KM-rSTz&g#bax?%Imf8J{?KrIMxdqqc?zB}^?bZnwx3n{!jbO`IA!xA8^&1~Tl zI=&WI84G-Tb4#eW|D(Aacz_piv9yehx_=>rqb~0MiDs?LEdWHocU5&3K&ssHt&|m! zrw52t`Ym0U$lF?75tV?V`*!hdp}^Z)O9kfjItS&bCX!mKYGS)2Hld9q#;f8Wiczlb zZllcgGCh-B^B2|HDmzUC^$`MY-#N9}ZU09-6zKyj)x)Fq%6j-7A)5Wr&U4ES%8D2S zBvr(v4pK$%s=wed87A^~G?!&n&l`lLuSaM?M@fjMM|og$w~Q@Lo*%rjdLapoP})!6 zz^@2UZQk#ss?Duru8X<27}8l;lRNr@5Y%Lt*w|S)1-XO}Jfp_<6L{gD;&s~K?b$^+ z3{@XsKzn6~H_HYH(PRM*e>$~-U42r4TOENcZ2^z$E?MQ3H*Fw5w1$6q9M`-V3ufp31)B zzNA*Xmr||Oa6>J&m!!tCs2xI-Md+NC)XOJ}cv&YQOEp@huewHW10~g{O(N10-4EQADL5V2 z8KL%2t$nq#dP; z^W+_+muxS69m*~0ZVA;6FEsBoL#p9bU+>S$M)xjJ8qGc1WAyzJ*(gty8URr+=B$=t z!M&en2$fq6LU7XvFQQ))Spxh5P-pnw*}cJ$Ruo!i{s9VrfNueQMKxTpE?XxxP41PK zd8=mj8lH>w>mk`Xt(Rtn2bgOm=Rz49%(u`@3Z#dCYY3ogyls_2kXm4!4eAy+6Dewe z2REo%;6`AemyZxlHcDZ=&fG#q%vfvF-CDKLJQG5T0J;^AFDro$CUpXUJ>h##(O|tJ zt=2NZ+{y$cKm<|VsPCgD_1afADI0afJdbmyGzeU~%~IfAl3CqwB!FVrX7dGHyx|4k zfR#jm0@iPfI$*ycMFD%dMHR4h$lz19Vz?$njQ0ctQBQ!6^lIR~dZTxvSFqf6Nsxz2 z`vt9HA2?{cGF&$ZLf78QN<4Ln{|ZVJiKrcNBzRsoM+#pmAolN&oZ_Wyfe@BbgHy2b z&JVZP9x;k)>~0=aUcODpQ28G1R+jIg*QqXhlukAN&Eu4NdU~6-ZI4gnV_DYB1C(x> z=G>>eX?lzpIFCa2!$$3s?DEa(>hnPiZ=4#SzjkGxv{}t#l6oJ~aQe{ik~Dqb-Y*9R0}&?vx}@Il+K&{R2re#~UzXy}rOqQnfr!7%1HoOYxIq`< zn;JY&J-5Jh3RJ5r%0Mkfh`hObMe>FRDw_rn1&R&|7rE+_&wK_u1X-F2kH4;(!EOhJ z+7Q0OMOg1hHN>ZYWwru{w?r%6k*9!NS;t=vpqlW#CQSh&ADK&r;ANaN@NgkUH8|;! zRD+x;4~*!OB`{3fcqA>DI5ZfA@K7UpU-hx%{Z{5!we6s`cuTtcBL1;?KnPw!r+5KR z@4(2O3JTNm{jFXM+IIp)_iKy1P~NY-4iKH%i+y2MKZkPzDa_-fSIQ`)*~QG+M61`* zTz?rWp?x=yv`tSksR5HIA%r7#-^K?-nxK4Tc^rI?I_XC49J-d^IclEpi=j{UoGIa9_i*yu? zudsdrMqgpgwOf=ctnH|TX*IziS*ybWc>5sM%#r|Dt2rzoxOae8eZ%PYmq#g0N=b3NjN9|l}nYk1|?Z*&Hk_l(^c*WQ4 z*G!6so4a)Ekl}V>D0iu#MbAoHx7-ItOPSS@+5+*x3%-UP0YH&%oI{EaFIW4+9%hBi z@b--iwQqOZ?RN1who!f-jdP#ZBSx7vJty~>o4f8Yhi|G9LE#p@AIS1d*iFP5DwA%WDmVG98w%xOA<5qt-q8E~^_KL9h!@iV-A9vxUt##B> z+LRjNpA)<0I$s!+oqfm?1Wz&mUu<5gUU6}&jD;U6 z<3=-_1SK>|PdF4T>tmExQ0p^LK{!;7EpOqcLiybp4H*4y-i7iOepi(Xebt4+MAUZ{ z)e+Qf=RC&7eJ2^?jsL)TFh(88rSDW7$wOq|AV8F8UPbDQ`Jgrb0vN+-)r>0gsKveB z29T~azm}xLhqnC)(dOZWTJq4w9Zzx5a6F-o%JGuOfaA4cHwxXrUM-6CYNgeCU_)M9uWOJ?xQs1=?hdlAG*OOZY>{9kM%dB?3T zzG+xZ(N<+O5twAL_>Pho&)?;@Jo$ULqny7yHP-;7L26!9ZE2rG6|e6bdr0Q0vI&Lh zHYlC$VJT<{!9V-fPIb6!=&0r`uYeI7_M{8b`t-3>`}Pfjly^V}pI~k~&|($;_LuyL zV2PI*KyfA_U8u-E!15&oFZud3o@w#9!~w`I{NOxk=v|P({b{T`m)JJY(hq7G2~^{i zH~OC8ef>cy?-v7;?EUFslJ{J!)_p_FF#3&CqxDcM--+l9c8Vk zRsD|v2D=4(`>@D%2%7Z6Kcs|;g@WP-+oGFbc;Y8=4ZU0(ulbhH?;kLFCcQu0KwJY; z_1SBrsXE`iMv``~@yLhnz>AxrDj*+$isnb*l0PzVI#Y z_nV~h@)TQxFy{T(O>&CylwJ#vKDp?>U7lR<`a1pp?Dp8DwA=or$L{i7vfYZTyXXLr zHhjM&58m#=3s@8Sd)f8c>H>f`+1Cnh0Xbkf10z3P^sTP0XbPu~TmO>si2FVX8I~iw ztL9wwB#+jQha@dtmk)r^SED22?U-=>;@BZYefIg+sLv*cRrR?J7#h~#bO#^Scr|8w z1q@bW@CiAbyc)9uq*tT)oVpr2q7;tCV9p`_yI@JrCW4FDMnbvQ-J@|vKMEF*u)K}D zXz67RLBO{HIsurhc>S`J6udRJd}~A_?G;rtrUH{JHs^*U#;a;3f>`Y*Z&)H&5jJ5{ zhYz)OVDOhAX3=<*^`>QP2wwIrfb$)&XAGlXpJc&49#Ib;TtH0oI1sVe3nG7UaTZu4!pk9d7# zD(Jd)eY9LPg?wuxu8l?4h!W>MD|7A&LP?*cp`AOMXoc(K?5-~=XI~-%&PIs}7AxO4 z2(|dgC6I3Nk$UDU^G}g?>ba>f}`x$r>LMuObAmRtwXLWVK4? z`}m-E11Z`#$(GxyyeIJ0YcO57(%|+KhQS8mvOzwn*?|;HYC^Tjg{>|=v*9H~in6zN z5h;5)^9q$H3Rj9KoqC!Qgm7k%Ho_|17~x83f&s#wk4{-+Dj`+QTx)W}Y=a=ak)l}% ztNPwnJEX9uxKT#wyq76bR>@43+!AFax0bQyH#8MQf^L6N0sby)Z2`j|64(ImvxUwF zBu`&)=tb3rmb1>I#}$!5_A=|tnx=-vnyIpNuO$Tk?Ar^ChRb^R(^Z$k&$D?OLe%?4 zS65~82Vf}rbROkoZK;?wne$!KfI}~9TMOe2C%k|z@ePS?0O%VM6{D@+-jJ9GNV2g3 z4J2dS^V6tBo^NcR^4u@2M=ZX-)ghvqNF6#Cs|}zo7TZK6HdqpS-a-=NecKQe(-%v@ zR;Q@g(i-bsEb$JYQF`#M-Ka3_R7<%z^3)%M5T-sA;c7d{D(@(QLcl6@6sg;*JBorp zCCfQGNpjq|;EW#Us&oS9V19}hu|6BKxlt;BaBDS6-)k)vW6fy_LBKZ`BT>y;ikY#J zSzZy}1E^QT(O9Sm?|rMo%<5QRR>1cay4A=&*4fxiA%b@K0&FIr8Z5w`DArzXGzU*s z@LLZ&`fK2NayC}!FJ&VK|1?0TCcX&bd2v3}$cx?$ATOTuXL-T7I$;KPx*c%t0gU6Z zT9n0zRrZ0Zq}`1SoTiBqONUB9&&F0tM*z|ihgt*7A^2zC%8SUVYw#d(KPusMYKi4X zsQmcdqY)+cjFJ41axNR?T&VVLj64?9WVN3PAnnvdYhkQ&yx{9@Y5?>JceY8&NeV(V z1oWAt?B2_v5XJ7sB*|x9@uut^p>&g#LOp@ePm6S&EO}Irm1|NSDA&Tt)-;f+_;=r8 zt%U28ig235E}l$LcIgZ98r6|!s;WA6ArrX+(qh#Nsc5`928Vm9I^v*};Rn}k zLjV$m55nZ@tguEymEc8R-{&Dj=338{%pD*dUFje~!{%D~9w3i&Xg(f*HuIDLxB;M^ zkH5@Q1;9T)F!&QEB4VS}Dl#pQ+~QH#1|&^T8ZVHeFpBxU1LX=Xvv$eohky1h|5a|y zUz}ZLt)T9$JQg}vb&RQwycuead zLlqOV*2*_hN?G;NVHLLnC>?xHCrk%TX|*M5tyX$1J2JjT50Puw-`w3Gg_L)21knxq z-j(cF0pFhcpC|;*QNOZpqpBVsB0~kJs!8_vs>WIvAX>`~)2wVQJBu|x8!%eyZPvkb z>;$j)7R?R>br~PFD_eS7v;^+(uJSO;lXiH$Qpz;>#o8U#@gaB#&G6v-Rs;~5J&YV- zvx|xvq-U?4YzW}>*EQ1P)90O%PrQ3>Ular>jP-Y^!Z;Eb=v*Sii9~6LrVa<0FO~YltYtSPgz6RMjM})&IBd})OiY;QixZlte>F(-@0M8R#$_UMZXMkh=OON zq~qh%K>$T?akw4P5g=2@w}z@*#mTh-V(nRLR_5AeR?zvtY=-ZfXWU6Eo}INW%LZQ% zz|fwCfAE9Nv`vYa=s)4V|D=d)JZGH`ML@u}4n_k=BMB72C8nL1IwKyri3s94Y5#dC zyEtyomVJ4<_tdHIr5Ox}pLl)+-+gIoD8s~XWIWZ8MS$oep#7m$ zc@l5{QY6*%i6q5iZ~!5S!OSPB7(4@pa;ttSn?t01DK!r+R=ScQR_CQktTQmlVkciq zVmzoTDtm%zeNZ)XYwc3;$E>BU)>-{F1jc0S7Sewem=V3gGm1-JMyqiiD#kR=6(^8S5 zU3$r_t<+iR-PKiuXgH640*kpV>21lI0i0`4%i!vzvIU8D8EoIa!@9P%;n>p*D#uPD zLypB{k{sjb_pTsB=l51*vPlOpIdv5&Iy9LzvyB~^Jk0i?%6t!%JSwwoy(I+yjCsP( zPcRzSSS4?mIU=Z6o%f(|Z#zdLyuxlp6t8I*SxNRxzujjlTwu zOGPvVh+4{-lDwtxrVxn?9Z#)OO4$?|B1G0Qm$n6nbETwCY7I-i6F_RuOW9_bL-5bO zwL7O72#tPX4-?DF+W6fXK1B6y0a3#F$lJ0u<^KE60O==6zEqOpJr|M+KbUn35u*11 zmZ@x0z9ZM%(vZ4XU6SI}-yI>EJEyLp8WduHq2Z@?O{sc$#Z5v6^Ba%WY^*8STc&$X zuKWK8_Go{e-koBtW#jwvkJ%P?0FtAd2NV!=WM)n++d_Co1qqBkzeiZ99aK*F`nIC@ z>jmGsOV|Sd8nEDAyj`qnB(-^7irw8jCB4^3Drr>~{L{U_ulX<1g=)nc+k(*~UiHn0 z*Fcd;x-&kanbKsf!5~LB`nETdQiuDs$^-GmF7ISl4*?PCWLq^?b+V_B!5>T&>tk$I zajTUyui=UM%TVwg6E#C?IZ-RJ7V#@U+KUdhJb11RFZf2_Hvsg4mFlc4*nWhPom$#i za%vf~cN^^`cDDUY$IbAHufaP&P@2YelPZq~vcfQC&tF{YCbf9pFy0Rbk{U*EcU8mq z01R2I86#Qb!-!=B=)f>?6T)PSAl#$;aGf+p#Y!5S8HXBhd#|K;5i2>&p{Zj$Lb-ZK zLM}GZd;*MU{WEMK4(MUK3?AV{-#7-31tFaO(oLw|eIQvT^_1#`OI8G0m)zG=lHAH# z{YZpH%uEw1cJ{KxfnB`lYj?6?7dH0oVsLM%$#Au^UbRiVm1+TTKBZ7ib-W;%-QUNS zkqx_#Sq7e(5EK0ueE$n3$Z*Q(b06Dg%zZ?Bi;{98l$T!C^UqIGSICF44l@9vXRa{G z_ocFlA!BUmSXqH=TeqR&Voh%KEs6R^hF6e4yqHF#jfu zFurEw^EhP#tOAf!Mw=KjUUG}CixN?i)0PBBF_V25w>L>4xt?4s3FX^7zysz(6ggepv%H`S(iI3(?MU|{)MOk|vC%K(AG z!eb-mbE&Zi(W}K8&9o_x9*skaP83v~Yf~OQ+Tt}>_E)9B&$|tS{eP7W^18{hNB2ES zj9M%W?fg-IssL&5C_uU7rvSJ{HPoOd^FObUI&!`(H2@)cRG{8UoBXIiZ`LgiL{pW1 zw>2|`;GccF!X>Bzq2|qsyRsaw!$K}XGOo2H-&MN~^^YH1rUmZ@3|{NB4?0o&JC-V%4RUzB5PUD>&Ym-^_S2uX zOfdE|Bht~XS;U(Um!S(4%&ifYZkBg zmcA?y#srS_(Py;lgQEF4$6|9=-&y(FJJ^6~%(;4^V3O zo?4&#PrG0<;mi~f-?|*@)jM=a(u-#8R;+*qk2TYjPFB_xlxFlZ<|g;3BH^hJWybV51BG(z|P?H@4eF>8rNf z&^sZ4gJkuP*y!`{-{XWheH>6If zD-+wA1zJMHOhvD7MP~+y{Vo$W$)OkL#K;`Zvi&|iKAka*mYMLwWTa{OQpBqFpJ4#vZv`9t#2 zaZEA%Ao61&Kw3Sp?F_-c`W9yjUWMZDNdPwxP}pNYA0wAOwAIhzJsGi;^~b$i0|?)R z$X?l+R!j59Hr|ra4+%8MCHo->=iNsp`^#f@125CGquCQ>1|@U>hz70MPm~qE*$X|A zA%6R^(*UT3EKhCWrV#wIZ+yDIWqp3r?U~Y0JV3@sV-u&JNpAD5(=`m#Xa*egTr~qO zgACMrl$iFflwikMzE%QATl24NlPLuM?CW)9(!d{(9Q#`7^=7Z3=dUG0+~W?z4UhA_ zQF%ND8S;452c=N_Xk92&yZu2mY8*m_M#t7)q!>aP(+Bgp+8BUn^8f1>n2SS$#;XYA zVq;4Upe*p+BJRxgPpeI~+W*B_lxJw+r>lNKJvv>r&~9g^t9VY1Mg~gATM3U+5?-ev z@mYXfanss578ojAOpsl*Zx-i;cF7Qrz+8Z6{`4WpF5UCshwOdyuqJ*uQ-p-b6oEZ#^229&;Kkh#kB`7-zWwrCW{ye~zaRxL}hIqOAAw)I5 zHLG2@?0bR~E&EpIwkwx?VdD*hi^CUOYbWeS z6r+4hS6-@rUbc4#QOFyXmm|PyFZ^f2`BqgV=lN=@<|KgVB*E?O@W|93RqWqh;2lH` z-`gvK%fn=ah^w*Ge4FwVCTO?#tV4%o)N1dP2~b|s=|GUd4pDR zvU8{U0H8ZHu!H0jS6Pk_rPcZlvMnz4)GM{4n^LO614HU{H?>q>q$pHhdnu(>dZkMB zmZiAws~;K$^AC_I?)y=sOv9uRgRA`X*ih*?Os%pMDRO)87^T~ezYVF4W7JZWkRqvF z6O~el&kU)z6J;sxdzt43^>~_0@d$K9iVVJ*r8Kx0AT8BwIi0wzGhSO+epTCg;I)-+ zv1E%o)ZvBU&`=rT_SPaqnYn4PY>!iUUmDbkl`_Q*{(uxkV8dF;D=rn3WJvw9RxQ;K zsl)_jI_>}0P$|4gtrGmoaH#Zlr9*iTq6qZeE=zH@fA>-y_sSHHz$LH2UVkYKrg&`_ zT=kb)sv%Mo%!K1gsh=2K@aW9qQicE2X&yXUw z3*Ay0EcMz>)a!`&aefTJN_F+N5SdO|woOS|F;Y4Zmo zI_qCb(cvx=1aqE9@%5#h9~|Y-FjS(wtLP-<*gG5{Qd*iMDRI9E!qvH;DbMR9J9}=O z=h8D&qc+$1Uv;fz{m&?3pZ8J`xl|p5Nb2|ZvJ|KO21*7(j0cGA4Q*wc`kg>&4t$>@66cxIYP*8{$KgyL5#L1Zj7BL@JEr$n zB=VEyVe??-|ObKGR)t)}YUs@`1 zi!lJ{lg%ty{Q1gsqBXf1X91*D%;Mh*euD@9?mJ_B;1zk4LlWT|rFBw+CH+Uf8U@%y z>74#~Ad<#LDc=L%jwEfTkIv~Y9q;9y-$IIh1vogDWRyz%) ze#U95WqORMt=tO*3v`eVX6rUlFx;OX*kv0VQl%TI zrG_9yQdOEOrB-;QhBsGBz4b~>Xrq)$_k-b8VjEeChhdwSO6Vk0JPc2fBHwq#C=F(8 zViG=BaQuNpt=B14|X%$CgY;vr-l zs&RWLId zeZH@hn(vh=`9Ll8&?{BxiBf7}N5iY3Ph=?`hPPg-N0LnOFr?^Y_&)HR(qIRKD3dq5 zQ%f!KO6~Zplse^=dh=P9;=Xt4Y#4lG36Pd8-1q57QQVUp0jjCNW3N;Jr&`L<#gHl< ztd!b|5QU*vuq?%WFVfYZ+NGB%?t5LN$oH5`N`rrRrIu$>OI`I!t;?a5s?yEy>hBzC zsaT}Qt5@MlspDR$LiuDV9)<$l4TD*W$P^Dl6{N^uzT!%Qju=C#LvgiKQKU$!M_Ebg zy;UCxaPj;ZAZ<$7fSdS83|{aZ!y;o1mE{#B6~62;{R9xboc(6S0M*<3uMPLCJIhrH z;OkC)>mu7IU=mvu5{oMZsBT>h2S!9*fLj;Ql>_{Rt#SZ=NR{83c#CRyYvLs=1~s7h z>w9_B8_)2#HlwQK8fUW3@i6yfhOY!4t_Ig=QCYlNfV8OOlF(opz zUd&fLK($^hPY~Y3SzSGVudDgVtE~vp$tzs)X&^BOT=?y^i ztX^c}0OhlK-I1b9%-S3>@q9BW6PM|76tp`?>JQL03UG*=%>$Z18Iiy;@(1WzyoCk{ z573=rkInHa>L1t>Vt^+v`n3t*%We*(3PlK?Y&@ujw46QleW3JhrOM`0=9h&*h$iw~ z+Xg5$!txgZhHfRs4+*e}e>w*6?L1yT&w!-*$0Kye!AXRRX;rd8S1Bxj8*p2(-5M5qE?(T z1c5yb=vf?eCQIU^&V={qKO?E1rNwa#aJfh44Y=iaV5tFDod6Bk`@TSal4m?H?;r`@ z=_fk=m>lpBjxr(Pn-hQRkBWHZ7Y&D=(Jbp z>tacWzoDo9ZKzy$Lr+oMiT>8l^!);Wr(7!~6<)6D_Y8LqtyHHC3rr-u|DA2hnDUmH+p>l ze<}sNzz@#K+C1nP{Twp?IZdKEZV0%6lPDz6E&kBQmH$8q9{LCs9X16dB|rGVbGQ6Q zz5NQ<9fB9oC8n~98Jnd{-;0LfgSO{?#jtGy#lxEeCaazm*${O5$8is? z)$QeJJ%_g_`+8G70}9oYki`S>mg8bKkkdB;5qi3MmlnXEuIAMgf)G9Mrfm)2k3#bS zF(X2BbFS4k)qvO)8A!26@o`%Kzd_7}9+ME>eS@U%7j?G>)Js0f%#*rSV^E_uwrjg8 zsfQo~7UA9Sd!&g2pP9964p6(}0b$ypJ@Sf@4-`KlMekMYb}&HsUd0JWk-?u2Nd~#+ zI}jqz6A!68H?=T?svMVuxaXY#(!g`+a3r5kc0(1KPxd&go=?v3*y35j&9rL?Diojv z7vuo(teuP$6@B$JrN^cp4XL}=)KV3YBB^J0lv3-xQhDymQoY&eSY{c7=<}n1ALtk( zUco+r*GAlSFh;fU(?6=(ukax(l%s?_kA%n6}F0*v?P9FJm3$Zb0MUdmKLHlU5s>+R@Jxf&lusiRIfK z0JSe^9KYZYgclItMu%Spqtuvoq<2WWnVeeZrPgMWDcfgAipy$(phh_r1t%gUVM=rnv9z zkRsos8Y>OX^h)(;td_d!l^WDSDHU_a@M=Q~wbW{)z^h_nM_Y$#Uw-IaL+VXiS&E0D z=skma)LEu@7=A#C3?{`Y4KDUd73d*Laf3nk4TG8c$rLx34ki>Z)l#{UBCpnsQ%arlO8q@fEmiEHA@ypqQmW7+!>dA5)KWhpMP8Mh zsgyeGmFhE7mg1GO^08sC!(5r-m2(s+GT38*WRO3mRyuueSQb3Bg=s4nID)?YtXe!U zG=;zY6xJ%w7CQI@mRqb*7(jgKM))EJ-|FW~&SD;Bh?*&BFvn$v$6!TGaGbi2tc&Fs za4M*lL8V#=6nm)aG#j~60JR>zuh$0JII}B$;IST2uby7%q34ifIh1N^Z zL^i56JKH;2tFzwG6Sv~<0-ECYR&JH)_J6(3G+&PL2%y6T`-}@vX zo=8QD^wVdC+4no7mv7_meBcLH#8CjHf$wSb9ktX)9A?;2L&Dej-aw+wlA_U3X)ec8 zpg_Js#;Hb=(Gm0b+Nxs?)$Oz8*1_Q4ZAX~+=Y*70JaE_TgJB!zq(fS$@zgBmKbZMd<-FwATx)W4 zr#ryhj8W!LZRB}JCpa#O7kvZ!2B73p_PheK<(yZf2rttWslw`{07?D+3oelC*# z08y=!Pz?A1q9fAJ4)Jp2X#`sE-Z$Pdcx~owjL}L=RY05$0$Vw6OSQ?B=J!MLwxqm@OL6CW-FJThi1xZu-f<}JNjiQ7iuSrE-f^&dlH6Q%V3I?c@vc-s9IP#Y zF)}!eg}o7Ma1hu6y@V5nZ=la%#x3mH#` z?GQ;x(%8g<)H1aPq*Sf&ud)Kq0jRb6*HIttmte(WUh%$qR(kLp`sy(4+-rGGRguk> z1XA!a(NOKce-0Ne_VKE3M;w@ed+++};P+pCwCZ_}gJx;~OT+il`dgW+e0GG>&!`~d zD>{XVy2S4$XBzfw#bsTSI`%(9he*E;ks3$6nYc%dl&|c)LYN@t^zcStvy;z`cyKlp zg970xgSTd<@|3|1ggkA@A2M&Al*~XQTw%P+fz7yhrcMU zYY7m%JugforImulTUB?2uupThf#))srE~HwjW>vqZLy^A@ba(eoT?ej6<|bCe`kbv zpU$b8y_{_aLfA=R*P_)bf_eIR;Th%$yTIjGPNqF6wZ#UuKmX(n{$XXxuo>sNwIY-s72;-PGYI>l%+ys z99(bJ3nnArxLtdl%ejaqHppN$OkuOym~qf+{FdL@7iZ#l!MFL$2f*m4?4m|Nr)quJ zYd%<|b~d)4vW(lk&;>P=X*oApzK~>+kK;Zc&^)$lkAfsFUN07M z^2IC9i4UiM!JG&$tj>w((^yJO(}*Z3O?guMfDlb)W24~b2ERR-JozE_+9 zIqC#xLU)9TFJB0&ZW>57?scCcNj^U2emm1@jF~W8NbXo zJyk+K#0I}ZW*3DjI{7{|*PR2juKN;yw{37mYV}C?X4lk*KtekW6Nwd-Nz>(t0i~%V zL3~Qw{0%@fCGJ?sDNTua9*hRcXwo+Es*+TNTy8GP(cHO6Wha|E^PcnqGSrj)URl|b z{wGBT?#Ol8}&cscz3@o4ak{lI2(dF{JXEY4{)6-fm-d28cu&PvxbyR zd}7)!EzsEKBdr0=`+Qzyi!y*&%784j)fsRAC^AkH;Acsf-I z;bKF+m&AC5mCOWUlqy@kmnw{_{Qyw1YbEMPYCKg=q8g-cLYa^w>D)qthN(XR?(nzWs zZZQ!cvWQ)(R=cs3t~}^}wgk04O{6IbUuk@32iIw(aifWoZP@X5`xI^u487Z@T1%(m zHB=>mp{!cp$LSC)TS>zVcW9>su9ZRV*$uQxo;_&AJ>z1fJ8-LA+DlfsSjvuu*uwTI zu~NV!7a@BWNo)~2G1CG-twI;)L`w+%89N?sE8c7C@9rvFnq(`cmt>2_{tAjw?3eaZ z#lCo_WXJiFXQS8*ta9g}(QV;-TYX>gWglmKQwRdS-R4LDlcO-SpXAwAC@Ow%(e(vD z{WkHsK2E1N(a)KDVwCrAv$_LU=_h9&Tsq+eUyFwkfXPmjc>a@QjjNnMi2BJq{dqsh zI}dXVdW0|AUff^VdDKJ*tnoQJ@AdZLb?WtK_TO*7|LFC0jX;Cez%X%akTjz43E$Q@ z2q;eYG7VNw_#PpIivSpYREvPHabS(+du>N5=XnWRu;vhr;-siA z!wQBE)^YCfpw=f&S_9l>Yk);~J?M>@{I+XR&f{BppH5OWinhRz zTQq29TL}};p3^1YcwA2*jd5KuU5+b1D`J`sX6c~C+}Td$L5m=ud<@6Wl??Of&HVrL zU76-?9z`1644o&t$t8p1|DOYS7D^5@W>N16w1D??hia`CI&*ycW!>)ouefuWlW(A$ zXT8J1nM0#D!S_vi@9=7wbEzo=0pGTo!R_OyzqqhMnpE*FV!p|+yO;?zB@rRWE zyiWew?5UHAYt(h}6(MYrF!0+%HVv||&PJ(D=34bKU`xrr1?n^1)ixK(an4hZv|YO16znf%ACsEFGM3(p zfz~UN7QnZUIr%Ljo|sFJq%&379=hyW-{Vf{OjTDVifA?XUJYNdT=%Rq3Wrw&e0yGT z2{dP1afWHj&N|t9GI@EfBa?JdswX}R`TiPQr&&m=OR8DOXkx- z3(hEB$T=J?>l%e_N*X*!Yi@WluhUI+=G_BGSL$Y{GD*(EwYfq&fHkhdty*)JgIdrJcJ@<8=Id9-z7$UxU?EuyF3cQe9=Ur?1 zy8uzwy2ug;n;$O}UF%%rl3qCXfidiI(HMVM{XDp??Re=7wuF4^MfDk8p&6XvJbxv1 z1w4CRy)&SSua!_5^DylCBE-tq(yE3FWd=r{yxdm{Rrf+A-bmK?ba4qnv`U}wMqZ`! z9i#vYP_#2uu>jmeO8-{5Gvxr1I__R?rTH1RnHtq-in{)-YKmF_nN;tb?V&nw>grqf zW0@U?Socna1%5TNz9cYk07^M!h@qbbW%41(1ASz+oPcWPB z_R+~-AhZ`;#Se~jwE$cQ|GkhNJX!eBISqcq7QXuyWjhm;rnJD~2epvV51-HkLHp!n zFN@;d{SgWv4eR+nOO?){=U!;28j1)rluiJ|?)}VSt+nNM!=n4Z_dfJYMCLEfwsZg* z5#M5t`5xj11M2LBfqcuC`?cRdl9Whb9bH9KF-Z)s#u#AqYMgBfH4`D)IbLoK zIn%NSScLwDPZ;Dh zV;fMz;rnoXM73lJj5XmL2pM1huK4wndQCbH_^0ekzu?ZNhb@9L=a30Rc2k!G7xK3%mw?rTxZ#dK9 zeKO+^VI>3kf_M-c0NVNuk8T3ro9M?m8Gwq{2eLb3+^6@*Q2#rmiWGG|V)(@Zq~8;%B8?baX(&RZRHCY+#HU=t5TczyOnPl% z)j;{y8L!HqalleS->S|Nnj0;TOyW<_sM~!nRSp+=;}x3sy-KLmcta>GS`y-&{htWY zVduutfzp#R+^1t+sWwd{DZZ(89wFLP6HNk@n`)IO7+y_pE=h5(#!NK4I^10C)h|d< zq|UWbN<9TgOKKCSJSCdyXG0}JXQ|=y$Em6S)B^U}TIi2c)$1Hs3m+c-)&o^Pd-cx6 zD80G|kd_o9d&PtK991ZopZlnTX_}<_&O2mYZ$muSqePAVN?%q?0TD`pQT^4y-iZ_i zd-708iZ{>8^#+kZ=b|a`_zUh+h@($o<5fL;z|xzw$VfY z^)`y6_VaL`HpFNu@-ZQc_NdI%78 zX|Li`J<%0pD*Y_Y8+dcC^$6+_r+;gImM1&BMYIP!w=3}Q@70{eS0yys&9bxdMkC&+?$bJZ!W%- zHh+F%y-(r7BcdC^_lEkz*s0zHR>GZVM0|~AEj2>lDEaBVv{Axs&W#@789NTWS30)u z2LsCZK?(iqg<@ogug&W;0VTT6v-3ls>ON0ALfJn;mp|-3sj1`M1%9v>UWgFAb7Rwg zfr^(<1vS-$__T{_&~@7Tq7h-~$3V7!$p_Eb-9d;3&$ORZgXe$9Q1Z6T=CaP7J3gW>%C<6k6#0&MQ3tdwuIoHeG|S+A7+j(g?acF zAwmyyNvaac87h=Fl*5fr+Z-8O7OI4ZTrHDl;*=pa~N~;G48hU@!R_nb$Dp_w!v{Em0 zkfHY?TCLX|sbszXnke-)d-aMnmGyX3iVrrd=58fZJSw%3O12u&L1}fJSFc+Kwcc^B zUhi(ov>QIeaBXcjwbTlvl3m-{OX*skVTN8(FIkTVWVDyM*I%Z1Ko%mEZ1wq2rPcDo z4Xb&Fsr5P{m8@5Ej8g9puU^a;wca&ZZ@@&QUY-$#f9od7da*Q0>f>Zypx{nOs8}@3 zRSJ4AyoNm({%|9Ha4Zc6M!P-DH5T?}zx9KTjnGnZ85hP&KJn-h2}5_JcpmTK4}tK} z^yWg4qtP_q442|ANSQ^F$vE;oi(+&rSUi-CWtxNgVuPu_v zZ0jVG-2cQ1hX2jhsr-Kq4DH5K)UGB-Vij4+zCy7|p+Ru>Ym+OG?i}G|-zx83FU${lmC#O*K(+gftWgEXR$E+0aJ7dQ5NOPX?!y3Rg*8`M zc)Sw-?(5Fc-XH)wHGD$KLlik3u2ajnv5ki)8?(^{8T!GCYP((R2QPRU%s_^Av|{#3 zna&~Mg?7mh_o#ck;n9b^+#@bD4;ivH@StRkCtQ{po`g#{C?{M+R<`T_=?|7JKju!SQ5E`c?4DXh&G|7oONBy=!e&Rt8F=Ik-=Zi zNd|co^367&@6NmY#jA5J{^S!+xO@UgO1P{STy|06ylXAYLXCvm%i^&D$c6BIAsu80 zy5O1yiG_f#haLU^NjhM$`;v>_tK=TWd!gXVO2`s4N?$a#yX;b(h&YtW!z9QIf7|4c z=c7Dto3veVDc&}D85nBqv@cxvsuWwkb@$i5AQpYq^(Qn;{JU@Q-@)}{p%T|5A>MPh zeg#55hFC(izSmrrsN2BHsJ96I!4D3I{Sa$#!!;i4ErP%K+UpP3lkKIvDcR#8-|!ZM z;4L4#!Sf?bi{GIkd?iGDlFKF%Zn{(tCl+ixnrfIU@vY}oZQwd>J=eYE;zujEJ2wEL zqZ8UKsdAfP!Qq2tZ`c-v=rQ4ezOP=_E4LMu*;)48^hrzrGI`nt$KbfNCDE z_||OxzJ^Smhms5r$!$*qNhgwlv};5DbuEA= z1Ca0y*t4iHdPhK!s1hcO0ROvC%&!9q{=?m@~?_Jlal8`|* zoK>T#%$R5`@jq9vDFpxQ>rzX&tpCiW$$gJp z1RT9~|H3D!)8zM}7Hk;{dkT0zEzf6{bW@y5y+Dd8GXAT}Dz<%=W;a|fW(SaT*DC3= zOL6;r88XRg1fr z9p&`GrBfDe+Rxs?rQ?O;lj7;XXfP?xt(*^UMinx(Bup~J=OY0E-`NjmTjGn1aXQ zb7cz}8iH4S1C`?$R1QA&7Y2$J4^y@(i{ia@GW&LGufqB<h8&hFi0|a_V%_sXdvXQ&i=T1@ z@!2IecOOZZyA%-X^99+(-dsUHuvrXmFP0%dQhOu*1T6lZZxY{u=W8p9=?yKDf!-_LH)sb1blPBZ>Zte|21|V;8j%3|2LgL za&v141nD)jAiXF>n$j*+dY3L8rAd<(DG89Up(hZeNiR#2A`ps5ClslIh$s+1z!ydS zGqZPZX0Dvy^Uw3V&*No3?|0_RnN#+hv%8;_7mji7H|zz+Fk#k|7s-&qn(5!)PKU`y z`Ec=?IUc6F@5Xt;6jxCdrcQZ*c`^_BrySKpyW(Lwix90m*R2*VR{kfm2wXs_%n^90 zdc|7dZYVuo^ew%AKGWX8TEZT8JhLD`RKNeLrEc@1VXl?Hk39`;JzZY88R^idJhUisDn;syK? zAia6(aB=vkAxn}8s6rnb@6;ijeQcatO7~jY*imU|Tu~6BP~PvTF4;z;C`iw`E2V}N z1E+00z-hLJ(UUnn0)V>kyRI<+<>?z<-Y*IP-#S?fpkZ!CKVhyhO!)8*JG+-90D`YE zMC*t9h3CXmRd^Mpc+M4y0g7_&^&nNwl|hC|H-5M<$D_H$3!Rk^ueWP)phVHkK0-#5 zQ}w@wK;0fKYnR(<4S?ZL$V8<>*S%0X32{^5Gfao(PLvLDd!Hg@w1H{D9+!GF+my71xm8IBZ{t?xpmL(9BU%NI&19)U)4 zOoNFjlH$Sai4+-J_KnhDy+l*$@;7R!c%(?`&bLY_9U$Frz04-=_9L$?%T~3m>`A7r zT)ULEwjx9o*J77i>atg=(|)B?p82NK{QYXFZb(rCQjZ9!wt>_UP*2bcAbtOl@DBo^ z6Tl0;{mE6YO4SoWh4&Q4Q^8cZ1D0rg=!tN}gN>offk`bAQC@p`DqQvc?4bbZw$rjg zc!o^#s#LltRQTw%9w8dN23=H-UjKNddR-P$JOQ?^04Z=NT2HyG4)uAYsBCL*gy&>; z#m88vPka;S{sw-3W4s#g=NsWk7<5E@gMJ@SLvPC+;TjJ`!=C{PzlW<<$7TUUv&FoBh}j}v9s7oa zaCIzS!JuI6$3MdP>KG?i|DWUlqd8G~YTX#@;ww5M9)_#d(dGi9ZGP7ptL=LT^Z#KF z#r&TyrsZe?YWPGF)}RhFe-#q1p&>~C;uG@L%y7Op!t)^$7$YAtgDk;XpGrE^rVehvcwCYh9m&^F?jm*dC-~abcZp z^AmaR!IknHHKUjCPk5mA-AmCcB?o5pZPkIKw=zC|8LoaS;{vY7Z`smVwaz&PMrpN; z+7eg}(SG|UoG-a$via3q#NE~5WgJv-!(WH5qz7)0!C>=rKI1n7=3)UB)pMxt_|Gsh zd1ZiwFT3#k>D-r;DaA3cTHv4u(?F`=5d=s&+G)7<>&xkcB5XZ!s^Bq4z%pS>XLo!504b9ygeo z-D7Z3Hl@LGIUuZOheyEspR!qaYH*9|03wUfR^V`Vs3k9ZA1ja5>5`xZO@Q~;{QC4* z<2pQ}mczoE2!~n^0m!ctw4>NC0r7S;YZ$r&?Py|{MJ#-Ar6&l{OErQlL0X($6p%Ng zv7duUTG&2fx2P7j*8xQRZR4CG5xB*J$apPo%PB0TvDWt67g$JtKdR{V^DKIM6}*HuSMLj^Ri|i3lu%L*Q~Hb`QTnO zQj{0J7Z-)#rCs2K;!9XUw7MlI_mRLt@Z!$@5?#aMLJ9?Gfb-(=_m*ow>Q72oet-ph z{C8C1HW&YdN|+rt%Zlva&`B>;E>;Ouy=5wWrG}dR;;HltvC2xHU@}F^31%j^j(>PN zdN_I}OpS5LePlU{SaRpoL!^v_^l}!(LVBe>S`|b>#R57R6Ra>|9Se-s)fp)f#~)&h$qVMz5vnvXqO&x7~`2T3Msk)cK20h%0i^bV1ofl zgR}dY1}6@X26-qqAVpF$hAO3U^f#rB3{^|LhZISj9;uYt>6Ho{rIxzrm2!+%O7-~6 z^s32twbVqU$g5ALD5b7=rRGeLQoOUeiWI$Zf6@$#@?g|!ufgN9lm-tSF?|o1Ee-O@ zxsMe29-gR_>X>dyHAs|Fd}Qi`6b*D$7RrH+Q=O5b@oV!!K7R3`sS7f67hh_T7@Byt zPd;rrr7e<9ajNqflZs1KQ@!;?^NCN!BIdo*`Y?zMViyP@CV#908Tu5FdC{>u&Tp(M+@76Sx z>-MjuE{U2i)Zo!?P!2?KwAp?vLfkX)0t`h6N?AcW9~_~Y;A{g(e-a!aDw?bO?p4Vd zu2jj^-Q$n$4wnvbuSa;PIu1#3gX@tZx0~cr8tm1>G&nVvT52#-BsC|WQmSHaQ|d%M zwNy8xK#I>vc-aE`m@1Z{YL#M0kwdvkDINOKE7hWuT56eBs?&Q)slU8Z^WT$F-1kv^ zP2ZnCNQspi}G?}v2Q#<6}-;|1Oq?DRC zz?5p$NJ{Ytbo|Vu>Nl4ZkH8m5Q3RTQA`J#obEo#643IwPlZXfS`qeSypa?Np}-#q*xqrvpg$X*D}X zu=_MVdKQQ?nN{66!;4uL8E~zFR-(6v7Z*zK3U%wP658h#y3=0>@k}}OHB>JwSBd_T zPw~$HU`+ADhejxG2LD6~oaWsL_qWdsv*7QKQ29F%87lbxQA&SX%>;;+*ZvwMGn+fz z8Y#Lj`E9(~=|M=5!P}FBLGJm0SpZSB=K4zMIWi=)Xqph>k-ql}K;-#_Y0`6E`$_jq zshhKuQl}9jsi@g%Dd(@IRG~zr)Skys|Fsik{d0$&0AQ4@_d=yg$4t|ql!a=CCL%>) zT9=}f8vUm!bvH#y@nCLwZc-WFNQ(RZ5GkXAzf~G6_QI6Py0@QgY?-D9J zc^4%CL=*nAyCM|J0XL8#p%(j;LUrexLNoTMguX_Ggu)I9AzpFM5Tc>5-XR%n-hQ7W zMeVoJadrDGteFPOoD>Fm2vQKD5cEE&^8A2T=>8cY#66$1(Db~(S+(brks{CSm%ww# z7AzO0zyUU>OaUqdey7kd_4rCed=y^x&6M3JMiJe8RrtjVIAxJpz-L!g1>A@X2@Ss~ zgm?izUkngc&H9_NfSk&{#H5z}A}QYT@*_nC*Z!_F*xM_0=XbSKvRCTS6CuSP^52IL zJmjBSFZwiM-`k(`w=Xq)%Kt(L@!}moh=S1Wh4g6?TVKq#EZ(ih{u41DCq;O{x8|w< zK;=*JxAI#R9;5hr3Eox6;H2hO{^%>;CC^bm!Ly$}xU@AmX@wQ{E#J0NzP7VRaQ(T! zFfGK-%I7p(u}BxM;!8NO(ZbKlPi$}>hYvS>#P6BHThICKB$s>&pt0$nkYNwf{QZUG zbQZfAGZG+n{?^X|uxrCB7(AZQT{O@-7-o&u4+L2iz19X~;J&zmRy(_hFkj2qNPQzuxnNBvL!80p)34{`t!INxOM#kMaY>K>_S1t0=BLZ{L!dCt;^>DUGb$VHX8^2Z2 z%2=f8TG;wwP!s~bjp7b~M&WLj5QW>xj_xc@g;2jPVO@%^&cF+(!WZfor-)H?B+Z|TbbqYc>rr*TG<7R`un@~=c8JXQcrJxo7U+RBd=@F)ZvHlvU&RvCpZ z0MX{e&{!+KtK#MRjD+xTQgk_G5bmiU9)!|IJVEGDP8o!bz-Y}AAqWS`DT8nhg(wKv zI{_G?=Xy^>f>(9>;~?qR+do$8TR}+j?&&N-bVhAQ1#3wdmCgQ>FMi!Sm*DOKqjeWr zu=1~HZ3-tW5b#aGf!EC753Q^WelkMzNsa53l_|KDl<2`p>|npHV&$JY<@HqjI@(l` zydFjYNk_wetfH)kCCK0o81>dMRYl-H9dD4L{@}xgLMqKr zBB|GT05YDwY-sHQJtAJjbmXTs2!gN7Lv1#+2J6K?vL@3hu(zJvDB%h2&rL;ec_j9g zMWYe6XuV=Ht8zK^e@IbNKGWVBsMTsMx^y0o60ty1JVv&b@!&mLNu;1(kJjF_vo?UD z;zi$16d~x*qP4#5mH9Igg-plze1$ismt+l_)8JnILB-cU!=SsN5`&ZQzYs;+ye`I+ z16`_%!PqZBsYA3u9jyFp9;czlbYA&| z(BNsh9yO@fr;g4t(#j89a*uXR@u7j!d7U{mLMcafxx<(6Ms*%2^az0nJ?YVd^XsV|k2fX)b+CPTE= z<3yhF$;S;O=?Q6=`UUCWp?uXiFG$a2X!;{twD#8o(S~_1UMCgehVApCiL!lirO^Ok zx5Dodc>7(C*Xc~=tZ7!&nasaDA{DeUGexUSV^y?mIVe%@*?5{YRGTo<+J+5h{H$e# z6_8kP*77`bm-z3#W5BM>o`g6*S0n^4*Uu(XWS(GnC6Cg(;;AYSrPD zWccsCVV?lkjn@CkPEp0&+f@jGw-vPScM41V6*)Vy0Yq=N9t0~(nst|T!n@|Ha_|X! zGd$%&=eoPW?{0Ksw0W1+O*{FB_&V1DP{X;xdzH?;&NJ5-`9|y&RZ@%D99j^B=&0q< zy;k*6%dLdr4Q2NIBCU2B?o;(vEQC(&aK&hYUP3uL?zd(gwYZVE{gLBCF=`j3lJtBrbo;*7k$S2UePdi=` z#HdfJk*@61f=Y3V2h)W`{`IY|0Mu`!TSsHR_O@?st?h>8!+vedab;d@^+G?Yp%T4J zrF|zzWGIi)i$Nadd&%&(YZM&;#`69F-)(jNV72%~z3qK-gOH&)Li>{< zg}JfqUwMqpKdE#mbh3xJuVzxGdzflJDs_&T%uF>?GtQ%P0~od^#y8{h&jtoE-q?&U zd&;WVj2{o2al`i8DG>nPy*@{Xy4ND7dH0$O2JjEN@!)u1Xs@2y`HJ&awpY)i8HXU< zBF0=07Wo9KY9c^%8{GSXybb1CRW*?6dIuJ(+g-Kp!SppZe7M1hNRh$D*Odm{Nv6TL z>uQ57fzrWXhRW77lp$LK?$eS*Vg&+N?Y%}Hf>oeNL$?PZb(rMIHPQ2jTJY4`7eM-NN zY<$y*2cVV*;K#ZuCeicm_)a& zx?j}WUWynDpy5?W3*i-y!&ooWP7T$tf)dS-)=7wO@RYCtWV{q}t0PEz-NMGVw8ydy z#xfrO8PUO+Td&vB))03Y@v3hwEJKRUT_0&>;{(=UHu9CMInUh`em9*rWRG)4Yuo$y zh8qM>jZc;>qd<)&%U#;rRC|`?05TpWYHw5R);UIlkY5KI|IX;C3@|c>u~@mL6~q;n`YQ5qasum(a)m%?XuXftrNPq>eN;DF8FBEQ_wtr7u#W3_$9ve6 z=LZ@Bq^0&1b(RtrVe{lDroBm+W7I6G((9}ZL$TVjIAkbaeoiA zwTJ9S0<)j5)w~mt=+-+8vYn+}skbfZ{q8`waY3{B2+<$bVh2Zxbxw5Whu`_N0(AnN zdg=&UL_ib*zHRp`fLeijL9yEXk;<;;RDVxp79ORn%qo*T%;##x66ax#Nrq>)b3Zsl zxA3_~tJ^?Tq^J$d8_nAQPp@IXm`iT_$wkhLWK4E#%ovgVyjPlp2B|@98KZ1a&rP-Q zV{Ktt)v-2ysE8-@zyqL$39SQJO{6jRu2)PF-N^HUl-XFjcvdUPeGfY1dm4Qyt<-qi zMeL&y@vY|}X&^;gS%oKxtl7)5ra?xMdolc8Y-G*wiMAO5Q3&{|{SQFxw#5z~+?`_M zXI&OE@BS`6-#rR`k1{?IJ9LVz50w-dU%~Ve^F2d|eY!|P-Zt!I0HSqFdV@#L={CNO z$w%-O&47fD7e#9^vu!=IMsOb7E67k8PRte!mqT&M08zIc5HGrIJ{O4D0uapwTE)X$ zAPTSf7H}ajS$*xfd7_Y9tb=ZfSrS!Z3xLTIo0%-cct2QsCxF-w9!r+}AipdB5FkBe zsZDv4{28ivw;VU5D$O8+d$4iCJY<8dOJEcNzBSbkz%2i=Z596U(^seWKuy8vE8}2@-hQiXAU}u2 zJGCvDfKsRSV4J#AyNDEZYCX2|PL20N_9wtlKXhZevLBjHPzPJAwrK|p>OFRdZjD#Q zQckLd-}I+o(-?kn^a9NgJyiD@ez|I7NzEAi+wBtL1<&(&y)?HzXP1o~6U@&#kKJ(D z_zHt#pG|Qb@f9#sQ?vmxp@1_`d;fs4DUa^`A39!52aWO+;phQT1YXQSdpwHc7l#CE zu?KCcQ>pp(LSbOSjk|c}VJa@P?*Lr)YaS4*-8?ALjnCRE9YpARA%g1|_=gETBS*4l zkz>MFL1Ehb?`<={91@t}ym!nI1|;6!%o|)gSZjC4##a71vN{L@PTzFMRtIka@d66* zWUO5jg>YNfnXXL6M1W`}I4)g8vMq?=ANqcngz$t3h4R;Q8{fL;Ca-x-Mjlm~EK$s3 za>7xi$vVK$X@Pm0N(O27kJ|WQvH^x`BmGZa<#cY(oM&uW6GP3U8r_;i6d6K{(~(M!IM`*_)u$d8 z_aB_x3MAdAOlS*d(?C~sqf%q4Cw6&HiP)vEkZ8c*8<%rygHPE8Kyu(k-R~j6S5E(4~n>5IMpW~G(|5z#2Egs6>{IPnS?KA*J zoqh3KsqzLPa%j_Y=@5@oy9AS3{@w7& zS2Dbp-~K&+jElFaQ%F(c%vr$BIt%XEEw5+EMMOY3^s^UwAR!)!wE1Qv3Kf-+;H|Hu z1{AeEXDPd~^)*6@;uTg_c*PAyCz}TAm6ZlLwaZIYt|%#9!re$U@8yWrN>sP2mS^uG zL|!FUS9U>m3giNJS zZF7C$7%yUPAZcFlTYWoU0!%Y3lF8MJL9IXo``3_n`0to^{Ejl;61WblEiu}JGfmO?(8R)azF5 zB2$c0|9YvCpGvAHtAQeqL5p&%Qh#Mt#(1Fe+O+<5{<sy+^+po-9EjC%rGei;DNK=?h- zSbixv+1?4CnL`9oUVtGeNCh}FO<3j7uL#jNWuGoGhzD%)Gt;$Z(`6|+HT_SMsy$m$ z+~9Jg$g4()!YeM7>6Mz2sFsR;Zc4>3R7y=jh*IMCLMg=!M*ih#xVux-4YxEvhU-VZ zQQ8}CD*gM7R7zpBI1!a<_RgML&-~UtFdz!A`BvWwueAl6gf$+`Y=4_B{jP==NHMAgj0o_f{OE?*%-PkM=hTn(ULqH7fLrZEUx7bxLBK{#1#Blk4 z{9*%kXMy^x_h-)-CMs!ghRZjz6Rd?`583QDlJ5~EMpZm%wug1J+l)&z`J zkgn^pkeu3w6uI7JCwH9(;w&_ zHCcmI6Kt}@C+5Im*BuDGjd(1F%f<`_)3o+FA~0BsJ8D<0Sfv7J1i5SvN3izOQ9FN- z`WWl(at#4;!n<&4ui!EJ{D3ID=G)!HB4d>12a!!YzUvU8+A4jL$CvwmnF|GK8_tN@ z`j#ykXhRpc^^<4pbErk(6>pB{Qa^wWI1eKA6`-G1nTUAf)95h!zkI$Ud%VYkC10d1jA zeBdkkImlUFx?=SB4Eq5->lh9s{$c5g0o4-zyCojJ9emARC)Yc88O3<@3?BnxR6Q53 zsj6o!GG;voYHeGlMh~;#hNOAx*&cUk`azgQRXuR*9?bK76Szaz-b@2BHPG_Y>vN!zh zO-~FrKBR06_kve%9^+z@2Lq(l&lF}`Om%qIczaI z@;X?9;py9K6(DfjCRR(R?g-Q-6%-b^SUQqM^DC%QyD!yh6ccJZliGdj@qc(R=|9h; z_5c}~)T^{Qlcpk-RfG*?l}1z7dyL*ID~r0m_I+W{6N07=gdZvmndW)Ut zx&fsAsEQ*U8#7+;&DjPUm=t_wyPAU^V&IjPwh{ctIezi#4!!`hlV$EPV2s1L)*P;2 ztktZiN&2u^#D|-}t!yr`0ga3|R?PaSMbiGSW2X6S|zvFd2M1Ou9e8C~QQauMw z2hAax`}+(SY>ly6Vm;9i`>+Ny{scg@qk^+b{a!r>-zec3*yt3Hl!3(>sxojaGBnq= zc7c3e(^%9CS9|1DyWLo&W;rd?dNdbm{0&=&fz&59cig6~>1{98y2G_XwA41Dnt9vk zeBP6$8Et&hl+|+=fV2%A9T^b&O!#-KA3ibeiq{XGh6mPHb#ka4Sf7kgR(})IS(J}g zWgPm1sn@-;NIgCyD|!Lc=yBi#ob3M4*}?Y|dFMO$5|H$0SIRt?>_SydcxyL2;4*M@ zj69_=aL2ni=CHHm*;p3cy0U;B%+cUrj$NaTx4B&%`&37Cw4XeM8roeHis$(c*F1Ti z(OsG6Pk=E-9hkfG+fJV6Dc1p_JO?ODpVh;`-=4x_UH1WyG;=-nsYA7tJirU(?xlnp zKQ@&34#jE~wQQMi{l`&oEAd?~$8g+q#;d*^R4t^k2G#NX;7G#2z7Bpje}QFY<39oK z^b36Mb7f!0@+iFItC)z0P~^ijpOHGB0|`6ll2p)*#>^MF_r`W9^|Np%YlgamLS^;U(rBq>@a2XIZ$|^(R{WI6Pd=VeQ5~~ z6FgkWJT;lNYNlRAkIq`j@GjENt=wnH48&aj;ENf&5_dNPh$<13!n8N@9Q-{byb_6|N<1^m5u(pXbfnQP2{Py( z?+f;zDm^6tqei$kaG|4NR~e| zp*seC#~24HH!X7H!cGzq-{6($YUuH;sK;>_eZVDx629Tsf8 zc)_>z=6N5Bh_}H>TZQA?n^8kN-kja4^d_nKGLJWfwsCK`PN^2a4B6@6T?H>uWxP%$ zy1rATvCg9b?`rr?3=4de`4=D><+bnRD8uh}{ab=HI!_UINPWLs7Af$#f;KT-X|H4} zkG);#(jG5;V}P_dCq?PG)9I$j>yseDHg5Tcz>fmNJ_P>goI~*;@P}ya>_zaq(*@DZ z_hGl_nE=&?-}OC@ja_h5#9IbLd=sTtYeSFM9Uo)uZQwdQ8|#_Aw9H_)-@KHep8y9d z#RjMUo#EJxk7pr*COfiOX?8&M@2)v&(<@2wimy`%J0R|ObC&;>GBN+!3kE5vhTc*o zRoCx;$?6g^?~10t-9G^&Wmc(QWKHwsn;%e)KJk&y%U#cC=ap=FA zd0#6vKLeb?-sZK`EXA^UAV7MV0H+;u7%%vy%OF&td-*^oUq<2)T=*8pP6ayo!Z1IX zf8a2b15W10XerMe+4cNE&VFd!d_yW%-di%ou>NhZu+DqH>`PK0S$DxZ{@w&T*;;9S z)~Dx2hL-w!UEZnKw3|fy=RvI*naPoUsUW)gQ z07<2o62VI$q5+JLhrv$aALcL0uvxKgX^FFZkB?M-5F=x$6jj zc#T|Y0ua?mn>wmM_HSwmU8t`VYT3dRvNli&okfPKc6K8nRGek>BY^Zxjhx{$pyL(a zf;DYv$~WOxksodw98LPY4zn>jb}>Cy$(R~`U#3;f(Tnp;h_B^gE=h*>xjnkAbnCDI-yQ4tZI8Nd z0wAlOSkqA%j@0fejlFm6%yM|o?B5-oVn)Ryw_KtRyGwLBiwC0m#y-x0c;|o@d;`(+ zESRNUp;mvxd!R7vozwD=h}dTbNL7{A4lqCI@B(C}$%eX~9yT>=X-@T<;^K%;1D+?c+pT z(hNBY_>smy(t;zDwhQ!acoA#mK1=xqP1d-N!0#h;AUprp&TX{Rh=^~hHr|AmF(xnL zMNGJR`L~LFuxsdT| z<~aE_4A1yb#3|!%%~582j{j)#$t<@%^*>~grq31D`6ACdcfdL>@@UKAoWUA8tB1~W z#){*XTz_wawG?rFW6e9ht1aL<4dd&RR2^SbOVsd2sN_P?aqx6r4kS&AdM}je%qK?NZc}qMOoalNG`!FG9UmW)b36sEW`7nXDp&tQKlKV0R?h4oJv*Brus5vw75* z^6T9=w2t*GZ>)Cq_lrWnH|wudSkH}DUMq}?WsZ-5q-74u#kjT3tWyTpz&!q8M_9HI zaBz<1dvFc^oh-6ms$k>w?%MFXwlUpU=Sh&BZy7z-?m_3UNmkw}C7UGRICai2F~w{tbkkCE|puOF~p!BDN|KH(m(n<9@l0Et<%WS=r?e(AE0CQOyJ z9Q#F(`CQ|dt^m;~==}de1-IF+oNL4!0g`&&rTdjVZ&iS35hmLKF@ffz{RinF1V>yw zi;05|h-n0$l(aYjYIquF%K{fc2ng-F)tlauP_IJD6mM-uIwWbzb3B zh5g{>D~NI0ArWKV<6f?|-{^6lACf&Tcd=}BP@+U3!>^AE7x@I@eIRLXy6Oq1dT)B8 z)Vpv>=%ukH^g%7KOcMyQT=cY)Z#(lZCseBqBz(^zM$3QN8KUnx?NooyVr`lknc){j zWO$)#tudi=3Gr#f;dFp#8j&+YJ&h=J6eyZTBxLYu1fLg_LFP$@vpe2!K?Ho;Rx`Yw zRR=RJiI{Q6&r7oGWth9(y(AhCpAwun4#G4gaPM~p>wv4@Az!%k0Z&RzyD9?5yPif> z0HUtv@>SmTaAviasdbYxyd3wD$ttn+F1*&_`#Z`maTx`@%Rc&uzqw)r|YRJjK~~e;1j9uflR``~Gmgh{8+0mF%eYU#po9l~%_# zGOZSW1XkZgk5KY58(S^|vlD*rGzQH0r_NRWQ3&|jb~XO5?V-HiaH%L`=PV?tNe(7gQy#P7OCvO2DI# zMcs0ftC6IM3sgd=_T#_KMsGi@(dXzvPhhLR5rGY36;P;{W zEP&G)mi+;)5Upl_t7%q=`FPn7L3)$~K=CNaU{<+EHG|*HjQ5eHgt|WP%k>TtzNr~n z2@T^8cVrG1@38nB$FDL#G{>oBgTuppbGZ0JNIZ!z4oo+uFPn0>_#P6k-R;BBBp$=@ z4^svDIxsX1iVG7u+*ZhN&^Z|9;%(tC7K9};)7=^HJHyEEQsJ&6elW>I1f!4w|HD6= z)h`8fJpA`~`n0J%!Zk1#UH6UFap2Ho1#NGHi*I_Rpc4F{Sa>`@R{zKtZDVKuU@g1V z^+}c*pPl7Qg+k)&EXnFpot6F)80|?pSFH97-a`*#Ge!X2;uEte%K^fPnGW^CzoEnj z_z@ccGLCCFT&kt6#Q;&+P@C=) zYCNG+ZNT8()m5iUxp(y!Kt>dP&Ml&l9GK--fE^%utwG*sm+G|!BM_o^hUbTd_I*AV zUsFpn)To%54j^gj2SE+e0OwObUW5V8qwux){I1_<-3YJv=FQkKo|3vevl|+!)u}glou*}--c$KeFNdK~LAzN= z^eH^W_5nn#BxhyWO8COmK~$oJsnGXbVz&;rZtxGA?{%Q`A8Wcs`bXhE`xeO-X1X)6 zj&O(9Q-T*dFQLPR9WwODib{1{YoXV|e@9Dvi2U6K*J-Ln4eb1fF8<+?`)tD8-wrN+ zR@ZeJ9!kM~_w~8MaEK0`TpJ` zFaz8PpwTjdn~0Xd$FR*~0HPU6LUR}q+cn|iH3ut>Ll6hM<4sh9T_0qqE%s{a;;VJj zSteeYdCdJO{QlG!%~G4XTG3Pn8H@)vdJR~^Xim4vXxJ>%=r*@7n!;jH1E8#N@v&Pq zE>7`^B{df@<6@2kun6NKj2q2eVcMhSE_c?r$ZMe6Gf#DPZK3Q1?i{@cLtf>8SnXH~ zVUf>UzBmRD&0EH|hFXelshqd?y#g}piI=f0Me1|SNl23B;Bhc4oM=fyHxgJ2y!TvG zD#O!z+FL1m&*uP9>W*zC%E|}a4@#pDK3<;xiew6$_( zaSRykV7ACuZE|Z7K|Zsn_Z~pB+o5bNGPI z^ieMLB5FgR45>ongcQ$-YS#gxoPdVa1ttP`5i9vmsBHYh#MuteyjkbrLucb48?ude zt%Ynr070JH7yT|7)pudChy!P~278#IQv}1O(e(>o@=T*gPLb1St~47d>L3%QK|Y5~ z6|+RHmr7(yc>U^!Q~89O_m5|hp-kR4Rb(b|TW;37f^32I!3q&w;zD)Q(GG`c2 zs8T`8lO%elG)f60DRR{Yh%$$|&VEUvH{mPq@kr9j`{SDsihq(^3+P7dZC{eET;G!& z`XZ4Xyb`*bOz2|4@EU*I4s6z5>>7eget5w*Ro`n505r4;+M~syj9jH5LUf~6bcrf& zzCecZCUuE2Z#H?X@p|FeQmzAtP_{r53)Y7%{Xep0)7PF5ok$VI=l550l0c8{Yx1P3 z?rZKNL(OO5O3^>?ta~`$lXV#@)mfKEGnH9BN_X+a=*H}(P6M(8{4PNo=B0MRGz~Ko5nsRlSq`Gq8}!~S zdIR1DuB|emT@vEyJ7*6-v>K6pkGdQE0Vx^*&+LV6bm<;tH=2ngHHV9PRL!B_USOy> zxc4fXLkomVlYG&Pw~3ns;ZYRqHZ*W9L@)83Oa0VQ-XordF>1fEVHCJ{3BGEz-_`Z) zA0Jo_myI#=qr)O6c;iY(2sEPgsfS(clsm7V;u)q;z;PkOvw1N>G$p8YTuupi!&!9k;&r7EJ=PwHP53{yW z?J`1j;8~db8Lshc5rO-4>W!yT4rYj3|HG_t^v!V_ zSNp|Js;)M+I53p&&ud&@3a3xTGW>y)GV)dGr zjY(n8hoV^?U|sf^y5REShptty3xfad>#?IAI73xB=CSa&E-QBmK>Cu$t{5ygUhq{p zfhsh)e)CMI;3+Ho!|0tul>&dddVnkV@4h1S>zl4vUcdtNq32=>$j716Km!jO066oEf>%!;}jWObQj*DD<_w zSE$hSv<*R#@Jctl<{Q1#)>n)awg(FxzLbzS5p)vT1?1Lx21nL_eJpdyX)&v@N^7pT zN5k*Y#vl~E1Qtfjq1*&-ENUiW~*n&kmcAr(Vs(0ZeM<+Qq? zk!{{yzT@v+HOjX)RFsd$vdtGaJ(W^8yQmZ%gq))QGS*XaMFwe~XOC1J?5=bjNaJ=Z zd!%Z&s~$p>aV2tyjN|wE^=_bJblM8na1wGv^3{a_EEJpX-85DX&gCG#@S1N`7yK2} zXa_kHOAs3-%F37C?%xNJ)}%}_M2`!L 1.1.1.1:443 [proto: 91/TLS][IP: 0/Unknown][Encrypted][Confidence: DPI][DPI packets: 6][cat: Web/5][22 pkts/2595 bytes <-> 28 pkts/6365 bytes][Goodput ratio: 52/74][8.11 sec][(Advertised) ALPNs: h2;http/1.1][TLS Supported Versions: TLSv1.3;TLSv1.2][bytes ratio: -0.421 (Download)][IAT c2s/s2c min/avg/max/stddev: 0/0 289/346 3093/3078 792/900][Pkt Len c2s/s2c min/avg/max/stddev: 56/62 118/227 317/1644 68/386][Risk: ** Missing SNI TLS Extn **** ALPN/SNI Mismatch **][Risk Score: 100][TLSv1.3][JA3C: 547df21d727c7b3a5dcb59aa0fd97c2c][JA3S: eb1d94daa7e0344597e756a1fb6e7054][Firefox][Cipher: TLS_AES_128_GCM_SHA256][Plen Bins: 26,0,11,26,0,3,14,0,7,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,3] diff --git a/windows/nDPI.vcxproj b/windows/nDPI.vcxproj index 3f2299c0b99..5dca751c7ff 100644 --- a/windows/nDPI.vcxproj +++ b/windows/nDPI.vcxproj @@ -126,13 +126,16 @@ - + + + + @@ -377,6 +380,7 @@ + diff --git a/windows/nDPI.vcxproj.filters b/windows/nDPI.vcxproj.filters index af43e8f1bd8..27af4a9de22 100644 --- a/windows/nDPI.vcxproj.filters +++ b/windows/nDPI.vcxproj.filters @@ -124,6 +124,10 @@ + + + + @@ -245,6 +249,7 @@ + diff --git a/windows/src/dirent.h b/windows/src/dirent.h new file mode 100644 index 00000000000..fd1e81354e7 --- /dev/null +++ b/windows/src/dirent.h @@ -0,0 +1,1239 @@ +/* + * Dirent interface for Microsoft Visual Studio + * + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#ifndef DIRENT_H +#define DIRENT_H + +/* Hide warnings about unreferenced local functions */ +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wunused-function" +#elif defined(_MSC_VER) +# pragma warning(disable:4505) +#elif defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wunused-function" +#endif + +/* + * Include windows.h without Windows Sockets 1.1 to prevent conflicts with + * Windows Sockets 2.0. + */ +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Indicates that d_type field is available in dirent structure */ +#define _DIRENT_HAVE_D_TYPE + +/* Indicates that d_namlen field is available in dirent structure */ +#define _DIRENT_HAVE_D_NAMLEN + +/* Entries missing from MSVC 6.0 */ +#if !defined(FILE_ATTRIBUTE_DEVICE) +# define FILE_ATTRIBUTE_DEVICE 0x40 +#endif + +/* File type and permission flags for stat(), general mask */ +#if !defined(S_IFMT) +# define S_IFMT _S_IFMT +#endif + +/* Directory bit */ +#if !defined(S_IFDIR) +# define S_IFDIR _S_IFDIR +#endif + +/* Character device bit */ +#if !defined(S_IFCHR) +# define S_IFCHR _S_IFCHR +#endif + +/* Pipe bit */ +#if !defined(S_IFFIFO) +# define S_IFFIFO _S_IFFIFO +#endif + +/* Regular file bit */ +#if !defined(S_IFREG) +# define S_IFREG _S_IFREG +#endif + +/* Read permission */ +#if !defined(S_IREAD) +# define S_IREAD _S_IREAD +#endif + +/* Write permission */ +#if !defined(S_IWRITE) +# define S_IWRITE _S_IWRITE +#endif + +/* Execute permission */ +#if !defined(S_IEXEC) +# define S_IEXEC _S_IEXEC +#endif + +/* Pipe */ +#if !defined(S_IFIFO) +# define S_IFIFO _S_IFIFO +#endif + +/* Block device */ +#if !defined(S_IFBLK) +# define S_IFBLK 0 +#endif + +/* + * Symbolic link. Be ware that S_IFLNK value and S_ISLNK() macro are only + * usable with dirent - they do not work with stat() function call! + */ +#if !defined(S_IFLNK) +# define S_IFLNK (_S_IFDIR | _S_IFREG) +#endif + +/* Socket */ +#if !defined(S_IFSOCK) +# define S_IFSOCK 0 +#endif + +/* Read user permission */ +#if !defined(S_IRUSR) +# define S_IRUSR S_IREAD +#endif + +/* Write user permission */ +#if !defined(S_IWUSR) +# define S_IWUSR S_IWRITE +#endif + +/* Execute user permission */ +#if !defined(S_IXUSR) +# define S_IXUSR 0 +#endif + +/* User full permissions */ +#if !defined(S_IRWXU) +# define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR) +#endif + +/* Read group permission */ +#if !defined(S_IRGRP) +# define S_IRGRP 0 +#endif + +/* Write group permission */ +#if !defined(S_IWGRP) +# define S_IWGRP 0 +#endif + +/* Execute group permission */ +#if !defined(S_IXGRP) +# define S_IXGRP 0 +#endif + +/* Group full permissions */ +#if !defined(S_IRWXG) +# define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP) +#endif + +/* Read others permission */ +#if !defined(S_IROTH) +# define S_IROTH 0 +#endif + +/* Write others permission */ +#if !defined(S_IWOTH) +# define S_IWOTH 0 +#endif + +/* Execute others permission */ +#if !defined(S_IXOTH) +# define S_IXOTH 0 +#endif + +/* Other full permissions */ +#if !defined(S_IRWXO) +# define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH) +#endif + +/* Maximum length of file name */ +#if !defined(PATH_MAX) +# define PATH_MAX MAX_PATH +#endif +#if !defined(FILENAME_MAX) +# define FILENAME_MAX MAX_PATH +#endif +#if !defined(NAME_MAX) +# define NAME_MAX FILENAME_MAX +#endif + +/* File type flags for d_type */ +#define DT_UNKNOWN 0 +#define DT_REG S_IFREG +#define DT_DIR S_IFDIR +#define DT_FIFO S_IFIFO +#define DT_SOCK S_IFSOCK +#define DT_CHR S_IFCHR +#define DT_BLK S_IFBLK +#define DT_LNK S_IFLNK + +/* Macros for converting between st_mode and d_type */ +#define IFTODT(mode) ((mode) & S_IFMT) +#define DTTOIF(type) (type) + +/* + * File type macros. Note that block devices and sockets cannot be + * distinguished on Windows, and the macros S_ISBLK and S_ISSOCK are only + * defined for compatibility. These macros should always return false on + * Windows. + */ +#if !defined(S_ISFIFO) +# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISDIR) +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) +# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISLNK) +# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) +# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISCHR) +# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISBLK) +# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#endif + +/* Return the exact length of the file name without zero terminator */ +#define _D_EXACT_NAMLEN(p) ((p)->d_namlen) + +/* Return the maximum size of a file name */ +#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Wide-character version */ +struct _wdirent { + /* Always zero */ + long d_ino; + + /* Position of next file in a directory stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + wchar_t d_name[PATH_MAX+1]; +}; +typedef struct _wdirent _wdirent; + +struct _WDIR { + /* Current directory entry */ + struct _wdirent ent; + + /* Private file data */ + WIN32_FIND_DATAW data; + + /* True if data is valid */ + int cached; + + /* True if next entry is invalid */ + int invalid; + + /* Win32 search handle */ + HANDLE handle; + + /* Initial directory name */ + wchar_t *patt; +}; +typedef struct _WDIR _WDIR; + +/* Multi-byte character version */ +struct dirent { + /* Always zero */ + long d_ino; + + /* Position of next file in a directory stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + char d_name[PATH_MAX+1]; +}; +typedef struct dirent dirent; + +struct DIR { + struct dirent ent; + struct _WDIR *wdirp; +}; +typedef struct DIR DIR; + + +/* Dirent functions */ +static DIR *opendir(const char *dirname); +static _WDIR *_wopendir(const wchar_t *dirname); + +static struct dirent *readdir(DIR *dirp); +static struct _wdirent *_wreaddir(_WDIR *dirp); + +static int readdir_r( + DIR *dirp, struct dirent *entry, struct dirent **result); +static int _wreaddir_r( + _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); + +static int closedir(DIR *dirp); +static int _wclosedir(_WDIR *dirp); + +static void rewinddir(DIR *dirp); +static void _wrewinddir(_WDIR *dirp); + +static long telldir(DIR *dirp); +static long _wtelldir(_WDIR *dirp); + +static void seekdir(DIR *dirp, long loc); +static void _wseekdir(_WDIR *dirp, long loc); + +static int scandir(const char *dirname, struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)); + +static int alphasort(const struct dirent **a, const struct dirent **b); + +static int versionsort(const struct dirent **a, const struct dirent **b); + +static int strverscmp(const char *a, const char *b); + +/* For compatibility with Symbian */ +#define wdirent _wdirent +#define WDIR _WDIR +#define wopendir _wopendir +#define wreaddir _wreaddir +#define wclosedir _wclosedir +#define wrewinddir _wrewinddir +#define wtelldir _wtelldir +#define wseekdir _wseekdir + +/* Compatibility with older Microsoft compilers and non-Microsoft compilers */ +#if !defined(_MSC_VER) || _MSC_VER < 1400 +# define wcstombs_s dirent_wcstombs_s +# define mbstowcs_s dirent_mbstowcs_s +#endif + +/* Optimize dirent_set_errno() away on modern Microsoft compilers */ +#if defined(_MSC_VER) && _MSC_VER >= 1400 +# define dirent_set_errno _set_errno +#endif + + +/* Internal utility functions */ +static WIN32_FIND_DATAW *dirent_first(_WDIR *dirp); +static WIN32_FIND_DATAW *dirent_next(_WDIR *dirp); +static long dirent_hash(WIN32_FIND_DATAW *datap); + +#if !defined(_MSC_VER) || _MSC_VER < 1400 +static int dirent_mbstowcs_s( + size_t *pReturnValue, wchar_t *wcstr, size_t sizeInWords, + const char *mbstr, size_t count); +#endif + +#if !defined(_MSC_VER) || _MSC_VER < 1400 +static int dirent_wcstombs_s( + size_t *pReturnValue, char *mbstr, size_t sizeInBytes, + const wchar_t *wcstr, size_t count); +#endif + +#if !defined(_MSC_VER) || _MSC_VER < 1400 +static void dirent_set_errno(int error); +#endif + + +/* + * Open directory stream DIRNAME for read and return a pointer to the + * internal working area that is used to retrieve individual directory + * entries. + */ +static _WDIR * +_wopendir(const wchar_t *dirname) +{ + wchar_t *p; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno(ENOENT); + return NULL; + } + + /* Allocate new _WDIR structure */ + _WDIR *dirp = (_WDIR*) malloc(sizeof(struct _WDIR)); + if (!dirp) + return NULL; + + /* Reset _WDIR structure */ + dirp->handle = INVALID_HANDLE_VALUE; + dirp->patt = NULL; + dirp->cached = 0; + dirp->invalid = 0; + + /* + * Compute the length of full path plus zero terminator + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + DWORD n = GetFullPathNameW(dirname, 0, NULL, NULL); +#else + /* WinRT */ + size_t n = wcslen(dirname); +#endif + + /* Allocate room for absolute directory name and search pattern */ + dirp->patt = (wchar_t*) malloc(sizeof(wchar_t) * n + 16); + if (dirp->patt == NULL) + goto exit_closedir; + + /* + * Convert relative directory name to an absolute one. This + * allows rewinddir() to function correctly even when current + * working directory is changed between opendir() and rewinddir(). + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW(dirname, n, dirp->patt, NULL); + if (n <= 0) + goto exit_closedir; +#else + /* WinRT */ + wcsncpy_s(dirp->patt, n+1, dirname, n); +#endif + + /* Append search pattern \* to the directory name */ + p = dirp->patt + n; + switch (p[-1]) { + case '\\': + case '/': + case ':': + /* Directory ends in path separator, e.g. c:\temp\ */ + /*NOP*/; + break; + + default: + /* Directory name doesn't end in path separator */ + *p++ = '\\'; + } + *p++ = '*'; + *p = '\0'; + + /* Open directory stream and retrieve the first entry */ + if (!dirent_first(dirp)) + goto exit_closedir; + + /* Success */ + return dirp; + + /* Failure */ +exit_closedir: + _wclosedir(dirp); + return NULL; +} + +/* + * Read next directory entry. + * + * Returns pointer to static directory entry which may be overwritten by + * subsequent calls to _wreaddir(). + */ +static struct _wdirent * +_wreaddir(_WDIR *dirp) +{ + /* + * Read directory entry to buffer. We can safely ignore the return + * value as entry will be set to NULL in case of error. + */ + struct _wdirent *entry; + (void) _wreaddir_r(dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry. + * + * Returns zero on success. If end of directory stream is reached, then sets + * result to NULL and returns zero. + */ +static int +_wreaddir_r( + _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result) +{ + /* Validate directory handle */ + if (!dirp || dirp->handle == INVALID_HANDLE_VALUE || !dirp->patt) { + dirent_set_errno(EBADF); + *result = NULL; + return -1; + } + + /* Read next directory entry */ + WIN32_FIND_DATAW *datap = dirent_next(dirp); + if (!datap) { + /* Return NULL to indicate end of directory */ + *result = NULL; + return /*OK*/0; + } + + /* + * Copy file name as wide-character string. If the file name is too + * long to fit in to the destination buffer, then truncate file name + * to PATH_MAX characters and zero-terminate the buffer. + */ + size_t i = 0; + while (i < PATH_MAX && datap->cFileName[i] != 0) { + entry->d_name[i] = datap->cFileName[i]; + i++; + } + entry->d_name[i] = 0; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = i; + + /* Determine file type */ + DWORD attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) + entry->d_type = DT_CHR; + else if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) != 0) + entry->d_type = DT_LNK; + else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) + entry->d_type = DT_DIR; + else + entry->d_type = DT_REG; + + /* Read the next directory entry to cache */ + datap = dirent_next(dirp); + if (datap) { + /* Compute 31-bit hash of the next directory entry */ + entry->d_off = dirent_hash(datap); + + /* Push the next directory entry back to cache */ + dirp->cached = 1; + } else { + /* End of directory stream */ + entry->d_off = (long) ((~0UL) >> 1); + } + + /* Reset other fields */ + entry->d_ino = 0; + entry->d_reclen = sizeof(struct _wdirent); + + /* Set result address */ + *result = entry; + return /*OK*/0; +} + +/* + * Close directory stream opened by opendir() function. This invalidates the + * DIR structure as well as any directory entry read previously by + * _wreaddir(). + */ +static int +_wclosedir(_WDIR *dirp) +{ + if (!dirp) { + dirent_set_errno(EBADF); + return /*failure*/-1; + } + + /* + * Release search handle if we have one. Being able to handle + * partially initialized _WDIR structure allows us to use this + * function to handle errors occuring within _wopendir. + */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose(dirp->handle); + } + + /* + * Release search pattern. Note that we don't need to care if + * dirp->patt is NULL or not: function free is guaranteed to act + * appropriately. + */ + free(dirp->patt); + + /* Release directory structure */ + free(dirp); + return /*success*/0; +} + +/* + * Rewind directory stream such that _wreaddir() returns the very first + * file name again. + */ +static void _wrewinddir(_WDIR* dirp) +{ + /* Check directory pointer */ + if (!dirp || dirp->handle == INVALID_HANDLE_VALUE || !dirp->patt) + return; + + /* Release existing search handle */ + FindClose(dirp->handle); + + /* Open new search handle */ + dirent_first(dirp); +} + +/* Get first directory entry */ +static WIN32_FIND_DATAW * +dirent_first(_WDIR *dirp) +{ + /* Open directory and retrieve the first entry */ + dirp->handle = FindFirstFileExW( + dirp->patt, FindExInfoStandard, &dirp->data, + FindExSearchNameMatch, NULL, 0); + if (dirp->handle == INVALID_HANDLE_VALUE) + goto error; + + /* A directory entry is now waiting in memory */ + dirp->cached = 1; + return &dirp->data; + +error: + /* Failed to open directory: no directory entry in memory */ + dirp->cached = 0; + dirp->invalid = 1; + + /* Set error code */ + DWORD errorcode = GetLastError(); + switch (errorcode) { + case ERROR_ACCESS_DENIED: + /* No read access to directory */ + dirent_set_errno(EACCES); + break; + + case ERROR_DIRECTORY: + /* Directory name is invalid */ + dirent_set_errno(ENOTDIR); + break; + + case ERROR_PATH_NOT_FOUND: + default: + /* Cannot find the file */ + dirent_set_errno(ENOENT); + } + return NULL; +} + +/* Get next directory entry */ +static WIN32_FIND_DATAW * +dirent_next(_WDIR *dirp) +{ + /* Return NULL if seek position was invalid */ + if (dirp->invalid) + return NULL; + + /* Is the next directory entry already in cache? */ + if (dirp->cached) { + /* Yes, a valid directory entry found in memory */ + dirp->cached = 0; + return &dirp->data; + } + + /* Read the next directory entry from stream */ + if (FindNextFileW(dirp->handle, &dirp->data) == FALSE) { + /* End of directory stream */ + return NULL; + } + + /* Success */ + return &dirp->data; +} + +/* + * Compute 31-bit hash of file name. + * + * See djb2 at http://www.cse.yorku.ca/~oz/hash.html + */ +static long +dirent_hash(WIN32_FIND_DATAW *datap) +{ + unsigned long hash = 5381; + unsigned long c; + const wchar_t *p = datap->cFileName; + const wchar_t *e = p + MAX_PATH; + while (p != e && (c = *p++) != 0) { + hash = (hash << 5) + hash + c; + } + + return (long) (hash & ((~0UL) >> 1)); +} + +/* Open directory stream using plain old C-string */ +static DIR *opendir(const char *dirname) +{ + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno(ENOENT); + return NULL; + } + + /* Allocate memory for DIR structure */ + struct DIR *dirp = (DIR*) malloc(sizeof(struct DIR)); + if (!dirp) + return NULL; + + /* Convert directory name to wide-character string */ + wchar_t wname[PATH_MAX + 1]; + size_t n; + int error = mbstowcs_s(&n, wname, PATH_MAX + 1, dirname, PATH_MAX+1); + if (error) + goto exit_failure; + + /* Open directory stream using wide-character name */ + dirp->wdirp = _wopendir(wname); + if (!dirp->wdirp) + goto exit_failure; + + /* Success */ + return dirp; + + /* Failure */ +exit_failure: + free(dirp); + return NULL; +} + +/* Read next directory entry */ +static struct dirent * +readdir(DIR *dirp) +{ + /* + * Read directory entry to buffer. We can safely ignore the return + * value as entry will be set to NULL in case of error. + */ + struct dirent *entry; + (void) readdir_r(dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry into called-allocated buffer. + * + * Returns zero on success. If the end of directory stream is reached, then + * sets result to NULL and returns zero. + */ +static int +readdir_r( + DIR *dirp, struct dirent *entry, struct dirent **result) +{ + /* Read next directory entry */ + WIN32_FIND_DATAW *datap = dirent_next(dirp->wdirp); + if (!datap) { + /* No more directory entries */ + *result = NULL; + return /*OK*/0; + } + + /* Attempt to convert file name to multi-byte string */ + size_t n; + int error = wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, + datap->cFileName, PATH_MAX + 1); + + /* + * If the file name cannot be represented by a multi-byte string, then + * attempt to use old 8+3 file name. This allows the program to + * access files although file names may seem unfamiliar to the user. + * + * Be ware that the code below cannot come up with a short file name + * unless the file system provides one. At least VirtualBox shared + * folders fail to do this. + */ + if (error && datap->cAlternateFileName[0] != '\0') { + error = wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, + datap->cAlternateFileName, PATH_MAX + 1); + } + + if (!error) { + /* Length of file name excluding zero terminator */ + entry->d_namlen = n - 1; + + /* Determine file type */ + DWORD attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) + entry->d_type = DT_CHR; + else if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) != 0) + entry->d_type = DT_LNK; + else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) + entry->d_type = DT_DIR; + else + entry->d_type = DT_REG; + + /* Get offset of next file */ + datap = dirent_next(dirp->wdirp); + if (datap) { + /* Compute 31-bit hash of the next directory entry */ + entry->d_off = dirent_hash(datap); + + /* Push the next directory entry back to cache */ + dirp->wdirp->cached = 1; + } else { + /* End of directory stream */ + entry->d_off = (long) ((~0UL) >> 1); + } + + /* Reset fields */ + entry->d_ino = 0; + entry->d_reclen = sizeof(struct dirent); + } else { + /* + * Cannot convert file name to multi-byte string so construct + * an erroneous directory entry and return that. Note that + * we cannot return NULL as that would stop the processing + * of directory entries completely. + */ + entry->d_name[0] = '?'; + entry->d_name[1] = '\0'; + entry->d_namlen = 1; + entry->d_type = DT_UNKNOWN; + entry->d_ino = 0; + entry->d_off = -1; + entry->d_reclen = 0; + } + + /* Return pointer to directory entry */ + *result = entry; + return /*OK*/0; +} + +/* Close directory stream */ +static int +closedir(DIR *dirp) +{ + int ok; + + if (!dirp) + goto exit_failure; + + /* Close wide-character directory stream */ + ok = _wclosedir(dirp->wdirp); + dirp->wdirp = NULL; + + /* Release multi-byte character version */ + free(dirp); + return ok; + +exit_failure: + /* Invalid directory stream */ + dirent_set_errno(EBADF); + return /*failure*/-1; +} + +/* Rewind directory stream to beginning */ +static void +rewinddir(DIR *dirp) +{ + if (!dirp) + return; + + /* Rewind wide-character string directory stream */ + _wrewinddir(dirp->wdirp); +} + +/* Get position of directory stream */ +static long +_wtelldir(_WDIR *dirp) +{ + if (!dirp || dirp->handle == INVALID_HANDLE_VALUE) { + dirent_set_errno(EBADF); + return /*failure*/-1; + } + + /* Read next file entry */ + WIN32_FIND_DATAW *datap = dirent_next(dirp); + if (!datap) { + /* End of directory stream */ + return (long) ((~0UL) >> 1); + } + + /* Store file entry to cache for readdir() */ + dirp->cached = 1; + + /* Return the 31-bit hash code to be used as stream position */ + return dirent_hash(datap); +} + +/* Get position of directory stream */ +static long +telldir(DIR *dirp) +{ + if (!dirp) { + dirent_set_errno(EBADF); + return -1; + } + + return _wtelldir(dirp->wdirp); +} + +/* Seek directory stream to offset */ +static void +_wseekdir(_WDIR *dirp, long loc) +{ + if (!dirp) + return; + + /* Directory must be open */ + if (dirp->handle == INVALID_HANDLE_VALUE) + goto exit_failure; + + /* Ensure that seek position is valid */ + if (loc < 0) + goto exit_failure; + + /* Restart directory stream from the beginning */ + FindClose(dirp->handle); + if (!dirent_first(dirp)) + goto exit_failure; + + /* Reset invalid flag so that we can read from the stream again */ + dirp->invalid = 0; + + /* + * Read directory entries from the beginning until the hash matches a + * file name. Be ware that hash code is only 31 bits longs and + * duplicates are possible: the hash code cannot return the position + * with 100.00% accuracy! Moreover, the method is slow for large + * directories. + */ + long hash; + do { + /* Read next directory entry */ + WIN32_FIND_DATAW *datap = dirent_next(dirp); + if (!datap) { + /* + * End of directory stream was reached before finding + * the requested location. Perhaps the file in + * question was deleted or moved out of the directory. + */ + goto exit_failure; + } + + /* Does the file name match the hash? */ + hash = dirent_hash(datap); + } while (hash != loc); + + /* + * File name matches the hash! Push the directory entry back to cache + * from where next readdir() will return it. + */ + dirp->cached = 1; + dirp->invalid = 0; + return; + +exit_failure: + /* Ensure that readdir will return NULL */ + dirp->invalid = 1; +} + +/* Seek directory stream to offset */ +static void +seekdir(DIR *dirp, long loc) +{ + if (!dirp) + return; + + _wseekdir(dirp->wdirp, loc); +} + +/* Scan directory for entries */ +static int +scandir( + const char *dirname, struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)) +{ + int result; + + /* Open directory stream */ + DIR *dir = opendir(dirname); + if (!dir) { + /* Cannot open directory */ + return /*Error*/ -1; + } + + /* Read directory entries to memory */ + struct dirent *tmp = NULL; + struct dirent **files = NULL; + size_t size = 0; + size_t allocated = 0; + while (1) { + /* Allocate room for a temporary directory entry */ + if (!tmp) { + tmp = (struct dirent*) malloc(sizeof(struct dirent)); + if (!tmp) + goto exit_failure; + } + + /* Read directory entry to temporary area */ + struct dirent *entry; + if (readdir_r(dir, tmp, &entry) != /*OK*/0) + goto exit_failure; + + /* Stop if we already read the last directory entry */ + if (entry == NULL) + goto exit_success; + + /* Determine whether to include the entry in results */ + if (filter && !filter(tmp)) + continue; + + /* Enlarge pointer table to make room for another pointer */ + if (size >= allocated) { + /* Compute number of entries in the new table */ + size_t num_entries = size * 2 + 16; + + /* Allocate new pointer table or enlarge existing */ + void *p = realloc(files, sizeof(void*) * num_entries); + if (!p) + goto exit_failure; + + /* Got the memory */ + files = (dirent**) p; + allocated = num_entries; + } + + /* Store the temporary entry to ptr table */ + files[size++] = tmp; + tmp = NULL; + } + +exit_failure: + /* Release allocated entries */ + for (size_t i = 0; i < size; i++) { + free(files[i]); + } + + /* Release the pointer table */ + free(files); + files = NULL; + + /* Exit with error code */ + result = /*error*/ -1; + goto exit_status; + +exit_success: + /* Sort directory entries */ + if (size > 1 && compare) { + qsort(files, size, sizeof(void*), + (int (*) (const void*, const void*)) compare); + } + + /* Pass pointer table to caller */ + if (namelist) + *namelist = files; + + /* Return the number of directory entries read */ + result = (int) size; + +exit_status: + /* Release temporary directory entry, if we had one */ + free(tmp); + + /* Close directory stream */ + closedir(dir); + return result; +} + +/* Alphabetical sorting */ +static int +alphasort(const struct dirent **a, const struct dirent **b) +{ + return strcoll((*a)->d_name, (*b)->d_name); +} + +/* Sort versions */ +static int +versionsort(const struct dirent **a, const struct dirent **b) +{ + return strverscmp((*a)->d_name, (*b)->d_name); +} + +/* Compare strings */ +static int +strverscmp(const char *a, const char *b) +{ + size_t i = 0; + size_t j; + + /* Find first difference */ + while (a[i] == b[i]) { + if (a[i] == '\0') { + /* No difference */ + return 0; + } + ++i; + } + + /* Count backwards and find the leftmost digit */ + j = i; + while (j > 0 && isdigit(a[j-1])) { + --j; + } + + /* Determine mode of comparison */ + if (a[j] == '0' || b[j] == '0') { + /* Find the next non-zero digit */ + while (a[j] == '0' && a[j] == b[j]) { + j++; + } + + /* String with more digits is smaller, e.g 002 < 01 */ + if (isdigit(a[j])) { + if (!isdigit(b[j])) { + return -1; + } + } else if (isdigit(b[j])) { + return 1; + } + } else if (isdigit(a[j]) && isdigit(b[j])) { + /* Numeric comparison */ + size_t k1 = j; + size_t k2 = j; + + /* Compute number of digits in each string */ + while (isdigit(a[k1])) { + k1++; + } + while (isdigit(b[k2])) { + k2++; + } + + /* Number with more digits is bigger, e.g 999 < 1000 */ + if (k1 < k2) + return -1; + else if (k1 > k2) + return 1; + } + + /* Alphabetical comparison */ + return (int) ((unsigned char) a[i]) - ((unsigned char) b[i]); +} + +/* Convert multi-byte string to wide character string */ +#if !defined(_MSC_VER) || _MSC_VER < 1400 +static int +dirent_mbstowcs_s( + size_t *pReturnValue, wchar_t *wcstr, + size_t sizeInWords, const char *mbstr, size_t count) +{ + /* Older Visual Studio or non-Microsoft compiler */ + size_t n = mbstowcs(wcstr, mbstr, sizeInWords); + if (wcstr && n >= count) + return /*error*/ 1; + + /* Zero-terminate output buffer */ + if (wcstr && sizeInWords) { + if (n >= sizeInWords) + n = sizeInWords - 1; + wcstr[n] = 0; + } + + /* Length of multi-byte string with zero terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + return 0; +} +#endif + +/* Convert wide-character string to multi-byte string */ +#if !defined(_MSC_VER) || _MSC_VER < 1400 +static int +dirent_wcstombs_s( + size_t *pReturnValue, char *mbstr, + size_t sizeInBytes, const wchar_t *wcstr, size_t count) +{ + /* Older Visual Studio or non-Microsoft compiler */ + size_t n = wcstombs(mbstr, wcstr, sizeInBytes); + if (mbstr && n >= count) + return /*error*/1; + + /* Zero-terminate output buffer */ + if (mbstr && sizeInBytes) { + if (n >= sizeInBytes) { + n = sizeInBytes - 1; + } + mbstr[n] = '\0'; + } + + /* Length of resulting multi-bytes string WITH zero-terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + return 0; +} +#endif + +/* Set errno variable */ +#if !defined(_MSC_VER) || _MSC_VER < 1400 +static void +dirent_set_errno(int error) +{ + /* Non-Microsoft compiler or older Microsoft compiler */ + errno = error; +} +#endif + +#ifdef __cplusplus +} +#endif +#endif /*DIRENT_H*/