/* vim: set expandtab ts=4 sw=4: */ /* * You may redistribute this program and/or modify it under the terms of * the GNU General Public License as published by the Free Software Foundation, * either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #define _POSIX_C_SOURCE 200112L #include "client/AdminClient.h" #include "admin/angel/Core.h" #include "admin/angel/InterfaceWaiter.h" #include "client/Configurator.h" #include "crypto/Key.h" #include "benc/Dict.h" #include "benc/Int.h" #include "benc/List.h" #include "benc/serialization/BencSerializer.h" #include "benc/serialization/json/JsonBencSerializer.h" #include "benc/serialization/json/JsonBencMessageReader.h" #include "benc/serialization/standard/BencMessageReader.h" #include "benc/serialization/standard/BencMessageWriter.h" #include "crypto/random/test/DeterminentRandomSeed.h" #include "crypto/AddressCalc.h" #include "crypto/CryptoAuth.h" #include "dht/Address.h" #include "exception/Except.h" #include "interface/Iface.h" #include "io/ArrayReader.h" #include "io/FileWriter.h" #include "io/Reader.h" #include "io/Writer.h" #include "memory/Allocator.h" #include "memory/MallocAllocator.h" #include "util/AddrTools.h" #include "util/ArchInfo.h" #include "util/Assert.h" #include "util/Base32.h" #include "util/CString.h" #include "util/Defined.h" #include "util/events/UDPAddrIface.h" #include "util/events/Time.h" #include "util/events/EventBase.h" #include "util/events/Pipe.h" #include "util/events/Process.h" #include "util/events/FakeNetwork.h" #include "util/Hex.h" #include "util/log/Log.h" #include "util/log/FileWriterLog.h" #include "util/SysInfo.h" #include "util/version/Version.h" #include "net/Benchmark.h" #include #include #include #include #include #include #include #define DEFAULT_TUN_DEV "tun0" #ifndef CJD_PACKAGE_VERSION #define CJD_PACKAGE_VERSION "unknown" #endif static int genconf(struct Allocator* alloc, struct Random* rand, bool eth, bool seed) { if (seed) { uint8_t seedbuf[64]; Bits_memset(seedbuf, 0, 64); Assert_true(64 == read(STDIN_FILENO, seedbuf, 64)); struct RandomSeed* rs = DeterminentRandomSeed_new(alloc, seedbuf); rand = Random_newWithSeed(alloc, NULL, rs, NULL); } uint8_t password[32]; uint8_t password2[32]; uint8_t password3[32]; uint8_t password4[32]; Random_base32(rand, password, 32); Random_base32(rand, password2, 32); Random_base32(rand, password3, 32); Random_base32(rand, password4, 32); uint16_t port = 0; while (port <= 1024) { port = Random_uint16(rand); } uint8_t publicKey[32]; uint8_t publicKeyBase32[53]; uint8_t ip[16]; uint8_t address[40]; uint8_t privateKey[32]; uint8_t privateKeyHex[65]; Key_gen(ip, publicKey, privateKey, rand); Base32_encode(publicKeyBase32, 53, publicKey, 32); Hex_encode(privateKeyHex, 65, privateKey, 32); AddrTools_printIp(address, ip); printf("{\n"); printf(" // Private key:\n" " // Your confidentiality and data integrity depend on this key, keep it secret!\n" " \"privateKey\": \"%s\",\n\n", privateKeyHex); printf(" // This key corresponds to the public key and ipv6 address:\n" " \"publicKey\": \"%s.k\",\n", publicKeyBase32); printf(" \"ipv6\": \"%s\",\n", address); printf("\n" " // Anyone connecting and offering these passwords on connection will be allowed.\n" " //\n" " // WARNING: If a \"login\" parameter is passed, someone sniffing on the wire can\n" " // sniff the packet and crack to find it. If the \"login\" is not passed\n" " // then the hash of the 'password' is effectively the login, therefore\n" " // that can be cracked.\n" " //\n" " \"authorizedPasswords\": [\n" " // Password is a unique string which is known to the client and server.\n" " // User is an optional login name and will also be used to display the peer.\n" " { \"password\": \"%s\", \"user\": \"default-login\" }\n", password); printf("\n" " // More passwords should look like this.\n" " // { \"password\": \"%s\", \"user\": \"my-second-peer\" },\n", password2); printf(" // { \"password\": \"%s\", \"user\": \"my-third-peer\" },\n", password3); printf(" // { \"password\": \"%s\", \"user\": \"my-fourth-peer\" },\n", password4); printf("\n" " // Below is an example of your connection credentials\n" " // that you can give to other people so they can connect\n" " // to you using your default password (from above).\n" " // The login field here yourself to your peer and the peerName field\n" " // is the name the peer which will be displayed in peerStats\n" " // Adding a unique password for each peer is advisable\n" " // so that leaks can be isolated.\n" " /*\n" " \"your.external.ip.goes.here:%u\": {\n", port); printf(" \"login\": \"default-login\",\n" " \"password\": \"%s\",\n", password); printf(" \"publicKey\": \"%s.k\",\n", publicKeyBase32); printf(" \"peerName\": \"your-name-goes-here\"\n" " },\n" " */\n"); printf(" ],\n" "\n" " // Settings for administering and extracting information from your router.\n" " // This interface provides functions which can be called through a UDP socket.\n" " // See admin/Readme.md for more information about the API and try:\n" " // ./tools/cexec\n" " // For a list of functions which can be called.\n" " // For example: ./tools/cexec 'memory()'\n" " // will call a function which gets the core's current memory consumption.\n" " // ./tools/cjdnslog\n" " // is a tool which uses this admin interface to get logs from cjdns.\n" " \"admin\": {\n" " // Port to bind the admin RPC server to.\n" " \"bind\": \"127.0.0.1:11234\",\n" "\n" " // Password for admin RPC server.\n" " // This is a static password by default, so that tools like\n" " // ./tools/cexec can use the API without you creating a\n" " // config file at ~/.cjdnsadmin first. If you decide to\n" " // expose the admin API to the network, change the password!\n" " \"password\": \"NONE\"\n"); printf(" },\n" "\n" " // Interfaces to connect to the switch core.\n" " \"interfaces\": {\n" " // The interface which connects over UDP/IP based VPN tunnel.\n" " \"UDPInterface\": [\n" " {\n" " // Bind to this port.\n" " \"bind\": \"0.0.0.0:%u\",\n", port); printf(" // Set the DSCP value for Qos. Default is 0.\n" " // \"dscp\": 46,\n" "\n" " // Automatically connect to other nodes on the same LAN\n" " // This works by binding a second port and sending beacons\n" " // containing the main data port.\n" " // beacon is a number between 0 and 2:\n" " // 0 -> do not beacon nor connect to other nodes who beacon\n" " // 1 -> quiet mode, accept beacons from other nodes only\n" " // 2 -> send and accept beacons\n" " // beaconDevices is a list which can contain names of devices such\n" " // as eth0, as well as broadcast addresses to send to, such as\n" " // 192.168.101.255, or the pseudo-name \"all\".\n" " // in order to auto-peer, all cjdns nodes must use the same\n" " // beaconPort.\n" " \"beacon\": 2,\n" " \"beaconDevices\": [ \"all\" ],\n" " \"beaconPort\": 64512,\n"); printf("\n" " // Nodes to connect to (IPv4 only).\n" " \"connectTo\": {\n" " // Add connection credentials here to join the network\n" " // If you have several, don't forget the separating commas\n" " // They should look like:\n" " // \"ipv4 address:port\": {\n" " // \"login\": \"(optional) name your peer has for you\"\n" " // \"password\": \"password to connect with\",\n" " // \"publicKey\": \"remote node key.k\",\n" " // \"peerName\": \"(optional) human-readable name for peer\"\n" " // },\n" " // Ask somebody who is already connected.\n" " }\n" " },\n" " {\n" " // Bind to this port.\n" " \"bind\": \"[::]:%u\",\n", port); printf(" // Set the DSCP value for Qos. Default is 0.\n" " // \"dscp\": 46,\n"); printf("\n" " // Nodes to connect to (IPv6 only).\n" " \"connectTo\": {\n" " // Add connection credentials here to join the network\n" " // Ask somebody who is already connected.\n" " }\n" " }\n" " ],\n\n"); #ifdef HAS_ETH_INTERFACE printf(" // The interface which allows peering using layer-2 ethernet frames\n" " \"%sETHInterface\": [\n" " // Alternatively bind to just one device and either beacon and/or\n" " // connect to a specified MAC address\n" " {\n" " // Bind to this device (interface name, not MAC)\n" " // \"all\" is a pseudo-name which will try to connect to all devices.\n" " \"bind\": \"all\",\n" "\n" " // Auto-connect to other cjdns nodes on the same network.\n" " // Options:\n" " //\n" " // 0 -- Disabled.\n" " //\n" " // 1 -- Accept beacons, this will cause cjdns to accept incoming\n" " // beacon messages and try connecting to the sender.\n" " //\n" " // 2 -- Accept and send beacons, this will cause cjdns to broadcast\n" " // messages on the local network which contain a randomly\n" " // generated per-session password, other nodes which have this\n" " // set to 1 or 2 will hear the beacon messages and connect\n" " // automatically.\n" " //\n" " \"beacon\": 2,\n" "\n" " // Node(s) to connect to manually\n" " // Note: does not work with \"all\" pseudo-device-name\n" " \"connectTo\": {\n" " // Credentials for connecting look similar to UDP credentials\n" " // except they begin with the mac address, for example:\n" " // \"01:02:03:04:05:06\":{\"password\":\"a\",\"publicKey\":\"b\"}\n" " }\n" " }\n" " ]\n\n", (eth) ? "" : "_disabled_"); #else printf(" ]\n"); #endif printf(" },\n" "\n" " // Configuration for the router.\n" " \"router\": {\n" " // supernodes, if none are specified they'll be taken from your peers\n" " \"supernodes\": [\n" " //\"6743gf5tw80ExampleExampleExampleExamplevlyb23zfnuzv0.k\",\n" " ],\n" "\n" " // The interface which is used for connecting to the cjdns network.\n" " \"interface\": {\n" " // The type of interface (only TUNInterface is supported for now)\n" " \"type\": \"TUNInterface\"\n"); #ifndef __APPLE__ printf("\n" " // The name of a persistent TUN device to use.\n" " // This for starting cjdroute as its own user.\n" " // *MOST USERS DON'T NEED THIS*\n" " //\"tunDevice\": \"" DEFAULT_TUN_DEV "\"\n"); #endif printf(" },\n" "\n" " // As an alternative to the TUN interface, you can create a socket interface\n" " // which will create a UNIX socket which emits packets that would otherwise\n" " // be sent through the TUN device.\n" " // To enable this interface, change the name of the above TUN interface to\n" " // \"_disabled_interface\" and change the name of this interface to\n" " // simply \"interface\"\n" " \"_disabled_interface\": {\n" " \"type\": \"SocketInterface\",\n" "\n" " // The filesystem path to the socket to create or connect to.\n" " \"socketFullPath\": \"/var/run/cjdns.sock\"\n" " },\n" "\n"); printf(" // System for tunneling IPv4 and ICANN IPv6 through cjdns.\n" " // This is using the cjdns switch layer as a VPN carrier.\n" " \"ipTunnel\": {\n" " // Nodes allowed to connect to us.\n" " // When a node with the given public key connects, give them the\n" " // ip4 and/or ip6 addresses listed.\n" " \"allowedConnections\": [\n"); printf(" // Give the client an address on 192.168.1.0/24, and an address\n" " // it thinks has all of IPv6 behind it.\n" " // ip4Prefix is the set of addresses which are routable from the tun\n" " // for example, if you're advertizing a VPN into a company network\n" " // which exists in 10.123.45.0/24 space, ip4Prefix should be 24\n" " // default is 32 for ipv4 and 128 for ipv6\n" " // so by default it will not install a route\n" " // ip4Alloc is the block of addresses which are allocated to the\n" " // for example if you want to issue 4 addresses to the client, those\n" " // being 192.168.123.0 to 192.168.123.3, you would set this to 30\n" " // default is 32 for ipv4 and 128 for ipv6 (1 address)\n" " // {\n" " // \"publicKey\": " "\"f64hfl7c4uxt6krmhPutTheRealAddressOfANodeHere7kfm5m0.k\",\n" " // \"ip4Address\": \"192.168.1.24\",\n" " // \"ip4Prefix\": 0,\n" " // \"ip4Alloc\": 32,\n" " // \"ip6Address\": \"2001:123:ab::10\",\n" " // \"ip6Prefix\": 0\n" " // \"ip6Alloc\": 64,\n" " // },\n" "\n" " // It's ok to only specify one address and prefix/alloc are optional.\n" " // {\n" " // \"publicKey\": " "\"ydq8csdk8p8ThisIsJustAnExampleAddresstxuyqdf27hvn2z0.k\",\n" " // \"ip4Address\": \"192.168.1.25\",\n" " // \"ip4Prefix\": 0,\n" " // }\n" " ],\n" "\n" " \"outgoingConnections\": [\n" " // Connect to one or more machines and ask them for IP addresses.\n" " // \"6743gf5tw80ExampleExampleExampleExamplevlyb23zfnuzv0.k\",\n" " // \"pw9tfmr8pcrExampleExampleExampleExample8rhg1pgwpwf80.k\",\n" " // \"g91lxyxhq0kExampleExampleExampleExample6t0mknuhw75l0.k\"\n" " ]\n" " }\n" " },\n" "\n"); printf(" // Dropping permissions.\n" " // In the event of a serious security exploit in cjdns, leak of confidential\n" " // network traffic and/or keys is highly likely but the following rules are\n" " // designed to prevent the attack from spreading to the system on which cjdns\n" " // is running.\n" " // Counter-intuitively, cjdns is *more* secure if it is started as root because\n" " // non-root users do not have permission to use chroot or change usernames,\n" " // limiting the effectiveness of the mitigations herein.\n" " \"security\": [\n" " // Change the user id to sandbox the cjdns process after it starts.\n" " // If keepNetAdmin is set to 0, IPTunnel will be unable to set IP addresses\n" " // and ETHInterface will be unable to hot-add new interfaces\n" " // Use { \"setuser\": 0 } to disable.\n" " // Default: enabled with keepNetAdmin\n"); if (Defined(android) || Defined(darwin)) { printf(" { \"setuser\": 0 },\n"); } else { printf(" { \"setuser\": \"nobody\", \"keepNetAdmin\": 1 },\n"); } printf("\n" " // Chroot changes the filesystem root directory which cjdns sees, blocking it\n" " // from accessing files outside of the chroot sandbox, if the user does not\n" " // have permission to use chroot(), this will fail quietly.\n" " // Use { \"chroot\": 0 } to disable.\n"); if (Defined(android)) { printf(" // Default: disabled\n" " { \"chroot\": 0 },\n"); } else { printf(" // Default: enabled (using \"/var/run\")\n" " { \"chroot\": \"/var/run/\" },\n"); } printf("\n" " // Nofiles is a deprecated security feature which prevents cjdns from opening\n" " // any files at all, using this will block setting of IP addresses and\n" " // hot-adding ETHInterface devices but for users who do not need this, it\n" " // provides a formidable sandbox.\n" " // Default: disabled\n" " { \"nofiles\": 0 },\n" "\n" " // Noforks will prevent cjdns from spawning any new processes or threads,\n" " // this prevents many types of exploits from attacking the wider system.\n" " // Default: enabled\n" " { \"noforks\": 1 },\n" "\n" " // Seccomp is the most advanced sandboxing feature in cjdns, it uses\n" " // SECCOMP_BPF to filter the system calls which cjdns is able to make on a\n" " // linux system, strictly limiting it's access to the outside world\n" " // This will fail quietly on any non-linux system\n"); if (Defined(android)) { printf(" // Default: disabled\n" " { \"seccomp\": 0 },\n"); } else { printf(" // Default: enabled\n" " { \"seccomp\": 1 },\n"); } printf("\n" " // The client sets up the core using a sequence of RPC calls, the responses\n" " // to these calls are verified but in the event that the client crashes\n" " // setup of the core completes, it could leave the core in an insecure state\n" " // This call constitutes the client telling the core that the security rules\n" " // have been fully applied and the core may run. Without it, the core will\n" " // exit within a few seconds with return code 232.\n" " // Default: enabled\n" " { \"setupComplete\": 1 }\n" " ],\n" "\n" " // Logging\n" " \"logging\": {\n" " // Uncomment to have cjdns log to stdout rather than making logs available\n" " // via the admin socket.\n" " // \"logTo\": \"stdout\"\n" " },\n" "\n" " // If set to non-zero, cjdns will not fork to the background.\n" " // Recommended for use in conjunction with \"logTo\":\"stdout\".\n"); printf(" \"noBackground\": %d,\n", Defined(win32) ? 1 : 0); printf("\n" " // Path for admin control pipe:\n" " // If you pass only a filename then cjdns will guess the full path\n" " // On unix the default path is /tmp/\n" " // On windows: \\\\.\\pipe\\\n" " \"pipe\": \"cjdroute.sock\",\n"); printf("\n" " // This is to make the configuration be parsed in strict mode, which allows\n" " // it to be edited externally using cjdnsconf.\n" " \"version\": 2\n"); printf("}\n"); return 0; } static int usage(struct Allocator* alloc, char* appName) { char* sysInfo = SysInfo_describe(SysInfo_detect(), alloc); printf("Cjdns %s %s\n" "Usage:\n" " cjdroute --help This information\n" " cjdroute --genconf [--eth] Generate a configuration file, write it to stdout\n" " if --eth is specified then eth beaconing will\n" " be enabled. Caution it can interfere with UDP\n" " beaconing\n" " cjdroute --genconf-seed [--eth] Generate a configuration file from a 64 byte seed\n" " which is read in from stdin." " cjdroute --bench Run some cryptography performance benchmarks.\n" " cjdroute --version Print the protocol version which this node speaks.\n" " cjdroute --cleanconf < conf Print a clean (valid json) version of the config.\n" " cjdroute --nobg Never fork to the background no matter the config.\n" "\n" "To get the router up and running.\n" "Step 1:\n" " Generate a new configuration file.\n" " cjdroute --genconf > cjdroute.conf\n" "\n" "Step 2:\n" " Find somebody to connect to.\n" " Check out the IRC channel #cjdns on Efnet and Freenode\n" " for information about how to meet new people and make connect to them.\n" " Read more here: https://github.com/cjdelisle/cjdns/#2-find-a-friend\n" "\n" "Step 3:\n" " Add that somebody's node to your cjdroute.conf file.\n" " https://github.com/cjdelisle/cjdns/#3-connect-your-node-to-your-friends-node\n" "\n" "Step 4:\n" " Fire it up!\n" " sudo cjdroute < cjdroute.conf\n" "\n" "For more information about other functions and non-standard setups, see README.md\n", ArchInfo_getArchStr(), sysInfo); return 0; } struct CheckRunningInstanceContext { struct EventBase* base; struct Allocator* alloc; struct AdminClient_Result* res; }; static void checkRunningInstanceCallback(struct AdminClient_Promise* p, struct AdminClient_Result* res) { struct CheckRunningInstanceContext* ctx = p->userData; // Prevent this from freeing until after we drop out of the loop. Allocator_adopt(ctx->alloc, p->alloc); ctx->res = res; EventBase_endLoop(ctx->base); } static void checkRunningInstance(struct Allocator* allocator, struct EventBase* base, String* addr, String* password, struct Log* logger, struct Except* eh) { struct Allocator* alloc = Allocator_child(allocator); struct Sockaddr_storage pingAddrStorage; if (Sockaddr_parse(addr->bytes, &pingAddrStorage)) { Except_throw(eh, "Unable to parse [%s] as an ip address port, eg: 127.0.0.1:11234", addr->bytes); } struct UDPAddrIface* udp = Except_er(eh, UDPAddrIface_new(base, NULL, alloc, logger)); struct AdminClient* adminClient = AdminClient_new(&udp->generic, &pingAddrStorage.addr, password, base, logger, alloc); // 100 milliseconds is plenty to wait for a process to respond on the same machine. adminClient->millisecondsToWait = 100; Dict* pingArgs = Dict_new(alloc); struct AdminClient_Promise* pingPromise = AdminClient_rpcCall(String_new("ping", alloc), pingArgs, adminClient, alloc); struct CheckRunningInstanceContext* ctx = Allocator_malloc(alloc, sizeof(struct CheckRunningInstanceContext)); ctx->base = base; ctx->alloc = alloc; ctx->res = NULL; pingPromise->callback = checkRunningInstanceCallback; pingPromise->userData = ctx; EventBase_beginLoop(base); Assert_true(ctx->res); if (ctx->res->err != AdminClient_Error_TIMEOUT) { Except_throw(eh, "Startup failed: cjdroute is already running. [%d]", ctx->res->err); } Allocator_free(alloc); } static void onCoreExit(int64_t exit_status, int term_signal) { Assert_failure("Core exited with status [%d], signal [%d]\n", (int)exit_status, term_signal); } #define Chunk_MAX_LEN 4000 struct Chunk { uint32_t length; struct Chunk* next; uint8_t buf[Chunk_MAX_LEN]; }; static struct Message* readToMsg(FILE* f, struct Allocator* alloc) { struct Allocator* child = Allocator_child(alloc); struct Chunk* c = NULL; uint32_t totalLength = 0; do { struct Chunk* cc = Allocator_calloc(child, sizeof(struct Chunk), 1); cc->length = fread(cc->buf, 1, Chunk_MAX_LEN, f); totalLength += cc->length; cc->next = c; c = cc; } while (c->length == Chunk_MAX_LEN); struct Message* out = Message_new(0, totalLength, alloc); while (c) { Er_assert(Message_epush(out, c->buf, c->length)); c = c->next; } Allocator_free(child); return out; } static String* getPipePath(Dict* config, struct Allocator* alloc) { String* pipePath = Dict_getStringC(config, "pipe"); char* pp = (pipePath) ? pipePath->bytes : "cjdroute.sock"; if (pp[0] == Pipe_PATH_SEP[0]) { return pipePath; } char* path = Pipe_PATH; if (Defined(android)) { char* t = getenv("TMPDIR"); if (!t) { t = getenv("HOME"); } if (t) { path = t; } } String* out = String_newBinary(NULL, strlen(pp) + strlen(Pipe_PATH_SEP) + strlen(path) + 2, alloc); snprintf(out->bytes, out->len, "%s%s%s", path, Pipe_PATH_SEP, pp); out->len = strlen(out->bytes); return out; } // This is invoked from cjdroute.rs int cjdroute2_main(int argc, char** argv); int cjdroute2_main(int argc, char** argv) { #ifdef Log_KEYS fprintf(stderr, "Log_LEVEL = KEYS, EXPECT TO SEE PRIVATE KEYS IN YOUR LOGS!\n"); #endif if (argc > 1 && (!CString_strcmp("angel", argv[1]) || !CString_strcmp("core", argv[1]))) { return Core_main(argc, argv); } Assert_ifParanoid(argc > 0); struct Except* eh = NULL; // Allow it to allocate 8MB struct Allocator* allocator = MallocAllocator_new(1<<23); struct Random* rand = Random_new(allocator, NULL, eh); struct EventBase* eventBase = EventBase_new(allocator); if (argc == 2) { // one argument if ((CString_strcmp(argv[1], "--help") == 0) || (CString_strcmp(argv[1], "-h") == 0)) { return usage(allocator, argv[0]); } else if (CString_strcmp(argv[1], "--genconf-seed") == 0) { return genconf(allocator, rand, 0, 1); } else if (CString_strcmp(argv[1], "--genconf") == 0) { return genconf(allocator, rand, 0, 0); } else if (CString_strcmp(argv[1], "--bench") == 0) { Benchmark_runAll(); return 0; } else if ((CString_strcmp(argv[1], "--version") == 0) || (CString_strcmp(argv[1], "-v") == 0)) { printf("Cjdns version: %s\n", CJD_PACKAGE_VERSION); printf("Cjdns protocol version: %d\n", Version_CURRENT_PROTOCOL); return 0; } else if (CString_strcmp(argv[1], "--cleanconf") == 0) { // Performed after reading configuration } else if (CString_strcmp(argv[1], "--nobg") == 0) { // Performed while reading configuration } else { fprintf(stderr, "%s: unrecognized option '%s'\n", argv[0], argv[1]); fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]); return -1; } } else if (argc > 2) { // more than one argument? // because of '--pidfile $filename'? if (CString_strcmp(argv[1], "--pidfile") == 0) { fprintf(stderr, "\n'--pidfile' option is deprecated.\n"); } else if (CString_strcmp(argv[1], "--genconf") == 0 || CString_strcmp(argv[1], "--genconf-seed") == 0) { bool eth = 0; for (int i = 2; i < argc; i++) { if (!CString_strcmp(argv[i], "--eth")) { eth = 1; } else { fprintf(stderr, "%s: unrecognized option '%s'\n", argv[0], argv[i]); fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]); return -1; } } return genconf(allocator, rand, eth, CString_strcmp(argv[1], "--genconf-seed") == 0); } else { fprintf(stderr, "%s: too many arguments [%s]\n", argv[0], argv[1]); fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]); } return -1; } if (isatty(STDIN_FILENO)) { // We were started from a terminal // The chances an user wants to type in a configuration // bij hand are pretty slim so we show him the usage return usage(allocator, argv[0]); } else { // We assume stdin is a configuration file and that we should // start routing } // First try reading the conf with the new parser, then try the old parser // and if the old parser fails or the parsed content contains "version": 2, // fail to launch struct Message* confMsg = readToMsg(stdin, allocator); struct Reader* confReader = ArrayReader_new(confMsg->bytes, confMsg->length, allocator); Dict _config; Dict* config = &_config; const char* err = JsonBencMessageReader_readNoExcept(confMsg, allocator, &config, false); if (!err) { // If old version is specified, always use old parser so there is no possible error uint64_t* v = Dict_getIntC(config, "version"); if (!v || *v < 2) { err = "using old parser"; } } if (err) { if (JsonBencSerializer_get()->parseDictionary(confReader, allocator, &_config)) { fprintf(stderr, "Failed to parse configuration.\n%s\n", err); return -1; } uint64_t* version = Dict_getIntC(config, "version"); if (version && *version >= 2) { fprintf(stderr, "Invalid cjdroute.conf\n%s\n", err); return -1; } } if (argc == 2 && CString_strcmp(argv[1], "--cleanconf") == 0) { // Slip a v2 in there because at this point, the conf file is definitely v2 valid Dict_putIntC(config, "version", 2, allocator); struct Writer* stdoutWriter = FileWriter_new(stdout, allocator); JsonBencSerializer_get()->serializeDictionary(stdoutWriter, config); printf("\n"); return 0; } int forceNoBackground = 0; if (argc == 2 && CString_strcmp(argv[1], "--nobg") == 0) { forceNoBackground = 1; } struct Log* logger = FileWriterLog_new(stdout, allocator); // --------------------- Get Admin --------------------- // Dict* configAdmin = Dict_getDictC(config, "admin"); String* adminPass = Dict_getStringC(configAdmin, "password"); String* adminBind = Dict_getStringC(configAdmin, "bind"); if (!adminPass) { adminPass = String_newBinary(NULL, 32, allocator); Random_base32(rand, (uint8_t*) adminPass->bytes, 32); adminPass->len = CString_strlen(adminPass->bytes); } if (!adminBind) { Except_throw(eh, "You must specify admin.bind in the cjdroute.conf file."); } // --------------------- Welcome to cjdns ---------------------- // char* sysInfo = SysInfo_describe(SysInfo_detect(), allocator); Log_info(logger, "%s %s %s", CJD_PACKAGE_VERSION, ArchInfo_getArchStr(), sysInfo); // --------------------- Check for running instance --------------------- // Log_info(logger, "Checking for running instance..."); checkRunningInstance(allocator, eventBase, adminBind, adminPass, logger, eh); // --------------------- Setup Pipes to Angel --------------------- // struct Allocator* corePipeAlloc = Allocator_child(allocator); String* pipePath = getPipePath(config, corePipeAlloc); if (!Defined(win32)) { // win32 sockets are not files char* lastsep = strrchr(pipePath->bytes, '/'); Assert_true(lastsep); *lastsep = '\0'; int ret = access(pipePath->bytes, W_OK); *lastsep = '/'; if (ret) { Except_throw(eh, "Pipe directory not writable: [%s]", pipePath->bytes); } if (unlink(pipePath->bytes) && (errno != ENOENT)) { Except_throw(eh, "Unable to delete existing pipe at path [%s] err [%s]", pipePath->bytes, strerror(errno)); } } char* args[] = { "core", pipePath->bytes, NULL }; // --------------------- Spawn Core --------------------- // String* privateKey = Dict_getStringC(config, "privateKey"); char* corePath = Process_getPath(allocator); if (!corePath) { Except_throw(eh, "Can't find a usable cjdns core executable, " "make sure it is in the same directory as cjdroute"); } if (!privateKey) { Except_throw(eh, "Need to specify privateKey."); } Process_spawn(corePath, args, eventBase, allocator, onCoreExit); // --------------------- Wait for socket ------------------------- // // cycle for up to 1 minute int exists = 0; for (int i = 0; i < 2 * 10 * 60; i++) { if (Except_er(eh, Pipe_exists(pipePath->bytes, allocator))) { exists = 1; break; } // sleep 50ms struct timespec timeout = { 0, 1000000 * 50 }; nanosleep(&timeout, NULL); } if (!exists) { Except_throw(eh, "Core did not setup pipe file [%s] within 60 seconds", pipePath->bytes); } // --------------------- Connect to socket ------------------------- // struct Pipe* corePipe = Except_er(eh, Pipe_named(pipePath->bytes, eventBase, logger, allocator)); // --------------------- Pre-Configure Core ------------------------- // Dict* preConf = Dict_new(allocator); Dict* adminPreConf = Dict_new(allocator); Dict_putDictC(preConf, "admin", adminPreConf, allocator); Dict_putStringC(preConf, "privateKey", privateKey, allocator); Dict_putStringC(adminPreConf, "bind", adminBind, allocator); Dict_putStringC(adminPreConf, "pass", adminPass, allocator); Dict* logging = Dict_getDictC(config, "logging"); if (logging) { Dict_putDictC(preConf, "logging", logging, allocator); } struct Message* toCoreMsg = Message_new(0, 1024, allocator); Er_assert(BencMessageWriter_write(preConf, toCoreMsg)); Iface_CALL(corePipe->iface.send, toCoreMsg, &corePipe->iface); Log_debug(logger, "Sent [%d] bytes to core", toCoreMsg->length); // --------------------- Get Response from Core --------------------- // struct Message* fromCoreMsg = InterfaceWaiter_waitForData(&corePipe->iface, eventBase, allocator, eh); Dict* responseFromCore = Except_er(eh, BencMessageReader_read(fromCoreMsg, allocator)); // --------------------- Close the Core Pipe --------------------- // Allocator_free(corePipeAlloc); corePipe = NULL; // --------------------- Get Admin Addr/Port/Passwd --------------------- // Dict* responseFromCoreAdmin = Dict_getDictC(responseFromCore, "admin"); adminBind = Dict_getStringC(responseFromCoreAdmin, "bind"); if (!adminBind) { Except_throw(eh, "didn't get address and port back from core"); } struct Sockaddr_storage adminAddr; if (Sockaddr_parse(adminBind->bytes, &adminAddr)) { Except_throw(eh, "Unable to parse [%s] as an ip address port, eg: 127.0.0.1:11234", adminBind->bytes); } //Assert_ifParanoid(EventBase_eventCount(eventBase) == 1); // --------------------- Configuration ------------------------- // Configurator_config(config, &adminAddr.addr, adminPass, eventBase, logger, allocator); // --------------------- noBackground ------------------------ // int64_t* noBackground = Dict_getIntC(config, "noBackground"); if (forceNoBackground || (noBackground && *noBackground)) { Log_debug(logger, "Keeping cjdns client alive because %s", (forceNoBackground) ? "--nobg was specified on the command line" : "noBackground was set in the configuration"); EventBase_beginLoop(eventBase); } // Freeing this allocator here causes the core to be terminated in the epoll syscall. //Allocator_free(allocator); return 0; }