From 1fd04557c706a5d0d35bca6e179edc1af6bbd9bb Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Tue, 13 Oct 2015 19:12:12 -0400 Subject: [PATCH] Refactored connect(), added checks to socket(), updated checklists --- netcon/NetconEthernetTap.cpp | 56 +++++++++++++++++------- netcon/intercept.c | 82 ++++++++++++++++++++++++----------- netcon/libintercept.so.1.0 | Bin 53400 -> 53656 bytes 3 files changed, 97 insertions(+), 41 deletions(-) diff --git a/netcon/NetconEthernetTap.cpp b/netcon/NetconEthernetTap.cpp index 5ef52b72c..0921f4602 100644 --- a/netcon/NetconEthernetTap.cpp +++ b/netcon/NetconEthernetTap.cpp @@ -503,7 +503,7 @@ int NetconEthernetTap::send_return_value(int fd, int retval, int _errno = 0) [I] EBADF - The descriptor is invalid. [I] ECONNABORTED - A connection has been aborted. [i] EFAULT - The addr argument is not in a writable part of the user address space. - [ ] EINTR - The system call was interrupted by a signal that was caught before a valid connection arrived; see signal(7). + [-] EINTR - The system call was interrupted by a signal that was caught before a valid connection arrived; see signal(7). [ ] EINVAL - Socket is not listening for connections, or addrlen is invalid (e.g., is negative). [I] EINVAL - (accept4()) invalid value in flags. [I] EMFILE - The per-process limit of open file descriptors has been reached. @@ -806,6 +806,12 @@ void NetconEthernetTap::handle_retval(PhySocket *sock, void **uptr, unsigned cha * @param structure containing the data and parameters for this client's RPC * + i := should be implemented in intercept lib + I := is implemented in intercept lib + X := is implemented in service + ? := required treatment Unknown + - := Not needed + [ ] EACCES - The address is protected, and the user is not the superuser. [X] EADDRINUSE - The given address is already in use. [I] EBADF - sockfd is not a valid descriptor. @@ -876,10 +882,16 @@ void NetconEthernetTap::handle_bind(PhySocket *sock, void **uptr, struct bind_st * @param structure containing the data and parameters for this client's RPC * - [?] EADDRINUSE - Another socket is already listening on the same port. - [X] EBADF - The argument sockfd is not a valid descriptor. - [i] ENOTSOCK - The argument sockfd is not a socket. - [i] EOPNOTSUPP - The socket is not of a type that supports the listen() operation. + i := should be implemented in intercept lib + I := is implemented in intercept lib + X := is implemented in service + ? := required treatment Unknown + - := Not needed + +[?] EADDRINUSE - Another socket is already listening on the same port. +[I] EBADF - The argument sockfd is not a valid descriptor. +[I] ENOTSOCK - The argument sockfd is not a socket. +[I] EOPNOTSUPP - The socket is not of a type that supports the listen() operation. */ void NetconEthernetTap::handle_listen(PhySocket *sock, void **uptr, struct listen_st *listen_rpc) @@ -890,6 +902,10 @@ void NetconEthernetTap::handle_listen(PhySocket *sock, void **uptr, struct liste fprintf(stderr, "handle_listen(): PCB is already in listening state.\n"); return; } + + // TODO: Implement liste_with_backlog + // FIXME: Correct return values from this method, most is handled in intercept lib + struct tcp_pcb* listening_pcb = lwipstack->tcp_listen(conn->pcb); if(listening_pcb != NULL) { conn->pcb = listening_pcb; @@ -924,13 +940,17 @@ void NetconEthernetTap::handle_listen(PhySocket *sock, void **uptr, struct liste * @param structure containing the data and parameters for this client's RPC * - TODO: set errno appropriately + i := should be implemented in intercept lib + I := is implemented in intercept lib + X := is implemented in service + ? := required treatment Unknown + - := Not needed [-] EACCES - Permission to create a socket of the specified type and/or protocol is denied. - [?] EAFNOSUPPORT - The implementation does not support the specified address family. - [?] EINVAL - Unknown protocol, or protocol family not available. - [?] EINVAL - Invalid flags in type. - [i] EMFILE - Process file table overflow. + [I] EAFNOSUPPORT - The implementation does not support the specified address family. + [I] EINVAL - Unknown protocol, or protocol family not available. + [I] EINVAL - Invalid flags in type. + [I] EMFILE - Process file table overflow. [i] ENFILE - The system limit on the total number of open files has been reached. [X] ENOBUFS or ENOMEM - Insufficient memory is available. The socket cannot be created until sufficient resources are freed. [?] EPROTONOSUPPORT - The protocol type or the specified protocol is not supported within this domain. @@ -974,23 +994,29 @@ void NetconEthernetTap::handle_socket(PhySocket *sock, void **uptr, struct socke * @param PhySocket associated with this RPC connection * @param structure containing the data and parameters for this client's RPC - --- Error handling in this method will only catch problems which are immeidately + --- Error handling in this method will only catch problems which are immedately apprent. Some errors will need to be caught in the nc_connected(0 callback - [i] EACCES - For UNIX domain sockets, which are identified by pathname: Write permission is denied ... + i := should be implemented in intercept lib + I := is implemented in intercept lib + X := is implemented in service + ? := required treatment Unknown + - := Not needed + + [-] EACCES - For UNIX domain sockets, which are identified by pathname: Write permission is denied ... [ ] EACCES, EPERM - The user tried to connect to a broadcast address without having the socket broadcast flag enabled ... [i] EADDRINUSE - Local address is already in use. [?] EAFNOSUPPORT - The passed address didn't have the correct address family in its sa_family field. [ ] EAGAIN - No more free local ports or insufficient entries in the routing cache. [ ] EALREADY - The socket is nonblocking and a previous connection attempt has not yet been completed. - [ ] EBADF - The file descriptor is not a valid index in the descriptor table. + [I] EBADF - The file descriptor is not a valid index in the descriptor table. [ ] ECONNREFUSED - No-one listening on the remote address. [i] EFAULT - The socket structure address is outside the user's address space. [ ] EINPROGRESS - The socket is nonblocking and the connection cannot be completed immediately. - [?] EINTR - The system call was interrupted by a signal that was caught. + [-] EINTR - The system call was interrupted by a signal that was caught. [X] EISCONN - The socket is already connected. [?] ENETUNREACH - Network is unreachable. - [ ] ENOTSOCK - The file descriptor is not associated with a socket. + [I] ENOTSOCK - The file descriptor is not associated with a socket. [X] ETIMEDOUT - Timeout while attempting connection. * diff --git a/netcon/intercept.c b/netcon/intercept.c index 5060aa2ce..4762b3091 100755 --- a/netcon/intercept.c +++ b/netcon/intercept.c @@ -528,6 +528,16 @@ int socket(SOCKET_SIG) return -EAFNOSUPPORT; if (socket_type < 0 || socket_type >= SOCK_MAX) return -EINVAL; + /* Check that we haven't hit the soft-limit file descriptors allowed */ + /* FIXME: Find number of open fds + struct rlimit rl; + getrlimit(RLIMIT_NOFILE, &rl); + if(sockfd >= rl.rlim_cur){ + errno = EMFILE; + return -1; + } + */ + /* FIXME: detect ENFILE condition */ #endif #ifdef DUMMY @@ -597,8 +607,21 @@ int socket(SOCKET_SIG) connect() intercept function */ int connect(CONNECT_SIG) { - int err; +#ifdef CHECKS + /* Check that this is a valid fd */ + if(fcntl(__fd, F_GETFD) < 0) { + return -1; + errno = EBADF; + } + /* Check that it is a socket */ + int sock_type; + socklen_t sock_type_len = sizeof(sock_type); + if(getsockopt(__fd, SOL_SOCKET, SO_TYPE, (void *) &sock_type, &sock_type_len) < 0) { + errno = ENOTSOCK; + return -1; + } /* FIXME: Check that address is in user space, return EFAULT ? */ +#endif #ifdef DUMMY dwr("connect(%d)\n", __fd); @@ -606,16 +629,17 @@ int connect(CONNECT_SIG) #else /* make sure we don't touch any standard outputs */ - if(__fd == STDIN_FILENO || __fd == STDOUT_FILENO || __fd == STDERR_FILENO) + if(__fd == STDIN_FILENO || __fd == STDOUT_FILENO || __fd == STDERR_FILENO){ + if (realconnect == NULL) { + dwr("Unresolved symbol: connect(). Library is exiting.\n"); + exit(-1); + } return(realconnect(__fd, __addr, __len)); - int sock_type = -1; - socklen_t sock_type_len = sizeof(sock_type); + } + struct sockaddr_in *connaddr; connaddr = (struct sockaddr_in *) __addr; - getsockopt(__fd, SOL_SOCKET, SO_TYPE, - (void *) &sock_type, &sock_type_len); - if(__addr != NULL && (connaddr->sin_family == AF_LOCAL || connaddr->sin_family == PF_NETLINK || connaddr->sin_family == AF_NETLINK @@ -624,13 +648,9 @@ int connect(CONNECT_SIG) return err; } - char cmd[BUF_SZ]; - if (realconnect == NULL) { - dwr("Unresolved symbol: connect()\n"); - return -1; - } - /* assemble and route command */ + int err; + char cmd[BUF_SZ]; memset(cmd, '\0', BUF_SZ); struct connect_st rpc_st; rpc_st.__tid = syscall(SYS_gettid); @@ -778,7 +798,6 @@ int accept(ACCEPT_SIG) #ifdef CHECKS /* Check that this is a valid fd */ if(fcntl(sockfd, F_GETFD) < 0) { - dwr("EBADF\n"); return -1; errno = EBADF; } @@ -787,13 +806,11 @@ int accept(ACCEPT_SIG) socklen_t sock_type_len = sizeof(sock_type); if(getsockopt(sockfd, SOL_SOCKET, SO_TYPE, (void *) &sock_type, &sock_type_len) < 0) { errno = ENOTSOCK; - dwr("ENOTSOCK\n"); return -1; } /* Check that this socket supports accept() */ if(!(sock_type && (SOCK_STREAM | SOCK_SEQPACKET))) { errno = EOPNOTSUPP; - dwr("EOPNOTSUPP\n"); return -1; } /* Check that we haven't hit the soft-limit file descriptors allowed */ @@ -801,7 +818,6 @@ int accept(ACCEPT_SIG) getrlimit(RLIMIT_NOFILE, &rl); if(sockfd >= rl.rlim_cur){ errno = EMFILE; - dwr("EMFILE\n"); return -1; } #endif @@ -822,16 +838,12 @@ int accept(ACCEPT_SIG) return -1; } - char gmybuf[16]; - int new_conn_socket; - - char c[1]; - int n = read(sockfd, c, sizeof(c)); + char gmybuf[16], c[1]; + int new_conn_socket, n = read(sockfd, c, sizeof(c)); if(n > 0) { ssize_t size = sock_fd_read(fdret_sock, gmybuf, sizeof(gmybuf), &new_conn_socket); - if(size > 0) - { + if(size > 0) { /* Send our local-fd number back to service so it can complete its mapping table */ memset(cmd, '\0', BUF_SZ); cmd[0] = RPC_FD_MAP_COMPLETION; @@ -869,9 +881,27 @@ int accept(ACCEPT_SIG) listen() intercept function */ int listen(LISTEN_SIG) { + #ifdef CHECKS + /* Check that this is a valid fd */ + if(fcntl(sockfd, F_GETFD) < 0) { + return -1; + errno = EBADF; + } + /* Check that it is a socket */ + int sock_type; + socklen_t sock_type_len = sizeof(sock_type); + if(getsockopt(sockfd, SOL_SOCKET, SO_TYPE, (void *) &sock_type, &sock_type_len) < 0) { + errno = ENOTSOCK; + return -1; + } + /* Check that this socket supports accept() */ + if(!(sock_type && (SOCK_STREAM | SOCK_SEQPACKET))) { + errno = EOPNOTSUPP; + return -1; + } + #endif + int err; - /* FIXME: Check that this socket supports listen(), return EOPNOTSUPP */ - /* FIXME: Check that the provided fd is a socket, return ENOTSOCK */ #ifdef DUMMY dwr("listen(%d)\n", sockfd); diff --git a/netcon/libintercept.so.1.0 b/netcon/libintercept.so.1.0 index 1dc1f0da0ae853d94bd429d850d81e0f1f459607..106c8c2061593866f0a44017a60d57438eff05b8 100755 GIT binary patch delta 14200 zcmbQSka@;p<_VgNCnsu|aT!=LfPsPK#H@XSZ;Zek1~@(0l5xIB&LLUr=_0`bKjThZ zX`TP={sxsMj?Hfvr!X?k-`vPt&m>gF2+_8bk%2*ofs27bX|fQTo0Nh&MA!lus*$iDYApoP0&d zyPj8`fx#nLWQPaih5rxudO@^CcL-4v>HgK=?DnCYy?AGO0;V zP7o<(e7O0Fh!7*=x5>{%zOfF`fEm5eA0NV}=J_ zJp29s|BJ+5|NndR+Sb4XFMsBGu z1hE#4zZ8u>7mYs^jo*vLZ-w%0VM)FgDgfh`A@iI6GkWy0rV24Il!$utibe^6-IyRW zxk6H|-baXmfxl%V0|P_ze>RU^R#z~8A($`e(aY)t=1&Cknc)0JFrU+-m(?C*Y-#g< zUXNZ@2M{~6`9GIOFRLAh9ozh$9i}F*`9Ft8FRLv`+_m{Xt4A-ZwGe|bLy2Yce<6=v zR%eieZu5T;7+bOVzc7pqa(9;?*xmL*lRc!~)L#%}U@$!JGVuHV|DZVA0p`AN`u_j_ z>vJBxwsRnSBM^V5N3U%ogs%kRFY)NLO^5J>K>Qw$URys1pXvMm|1V4agCZOpleRh# z@mJse|9=^dBwo)Ak+=ksFhi1fDFAZUi(MdbQ6%w`5b;GI@h|@n7Oa4XcY(w&BZ+rF z#EU@Un~=oQAmWpaWP&9wLf8|&{Quwhn1O+Vp+q9uqu2KIei9^(b#r=@QpYwx43oLGiEPf0jE{80>3?iNl%9m`& z5=~%<7oR?%ImiYgehVbtYikbSo%!_t|4T!#uNpu(#-rC(9U`*{BqI)%fp|!Qe{-$e zJVqu?!O5};W=xZLCxcK&wkyyDULMuLTb;f4K&|NmPL@OQ{F zg3_vw3Wraxiwe&!W(EcZh~oRlT~v5j7#J8{Ecpmg`UjMielzj6Ee4h4t#3;t__w<< zq&5Fw9+p#J z=!{VjJm#Vz!06JMqN3{2S)(HB(OIG*>e2bW(?vz|Mfu17|GRA^6%-hJI^Vrm`QiV6 z!vimhK<0G5?>zj%lS@yID0A6c{{O50r$tbUyOvtbf;e z|NjMa#SRQ$NsyfAFL?z9O!*z4TIBWY2`_HH`~UwqLj(f@14O99qt`Z`pMl}Uq7VQ7 zH=qCi!t2BT|7m)#lJ~#rB6$S{1fTi8YS-k$iW-ayC%;iNV*2-fvVxL{z>9Y%fnqp0 zMM+ux>3fj3+vOA(JbFu11YU@~2YdV;NG;goTi*Tu|6={*bxN{~m6OjX=`ngu{;i~@ zWc?P^g=;)|ZNKv}FuY)T4{_nD$7{8h!K69j0_CjwtTW6m-?vifSjwaO8}Hw zoIs9kJ-`oE;?WzUVgO1ApcK=s8z-&6;L&;iv&ZUg7Zo0l&iBV$R18EqT~rKTNWc64 zzgyLIuZ#l2E(R6`29M5rFD%|bTzC?syYu~v7jORmfARSBSU~%-K8mtb~NP$RAg{XUx3>JsSHMpb!71BJQ-2X!L z?f?HT96(Cy8PZ{PfJH{QnQO<)!X_SV`0P z@c;Mk|4TVuHopdyunaq1{r~^}Fe1@7hJ+d(2zKoJ0?CZOJsRH#fU@nESCAm7X933y z-h3Mg4ZepA3=G{aDjcl`_*=HZ@^B9*OnYr#vV*dJj*39%J;MVp+~0uG+fGnX3sge{tYPMK9mAQ@4FX;uR+GV zuzLOfzfb4$&ciPRUjP69g8LOHYCxqJIO~GE1CBXvE@f7#Lot zz(qmw9=)ckI2af_x>=uyDlm9-TNoaAk@Os}`~Sb;+5i6&p8fy7;MxEG z8=n3Df8g2w{}-N3{%4p~-*Wf={~lHbh9~#`|3AaXz##JA|9=}M1_qS}|No0HGBAWZ z{QqBvk%3{#!~g$%7#SE?9{vAc0^&dZ|NjX{{nP*dS(q3YN}m4zufoK@aOCO#{~;j$ zv;Y5dm>3wAJp2E@1;l^;|Nk5i|M~y_TbLLat~~$${|*yFJwwRL|NmK-85lrOB*VW=3<{Gc8p|^}Ox|d$&eZW>@-5>q zMuEwOCgMyh9!~Z$kz?eToM|HO>GAmge`kM6HISGmpMVb^2ZIU& z1H&Fh28I_;CQF&BF@1P4*~wH*(&O3x|EwTYP^`nizyOLBhslkm`iu;dH=4RLa!med z8ebp7z`y_s_6aZl{|9&4o%jTLnVk3}dYGO06xvuE`81kYL--DGFkR*1({SWdaO9J4 z;uCP<450X4F!`Zb zFy|X)28I)_|NrNjY-?_xX2Qb2FyZZgQ1=*Q1{k}58Zs;l3^U&T|L+8fJSRSZ9wtXV zi8hc2nppz*HgGVVM87-_? z|A1MJ7SgOTtPBhqn-eYmvrN9Qn`JYPoe49OvfgATM=vHOk;#3IHo`mAL9J$xdGL{r z$$>kCCm(R+;?XmJ$t7q^-m_hfaqDJPrwVTU{b~#h{oD);8jPWw%pLk5%?u0-nt7lQ zVqjo61&(bkKPdO=K8< zn2H$~)c=Y?ta+=(z~Bl|#AwK%E-3~P2I(z_2y-%XXhQ@+ddnCz7y~((-%OtDZ|DY6 zwFj=s09h4-dIi*|pK1&Yw_%FzYe5t-Xw^fdS=A?V1sLjqjMC+UI@SPc6ocklA&7cG zbp{6Gzfg{>`sB<2^LkBn1_sSpP-8$G^;J*~$gnjq!&IQIXV6>^l{QspU{K!=<$%=Q zfT=Ztt33jh2Ag{q%5hX@VBq2hJCcdB{=Wue2q&`xOpE$=Bvtw_RS-oUa7A3=5D$Tj z3PDxm3s=;hw1F*!U6$5HkGB7ZJq``V8Oim0o zmIg<8Et1@1nAQ)I*9U7eX{v9&6I{tu53O!_~l`~``t z$013#!lXaLrSp)am%yZ7z@_UD()A37Ve1E-!_U zXE+R#-U^de*F%zi1e0C~mv%#vW|V;XtQsyIf+VdV;S7~;fy?J1$=k!U_ravK+K{Bd z;W%A)a(PsJqzY3d4+8`9#0%xliChc}OnOY~IT;vSI5`;@n5>yra)9IxlshtUGcYjO zFdboHVBqCpU{DvAhXh5jIs=1(B-k-b9J?6QgP`K|>XZGW#Tc(nPK%af?4H~j?I<}- zoq=H^RA~(ZgXRgS(o>V~M_Ymm5KUcBw}gR#;UG5ygO)jz1JVa7UTYW_8074k85o2+ z;}{qi8lckPW*P$n1LGVO83qQMXCQ|sFo9G!^Dr>j{D$z|*cceTfcT=!3=B*OOl~X; z3`=<#7+mz(85o!{nW8`%m?xenciGFtz`&FRR=_;*Ou1tT$bbf>6jla?Fg^wb`_!T$ z2Bsdc1v~f{7@U%GQy7?f;i2dV4#j>ZM@|NYcYF*C$}1*+jFI$f2Wzm~rU(f(kbjC9 z;B5#dkl)l7LVdJIoq<738WN^pMWDvW8K|VHFatx_Wb0VvdNHV3peO*9lpr%fQQ!#` zuUBVaXoDJ4#=xK+4i%rz#lSEREFQvC%fO%+2Neeex_Snb0}AxRU`3%!Weg04)1cyy zp<>0LDldcy+yVl*Uh^tc0jL$G{tn6k75n@$V4rX@dl*9e&Y<-TD$S}6mSW=k&%hve zk%57MGYb?A3`!7%um&>YoP#n93=Vk=OiRF_Si;Z1V6)W`6i-W-KvHW#0u~$$AO#@) zGZ4QRl$s&(@&XJDHp>~o^2cQu7$O817+l$uCch6ApPV13?J0m^p`9!PgUwMk1_q`r zOd$3|kO^m4!TggP3=F40e9&wj(-yFkm<1UaT*V+J{)!W?SAd#`NSt=^3=B4*AU7Tb z>#z}IV6ZU)<*K7hpv3tW#7_ey#-rf0H%|zbIM0FAGf%t$O`GSz^2`(Olta?uHL$$D zFarZ5tv+B%31($rm>~>HobRED^FcWzaeiQ8;bdU=F3iB7ZVz=QD9H)QL1F`*LDhrO+-;~hD9sf?#X)KAHB=mw=DNY+AOl$PE>s2Born}@=m%B(7rVS>DO4V0ntC6U z14^Nq@?ej1GB?0e=rpJ_N(w#CR?om7{27!);S;Q&QAcp$b6gP?zcS1q6;niD@hii8 zhlPP5Pn3bd1{}XK%wIq=WMZ)RRRybOp11`XziP~&!ijm}j&i4>90n#s<|-~oA?%b~ z4=QV%nIR?igK|hI?a2%&r7x5_B{ML2GEZg#Dc@Y~QUpqEUQpBbl-uVqFhO`1#26Ud z5*Qd5b}KM2K#F!>W>C@2CeFa%Hi3bGVJ1jo4=4wPfc4vmGcc$xRRM=T0|O|QLlnS) z1}YrXcYu0}3=9lc)fgCx!Q!BPl?G!VGZzC3gZc@mG$?wz!P1cEJqr~FMF@jd9+cM& zE)+sQHIfEnAftl@V-WMiJ>{TYin1ms{uvlRsr3fbAaE)G6_Br>Q3gt_9Ey`yCMnhP zLXBJtDo_={?qxLBU<_fNc%Ym?-3KcBP>q4X3nI$|HjR1W=5mnLh75+^plY&^I4 zlNUPg4)TJgDKf`b6_Q%PJyy+Zs1!K46+k&4O>|2^@!zSOUel zICwOGfdLffTFT%O$`dLC3QvD!uzF5r6=;}(DqK*9KLM%;RE(uV6oFeWjZkq=0Al(3}Pp2bW?CKpapG0j2XjU{#^us&Oeu0y=hKcpA!i3{?P5 zabRUnp)#Okp$RH@KmiZpFt~#vL|lb|!2`tb096+rpfZ9%&l=R4iO^ADVBlol;HUs8 zA=x-UQ3ci%3fBax7d;RJs2XI@JIlbpz!aGU*1iIyoq>UYjl)QV0b*q*NH?hZ^8nR( zV9OcwY(b?$)I1fCl?&i{3RD;v^z=Cx>KT}#A@uu638U5<3VMi2dDsK&^ryP zUt^wwO_~5z&Bn14W;>%Q%)=m!40)4dT>caX;_&>RKCgUdio zl!Rko?FDe{eK75>;M(DV0(Jp|>@1M06Thm0V&lx@sp)a`scH-iT+DBvBS!Oiw=*y> zcuIrJR{}9PnH`|!d$}_+FtDxYWny6PddkGWz_t>^^b7&DVq0}tK!JAvs?^gBWKA1H zkiiMk!1PWjFJb^;rgEQDCI$u&7BQ;#y~V@80K%eqjJ^}u7#KiUG=tHXpP7LHghlr- z`hH|*U;tq@KW5(?Rt5$T7L#Z4^Ap^%lKofT|$qCSfY7COs zp(?dG7#Om_DnmG#XF{c=7z2etRk-vWs48;~1_sIZlP?sAaLiEH5>+ zm0@6zeF8Nqn1g|#l#PKw4pg&msF!05WHexqSA;ee;y6Gup`6UiCkIz(F&Su1uCI`{ zO^{(=5KAgeV_-DzWnf@9B*VZU8UTtIbC4w#vQQV8Z(?9zST756fh9;XL2mNxiX`^) zatsWz9h#F}DwXU1AlbEx9ctftSoq1ZL9=Zs2Lr=Jh%}=igQ6T%xRHZ_L0$vO0jZH= z3}rNAU{JgYHMW<7fkE~elmqq;%ui3ic7WzKgYYmulIrNx?*Dxn=cyWMC>RijjzIkDLIU|#T`evz4V@9R~jm`dDe-K6$4`;yDkv!x`haZFec1BAlC)X zhw%;~6hfI23^4aVRKj@QCLdfX<#rbuWU#FIDM1i+YClQ%ArN)Ure$U$iihTzF8lSKk1v%o|=pfda*nUKjGp_4gcCbI-g zW`Rk-?BD_^m@E)8nPoByOe|~ig=K28un`niXvdn7Ar!`BQ<`izR|boU5F{1zw#bBG znh}Ul0SXdOUHO?Ya6W{ML_#fvsb*vdf-%{YChuF0&GHB&6+h<4$ihZsM4@IeGK9jI zVh|5tst7?+F>i}ZJ*J94gbIjrL9vSI0odS;9l~-L^E+H97{(NZ_B%mlh0cfZ%#?#5 zRxmOIn?Y4rL1~Ce7%vPe%E%B5V@5%RAPQl;Y08ru=S#wemh|9G34t-U!-c|NOqhE> zMuos#XAZT1ks%DmggFPI5XK8fD1#sD$yRDTDJlX4nP7!cGKkRt$`Z?7Bd> z>nsrpVN95NKz0Vshw&m23ZYC?*9Fc;a$VvkNds7%{e}lr0E{UJH!B#%6jh%5afYN@ zGgL7wNEsOjgf3{L3KaQ_3}H=BQ=y?V2Wk{-7>yO2gC$_x4Nz%BnE(^H1(m@l6JQcB zpCOkCFfjqBBX}p@SSD8w8@k#D6=h@yg)wWOqaBP4!7%1ys1PXNL+8VI|Dd9b48bra z6SSWWQ3&IuKt&lDf}u<&h6bn@L?w*Z2^D2z2!=5yL4_a+VZ2qU;A{Z%#yYq+LSW1; zxa-1TOqjz#Muot=@gJcO#)LT>q7cS|x*b%3LYa;z-UykG>5VXK-Uyiw_r~N6Yo+pG zzJSF5BSRpJ`3UZ~7#I`T7lCkJfcnXhU^6QO$*jrTizSUP^#x+n7l_a|dEa^|S(vtMaHj{sn8#E>6<9uO zaO^c)MJS9p8#;=^$Pf%;?t}_K{0rkSJVxfiWAPLd*;kpO(WoFsni4hs=lZu0fSDGQ_}`FsmU7!8}kOLr5K*fMEWb57!s~ zW3E*LyEP0d0~==p7ii#i3QPp%PEJq>2x;EHgkY}a21!kPS`Kdezyx7-gC##RhRg>w zeqe%Yq1JAGy^)_)Dg>G~uS02A5}u?wIgwFJsvIf|lSWFviHu@g0>Y3KD5SnQW&2si z$tF9$NLoPqXb>3)$;`mR@Mf~(E^)>enL8FnZ(U%G3Ish+SS@(v6hy{w613=D_)wJ*0rOp3&J$hLefY_PM|G7MR zS?7V+vCaS4VQK=K|8scsvd#sGyEgx4_2^}tEy!TZP-5BqU&y1EbumanxB0&ajIG%G zUl_&)x%-s>*xmC5CwoY}sW%X0U@$!JGVuHV|DZUN0CQhBegFUe^*N7T+dl#z=@&*I z{!Wix+lLUo5{SRVqu2I0gf9f*_jvT$u7mKIzW@LKvgAJ~!oe|V+XE4Q_3i)vm*Gg_ z_1O@KOCSj|BneN5_%4vRD3Z7)M0^oQ{L4Rt1so9ZE|BWf{91JBA(H_0F+LPzX$T8jGpS)K_Jn$-5{Y%F$|Nl<_N$!U5 z4MBX5UfW7A_l44z|NmcBAvre>BF+U8PeKw;;-9Q4n^&I&k#73@|34^?9OjpY1lI(x zI7sMa+UNiO(_rEd^*^nh3@qvTu(25eLgaJXFEAxmIo-BhyBK$+8M&OrLos zhbrjTJ9b_P4fg2#+WFg~@r?uvDDN=ww_O4k#y%<>KD{m~JiC|~7#Q|}>dNCTDjcA) z^SFx&4+{eW!;6(4|Nr-BeOn^IzulD~t@#Hdf6Fum1`tbtzoiFcO0(?|1qD#XoW;Pv z;M1FDKK=#s0g}rrl_bM zb5Rjs^ysWnk@e^-Q4#g%eBbG!qWPl!|vMAM;@K;I`97%HBwMu@Mt_D;mE?k0G0IUwY|a(4*G8& z{{PVPBev(&UNJHY^nk=lO!T52qjgk?Q--pQsN+tpp?@|2HIC+bb zvbyO9kZZ5YDKL2SmZ%84NPQ1>uO3J(*u5<8|NnpSfAT*iS;mu-Wt8<8XHE82R#WPJ z`~UxOh6n}*28fH+c=XzC=Vf4c5%m85|K{`mU;LUpSy_X#@a_Nqtq1r!Iwv1dR^q(& z4#L|%`Mt6fN-x^>q{D=>I;-v8{ey4yvC z$D{N8F&7mBkxmyC!x!1_{{Qb*wdIvnVA#dL!oc9sdGAHX8;A>EfRae(`xh2(|Nnns z{ATiL74`a8GT^u|fJV(>NY;Dt`8BAbw!Or`z;K))o&g+F``^N1YCSB9UYv%g+Xhw# zYGoK6crhKWuHB>8wvGd=xd|rtG9R2xpslX@URzHdu!1CzqiSD+A|M?W0WV#^Y9U$b zCpQDb>udb-pybTp(QA7U#6R}J3*rKKunRy@XL#U+7~C@E|6qTBWDO6z`1c0tgO{(t zMWX{ledELb-@pGa<#;*yHK=f9*zxNB|Nn;(iOw-3)UoqRFgVA8!n6556MtJf0|P_n zKaXD9r|e+2FMk6{Uml&$!Flbo2Q;5ec>VuBC{wZebgSNx1ZOhFV=O9Moh~YzKAq2B zEO_%j_3rUK_(kFC|Nmd)yaELYxVULN0&)Q)gTWiF z|3#HR`XO?l+;w=eka|Pzs#hS7*QjuKbh@Z;yqNtG#1;L*1xgVIJv!g-69FZ%DCf~&SoW3S0grC$7oM;F|9^1=q^;Z9qu2B}7Xw4*A;SYN48dBA_bY((vw#%& zznuJE-I{9>Si<^+)T_xZ8grOxUQB+ZVaT(96J#UE3G-i0me)*W{4u#z(c1vqtA zzVYa_73O4Mcya5+|Nr|znd8L=kW{bj2@VE^7f0ZtAbF2o(_I`43?ALAEn*4`9^Dp( z2VOA2RqO!kGhM$~N9!H)6q2$5;|7RE(7?wQv|KEm*fnm#o z|Nli885o{C{QqBvk%2+w(f|KGj0_A@9{vAc0^&dU|NjX{{nP*dS(q3YSf2g=ufoK@ z5c2H*{}2%W`TzepObiSr&;S2#0r6k_|33%BfARnS7A6LUlo$X1-(g~?XL$1R|9=){ z28J&$|Noa^W?*>o>i>TqW(Ee6H~;@nVP;@ZdH4VSA7%!IkoW)p`>-%Dq`d$CKZS*X zq2&Gl|1~TO3|~I{|9^#rfx+eT|NkKDu{uxV)8d*A;}r{|NqwjML9^Efq_AWfq}tGU<+Ir85k1o z{r}Gn5{F`t#W9Qw3_l+K|Gxq%2BJXXpb(ufInh+yXvLHN{||$Np%^5)1Z3LN|Np`L zS5H0xA3hES6$S=|J&X(t6;CIxGF4-0cslu#shZ@CXaE1RLUn>D9R>ylP^?^-ENG_B zIAOA*nLFc*$(?5Lygm#J44`0Fc=i9k;^eg!5-C1>2RN86aq($5^C>v;NjUKdIPq~f za))y9X*i;YK*BHt)I50o|34eZ9w-JmHiwyk;m0df@el?Eh8AW9h7Xe)&4W4bFf%Yn zy!rp%XYyTh`#2RA1_p(9|No0Y%>Yq03=9l5EDQ`9@BaVa1QKxK6X;=bVH95ggMglCQbq^{7G6!r%!sID_i()}sJzl7>9Z+L67(Yw}Y7~Q3Jyg0*Z8BGYp&rO6T|TH|Z$OP=(0nTdvARo* zfx-AMlrv3ja%O;e{US962F+PeV?Z4BRZtGdur)BlT3}Xcu7^smQ)6II-w)-0)ZT!p zodj2V1S$Z7fE0nHwLX9; z1_n?xg3~_3(Rh$uO-mCa(|HW?H1S`A%>pQ$5H^cM+JWpn?-*u)3c#h=RB%4JI8A zmySb{ZiPt)!=>|(q?f>?J>b%H2c%Bd>l->09=|4R7)~I0;U!w%?_8ALz12Ylm4#_2}}kp zGbCwnWHEi!o_ri-b(3=3e=1#oFGS%|MdZUdD=VE@#@q_y;rq`^|1I+MRg zKzynp4)rOcF2oE5lU+#4-C@%9aOq1((rGYhL%8%SBIHRNe*Jo12u+vh9xk0 zS-89uLZ0C;Oj;Bst*(b8{Rk$_372+5l4g{E`s_9=Ak;&Uq!lEbq4H1Q@_9(|_Au@5 zVA5J`NYdbN{H`;(JgPoYim8%^fq{AAg>vUaE(QiB6{hu^3=A%uoD2+1hD<9tKynAl z9htZp7?_NhjxaGW@bWM)sEf-(g5r%D0|RIj6jc6k>|#(4f{Oo8o9rJg#^^UWEn1F| zWpZn@qa?pN1H(p1u)CRR7#K89K$SXAz8`G~E3+}F_K34U<2*y6hTgf zgh4R_ybS>g4+iz)P#>uZGcZ_6Pu5KppIi{jUC#&A0}ARuX^37>P&-1!f2c7qWJ1M3 zZ4G~@_2vaQsgJu|192B_f2~Z9wa6ttjD1gft7_??V#X;pcIGLo^!xAWi z+(iZk2F@%{NNs>BgEe0n=fuk}FgWBfFiik^^cx=ogUwb@$W34Z$$**wHWnNVAbAi! zgP(!HrWlmoAo2@A{N;>b`BWJOhFkm$46fV2mQ4N}Dm>XVRAlngIC0MdU?BzuguQoU z7#M7hvN13)Enot%g#{QGY|gNP`6oFT7<@qdr67|RfL+!qz`)>oOmXu2Q1Qtz@#6It zpeB|O;zZOAjmXzJ17T~B9B8fa58^@w_Dtx(sgPO zzi|F%V30e{R?om7{27!|;j@dNQ9K3)2F5uL6=Ctq!3u1=80RN@hi*>Dn^(m?kIOE%3)wqV6NhV6qioP^`KJ0m>E*G zJ}8HjrLxLD8!TmWD)cEmRy7Aq-k_P#%jq*Z@%7p}`o) z=%B$E#5{3NIRk@wzOp7L{uvlRskIDh5I7ZpQtK>elz~#~G6hgMSHGoPgE0aWkPPa3 zp|Wb~3=BsgS{cnX7(PJ2CatN5B&}%-<$xTaS)Yc)Q?G(@K*k(}8PfwZMr#t1G}r;FU{VZn^SBro zgnxrl^#Z6WXg`dBaZZ{NEZu(wmqTB~VCnv|2@@#cJBY&yw$DtUgb(WE*zoW&Ffc(h z+z@AAFkTBa_m14;eR1OT3=HlWYzz#-FF|%9CiWq*&p4-C8D?i1ICTn2FfiEUu`n<& zLHKPFuqrqMEUzpHD+){Cc?VJyRxsCbGBD&wGBBvWggU_j)C~Kh1de1*=Ht*%09C4> zPCWy7q=10|Q~-)7gB3A>3P1&@I4EoAK*ehq7}WKk;-CW11uP!IRL;PlX$lnwmw)z9 z4k&|UDnt8ypvH^~R00&83?87sn4rwS;0a=Qf+`J9P)Wg{XANq7L@ZTiVBlol;0USj z**HL<1lDs7t_M^}dLjr=oyeefmVtqRDe^H`_X@D?dIknIj@8Nx5G$Ef7#KW3m7pi6 z<^x;Kpl1sz8=}NjKvpgQYXa$csm#Ejrw=NGq9LxC0hI!~!y9A}*zKTn;R#9%40@+Q zm26A`*q{ke)%9!~Mk-L-tKgbIIvMm*Ke)EbV9M8_D2F;W1LWAa!(crP za6MCDdfuSxsb^sDT*Se^01aL-F79}7Tg01vi#lXM-_24p4 zdnLghth)fNn_U&^zht;>c%XO+gA8Jjodt4sVxB4}WL+oAWW?3qQ)OV_V)g;`%^4UN z=JRf6U|{f)23ep4VsbJ&KrQfgXJ%kvThYtJz~KFqiGhJ_C5Y)20%~Qp>au`*asaB- z%MD~r8$^)736gqzQp$@MK$xlAHiurn9YybFNc+Z0ffcmnf!cM85lrVG?CHInVW$DgvGxw`GH2FKv=Yd z$q$ml85l$tGx_dhU|;}Y2?xJIRtAPbR)%^8@%K#rD_9vA=CLv`NHc&~ep|U27(kdU zi_s6X;sAt2m00}Tm>3v9So{x@ZzmfA0|+xU`GH1WL0Fuf*;ky4fdPai7x;29GBAL! zlo3NA2Ll5OgFg!c0|z5$(t(GShk@Y)I|D<505m(o7Ax)F%$^m*#?+uTIl4fek$ZA= zfh=SAr!Q43giV%DF(@9d-r=DaKGn0|rS}b%-xTIT#pZAi~TOU(_;4%1r)U zAYab_U0=l@X%1DW&B4Hs4OSS!$;=6rmSPM9wUMN4po+{n7#Jk|p&XEo=`bC!z#!`~d3A-XP@@LKoHz~!2075Mt@z|y6KD5n(SJVrwX21Qe-ST6?ygRC2r z12R(%qKK0@1ZJjuz~-VVW0rb7MtezSJ}Yi(ZgojdVL?VAb_a=RPLhnE)kLCa!p>{7G&%F=^OKLhEL2~9A48o%$wVy z8JJiY*&^7OIXO!1a40QkEAEe+Z|xm&(&|5tDt|gX=GF zgg2i%_>`IZ+Vr|9%$#W)!Yv#VC+{uH@V1Kdj^xm9Z(hsH>%|<%A;heIjd@=^heBj= zdg&_=!JJWYo%s|4hr*4u%oR_V%Re!DPXLR{&AZOLul|}yXAAQSkmEj0`Eq7KWU;5W zcQeQ#k?V^Lm~&4s3#T#hY*y=Dks%ny?12hF6vB85C&w>UO4|gtDg?%qg(_!c2!k$?KO$8XbbV59Vo{{zup)uuLieHdrABHJF1T zcrwdmk$}l8FcAl+3_nOFWHLwSWR950ECG{QU=lFra)A^~7KoY5GMNP?mO1(Iay41l z=)+d1QH%_sFy=PJ$#$D%WH42Pz*S6EST2jjh(Ndk#mUz5WU#0RLQ-*X1vV8CNGfXQ z%E-coD?qJyP>N+_2!%0^K^%&yA_T4i>`+Vvfp7(2hr&lN-XTH*##|2VXfrYd!V+1{Gyw2!=6VL4_a+VLUcy|BI0!7|N7n;DU-lRKj>}%99h8O2S=t5AM1U z7&9Ge10zEij0tlO$fyvw>z*SN!k94kKor7w>`<35GK4{ysICi{kK{VRWeC^70*sL% z5XL+Lw=@REM0Q;u+;uMz3Smr`dqA!WoDbu1!d({wWum$+5ZQGKnL`(uh0` z6X}A=VB~R_1k7j1c^oEo0IHdH^6lkv^|0}qJgCzc8A4%99cXWyks%nyoD3BL1$^jy z7!TCdfcP*N#@vLW5XMu7_AD70f}u<&24|=k#7Gz~5Gu;Z5Da4`LWLj-VZ1r2;A{Z% zMjYH5Auy&D)G3S%VK64l;UJ?z;NDn|PzYnf91c+k<3Zidz{n5=Wukf`WIm=h!mxQG zWImENKq&&|4OkQ~G6ceyli;q4fiaQ25eWCjMub8b6XtM`Hv;Fwcypiu%E%A{iaiDf zhRFt-IAvf%K(cUi0wka+)h8FOmy&^vPw62P!2JtW02>7>M<{^%9;Cn+Hh8iIYAz!~ zD2xdk{{T5PbUuu?O?C3zMUq(bg<#Vcf~;@CVo4)R`vS4)3q>7&8JY1o0b;mj@MPWC(^ai_}21Y(32M+i=rE zU`!skrWhDg9xBAlF!5#w>&j#lVSow55x5Wow>4lQFn4o;NXd=Z37s6sEI#?bfdEjeTKdi+Co0<6s6^FH7VB*Xy z5cL-nCuber2kMb)9dTotps~5=$T4=NgJP4d&gcokd;+5nPOdzo&xzQ_wDF9&V1zNq zVGIl~+F