diff --git a/README.md b/README.md index 1fc2dda..7846e03 100644 --- a/README.md +++ b/README.md @@ -8,18 +8,15 @@ [![irc](https://img.shields.io/badge/IRC-%23zerotier%20on%20freenode-orange.svg)](https://webchat.freenode.net/?channels=zerotier) -Pre-Built Binaries/Packages Here: [zerotier.com/download.shtml](https://zerotier.com/download.shtml?pk_campaign=github_ZeroTierNAS) +Pre-Built Binaries/Packages Here: [zerotier.com/download.shtml](https://zerotier.com/download.shtml?pk_campaign=github_ZeroTierNAS). ## Example ``` -string str = "welcome to the machine"; -zts_start("./zt"); -while(!zts_running()) - sleep(1); -zts_join("e5cd7a7b1c0fa971"); -while(!zts_has_address(nwid)) - sleep(1); +char *str = "welcome to the machine"; +char *nwid = "c7cd7c9e1b0f52a2"; + +zts_simple_start("./zt", nwid); if((fd = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("error creating ZeroTier socket"); } diff --git a/artwork/AppIcon_87x87.png b/artwork/AppIcon_87x87.png new file mode 100644 index 0000000..2cdc696 Binary files /dev/null and b/artwork/AppIcon_87x87.png differ diff --git a/ext/picotcp/modules/pico_ipv6.c b/ext/picotcp/modules/pico_ipv6.c index 7cc25e3..08bbc48 100644 --- a/ext/picotcp/modules/pico_ipv6.c +++ b/ext/picotcp/modules/pico_ipv6.c @@ -1551,7 +1551,7 @@ static void pico_ipv6_nd_dad(pico_time now, void *arg) } else { if (l->dup_detect_retrans-- == 0) { - dbg("IPv6: DAD verified valid address.\n"); + //dbg("IPv6: DAD verified valid address.\n"); l->istentative = 0; } else { /* Duplicate Address Detection */ diff --git a/include/ZeroTierSDK.h b/include/ZeroTierSDK.h index ed94494..4e84caf 100644 --- a/include/ZeroTierSDK.h +++ b/include/ZeroTierSDK.h @@ -37,6 +37,7 @@ #define ZT_PHY_POLL_INTERVAL 10 // ms #define ZT_ACCEPT_RECHECK_DELAY 100 // ms (for blocking zts_accept() calls) #define ZT_CONNECT_RECHECK_DELAY 100 // ms (for blocking zts_connect() calls) +#define ZT_API_CHECK_INTERVAL 500 // ms #define MAX_PICO_FRAME_RX_BUF_SZ ZT_MAX_MTU * 128 @@ -116,6 +117,11 @@ extern "C" { */ void zts_start(const char *path); +/** + * Convenience function: Starts, waits for service, joins one network, waits for address, returns + */ +void zts_simple_start(const char *path, const char *nwid); + /** * Stops the core ZeroTier service */ @@ -367,6 +373,12 @@ namespace ZeroTier */ int zts_nsockets(); +/* + * Returns maximum number of sockets allowed by network stack + */ +int zts_maxsockets(); + +int pico_ntimers(); /** * Don't call this directly, use 'zts_start()' */ diff --git a/src/SocketTap.cpp b/src/SocketTap.cpp index 49c8f11..e1406a2 100644 --- a/src/SocketTap.cpp +++ b/src/SocketTap.cpp @@ -198,6 +198,13 @@ namespace ZeroTier { _multicastGroups.swap(newGroups); } + void SocketTap::setMtu(unsigned int mtu) + { + if (_mtu != mtu) { + _mtu = mtu; + } + } + void SocketTap::threadMain() throw() { diff --git a/src/SocketTap.hpp b/src/SocketTap.hpp index 6983cfd..c6e488b 100644 --- a/src/SocketTap.hpp +++ b/src/SocketTap.hpp @@ -118,6 +118,11 @@ namespace ZeroTier { */ void scanMulticastGroups(std::vector &added,std::vector &removed); + /* + * + */ + void setMtu(unsigned int mtu); + /* * */ diff --git a/src/ZeroTierSDK.cpp b/src/ZeroTierSDK.cpp index a8e6f8c..5e84ece 100644 --- a/src/ZeroTierSDK.cpp +++ b/src/ZeroTierSDK.cpp @@ -101,6 +101,16 @@ void zts_start(const char *path) pthread_create(&service_thread, NULL, zts_start_service, (void *)(path)); } +void zts_simple_start(const char *path, const char *nwid) +{ + zts_start(path); + while(!zts_running()) + usleep(ZT_API_CHECK_INTERVAL * 1000); + zts_join(nwid); + while(!zts_has_address(nwid)) + usleep(ZT_API_CHECK_INTERVAL * 1000); +} + void zts_stop() { if(zt1Service) { zt1Service->terminate(); @@ -337,7 +347,7 @@ int zts_socket(ZT_SOCKET_SIG) { else { ZeroTier::_multiplexer_lock.lock(); //DEBUG_INFO("unmap=%d, fdmap=%d", ZeroTier::unmap.size(), ZeroTier::fdmap.size()); - DEBUG_INFO("timers = %d, max = %d", pico_ntimers(), PICO_MAX_TIMERS); + //DEBUG_INFO("timers = %d, max = %d", pico_ntimers(), PICO_MAX_TIMERS); if(pico_ntimers() >= PICO_MAX_TIMERS) { DEBUG_ERROR("cannot provision additional socket due to limitation of PICO_MAX_TIMERS. current = %d", pico_ntimers()); errno = EMFILE; @@ -413,7 +423,7 @@ Darwin: [ ] [ECONNRESET] Remote host reset the connection request. */ int zts_connect(ZT_CONNECT_SIG) { - DEBUG_INFO("fd = %d", fd); + //DEBUG_INFO("fd = %d", fd); int err = 0; if(fd < 0) { errno = EBADF; @@ -490,7 +500,6 @@ int zts_connect(ZT_CONNECT_SIG) { // non-blocking if(err == 0 && !blocking) { - DEBUG_INFO("NONBLOCKING!"); errno = EINPROGRESS; err = -1; } @@ -498,7 +507,6 @@ int zts_connect(ZT_CONNECT_SIG) { { // FIXME: Double check that accept/connect queues in multithreaded apps don't get mixed up if(err == 0 && blocking) { - DEBUG_INFO("BLOCKING!"); bool complete = false; while(true) { @@ -627,7 +635,7 @@ int zts_listen(ZT_LISTEN_SIG) { ZeroTier::_multiplexer_lock.lock(); std::pair *p = ZeroTier::fdmap[fd]; if(!p) { - DEBUG_ERROR("unable to locate connection pair (did you zbind()?"); + DEBUG_ERROR("unable to locate connection pair. did you bind?"); return -1; } ZeroTier::Connection *conn = p->first; @@ -752,7 +760,7 @@ EPERM Firewall rules forbid connection. */ int zts_setsockopt(ZT_SETSOCKOPT_SIG) { - DEBUG_INFO("fd = %d", fd); + //DEBUG_INFO("fd = %d", fd); int err = 0; if(fd < 0) { errno = EBADF; @@ -776,7 +784,7 @@ int zts_setsockopt(ZT_SETSOCKOPT_SIG) */ int zts_getsockopt(ZT_GETSOCKOPT_SIG) { - DEBUG_INFO("fd = %d", fd); + //DEBUG_INFO("fd = %d", fd); int err = 0; if(fd < 0) { errno = EBADF; @@ -919,7 +927,7 @@ int zts_close(ZT_CLOSE_SIG) int zts_fcntl(ZT_FCNTL_SIG) { - DEBUG_INFO("fd = %d", fd); + //DEBUG_INFO("fd = %d", fd); int err; if(fd < 0) { errno = EBADF; @@ -1242,7 +1250,7 @@ namespace ZeroTier { #endif /****************************************************************************/ -/* SDK Socket API Helper functions --- DONT CALL THESE DIRECTLY */ +/* SDK Socket API Helper functions --- DON'T CALL THESE DIRECTLY */ /****************************************************************************/ int zts_nsockets() @@ -1253,6 +1261,12 @@ int zts_nsockets() return num; } +int zts_maxsockets() +{ + // TODO: This is only an approximation + return PICO_MAX_TIMERS - 10; +} + // Starts a ZeroTier service in the background void *zts_start_service(void *thread_id) { diff --git a/src/picoTCP.cpp b/src/picoTCP.cpp index 7247257..c4a1cd7 100644 --- a/src/picoTCP.cpp +++ b/src/picoTCP.cpp @@ -353,7 +353,7 @@ namespace ZeroTier { int pico_eth_send(struct pico_device *dev, void *buf, int len) { - DEBUG_INFO(); + //DEBUG_INFO(); SocketTap *tap = (SocketTap*)(dev->tap); if(!tap) { DEBUG_ERROR("invalid dev->tap"); @@ -367,7 +367,7 @@ namespace ZeroTier { dest_mac.setTo(ethhdr->daddr, 6); tap->_handler(tap->_arg,NULL,tap->_nwid,src_mac,dest_mac, Utils::ntoh((uint16_t)ethhdr->proto),0, ((char*)buf) + sizeof(struct pico_eth_hdr),len - sizeof(struct pico_eth_hdr)); - DEBUG_INFO("len = %d", len); + //DEBUG_INFO("len = %d", len); return len; } @@ -453,9 +453,9 @@ namespace ZeroTier { char ipv4_str[INET_ADDRSTRLEN]; inet_ntop(AF_INET, (const void *)&in4->sin_addr.s_addr, ipv4_str, INET_ADDRSTRLEN); pico_string_to_ipv4(ipv4_str, &(zaddr.addr)); - DEBUG_ATTN("addr=%s:%d", ipv4_str, Utils::ntoh( in4->sin_port )); + //DEBUG_ATTN("addr=%s:%d", ipv4_str, Utils::ntoh( in4->sin_port )); err = pico_socket_connect(conn->picosock, &zaddr, in4->sin_port); - DEBUG_INFO("connect_err = %d", err); + //DEBUG_INFO("connect_err = %d", err); #elif defined(SDK_IPV6) struct pico_ip6 zaddr; @@ -463,7 +463,7 @@ namespace ZeroTier { char ipv6_str[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &(in6->sin6_addr), ipv6_str, INET6_ADDRSTRLEN); pico_string_to_ipv6(ipv6_str, zaddr.addr); - DEBUG_ATTN("addr=%s:%d", ipv6_str, Utils::ntoh(in6->sin6_port)); + //DEBUG_ATTN("addr=%s:%d", ipv6_str, Utils::ntoh(in6->sin6_port)); err = pico_socket_connect(conn->picosock, &zaddr, in6->sin6_port); #endif @@ -687,7 +687,7 @@ namespace ZeroTier { int picoTCP::pico_Close(Connection *conn) { - DEBUG_INFO(); + //DEBUG_INFO(); if(!conn || !conn->picosock) return ZT_ERR_GENERAL_FAILURE; int err; diff --git a/test/selftest.cpp b/test/selftest.cpp index 92a483e..abc60f0 100644 --- a/test/selftest.cpp +++ b/test/selftest.cpp @@ -37,9 +37,13 @@ #include #include #include +#include #include #include +#include +#include +#include #include "ZeroTierSDK.h" @@ -47,7 +51,7 @@ #define FAILED -1 #define ECHO_INTERVAL 100000 // us -#define SLAM_INTERVAL 50000 +#define SLAM_INTERVAL 500000 #define STR_SIZE 32 #define TEST_OP_N_BYTES 10 @@ -65,6 +69,8 @@ char str[STR_SIZE]; +std::map testConf; + /* Tests in this file: Basic RX/TX connect()/accept() Functionality: @@ -99,6 +105,30 @@ Correctness: */ +void displayResults(int *results, int size) +{ + int success = 0, failure = 0; + for(int i=0; i 0) { - if((err = zts_close(sock)) < 0) { - std::cout << "error closing socket (errno = " << errno << ")" << std::endl; - //return -1; - } - } + // connect() + if(false) { + server = gethostbyname2("::",AF_INET6); + memset((char *) &addr6, 0, sizeof(addr6)); + addr6.sin6_flowinfo = 0; + addr6.sin6_family = AF_INET6; + addr6.sin6_port = htons(port); + addr6.sin6_addr = in6addr_any; + err = zts_connect(sock, (struct sockaddr *)&addr6, (socklen_t)(sizeof addr6)); } + if(true) { + addr.sin_port = htons(port); + addr.sin_addr.s_addr = inet_addr("10.9.9.51"); + //addr.sin_addr.s_addr = htons(INADDR_ANY); + addr.sin_family = AF_INET; + err = zts_connect(sock, (struct sockaddr *)&addr, (socklen_t)(sizeof addr)); + } + + if(errno != EINPROGRESS) { // acceptable error for non-block mode + if(err < 0) + std::cout << "error connecting socket (errno = " << errno << ")" << std::endl; + results[j] = std::min(results[j], err); + } + + // close() + if((err = zts_close(sock)) < 0) + std::cout << "error closing socket (errno = " << errno << ")" << std::endl; + results[j] = std::min(results[j], err); } + + //while(pico_ntimers() > start_stack_timer_count) { + // sleep(10); + // printf("timers = %d\n", pico_ntimers()); + //} + + displayResults(results, num_times); if(zts_nsockets() == 0) std::cout << "PASSED [slam open, connect, close]" << std::endl; else std::cout << "FAILED [slam open, connect, close]" << std::endl; } - } @@ -829,8 +864,9 @@ int do_test(std::string path, std::string nwid, int type, int protocol, int mode int main(int argc , char *argv[]) { - if(argc < 3) { - printf("usage: ./unit <4|6> \n"); + if(argc < 1) { + printf("usage(1): ./unit <4|6> \n"); + printf("usage(2): selftest.conf\n"); return 1; } @@ -839,40 +875,96 @@ int main(int argc , char *argv[]) int protocol = 0; int mode = 0; int port = 0; + int local_port = 0; + int remote_port = 0; int operation = 0; int n_count = 0; int delay = 0; std::string path = argv[1]; - std::string nwid = argv[2]; - std::string stype = argv[3]; - std::string ipstr, ipstr6; + std::string nwid; + std::string stype; + std::string ipstr, ipstr6, local_ipstr, local_ipstr6, remote_ipstr, remote_ipstr6; memcpy(str, "welcome to the machine", 22); + // if a test config file was specified: + // load addresses/path, perform comprehensive test + if(path.find(".conf") != std::string::npos) + { + loadTestConfigFile(path); + nwid = testConf["nwid"]; + path = testConf["local_path"]; + stype = "comprehensive"; + local_ipstr = testConf["local_ipv4"]; + local_ipstr6 = testConf["local_ipv6"]; + remote_ipstr = testConf["remote_ipv4"]; + remote_ipstr6 = testConf["remote_ipv6"]; + std::string smode = testConf["mode"]; + + if(strcmp(smode.c_str(), "server") == 0) + mode = TEST_MODE_SERVER; + else + mode = TEST_MODE_CLIENT; + + local_port = atoi(testConf["local_port"].c_str()); + remote_port = atoi(testConf["remote_port"].c_str()); + + fprintf(stderr, "local_ipstr = %s\n", local_ipstr.c_str()); + fprintf(stderr, "local_ipstr6 = %s\n", local_ipstr6.c_str()); + fprintf(stderr, "remote_ipstr = %s\n", remote_ipstr.c_str()); + fprintf(stderr, "remote_ipstr6 = %s\n", remote_ipstr6.c_str()); + + fprintf(stderr, "remote_port = %d\n", remote_port); + fprintf(stderr, "local_port = %d\n", local_port); + } + else + { + nwid = argv[2]; + stype = argv[3]; + } + + fprintf(stderr, "path = %s\n", path.c_str()); + fprintf(stderr, "nwid = %s\n", nwid.c_str()); + fprintf(stderr, "type = %s\n", stype.c_str()); + // If we're performing a non-random test, join the network we want to test on - // and wait until the service initializes the SocketTap and provides an address - if(stype == "simple" || stype == "sustained" || stype == "comprehensive") { - zts_start(path.c_str()); - printf("waiting for service to start...\n"); - while(!zts_running()) - sleep(1); - printf("joining network...\n"); - zts_join(nwid.c_str()); - printf("waiting for address assignment...\n"); - while(!zts_has_address(nwid.c_str())) - sleep(1); + // and wait until the service initializes and provides an address + + if(stype == "simple") { + + printf("waiting for libzt to come online\n"); + zts_simple_start(path.c_str(), nwid.c_str()); + + // What follows is a long-form of zts_simple_start() + + // zts_start(path.c_str()); + // printf("waiting for service to start...\n"); + // while(!zts_running()) + // sleep(1); + // printf("joining network...\n"); + // zts_join(nwid.c_str()); + // printf("waiting for address assignment...\n"); + // while(!zts_has_address(nwid.c_str())) + // sleep(1); + printf("complete\n"); } - slam_api_test(); - return 0; + // SLAM + // Perform thsouands of repetitions of the same plausible API sequences to detect faults + if(stype == "slam") + { + slam_api_test(); + return 0; + } // SIMPLE // performs a one-off test of a particular subset of the API // For instance (ipv4 client, ipv6 server, etc) if(stype == "simple") { + printf("performing SIMPLE test\n"); // Parse args type = TEST_TYPE_SIMPLE; protocol = atoi(argv[4]); @@ -895,6 +987,7 @@ int main(int argc , char *argv[]) // Performs a stress test for benchmarking performance if(stype == "sustained") { + printf("performing SUSTAINED test\n"); type = TEST_TYPE_SUSTAINED; protocol = atoi(argv[4]); if(!strcmp(argv[5],"client")) @@ -935,7 +1028,9 @@ int main(int argc , char *argv[]) // Tests ALL API calls if(stype == "comprehensive") { + printf("performing COMPREHENSIVE test\n"); // Parse args + /* type = TEST_TYPE_SIMPLE; if(!strcmp(argv[4],"client")) mode = TEST_MODE_CLIENT; @@ -944,47 +1039,61 @@ int main(int argc , char *argv[]) ipstr = argv[5]; ipstr6 = argv[6]; port = atoi(argv[7]); + */ /* Each host must operate as the counterpoint to the other, thus, each mode * will call the same test helper functions in different orders * Additionally, the test will use the preset paremeters below for the test: */ - int test = 0; - printf("comprehensive\n"); - printf("test = %d\n", test); - test = !test; - printf("test = %d\n", test); - delay = 0; n_count = 10; type = TEST_TYPE_SIMPLE; operation = TEST_OP_N_TIMES; - // IPV4 - protocol = 4; - // perform first test arrangement - do_test(path, nwid, type, protocol, mode, ipstr, port, operation, n_count, delay); - sleep(1); - do_test(path, nwid, type, protocol, mode, ipstr, port, operation, n_count, delay); - sleep(1); - // swtich modes - if(mode == TEST_MODE_SERVER) - mode = TEST_MODE_CLIENT; - else if(mode == TEST_MODE_CLIENT) - mode = TEST_MODE_SERVER; - // perform second test arrangement - do_test(path, nwid, type, protocol, mode, ipstr, port, operation, n_count, delay); - sleep(1); - do_test(path, nwid, type, protocol, mode, ipstr, port, operation, n_count, delay); + if(mode == TEST_MODE_SERVER) { + printf("starting comprehensive test as SERVER\n"); + port = local_port; + ipstr = local_ipstr; + } + else if(mode == TEST_MODE_CLIENT) { + printf("starting comprehensive test as CLIENT\n"); + sleep(10); // give the server some time to come online before beginning test + port = remote_port; + ipstr = remote_ipstr; + } + // IPV4 (first test) + protocol = 4; + do_test(path, nwid, type, protocol, mode, ipstr, port, operation, n_count, delay); + sleep(3); + + // swtich modes (client/server) + if(mode == TEST_MODE_SERVER) { + printf("switching from SERVER to CLIENT mode\n"); + port = remote_port; + ipstr = remote_ipstr; + mode = TEST_MODE_CLIENT; + } + else if(mode == TEST_MODE_CLIENT) { + printf("switching from CLIENT to SERVER mode\n"); + port = local_port; + ipstr = local_ipstr; + mode = TEST_MODE_SERVER; + } + + // IPV4 (second test) + do_test(path, nwid, type, protocol, mode, ipstr, port, operation, n_count, delay); + sleep(3); + + /* // IPV6 protocol = 6; // perform first test arrangement do_test(path, nwid, type, protocol, mode, ipstr6, port, operation, n_count, delay); sleep(1); - do_test(path, nwid, type, protocol, mode, ipstr6, port, operation, n_count, delay); - sleep(1); + //do_test(path, nwid, type, protocol, mode, ipstr6, port, operation, n_count, delay); + //sleep(1); // swtich modes if(mode == TEST_MODE_SERVER) mode = TEST_MODE_CLIENT; @@ -992,9 +1101,9 @@ int main(int argc , char *argv[]) mode = TEST_MODE_SERVER; // perform second test arrangement do_test(path, nwid, type, protocol, mode, ipstr6, port, operation, n_count, delay); - sleep(1); - do_test(path, nwid, type, protocol, mode, ipstr6, port, operation, n_count, delay); - + //sleep(1); + //do_test(path, nwid, type, protocol, mode, ipstr6, port, operation, n_count, delay); + */ /* ipv4_tcp_client_test diff --git a/test/testhost1.conf b/test/testhost1.conf new file mode 100644 index 0000000..41eb0f8 --- /dev/null +++ b/test/testhost1.conf @@ -0,0 +1,12 @@ +nwid c7cd7c9e1b0f52a2 +mode client + +local_path zt1 +local_port 4545 +local_ipv4 10.9.9.40 +local_ipv6 fde5:cd72:9e17:0fdb:7e99:1369:4d5b:fe3f + +remote_path zt2 +remote_port 4545 +remote_ipv4 10.9.9.41 +remote_ipv6 fde5:cd7a:9edc:0f12:7399:98b2:5722:9143 \ No newline at end of file diff --git a/test/testhost2.conf b/test/testhost2.conf new file mode 100644 index 0000000..ce3443e --- /dev/null +++ b/test/testhost2.conf @@ -0,0 +1,12 @@ +nwid c7cd7c9e1b0f52a2 +mode server + +local_path zt2 +local_port 4545 +local_ipv4 10.9.9.41 +local_ipv6 fde5:cd7a:9edc:0f12:7399:98b2:5722:9143 + +remote_path zt1 +remote_port 4545 +remote_ipv4 10.9.9.40 +remote_ipv6 fde5:cd72:9e17:0fdb:7e99:1369:4d5b:fe3f \ No newline at end of file diff --git a/zt1/networks.d/e5cd7a9e1c0fd272.conf b/zt1/networks.d/e5cd7a9e1c0fd272.conf new file mode 100644 index 0000000..b76b150 --- /dev/null +++ b/zt1/networks.d/e5cd7a9e1c0fd272.conf @@ -0,0 +1,16 @@ +v=7 +nwid=e5cd7a9e1c0fd272 +ts=15c5afae27a +ctmd=6ddd00 +r=3a +id=82dc95f9d5 +f=6 +ml=20 +t=0 +n=OPNET +mtu=af0 +C=\0\0\0\0\0\0\0\0\0\0\0\\Zz\0\0\0\0\0m\0\0\0\0\0\0\0\0zr\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0ܕzM\emDĺv_4IĶK۬ޖn6 12AI?ЈĮ}Vם3@3a,|FSd,ߓ^(|JBE&\0Eм +COO=zr\0\0\\Zz\0\0\0\0\0\0\0\0\0\0\0\0zrܕ\n 2\0\0\0\0\0\0\0\0\0\0\0\0ܕz\0`8&zE?W}++G2?T{{U/d39.q{jP#luLAΙ\e>qˊ\0(w\0\0 +RT=\n \0\0\0\0\0\0\0\0 +I=zrܕ\0X\n 2\0 +R=\0\0\0\0 \ No newline at end of file diff --git a/zt2/networks.d/e5cd7a9e1c0fd272.conf b/zt2/networks.d/e5cd7a9e1c0fd272.conf new file mode 100644 index 0000000..63b2d3c --- /dev/null +++ b/zt2/networks.d/e5cd7a9e1c0fd272.conf @@ -0,0 +1,16 @@ +v=7 +nwid=e5cd7a9e1c0fd272 +ts=15c5af60144 +ctmd=6ddd00 +r=3a +id=9b202b6db2 +f=6 +ml=20 +t=0 +n=OPNET +mtu=af0 +C=\0\0\0\0\0\0\0\0\0\0\0\\ZD\0\0\0\0\0m\0\0\0\0\0\0\0\0zr\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 +mz<ן%LpA\euS;Gw|W*y`C*k̥\\^QKf m2faq 0Kc/V C搬vC]Rӗ +COO=zr\0\0\\ZD\0\0\0\0\0\0\0\0\0\0\0\0zr +m\n 3\0\0\0\0\0\0\0\0\0\0\0\0 +mz\0`; lTw͐^ot??Fج$2czT\nT'S.Fa46$3`WT5V&n9ByI.Pu\0\0 +RT=\n \0\0\0\0\0\0\0\0 +I=zr +m\0X\n 3\0 +R=\0\0\0\0 \ No newline at end of file