Browse Source

Fuzz testing work in progress

Caleb James DeLisle 5 years ago
parent
commit
4100252ba3

+ 0 - 287
benc/serialization/json/test/JsonBencMessageReader_fuzzGen.js

@@ -1,287 +0,0 @@
-'use strict';
-// stringify it to reduce size
-const DEFAULT_CONF = JSON.stringify({
-    // Private key:
-    // Your confidentiality and data integrity depend on this key, keep it secret!
-    "privateKey": "ba13c8023eda49bd626ebfda4ecf9abd0eda6e07a8e77f19aa0b20c0d69eb915",
-
-    // This key corresponds to the public key and ipv6 address:
-    "publicKey": "hukfzjh5n30m10303bkhjg1wvuft90s0cqqg5jrjgg781dbly2z0.k",
-    "ipv6": "fccf:4352:ac9:7d1f:b32b:ba08:f086:8432",
-
-    // Anyone connecting and offering these passwords on connection will be allowed.
-    //
-    // WARNING: If a "login" parameter is passed, someone sniffing on the wire can
-    //          sniff the packet and crack to find it. If the "login" is not passed
-    //          then the hash of the 'password' is effectively the login, therefore
-    //          that can be cracked.
-    //
-    "authorizedPasswords":
-    [
-        // A unique string which is known to the client and server.
-        // Specify an optional user to identify the peer locally.
-        // It is not used for authentication.
-        {"password": "jq89d23lbvm26tk4t36yg5ugj717wkr", "user": "default-login"}
-
-        // More passwords should look like this.
-        // {"password": "l5t3tpn7z710p8cxly59zxbmy5n8buu", "user": "my-second-peer"},
-        // {"password": "97ylvcn49q4y19f7w7f248hts5l7pbp", "user": "my-third-peer"},
-        // {"password": "yxdzufqxwkbgp4ubusglxm5su4hwp41", "user": "my-fourth-peer"},
-
-        // Below is an example of your connection credentials
-        // that you can give to other people so they can connect
-        // to you using your default password (from above).
-        // The login field here yourself to your peer and the peerName field
-        // is the name the peer which will be displayed in peerStats
-        // Adding a unique password for each peer is advisable
-        // so that leaks can be isolated.
-        /*
-        "your.external.ip.goes.here:61151": {
-            "login": "default-login",
-            "password":"jq89d23lbvm26tk4t36yg5ugj717wkr",
-            "publicKey":"hukfzjh5n30m10303bkhjg1wvuft90s0cqqg5jrjgg781dbly2z0.k",
-            "peerName":"your-name-goes-here"
-        },
-        */
-    ],
-
-    // Settings for administering and extracting information from your router.
-    // This interface provides functions which can be called through a UDP socket.
-    // See admin/Readme.md for more information about the API and try:
-    // ./tools/cexec
-    // For a list of functions which can be called.
-    // For example: ./tools/cexec 'memory()'
-    // will call a function which gets the core's current memory consumption.
-    // ./tools/cjdnslog
-    // is a tool which uses this admin interface to get logs from cjdns.
-    "admin":
-    {
-        // Port to bind the admin RPC server to.
-        "bind": "127.0.0.1:11234",
-
-        // Password for admin RPC server.
-        // This is a static password by default, so that tools like
-        // ./tools/cexec can use the API without you creating a
-        // config file at ~/.cjdnsadmin first. If you decide to
-        // expose the admin API to the network, change the password!
-        "password": "NONE"
-    },
-
-    // Interfaces to connect to the switch core.
-    "interfaces":
-    {
-        // The interface which connects over UDP/IP based VPN tunnel.
-        "UDPInterface":
-        [
-            {
-                // Bind to this port.
-                "bind": "0.0.0.0:61151",
-
-                // Nodes to connect to (IPv4 only).
-                "connectTo":
-                {
-                    // Add connection credentials here to join the network
-                    // If you have several, don't forget the separating commas
-                    // They should look like:
-                    // "ipv4 address:port": {
-                    //     "login": "(optional) name your peer has for you"
-                    //     "password": "password to connect with",
-                    //     "publicKey": "remote node key.k",
-                    //     "peerName": "(optional) human-readable name for peer"
-                    // },
-                    // Ask somebody who is already connected.
-                }
-            },
-            {
-                // Bind to this port.
-                "bind": "[::]:61151",
-
-                // Nodes to connect to (IPv6 only).
-                "connectTo":
-                {
-                    // Add connection credentials here to join the network
-                    // Ask somebody who is already connected.
-                }
-            }
-        ]
-,
-        "ETHInterface":
-        [
-            // Alternatively bind to just one device and either beacon and/or
-            // connect to a specified MAC address
-            {
-                // Bind to this device (interface name, not MAC)
-                // "all" is a pseudo-name which will try to connect to all devices.
-                "bind": "all",
-
-                // Auto-connect to other cjdns nodes on the same network.
-                // Options:
-                //
-                // 0 -- Disabled.
-                //
-                // 1 -- Accept beacons, this will cause cjdns to accept incoming
-                //      beacon messages and try connecting to the sender.
-                //
-                // 2 -- Accept and send beacons, this will cause cjdns to broadcast
-                //      messages on the local network which contain a randomly
-                //      generated per-session password, other nodes which have this
-                //      set to 1 or 2 will hear the beacon messages and connect
-                //      automatically.
-                //
-                "beacon": 2,
-
-                // Node(s) to connect to manually
-                // Note: does not work with "all" pseudo-device-name
-                "connectTo":
-                {
-                    // Credentials for connecting look similar to UDP credentials
-                    // except they begin with the mac address, for example:
-                    // "01:02:03:04:05:06":{"password":"a","publicKey":"b"}
-                }
-            }
-        ]
-
-    },
-
-    // Configuration for the router.
-    "router":
-    {
-        // The interface which is used for connecting to the cjdns network.
-        "interface":
-        {
-            // The type of interface (only TUNInterface is supported for now)
-            "type": "TUNInterface"
-            // The type of tunfd (only "android" for now)
-            // If "android" here, the tunDevice should be used as the pipe path
-            // to transfer the tun file description.
-            // "tunfd" : "android"
-        },
-
-        // System for tunneling IPv4 and ICANN IPv6 through cjdns.
-        // This is using the cjdns switch layer as a VPN carrier.
-        "ipTunnel":
-        {
-            // Nodes allowed to connect to us.
-            // When a node with the given public key connects, give them the
-            // ip4 and/or ip6 addresses listed.
-            "allowedConnections":
-            [
-                // Give the client an address on 192.168.1.0/24, and an address
-                // it thinks has all of IPv6 behind it.
-                // ip4Prefix is the set of addresses which are routable from the tun
-                // for example, if you're advertizing a VPN into a company network
-                // which exists in 10.123.45.0/24 space, ip4Prefix should be 24
-                // default is 32 for ipv4 and 128 for ipv6
-                // so by default it will not install a route
-                // ip4Alloc is the block of addresses which are allocated to the
-                // for example if you want to issue 4 addresses to the client, those
-                // being 192.168.123.0 to 192.168.123.3, you would set this to 30
-                // default is 32 for ipv4 and 128 for ipv6 (1 address)
-                // {
-                //     "publicKey": "f64hfl7c4uxt6krmhPutTheRealAddressOfANodeHere7kfm5m0.k",
-                //     "ip4Address": "192.168.1.24",
-                //     "ip4Prefix": 0,
-                //     "ip4Alloc": 32,
-                //     "ip6Address": "2001:123:ab::10",
-                //     "ip6Prefix": 0
-                //     "ip6Alloc": 64,
-                // },
-
-                // It's ok to only specify one address and prefix/alloc are optional.
-                // {
-                //     "publicKey": "ydq8csdk8p8ThisIsJustAnExampleAddresstxuyqdf27hvn2z0.k",
-                //     "ip4Address": "192.168.1.25",
-                //     "ip4Prefix": 0,
-                // }
-            ],
-
-            "outgoingConnections":
-            [
-                // Connect to one or more machines and ask them for IP addresses.
-                // "6743gf5tw80ExampleExampleExampleExamplevlyb23zfnuzv0.k",
-                // "pw9tfmr8pcrExampleExampleExampleExample8rhg1pgwpwf80.k",
-                // "g91lxyxhq0kExampleExampleExampleExample6t0mknuhw75l0.k"
-            ]
-        }
-    },
-
-    // Dropping permissions.
-    // In the event of a serious security exploit in cjdns, leak of confidential
-    // network traffic and/or keys is highly likely but the following rules are
-    // designed to prevent the attack from spreading to the system on which cjdns
-    // is running.
-    // Counter-intuitively, cjdns is *more* secure if it is started as root because
-    // non-root users do not have permission to use chroot or change usernames,
-    // limiting the effectiveness of the mitigations herein.
-    "security":
-    [
-        // Change the user id to sandbox the cjdns process after it starts.
-        // If keepNetAdmin is set to 0, IPTunnel will be unable to set IP addresses
-        // and ETHInterface will be unable to hot-add new interfaces
-        // Use { "setuser": 0 } to disable.
-        // Default: enabled with keepNetAdmin
-        { "setuser": "nobody", "keepNetAdmin": 1 },
-
-        // Chroot changes the filesystem root directory which cjdns sees, blocking it
-        // from accessing files outside of the chroot sandbox, if the user does not
-        // have permission to use chroot(), this will fail quietly.
-        // Use { "chroot": 0 } to disable.
-        // Default: enabled (using "/var/run")
-        { "chroot": "/var/run/" },
-
-        // Nofiles is a deprecated security feature which prevents cjdns from opening
-        // any files at all, using this will block setting of IP addresses and
-        // hot-adding ETHInterface devices but for users who do not need this, it
-        // provides a formidable sandbox.
-        // Default: disabled
-        { "nofiles": 0 },
-
-        // Noforks will prevent cjdns from spawning any new processes or threads,
-        // this prevents many types of exploits from attacking the wider system.
-        // Default: enabled
-        { "noforks": 1 },
-
-        // Seccomp is the most advanced sandboxing feature in cjdns, it uses
-        // SECCOMP_BPF to filter the system calls which cjdns is able to make on a
-        // linux system, strictly limiting it's access to the outside world
-        // This will fail quietly on any non-linux system
-        // Default: enabled
-        { "seccomp": 1 },
-
-        // The client sets up the core using a sequence of RPC calls, the responses
-        // to these calls are verified but in the event that the client crashes
-        // setup of the core completes, it could leave the core in an insecure state
-        // This call constitutes the client telling the core that the security rules
-        // have been fully applied and the core may run. Without it, the core will
-        // exit within a few seconds with return code 232.
-        // Default: enabled
-        { "setupComplete": 1 }
-    ],
-
-    // Logging
-    "logging":
-    {
-        // Uncomment to have cjdns log to stdout rather than making logs available
-        // via the admin socket.
-        // "logTo":"stdout"
-    },
-
-    // If set to non-zero, cjdns will not fork to the background.
-    // Recommended for use in conjunction with "logTo":"stdout".
-    "noBackground":0,
-
-    // Pipe file will store in this path, recommended value: /tmp (for unix),
-    // \\\\.\\pipe (for windows)
-    // /data/local/tmp (for rooted android)
-    // /data/data/AppName (for non-root android)
-    "pipe":"/tmp",
-});
-
-module.exports.defaultConfLen = () => { return DEFAULT_CONF.length; };
-module.exports.defaultConf = (x) => {
-    const out = [];
-    for (let i = 0; i < DEFAULT_CONF.length; i++) {
-        out.push(x + '[' + i + ']=' + DEFAULT_CONF.charCodeAt(i) + ';');
-    }
-    return out.join('\n');
-}

+ 0 - 23
benc/serialization/json/test/JsonBencMessageReader_fuzz_test.c

@@ -15,30 +15,7 @@
 #include "benc/serialization/json/JsonBencMessageReader.h"
 #include "crypto/random/Random.h"
 #include "memory/Allocator.h"
-#include "memory/MallocAllocator.h"
-#include "util/Bits.h"
-#include "util/Identity.h"
-#include "util/log/FileWriterLog.h"
-#include "util/CString.h"
-#include "util/events/EventBase.h"
-#include "crypto/random/test/DeterminentRandomSeed.h"
 #include "test/FuzzTest.h"
-#include "util/Js.h"
-
-Js({ file.lib = require('benc/serialization/json/test/JsonBencMessageReader_fuzzGen.js'); })
-
-struct FuzzTest* CJDNS_FUZZ_MK(struct Allocator* alloc)
-{
-    int len = 0;
-    Js({ return 'len=' + file.lib.defaultConfLen(); });
-    struct Message* msg = Message_new(len, 0, alloc);
-    uint8_t* x = msg->bytes;
-    Js({ return file.lib.defaultConf("x"); });
-    struct FuzzTest* out = Allocator_calloc(alloc, sizeof(struct FuzzTest), 1);
-    out->name = "JsonBencMessageReader_default";
-    out->fuzz = msg;
-    return out;
-}
 
 void CJDNS_FUZZ_MAIN(void* vctx, struct Message* fuzz)
 {

+ 28 - 0
benc/serialization/json/test/JsonBencMessageReader_fuzz_test_cases/ConfFile.hex

@@ -0,0 +1,28 @@
+# Simple cjdroute.conf file, packed with no comments
+7b22707269766174654b6579223a226261313363383032336564
+613439626436323665626664613465636639616264306564613665303761
+3865373766313961613062323063306436396562393135222c227075626c
+69634b6579223a2268756b667a6a68356e33306d3130333033626b686a67
+3177767566743930733063717167356a726a676737383164626c79327a30
+2e6b222c2269707636223a22666363663a343335323a6163393a37643166
+3a623332623a626130383a663038363a38343332222c22617574686f7269
+7a656450617373776f726473223a5b7b2270617373776f7264223a226a71
+38396432336c62766d3236746b3474333679673575676a373137776b7222
+2c2275736572223a2264656661756c742d6c6f67696e227d5d2c2261646d
+696e223a7b2262696e64223a223132372e302e302e313a3131323334222c
+2270617373776f7264223a224e4f4e45227d2c22696e7465726661636573
+223a7b22554450496e74657266616365223a5b7b2262696e64223a22302e
+302e302e303a3631313531222c22636f6e6e656374546f223a7b7d7d2c7b
+2262696e64223a225b3a3a5d3a3631313531222c22636f6e6e656374546f
+223a7b7d7d5d2c22455448496e74657266616365223a5b7b2262696e6422
+3a22616c6c222c22626561636f6e223a322c22636f6e6e656374546f223a
+7b7d7d5d7d2c22726f75746572223a7b22696e74657266616365223a7b22
+74797065223a2254554e496e74657266616365227d2c22697054756e6e65
+6c223a7b22616c6c6f776564436f6e6e656374696f6e73223a5b5d2c226f
+7574676f696e67436f6e6e656374696f6e73223a5b5d7d7d2c2273656375
+72697479223a5b7b2273657475736572223a226e6f626f6479222c226b65
+65704e657441646d696e223a317d2c7b226368726f6f74223a222f766172
+2f72756e2f227d2c7b226e6f66696c6573223a307d2c7b226e6f666f726b
+73223a317d2c7b22736563636f6d70223a317d2c7b227365747570436f6d
+706c657465223a317d5d2c226c6f6767696e67223a7b7d2c226e6f426163
+6b67726f756e64223a302c2270697065223a222f746d70227d

+ 0 - 10
crypto/test/CryptoAuth_fuzz_test.c

@@ -267,16 +267,6 @@ static void mainLoop(struct Context* ctx)
     Assert_failure("Nodes could not sync");
 }
 
-struct FuzzTest* CJDNS_FUZZ_MK(struct Allocator* alloc)
-{
-    struct Message* msg = Message_new(64, 0, alloc);
-    Bits_memset(msg->bytes, 0, 64);
-    struct FuzzTest* out = Allocator_calloc(alloc, sizeof(struct FuzzTest), 1);
-    out->name = "CryptoAuth_fuzz_test_default";
-    out->fuzz = msg;
-    return out;
-}
-
 void* CJDNS_FUZZ_INIT(struct Allocator* alloc, struct Random* rand)
 {
     struct Context* ctx = Allocator_calloc(alloc, sizeof(struct Context), 1);

+ 4 - 0
crypto/test/CryptoAuth_fuzz_test_cases/Default.hex

@@ -0,0 +1,4 @@
+# This test only seeds a DeterminentRandomSeed so we're just
+# specifying the default as all zeros.
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000

+ 8 - 1
interface/ASynchronizer.c

@@ -39,6 +39,7 @@ struct ASynchronizer_pvt
     struct ArrayList_Messages* msgsToB;
 
     struct Allocator* timeoutAlloc;
+    struct Timeout* intr;
     int dryCycles;
 
     Identity
@@ -80,9 +81,15 @@ static void timeoutTrigger(void* vASynchronizer)
 
 static void checkTimeout(struct ASynchronizer_pvt* as)
 {
+    // The timeout might still be present but inactive because Timeout_clearAll() was called
+    // to setup a test, in that case lets re-arm it in order to get the message to the other side.
+    if (as->intr && !Timeout_isActive(as->intr)) {
+        Allocator_free(as->timeoutAlloc);
+        as->timeoutAlloc = NULL;
+    }
     if (as->timeoutAlloc) { return; }
     as->timeoutAlloc = Allocator_child(as->alloc);
-    Timeout_setInterval(timeoutTrigger, as, 1, as->base, as->timeoutAlloc);
+    as->intr = Timeout_setInterval(timeoutTrigger, as, 1, as->base, as->timeoutAlloc);
 }
 
 static Iface_DEFUN fromA(struct Message* msg, struct Iface* ifA)

+ 2 - 9
test/FuzzTest.h

@@ -15,18 +15,11 @@
 #ifndef FuzzTest_H
 #define FuzzTest_H
 
+#include "memory/Allocator.h"
+#include "crypto/random/Random.h"
 #include "wire/Message.h"
-#include "util/Linker.h"
-
-struct FuzzTest
-{
-    char* name;
-    struct Message* fuzz;
-    struct FuzzTest* next;
-};
 
 void CJDNS_FUZZ_MAIN(void* vctx, struct Message* fuzz);
-struct FuzzTest* CJDNS_FUZZ_MK(struct Allocator* alloc);
 void* CJDNS_FUZZ_INIT(struct Allocator* alloc, struct Random* rand);
 
 #endif

+ 32 - 19
test/Afl_fuzz_test_disabled.c → test/Main_fuzz_test.c

@@ -13,18 +13,18 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 #include "crypto/Key.h"
-//#include "io/FileWriter.h"
+#include "io/FileWriter.h"
 //#include "memory/MallocAllocator.h"
 #include "memory/Allocator.h"
 #include "crypto/random/Random.h"
 #include "interface/Iface.h"
 //#include "util/Base32.h"
 #include "util/Checksum.h"
-//#include "util/log/WriterLog.h"
+#include "util/log/WriterLog.h"
 #include "test/TestFramework.h"
 #include "wire/Headers.h"
-//#include "wire/Ethernet.h"
-//#include "interface/tuntap/TUNMessageType.h"
+#include "wire/Ethernet.h"
+#include "interface/tuntap/TUNMessageType.h"
 //#include "util/Hex.h"
 #include "util/events/Time.h"
 #include "util/events/Timeout.h"
@@ -71,6 +71,7 @@ static void notLinkedYet(struct Context* ctx)
 static void checkLinkage(void* vContext)
 {
     struct Context* ctx = Identity_check((struct Context*) vContext);
+    Log_debug(ctx->logger, "Check linkage");
 
     if (!ctx->beaconsSent) {
         if (Pathfinder_getNodeStore(ctx->nodeA->pathfinder) &&
@@ -83,7 +84,6 @@ static void checkLinkage(void* vContext)
         return;
     }
 
-
     if (Pathfinder_getNodeStore(ctx->nodeA->pathfinder)->nodeCount < 2) {
         notLinkedYet(ctx);
         return;
@@ -96,7 +96,9 @@ static void checkLinkage(void* vContext)
     Log_debug(ctx->logger, "B seems to be linked with A");
     Log_debug(ctx->logger, "\n\nSetup Complete\n\n");
 
-    Timeout_clearTimeout(ctx->checkLinkageTimeout);
+    //Timeout_clearTimeout(ctx->checkLinkageTimeout);
+    //EventBase_endLoop(ctx->base);
+    Timeout_clearAll(ctx->base);
 }
 
 /*
@@ -121,21 +123,12 @@ static void runTest(struct Context* tn)
 }
 */
 
-struct FuzzTest* CJDNS_FUZZ_MK(struct Allocator* alloc)
-{
-    struct Message* msg = Message_new(0, 2, alloc);
-    Message_push16(msg, 500, NULL);
-    struct FuzzTest* out = Allocator_calloc(alloc, sizeof(struct FuzzTest), 1);
-    out->name = "Map_fuzz_test_default";
-    out->fuzz = msg;
-    return out;
-}
 
 void* CJDNS_FUZZ_INIT(struct Allocator* alloc, struct Random* rand)
 {
-    struct Log* logger = NULL;
-    //struct Writer* logwriter = FileWriter_new(stdout, alloc);
-    //struct Log* logger = WriterLog_new(logwriter, alloc);
+    struct Writer* logwriter = FileWriter_new(stdout, alloc);
+    struct Log* logger = WriterLog_new(logwriter, alloc);
+
     struct EventBase* base = EventBase_new(alloc);
     struct Context* ctx = Allocator_calloc(alloc, sizeof(struct Context), 1);
     Identity_set(ctx);
@@ -172,6 +165,7 @@ void* CJDNS_FUZZ_INIT(struct Allocator* alloc, struct Random* rand)
 
 void CJDNS_FUZZ_MAIN(void* vctx, struct Message* msg)
 {
+    printf("\n\nEnter main\n\n");
     struct Context* ctx = Identity_check((struct Context*) vctx);
     struct TestFramework* from = ctx->nodeA;
     struct TestFramework* to = ctx->nodeB;
@@ -185,7 +179,12 @@ void CJDNS_FUZZ_MAIN(void* vctx, struct Message* msg)
         struct RouteHeader* rh = (struct RouteHeader*) msg->bytes;
         if (!Bits_isZero(rh->ip6, 16)) { Bits_memcpy(rh->ip6, to->ip, 16); }
         if (!Bits_isZero(rh->publicKey, 32)) { Bits_memcpy(rh->publicKey, to->publicKey, 32); }
-        rh->sh.label_be = EncodingScheme_serializeDirector(from->scheme, 1, -1);
+
+        uint64_t label = EncodingScheme_serializeDirector(from->scheme, 0, -1);
+        int f = EncodingScheme_getFormNum(from->scheme, label);
+        label |= 1 << (from->scheme->forms[f].prefixLen + from->scheme->forms[f].bitCount);
+        rh->sh.label_be = Endian_hostToBigEndian64(label);
+        SwitchHeader_setLabelShift(&rh->sh, 0);
     }
 
     // We're not limited to sending data types which we have registered for
@@ -204,9 +203,23 @@ void CJDNS_FUZZ_MAIN(void* vctx, struct Message* msg)
     Bits_memcpy(&srcAndDest, from->ip, 16);
     uint16_t checksum = Checksum_udpIp6(srcAndDest, msg->bytes, msg->length);
     ((struct Headers_UDPHeader*)msg->bytes)->checksum_be = checksum;
+
     TestFramework_craftIPHeader(msg, srcAndDest, &srcAndDest[16]);
+    ((struct Headers_IP6Header*) msg->bytes)->nextHeader = 17;
+
+    TUNMessageType_push(msg, Ethernet_TYPE_IP6, NULL);
 
     Iface_send(&ctx->tunA, Message_clone(msg, from->alloc));
 
     TestFramework_assertLastMessageUnaltered(ctx->nodeA);
+
+    printf("\n\nDropping out\n\n");
+
+    //from->blockIncomingMsgs = true;
+    //to->blockIncomingMsgs = true;
+
+    printf("%d events", EventBase_eventCount(ctx->base));
+
+    EventBase_beginLoop(ctx->base);
+    //EventBase_endLoop(ctx->base);
 }

+ 1 - 0
test/Main_fuzz_test_cases/DhtFindNodeQuery.hex

@@ -0,0 +1 @@
+f0fb3dd2076c775b31e9e3701dbcff38269d389be8e3ce1f93c5bdf247d8f577000000003a7bd3430021ffff0000001001000129fc756f0df3714a4268259113d5eb6f0410000100643030303030323a6569693065323a6573353a6114458100313a7069313665313a71323a666e333a74617231363afc4ad89041cbd4154d124fd0121165da343a7478696431323aa9c592fc1dcb6f374a67f52365

+ 1 - 0
test/Main_fuzz_test_cases/DhtGetPeersQuery.hex

@@ -0,0 +1 @@
+d66adab960d7b7e657b5c239786301e331550a4181dbbb8b20a7cb19f00f825200000000144d67a30060ffff000000140100000bfcd97f8ae0504b487fd607fa55096e26100001006430323a6569693065323a6573353a6114458100313a7069323065313a71323a6770333a746172383a000000009b64dc53343a7478696431333a30a9342ffb4b0eebc9d3e57d3365

+ 1 - 0
test/Main_fuzz_test_cases/DhtPingQuery.hex

@@ -0,0 +1 @@
+b4058c12d011b1652b8b10e685938e92325ca81f02bbff24671f21a7fb41a40700000001450bd343000000000000001000000000fcdeb3032fe2c57363b5123690d932691000010064323a6569693065323a6573353a6114458100313a7069323065313a71323a706e343a7478696431333a31c0cd38edb95f880bdf6b2e9765

+ 15 - 6
test/TestFramework.c

@@ -15,7 +15,9 @@
 #include "crypto/random/Random.h"
 #include "crypto/CryptoAuth.h"
 #include "crypto/AddressCalc.h"
+#ifndef SUBNODE
 #include "dht/Pathfinder.h"
+#endif
 #include "io/Writer.h"
 #include "io/FileWriter.h"
 #include "util/log/Log.h"
@@ -128,11 +130,15 @@ struct TestFramework* TestFramework_setUp(char* privateKey,
     Iface_plumb(&spfAsync->ifA, &spf->eventIf);
     EventEmitter_regPathfinderIface(nc->ee, &spfAsync->ifB);
 
-    struct Pathfinder* pf = Pathfinder_register(allocator, logger, base, rand, NULL);
-    pf->fullVerify = true;
-    struct ASynchronizer* pfAsync = ASynchronizer_new(allocator, base, logger);
-    Iface_plumb(&pfAsync->ifA, &pf->eventIf);
-    EventEmitter_regPathfinderIface(nc->ee, &pfAsync->ifB);
+    #ifndef SUBNODE
+        struct Pathfinder* pf = Pathfinder_register(allocator, logger, base, rand, NULL);
+        pf->fullVerify = true;
+        struct ASynchronizer* pfAsync = ASynchronizer_new(allocator, base, logger);
+        Iface_plumb(&pfAsync->ifA, &pf->eventIf);
+        EventEmitter_regPathfinderIface(nc->ee, &pfAsync->ifB);
+    #endif
+
+    SubnodePathfinder_start(spf);
 
     struct TestFramework* tf = Allocator_calloc(allocator, sizeof(struct TestFramework), 1);
     Identity_set(tf);
@@ -144,7 +150,10 @@ struct TestFramework* TestFramework_setUp(char* privateKey,
     tf->tunIf = &nc->tunAdapt->tunIf;
     tf->publicKey = nc->myAddress->key;
     tf->ip = nc->myAddress->ip6.bytes;
+    #ifndef SUBNODE
     tf->pathfinder = pf;
+    #endif
+    tf->subnodePathfinder = spf;
     tf->scheme = scheme;
 
     return tf;
@@ -224,4 +233,4 @@ void TestFramework_craftIPHeader(struct Message* msg, uint8_t srcAddr[16], uint8
     Bits_memcpy(ip->sourceAddr, srcAddr, 16);
     Bits_memcpy(ip->destinationAddr, destAddr, 16);
     Headers_setIpVersion(ip);
-}
+}

+ 5 - 0
test/TestFramework.h

@@ -26,7 +26,12 @@ struct TestFramework
     struct Random* rand;
     struct EventBase* eventBase;
     struct Log* logger;
+
+    #ifndef SUBNODE
     struct Pathfinder* pathfinder;
+    #endif
+    struct SubnodePathfinder* subnodePathfinder;
+
     struct Iface* tunIf;
     struct NetCore* nc;
     struct EncodingScheme* scheme;

+ 12 - 6
test/testcjdroute.c

@@ -48,15 +48,18 @@ static const struct {
     Test func;
     char* name;
 } TESTS[] = { Js({ return file.testcjdroute_tests }) };
+static const int TEST_COUNT = (int) (sizeof(TESTS) / sizeof(*TESTS));
 
 static const struct {
     FuzzTestInit init;
     FuzzTest fuzz;
-    MkFuzz mkFuzz;
     char* name;
 } FUZZ_TESTS[] = { Js({ return file.testcjdroute_fuzzTests }) };
 static const int FUZZ_TEST_COUNT = (int) (sizeof(FUZZ_TESTS) / sizeof(*FUZZ_TESTS));
 
+static const char* FUZZ_CASES[] = { Js({ return file.testcjdroute_fuzzCases }) };
+static const int FUZZ_CASE_COUNT = (int) (sizeof(FUZZ_CASES) / sizeof(*FUZZ_CASES));
+
 static uint64_t runTest(Test test,
                         char* name,
                         uint64_t startTime,
@@ -82,9 +85,13 @@ static void usage(char* appName)
     printf("%s <test>     run one test\n", appName);
     printf("%s all        run every test\n\n", appName);
     printf("Available Tests:\n");
-    for (int i = 0; i < (int)(sizeof(TESTS)/sizeof(*TESTS)); i++) {
+    for (int i = 0; i < TEST_COUNT; i++) {
         printf("%s\n", TESTS[i].name);
     }
+    printf("Available Fuzz Tests:\n");
+    for (int i = 0; i < FUZZ_CASE_COUNT; i++) {
+        printf("%s fuzz < %s\n", appName, FUZZ_CASES[i]);
+    }
 }
 
 static struct Message* readStdin(struct Allocator* alloc)
@@ -95,7 +102,7 @@ static struct Message* readStdin(struct Allocator* alloc)
         printf("No test files over 4096 bytes\n");
         length = 0;
     }
-    struct Message* msg = Message_new(length, 0, alloc);
+    struct Message* msg = Message_new(length, 128, alloc);
     Bits_memcpy(msg->bytes, buff, length);
     return msg;
 }
@@ -152,7 +159,7 @@ static int fuzzMain()
     Allocator_free(alloc);
     return 0;
 }
-
+/*
 static int mkFuzz()
 {
     struct Allocator* alloc = MallocAllocator_new(1<<24);
@@ -173,11 +180,10 @@ static int mkFuzz()
         Allocator_free(child);
     }
     return 0;
-}
+}*/
 
 int main(int argc, char** argv)
 {
-    if (argc > 1 && !CString_strcmp("mkfuzz", argv[1])) { return mkFuzz(); }
     if (argc > 1 && !CString_strcmp("fuzz", argv[1])) { return fuzzMain(); }
     struct Allocator* alloc = MallocAllocator_new(4096);
     struct EventBase* base = EventBase_new(alloc);

+ 99 - 19
test/testcjdroute.js

@@ -52,44 +52,124 @@ var getTests = function (file, tests, isSubnode, callback) {
     });
 };
 
+var rmFuzz = function (builder, cb) {
+    var inputs = builder.config.buildDir + '/fuzz_inputs';
+    Fs_stat(inputs, function (err, stat) {
+        if (err && err.code === 'ENOENT') {
+            Fs.mkdir(inputs, function (err, ret) {
+                if (err) { throw err; }
+                return void cb();
+            });
+            return;
+        } else if (err) {
+            throw err;
+        }
+        if (!stat.isDirectory()) {
+            throw new Error(inputs + ' found, and it is a file, not a directory');
+        }
+        Fs.readdir(inputs, function (err, list) {
+            if (err) { throw err; }
+            var nt = nThen;
+            list.forEach((f) => {
+                nt = nt(function (w) {
+                    Fs.unlink(inputs + '/' + f, w(function (err) {
+                        if (err) { throw err; }
+                    }));
+                }).nThen;
+            })
+            nt(function (w) { cb(); });
+        });
+    });
+};
+
+var mkFuzzCase = function (inFile, outPath, testId, cb) {
+    Fs.readFile(inFile, 'utf8', function (err, ret) {
+        if (err) { throw err; }
+        ret = ret.replace(/#[^\n]*\n/g, '');
+        ret = ret.replace(/[\s]*/g, '');
+        var out = Buffer.from(ret, 'hex');
+        var id = Buffer.alloc(4);
+        id.writeInt32BE(testId, 0);
+        Fs.writeFile(outPath, Buffer.concat([id, out]), function (err) {
+            if (err) { throw err; }
+            cb();
+        });
+    });
+};
+
+var mkFuzz = function (builder, testPath, testId, fuzzCases, cb) {
+    var inputs = builder.config.buildDir + '/fuzz_inputs';
+    nThen(function (w) {
+        var casesPath = testPath.replace(/\.c$/, '_cases');
+        Fs.readdir(casesPath, w(function (err, list) {
+            if (err && err.code === 'ENOENT') {
+                return void console.log("Fuzz test [" + testPath + "] has no test cases");
+            }
+            if (err) { throw err; }
+            var nt = nThen;
+            list.forEach((f) => {
+                nt = nt(function (w) {
+                    var fNoExt = f.replace(/\..*$/, '');
+                    var outPath = inputs + '/' + (testPath + fNoExt).replace(/[^a-zA-Z0-9_-]/g, '_');
+                    fuzzCases.push('"' + outPath + '",');
+                    var inFile = casesPath + '/' + f;
+                    mkFuzzCase(inFile, outPath, testId, w());
+                }).nThen;
+            })
+            nt(w());
+        }));
+    }).nThen(function (w) {
+        cb();
+    });
+};
+
 var generate = module.exports.generate = function (file, builder, isSubnode, callback)
 {
     var tests = [];
-    getTests('.', tests, isSubnode, function () {
-        var prototypes = [];
-        var listContent = [];
-        var fuzzTests = [];
+    var fuzzCases = [];
+    var prototypes = [];
+    var listContent = [];
+    var fuzzTests = [];
+
+    nThen(function (w) {
+        getTests('.', tests, isSubnode, w());
+        rmFuzz(builder, w());
+    }).nThen(function (w) {
         tests.forEach(function (test) {
+            //console.log(test);
             var testProto = /^.*\/([^\/]+)\.c$/.exec(test)[1];
-            var main = testProto + '_main';
-            (builder.config['cflags'+test] =
-                builder.config['cflags'+test] || [])
-                .push('-D', 'main='+main,
-                      '-D', main+'(...)='+main+'(int argc, char** argv);int '+main+'(int argc, char** argv)',
-                      '-D', 'CJDNS_FUZZ_INIT='+testProto+'_FUZZ_INIT',
-                      '-D', 'CJDNS_FUZZ_MAIN='+testProto+'_FUZZ_MAIN',
-                      '-D', 'CJDNS_FUZZ_MK='+testProto+'_FUZZ_MK'
-                );
             file.links.push(test);
+            var cflags = (builder.config['cflags'+test] = builder.config['cflags'+test] || []);
             if (test.indexOf('_fuzz_test') > -1) {
-                prototypes.push('void* '+testProto+'_FUZZ_INIT(struct Allocator* alloc, ' +
-                    'struct Random* rand);');
-                prototypes.push('void '+testProto+'_FUZZ_MAIN(void* ctx, struct Message* fuzz);');
-                prototypes.push('struct FuzzTest* '+testProto+'_FUZZ_MK(struct Allocator* alloc);');
+                cflags.push(
+                    '-D', 'CJDNS_FUZZ_INIT='+testProto+'_FUZZ_INIT',
+                    '-D', 'CJDNS_FUZZ_MAIN='+testProto+'_FUZZ_MAIN'
+                );
+                prototypes.push(
+                    'void* '+testProto+'_FUZZ_INIT(struct Allocator* alloc, struct Random* rand);',
+                    'void '+testProto+'_FUZZ_MAIN(void* ctx, struct Message* fuzz);'
+                );
+                mkFuzz(builder, test, fuzzTests.length, fuzzCases, w());
                 fuzzTests.push('{' +
                     '.init = '+testProto+'_FUZZ_INIT, ' +
                     '.name = "'+test.replace(/^.*\/|.c$/g, '')+'", ' +
                     '.fuzz = '+testProto+'_FUZZ_MAIN, ' +
-                    '.mkFuzz = '+testProto+'_FUZZ_MK, ' +
                 '},');
             } else {
+                var main = testProto + '_main';
+                cflags.push(
+                    '-D', 'main='+main,
+                    '-D', main+'(...)='+main+'(int argc, char** argv);int '+main+'(int argc, char** argv)'
+                );
                 prototypes.push('int '+main+'(int argc, char** argv);');
                 listContent.push('{ .func = '+main+', .name = "'+test.replace(/^.*\/|.c$/g, '')+'" },');
             }
         });
+    }).nThen(function (w) {
+        file.testcjdroute_fuzzCases = fuzzCases.join('\n');
         file.testcjdroute_tests = listContent.join('\n');
         file.testcjdroute_fuzzTests = fuzzTests.join('\n');
         file.testcjdroute_prototypes = prototypes.join('\n');
         callback();
-    });
+    })
 };

+ 4 - 0
util/events/Timeout.h

@@ -83,4 +83,8 @@ void Timeout_resetTimeout(struct Timeout* timeout,
  */
 void Timeout_clearTimeout(struct Timeout* timeout);
 
+void Timeout_clearAll(struct EventBase* eventBase);
+
+int Timeout_isActive(struct Timeout* timeout);
+
 #endif

+ 2 - 0
util/events/libuv/EventBase_pvt.h

@@ -41,6 +41,8 @@ struct EventBase_pvt
     /** Number of milliseconds since epoch when the clock was calibrated. */
     uint64_t baseTime;
 
+    void* timeouts;
+
     Identity
 };
 

+ 51 - 0
util/events/libuv/Timeout.c

@@ -32,9 +32,36 @@ struct Timeout
 
     struct Allocator* alloc;
 
+    struct Timeout* next;
+    struct Timeout** selfPtr;
+    struct EventBase_pvt* base;
+
     Identity
 };
 
+static void link(struct Timeout* timeout)
+{
+    timeout->next = (struct Timeout*) timeout->base->timeouts;
+    if (timeout->next) {
+        timeout->next->selfPtr = &timeout->next;
+    }
+    timeout->base->timeouts = timeout;
+    timeout->selfPtr = (struct Timeout**) &timeout->base->timeouts;
+}
+
+static void unlink(struct Timeout* timeout)
+{
+    if (timeout->selfPtr) {
+        *timeout->selfPtr = timeout->next;
+        if (timeout->next) {
+            Assert_true(&timeout->next == timeout->next->selfPtr);
+            timeout->next->selfPtr = timeout->selfPtr;
+            timeout->next = NULL;
+        }
+        timeout->selfPtr = NULL;
+    }
+}
+
 /**
  * The callback to be called by libuv.
  */
@@ -55,6 +82,7 @@ static void onFree2(uv_handle_t* timer)
 static int onFree(struct Allocator_OnFreeJob* job)
 {
     struct Timeout* t = Identity_check((struct Timeout*) job->userData);
+    unlink(t);
     t->timer.data = job;
     uv_close((uv_handle_t*) &t->timer, onFree2);
     return Allocator_ONFREE_ASYNC;
@@ -91,6 +119,7 @@ static struct Timeout* setTimeout(void (* const callback)(void* callbackContext)
     timeout->milliseconds = milliseconds;
     timeout->alloc = alloc;
     timeout->isInterval = interval;
+    timeout->base = base;
     Identity_set(timeout);
 
     uv_timer_init(base->loop, &timeout->timer);
@@ -100,6 +129,8 @@ static struct Timeout* setTimeout(void (* const callback)(void* callbackContext)
 
     Allocator_onFree(alloc, onFree, timeout);
 
+    link(timeout);
+
     return timeout;
 }
 
@@ -132,13 +163,33 @@ void Timeout_resetTimeout(struct Timeout* timeout,
                           const uint64_t milliseconds)
 {
     Timeout_clearTimeout(timeout);
+    link(timeout);
     uv_timer_start(&timeout->timer, handleEvent, milliseconds, 0);
 }
 
 /** See: Timeout.h */
 void Timeout_clearTimeout(struct Timeout* timeout)
 {
+    unlink(timeout);
     if (!uv_is_closing((uv_handle_t*) &timeout->timer)) {
         uv_timer_stop(&timeout->timer);
     }
 }
+
+void Timeout_clearAll(struct EventBase* eventBase)
+{
+    struct EventBase_pvt* base = EventBase_privatize(eventBase);
+    struct Timeout* to = base->timeouts;
+    if (!to) { return; }
+    while (to) {
+        struct Timeout* next = to->next;
+        Timeout_clearTimeout(to);
+        to = next;
+    }
+    Assert_true(!base->timeouts);
+}
+
+int Timeout_isActive(struct Timeout* timeout)
+{
+    return (timeout && timeout->selfPtr);
+}

+ 0 - 10
util/test/Map_fuzz_test.c

@@ -24,16 +24,6 @@
 #define Map_ENABLE_HANDLES
 #include "util/Map.h"
 
-struct FuzzTest* CJDNS_FUZZ_MK(struct Allocator* alloc)
-{
-    struct Message* msg = Message_new(0, 2, alloc);
-    Message_push16(msg, 500, NULL);
-    struct FuzzTest* out = Allocator_calloc(alloc, sizeof(struct FuzzTest), 1);
-    out->name = "Map_fuzz_test_default";
-    out->fuzz = msg;
-    return out;
-}
-
 void* CJDNS_FUZZ_INIT(struct Allocator* alloc, struct Random* rand)
 {
     return alloc;

+ 2 - 0
util/test/Map_fuzz_test_cases/Default.hex

@@ -0,0 +1,2 @@
+# The only randomness is the size of the map, we'll start off with 512
+0200