Caleb James DeLisle 9 years ago
parent
commit
03f548f857
10 changed files with 614 additions and 136 deletions
  1. 8 3
      interface/SessionManager.h
  2. 352 0
      net/BailingWire.c
  3. 93 0
      net/BailingWire.h
  4. 0 76
      net/Bridge.c
  5. 0 37
      net/Bridge.h
  6. 4 14
      net/Ducttape.c
  7. 60 0
      net/Event.h
  8. 80 0
      net/EventEmitter.c
  9. 11 6
      net/EventEmitter.h
  10. 6 0
      util/Identity.h

+ 8 - 3
interface/SessionManager.h

@@ -48,8 +48,8 @@ struct SessionManager_Session
     /** The handle which will be used to lookup this session on our side, big endian. */
     uint32_t receiveHandle_be;
 
-    /** The handle which we are expected to send to identify ourselves, big endian. */
-    uint32_t sendHandle_be;
+    /** The handle which we are expected to send to identify ourselves */
+    uint32_t sendHandle;
 
     /** The version of the other node. */
     uint32_t version;
@@ -59,9 +59,14 @@ struct SessionManager_Session
 
     /** Some label which is known to go to this node... */
     uint64_t knownSwitchLabel;
+
+    /** Quality of switch label. */
+    uint32_t metric;
+
+    /** 
+    uint32_t timeDiscoveredSeconds;
 };
 
-// TODO(cjd): ArrayList.h
 struct SessionManager_HandleList
 {
     uint32_t count;

+ 352 - 0
net/BailingWire.c

@@ -0,0 +1,352 @@
+/* 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 <http://www.gnu.org/licenses/>.
+ */
+#include "interface/Interface.h"
+#include "memory/Allocator.h"
+#include "net/Event.h"
+#include "net/BailingWire.h"
+
+struct BufferedMessage
+{
+    struct Message* msg;
+    struct Allocator* alloc;
+    uint32_t timeSent;
+    bool confirmed;
+};
+
+struct Ip6 {
+    uint8_t bytes[16];
+};
+#define Map_KEY_TYPE struct Ip6
+#define Map_VALUE_TYPE struct BufferedMessage*
+#define Map_NAME BufferedMessages
+#include "util/Map.h"
+
+struct BailingWire_pvt
+{
+    struct BailingWire pub;
+    struct Interface_Two eventIf;
+    struct Allocator* alloc;
+    struct Map_BufferedMessages bufMap;
+
+    /** global crap about the "active message" (Passing data around cryptoAuth) */
+    struct SessionManager_Session* currentSession;
+    bool currentMessageSetup;
+    struct SwitchHeader* currentSwitchHeader;
+
+    Identity
+};
+
+#define debugHandles(logger, session, message, ...) \
+    do {                                                                               \
+        uint8_t ip[40];                                                                \
+        AddrTools_printIp(ip, session->ip6);                                      \
+        Log_debug(logger, "ver[%u] send[%d] recv[%u] ip[%s] " message,                 \
+                  session->version,                                                    \
+                  Endian_hostToBigEndian32(session->sendHandle_be),                    \
+                  Endian_hostToBigEndian32(session->receiveHandle_be),                 \
+                  ip,                                                                  \
+                  __VA_ARGS__);                                                        \
+    } while (0)
+//CHECKFILES_IGNORE expecting a ;
+
+#define debugHandles0(logger, session, message) \
+    debugHandles(logger, session, message "%s", "")
+
+#define debugHandlesAndLabel(logger, session, label, message, ...) \
+    do {                                                                               \
+        uint8_t path[20];                                                              \
+        AddrTools_printPath(path, label);                                              \
+        debugHandles(logger, session, "path[%s] " message, path, __VA_ARGS__);         \
+    } while (0)
+//CHECKFILES_IGNORE expecting a ;
+
+#define debugHandlesAndLabel0(logger, session, label, message) \
+    debugHandlesAndLabel(logger, session, label, "%s", message)
+
+static uint8_t incomingFromSwitchPostCryptoAuth(struct Message* msg, struct Interface* iface)
+{
+    struct BailingWire_pvt* bw = Identity_check((struct BailingWire_pvt*) iface->receiverContext);
+
+    struct SessionManager_Session* session = bw->currentSession;
+    bool currentMessageSetup = bw->currentMessageSetup;
+    struct SwitchHeader* sh = bw->currentSwitchHeader;
+    bw->currentSession = NULL;
+    bw->currentMessageSetup = false;
+    bw->currentSwitchHeader = NULL;
+
+    // CryptoAuth exports the nonce, we're still not using it for anything...
+    Message_shift(msg, -4, NULL);
+
+    if (currentMessageSetup) {
+        session->sendHandle = Message_pop32(msg, NULL);
+    }
+
+    Message_shift(msg, BailingWire_InsideHeader_SIZE, NULL);
+    struct BailingWire_InsideHeader* header = (struct BailingWire_InsideHeader*) msg->bytes;
+
+    Bits_memcpyConst(&header->sh, sh, SwitchHeader_SIZE);
+    header->version = session->version;
+    Bits_memcpyConst(header->ip6, session->ip6, 16);
+    uint8_t* pubKey = CryptoAuth_getHerPublicKey(session->internal);
+    Bits_memcpyConst(header->publicKey, pubKey, 32);
+    header->publicKeyKnown = 1;
+
+    Interface_send(&bw->pub.insideIf, msg);
+    // Never return errors here because they can cause unencrypted stuff to be returned as an error.
+    return 0;
+}
+
+static int incomingFromSwitchIf(struct Interface_Two* iface, struct Message* msg)
+{
+    struct BailingWire_pvt* bw = Identity_containerOf(iface, struct BailingWire_pvt, pub.switchIf);
+
+    // SwitchHeader, handle, small cryptoAuth header
+    if (msg->length < SwitchHeader_SIZE + 4 + 20) {
+        Log_debug(bw->logger, "runt");
+        return 0;
+    }
+
+    struct SwitchHeader* switchHeader = (struct SwitchHeader*) msg->bytes;
+    Message_shift(msg, -SwitchHeader_SIZE, NULL);
+
+    bool currentMessageSetup;
+    struct SessionManager_Session* session;
+    uint32_t nonceOrHandle = Endian_bigEndianToHost32(((uint32_t*)message->bytes)[0]);
+    if (nonceOrHandle > 3) {
+        // > 3 it's a handle.
+        session = SessionManager_sessionForHandle(nonceOrHandle, bw->pub.sessionManager);
+        if (!session) {
+            Log_debug(bw->logger, "Got message with unrecognized handle");
+            return 0;
+        }
+    } else {
+        // handle + big cryptoauth header
+        if (message->length < CryptoHeader_SIZE + 4) {
+            Log_debug(bw->logger, "runt");
+            return 0;
+        }
+        union CryptoHeader* caHeader = (union CryptoHeader*) message->bytes;
+        uint8_t* herKey = caHeader->handshake.publicKey;
+        uint8_t ip6[16];
+        // a packet which claims to be "from us" causes problems
+        if (!AddressCalc_addressForPublicKey(ip6, herKey)) {
+            Log_debug(context->logger, "Handshake with non-fc key");
+            return 0;
+        }
+
+        if (!Bits_memcmp(herKey, &bw->ca.publicKey, 32)) {
+            Log_debug(context->logger, "Handshake from 'ourselves'");
+            return 0;
+        }
+
+        session = SessionManager_getSession(ip6, herKey, bw->pub.sessionManager);
+        currentMessageSetup = true;
+
+        debugHandlesAndLabel(bw->logger, session,
+                             Endian_bigEndianToHost64(switchHeader->label_be),
+                             "new session nonce[%d]", nonceOrHandle);
+    }
+
+    Assert_true(false == bw->currentMessageSetup);
+    Assert_true(NULL == bw->currentSession);
+    Assert_true(NULL == bw->currentSwitchHeader);
+    bw->currentMessageSetup = currentMessageSetup;
+    bw->currentSession = session;
+    bw->currentSwitchHeader = switchHeader;
+    // --> incomingFromSwitchPostCryptoAuth
+    int ret = Interface_receiveMessage(&session->external, message);
+    if (ret) {
+        bw->currentMessageSetup = false;
+        bw->currentSession = NULL;
+        bw->currentSwitchHeader = NULL;
+
+        debugHandlesAndLabel(bw->logger, session,
+                             Endian_bigEndianToHost64(switchHeader->label_be),
+                             "DROP Failed decrypting message NoH[%d] state[%s]",
+                             nonceOrHandle,
+                             CryptoAuth_stateString(CryptoAuth_getState(session->internal)));
+        return Error_AUTHENTICATION;
+    }
+    Assert_true(false == bw->currentMessageSetup);
+    Assert_true(NULL == bw->currentSession);
+    Assert_true(NULL == bw->currentSwitchHeader);
+
+    return 0;
+}
+
+void checkTimedOutBuffers(void* vBailingWire)
+{
+    struct BailingWire_pvt* bw = Identity_check((struct BailingWire_pvt*) vBailingWire);
+...
+}
+
+static int needsLookup(struct BailingWire_pvt* bw, struct Message* msg)
+{
+    struct BailingWire_InsideHeader* header = (struct BailingWire_InsideHeader*) msg->bytes;
+    int index = Map_BufferedMessages_indexForKey(bw->bufMap, (struct Ip6*)header->ip6 );
+    if (index > -1) {
+        Log_debug(bw->log, "DROP message which needs lookup because one is in progress");
+        return 0;
+    }
+    if (bw->bufMap.count >= bw->pub.maxBufferedMessages) {
+        checkTimedOutBuffers(bw);
+        if (bw->bufMap.count >= bw->pub.maxBufferedMessages) {
+            Log_debug(bw->log, "DROP message needing lookup maxBufferedMessages ([%d]) is reached",
+                      bw->pub.maxBufferedMessages);
+            return 0;
+        }
+    }
+    struct Allocator* lookupAlloc = Allocator_child(bw->alloc);
+    struct BufferedMessage* buffered =
+        Allocator_calloc(lookupAlloc, sizeof(struct BufferedMessage), 1);
+    buffered->msg = msg;
+    buffered->alloc = lookupAlloc;
+    buffered->timeSent = Time_currentTimeSeconds(bw->eventBase);
+    Allocator_adopt(lookupAlloc, msg->alloc);
+    Assert_true(Map_BufferedMessages_put(bw->bufMap, (struct Ip6*)header->ip6, buffered) > -1);
+
+    struct Allocator* eventAlloc = Allocator_child(lookupAlloc);
+    struct Message* eventMsg = Message_new(0, 512, eventAlloc);
+    Message_push(eventMsg, header->ip6, 16, NULL);
+    Message_push32(eventMsg, Event_SEARCH, NULL);
+    Interface_send(&bw->eventIf, eventMsg);
+    Allocator_free(eventAlloc);
+}
+
+static uint8_t readyToSendPostCryptoAuth(struct Message* msg, struct Interface* iface)
+{
+    struct BailingWire_pvt* bw = Identity_check((struct BailingWire_pvt*) iface->senderContext);
+    struct SwitchHeader* sh = bw->currentSwitchHeader;
+    struct SessionManager_Session* sess = bw->currentSession;
+    bw->currentSession = NULL;
+    bw->currentSwitchHeader = NULL;
+    if (CryptoAuth_getState(sess->internal) >= CryptoAuth_HANDSHAKE3) {
+        //debugHandlesAndLabel0(context->logger, session, label, "layer2 sending run message");
+        Message_push32(msg, sess->sendHandle, NULL);
+        Message_push(msg, sh, SwitchHeader_SIZE, NULL);
+    } else {
+        debugHandlesAndLabel0(context->logger, session, label, "layer2 sending start message");
+        Message_shift(msg, SwitchHeader_SIZE, NULL);
+        Assert_true((uint8_t)sh == msg->bytes);
+    }
+    return Interface_send(bw->pub.switchIf, msg);
+}
+
+static int readyToSend(struct BailingWire_pvt* bw,
+                       struct SessionManager_Session* sess,
+                       struct Message* msg)
+{
+    struct BailingWire_InsideHeader* header = (struct BailingWire_InsideHeader*) msg->bytes;
+    Assert_true(!bw->currentSession);
+    Assert_true(!bw->currentSwitchHeader);
+    bw->currentSession = sess;
+    bw->currentSwitchHeader = &header->sh;
+    // --> readyToSendPostCryptoAuth
+    int ret = Interface_sendMessage(sess->internal, msg);
+    Assert_true(!bw->currentSession);
+    Assert_true(!bw->currentSwitchHeader);
+    return ret;
+}
+
+static int incomingFromInsideIf(struct Interface_Two* iface, struct Message* msg)
+{
+    struct BailingWire_pvt* bw = Identity_containerOf(iface, struct BailingWire_pvt, pub.insideIf);
+    Assert_true(msg->length >= BailingWire_InsideHeader_SIZE);
+    struct BailingWire_InsideHeader* header = (struct BailingWire_InsideHeader*) msg->bytes;
+
+    uint8_t* publicKey = (header->publicKeyKnown) ? header->publicKey : NULL;
+    struct SessionManager_Session* sess;
+    if (header->publicKeyKnown && header->sh.label_be) {
+        sess = SessionManager_getSession(header->ip6, publicKey, bw->pub.sessionManager);
+    } else {
+        sess = SessionManager_sessionForIp6(header->ip6, bw->pub.sessionManager);
+        if (!sess) { return needsLookup(bw, msg); }
+    }
+
+    if (header->sh.label_be) {
+        // fallthrough
+    } else if (sess->knownSwitchLabel) {
+        header->sh.label_be = Endian_hostToBigEndian64(session->knownSwitchLabel);
+    } else {
+        return needsLookup(bw, msg);
+    }
+
+    return readyToSend(bw, session, msg);
+}
+
+static int discovery(struct BailingWire_pvt* bw, struct Message* msg)
+{
+    uint8_t ip6[16];
+    Message_pop(msg, ip6, 16, NULL);
+}
+
+static int incomingFromEventIf(struct Interface_Two* iface, struct Message* msg)
+{
+    struct BailingWire_pvt* bw = Identity_containerOf(iface, struct BailingWire_pvt, eventIf);
+    enum Event ev = Message_pop32(msg, NULL);
+
+    if (ev == Event_DISCOVERY) {
+        return discovery(bw, msg);
+    }
+
+    struct Ip6 ip6;
+    Message_pop(msg, &ip6, 16, NULL);
+    int index = Map_BufferedMessages_indexForKey(bw->bufMap, (struct Ip6*)header->ip6);
+    if (index == -1) { return 0; }
+    struct BufferedMessage* bm = bw->bufMap.values[index];
+    if (ev == Event_SEARCH_BEGIN) {
+        bm->confirmed = true;
+        return 0;
+    } else if (ev == Event_SEARCH_END) {
+        if (Defined(Log_DEBUG)) {
+            uint8_t ipStr[40];
+            AddrTools_printIp(ipStr, ip6.bytes);
+            Log_debug(bw->log, "DROP buffered packet to [%s] because search found nothing");
+        }
+        Map_BufferedMessages_remove(bw->bufMap, index);
+        Allocator_free(bm->alloc);
+        return 0;
+    }
+    Assert_failure("2+2=5");
+}
+
+struct BailingWire* BailingWire_new(struct Allocator* alloc,
+                                    struct EventBase* eventBase,
+                                    struct CryptoAuth* cryptoAuth,
+                                    struct Random* rand,
+                                    struct EventEmitter* ee)
+{
+    struct BailingWire_pvt* bw = Allocator_calloc(alloc, sizeof(struct BailingWire_pvt), 1);
+    bw->sm = sm;
+    bw->alloc = alloc;
+    bw->pub.switchIf.send = incomingFromSwitchIf;
+    bw->pub.insideIf.send = incomingFromInsideIf;
+    bw->eventIf.send = incomingFromEventIf;
+    bw->bufMap.allocator = alloc;
+
+    EventEmitter_regIface(ee, &bw->eventIf, Event_DISCOVERY);
+    EventEmitter_regIface(ee, &bw->eventIf, Event_SEARCH_BEGIN);
+    EventEmitter_regIface(ee, &bw->eventIf, Event_SEARCH_END);
+
+    bw->pub.sessionManager = SessionManager_new(incomingFromSwitchPostCryptoAuth,
+                                                readyToSendPostCryptoAuth,
+                                                bw,
+                                                eventBase,
+                                                cryptoAuth,
+                                                rand,
+                                                alloc);
+
+    return &bw->pub;
+}

+ 93 - 0
net/BailingWire.h

@@ -0,0 +1,93 @@
+/* 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 <http://www.gnu.org/licenses/>.
+ */
+#ifndef BailingWire_H
+#define BailingWire_H
+
+#include "interface/Interface.h"
+#include "interface/SessionManager.h"
+#include "memory/Allocator.h"
+#include "net/Event.h"
+#include "net/EventEmitter.h"
+#include "util/Linker.h"
+Linker_require("net/BailingWire.c")
+
+/**
+ * Called BailingWire because I can't think of what this should be called.
+ * Purpose of this module is to take packets from "the inside" which contain ipv6 address and
+ * skeleton switch header and find an appropriate CryptoAuth session for them or begin one.
+ * If a key for this node cannot be found then the packet will be blocked and a search will be
+ * triggered. If the skeleton switch header contains "zero" as the switch label, the packet will
+ * also be buffered and a search triggered. If a search is in progress (and another packet is
+ * already buffered, the packet will be dropped instead).
+ * Incoming messages from the outside will be decrypted and their key and path will be stored.
+ */
+struct BailingWire
+{
+    /** Sends and handles packets prepped to/from switch. */
+    struct Interface_Two switchIf;
+
+    /**
+     * Sends and handles packets with BailingWire_InsideHeader on top.
+     * When sending a packet to BailingWire:
+     *     header.sh.label_be may be zero
+     *     version may be zero
+     *     publicKey may be zero
+     * If these values are not known, the packet will be taken from the cache or a search will
+     * be triggered.
+     */
+    struct Interface_Two insideIf;
+
+    /**
+     * Maximum number of packets to hold in buffer before summarily dropping...
+     */
+    int maxBufferedMessages;
+};
+
+struct BailingWire_InsideHeader
+{
+    /**
+     * The switch header to use.
+     * label_be may be zero if unknown.
+     * version will be automatically set to the node's current version.
+     */
+    struct SwitchHeader sh;
+
+    /** Protocol version of peer node, 0 if unknown. */
+    uint32_t version;
+
+    /** IPv6 of peer node REQUIRED */
+    uint8_t ip6[16];
+
+    /** public key of peer node. */
+    uint8_t publicKey[32];
+
+    /** 0 if the publicKey is unknown and 'publicKey' holds nothing of value. */
+    uint32_t publicKeyKnown;
+
+    /**
+     * Pads out the size of the inside header to prevent the SwitchHeader possibly being
+     * clobbered by the cryptoAuth so that the switch header will not be clobbered.
+     * Largest overhead:  [ CryptoHeader ][ encrypted Handle (4) ][ encrypted content ..... ]
+     */
+    uint8_t padding[68];
+};
+#define BailingWire_InsideHeader_SIZE (CryptoHeader_SIZE + 4 + SwitchHeader_SIZE)
+Assert_compileTime(BailingWire_InsideHeader_SIZE == sizeof(struct BailingWire_InsideHeader));
+
+struct BailingWire* BailingWire_new(struct Allocator* alloc,
+                                    struct SessionManager* sm,
+                                    struct EventEmitter* ee);
+
+#endif

+ 0 - 76
net/Bridge.c

@@ -1,76 +0,0 @@
-/* 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 <http://www.gnu.org/licenses/>.
- */
-#include "interface/Interface.h"
-#include "memory/Allocator.h"
-#include "wire/ContentType.h"
-#include "net/Bridge.h"
-#include "util/Identity.h"
-
-#define Map_KEY_TYPE enum ContentType
-#define Map_VALUE_TYPE struct Interface_Two*
-#define Map_NAME InterfaceByContentType
-#include "util/Map.h"
-
-struct Bridge_pvt
-{
-    struct Bridge pub;
-    struct Allocator* alloc;
-    struct Map_InterfaceByContentType map;
-    Identity
-};
-
-int incoming(struct Interface_Two* coreIf, struct Message* msg)
-{
-    struct Bridge_pvt* br = Identity_check((struct Bridge_pvt*) coreIf);
-    Assert_true(msg->length >= BridgeHeader_SIZE);
-    Assert_true(!(msg->bytes % 4) && "alignment");
-    struct BridgeHeader* bh = (struct BridgeHeader*) msg->bytes;
-    Assert_true(bh->version == BridgeHeader_CURRENT_VERSION);
-    enum ContentType type = bh->type;
-    if (bh->type <= 255) {
-        return Interface_send(&br->pub.cjdnsIp6If, msg);
-    }
-    int index = Map_InterfaceByContentType_indexForKey(&br->map, &type);
-    if (index > -1) {
-        struct Bridge_Entry* e = br->map.values[index];
-        if (e->iface.send) {
-            // We have to call this manually because we don't have an interface handy which is
-            // actually plumbed with this one. See hack below...
-            return e->iface.send(&e->iface, msg);
-        }
-    }
-    return Error_UNDELIVERABLE;
-}
-
-void Bridge_regIface(struct Bridge* bridge, struct Interface_Two* iface, enum ContentType type)
-{
-    struct Bridge_pvt* br = Identity_check((struct Bridge_pvt*) bridge);
-    int index = Map_InterfaceByContentType_indexForKey(&br->map, &type);
-    Assert_true(index == -1);
-    Assert_true(Map_InterfaceByContentType_put(&br->map, &type, &iface) > -1);
-    // This is a hack, a 1-way interface connection which allows many interfaces to seem to be
-    // connected to this one.
-    iface->connectedIf = br->pub.cjdnsIp6If;
-}
-
-struct Bridge* Bridge_new(struct Allocator* alloc)
-{
-    struct Bridge_pvt* br = Allocator_calloc(alloc, sizeof(struct Bridge_pvt), 1);
-    br->alloc = alloc;
-    br->map.allocator = alloc;
-    br->pub.coreIf.cjdnsIp6If = incoming;
-    Identity_set(br);
-    return &br->pub;
-}

+ 0 - 37
net/Bridge.h

@@ -1,37 +0,0 @@
-/* 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 <http://www.gnu.org/licenses/>.
- */
-#ifndef Bridge_H
-#define Bridge_H
-
-#include "interface/Interface.h"
-#include "memory/Allocator.h"
-#include "wire/ContentType.h"
-#include "util/Linker.h"
-Linker_require("net/Bridge.c")
-
-struct Bridge
-{
-    /**
-     * Special iface which handles all types <= 255, handling ranges is not generally needed,
-     * just in this edge case.
-     */
-    struct Interface_Two cjdnsIp6If;
-};
-
-void Bridge_regIface(struct Bridge* bridge, struct Interface_Two* iface, enum ContentType type);
-
-struct Bridge* Bridge_new(struct Allocator* alloc);
-
-#endif

+ 4 - 14
net/Ducttape.c

@@ -49,11 +49,6 @@
 
 #define FC_ONE "\xfc\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1"
 
-#define DUCTTAPE_FOR_IFACE(iface) \
-    Identity_check( (struct Ducttape_pvt*)                                      \
-            ((uint8_t*)(iface) - offsetof(struct Ducttape, iface))              \
-    )
-
 /** Header must not be encrypted and must be aligned on the beginning of the ipv6 header. */
 static inline uint8_t sendToRouter(struct Message* message,
                                    struct Ducttape_MessageHeader* dtHeader,
@@ -117,7 +112,7 @@ static struct Ducttape_MessageHeader* getDtHeader(struct Message* message, bool
 // see ducttape.h -> dhtIf
 static int incomingFromDHTInterface(struct Interface_Two* dhtIf, struct Message* msg)
 {
-    struct Ducttape_pvt* ctx = DUCTTAPE_FOR_IFACE(dhtIf);
+    struct Ducttape_pvt* ctx = Identity_containerOf(dhtIf, struct Ducttape_pvt, pub.dhtIf);
 
     struct Address addr;
     Message_pop(msg, &addr, Address_SIZE, NULL);
@@ -380,7 +375,7 @@ static inline bool isForMe(struct Message* message, struct Ducttape_pvt* context
 
 static int incomingFromMagicInterface(struct Interface_Two* magicIf, struct Message* msg)
 {
-    struct Ducttape_pvt* ctx = DUCTTAPE_FOR_IFACE(magicIf);
+    struct Ducttape_pvt* ctx = Identity_containerOf(magicIf, struct Ducttape_pvt, pub.magicIf);
 
     Assert_ifParanoid(msg->length >= Headers_IP6Header_SIZE);
     #ifdef PARANOIA
@@ -868,7 +863,7 @@ static uint8_t outgoingFromCryptoAuth(struct Message* message, struct Interface*
  */
 static uint8_t incomingFromSwitch(struct Message* message, struct Interface* switchIf)
 {
-    struct Ducttape_pvt* context = DUCTTAPE_FOR_IFACE(switchIf);
+    struct Ducttape_pvt* ctx = Identity_containerOf(switchIf, struct Ducttape_pvt, pub.switchIf);
 
     struct Ducttape_MessageHeader* dtHeader = getDtHeader(message, true);
 
@@ -903,11 +898,6 @@ static uint8_t incomingFromSwitch(struct Message* message, struct Interface* swi
         session = SessionManager_sessionForHandle(nonceOrHandle, context->sm);
 
         if (session) {
-            uint32_t nonce = Endian_bigEndianToHost32(((uint32_t*)message->bytes)[0]);
-            if (nonce == ~0u) {
-                Log_debug(context->logger, "DROP connectToMe packet at switch layer");
-                return 0;
-            }
             /*
             debugHandlesAndLabel(context->logger, session,
                                  Endian_bigEndianToHost64(switchHeader->label_be),
@@ -969,7 +959,7 @@ static uint8_t incomingFromSwitch(struct Message* message, struct Interface* swi
 
 static int incomingFromControlHandler(struct Interface_Two* controlIf, struct Message* message)
 {
-    struct Ducttape_pvt* ctx = DUCTTAPE_FOR_IFACE(controlIf);
+    struct Ducttape_pvt* ctx = Identity_containerOf(controlIf, struct Ducttape_pvt, pub.controlIf);
     Assert_true(ctx->pub.switchIf.receiveMessage);
     return Interface_receiveMessage(&ctx->pub.switchIf, message);
 }

+ 60 - 0
net/Event.h

@@ -0,0 +1,60 @@
+/* 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 <http://www.gnu.org/licenses/>.
+ */
+#ifndef Event_H
+#define Event_H
+
+enum Event
+{
+    Event_SEARCH,
+
+    /**
+     * Acknoledges the search has begun and a SEARCH_END is guaranteed to be sent later.
+     * contains a SearchHeader.
+     */
+    Event_SEARCH_BEGIN,
+
+    /** End of search, contains a SearchHeader. */
+    Event_SEARCH_END,
+
+    Event_DISCOVERY,
+
+    /** Must be the last entry, never emitted. */
+    Event_INVALID
+};
+
+struct Event_SearchHeader
+{
+    enum Event event_be;
+
+    uint8_t ipv6[16];
+};
+
+struct Event_DiscoveryHeader
+{
+    enum Event event_be;
+
+    uint8_t ip6[16];
+
+    uint8_t publicKey[32];
+
+    uint64_t path_be;
+
+    uint32_t version_be;
+
+    /** Quality of path represented by switch label (0:best ffffffff:worst) */
+    uint32_t metric_be;
+};
+
+#endif

+ 80 - 0
net/EventEmitter.c

@@ -0,0 +1,80 @@
+/* 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 <http://www.gnu.org/licenses/>.
+ */
+#include "interface/Interface.h"
+#include "memory/Allocator.h"
+#include "net/Event.h"
+#include "net/EventEmitter.h"
+#include "util/Identity.h"
+
+struct EventEmitter_pvt
+{
+    struct EventEmitter pub;
+    struct Interface_Two trickIf;
+    struct Allocator* alloc;
+    struct List_Ifaces* listTable[Event_INVALID];
+    Identity
+};
+
+static struct List_Ifaces* getHandlers(struct EventEmitter_pvt* ee enum Event ev, bool create)
+{
+    // enums are signed D:
+    ev &= 0xffff;
+    if (ev >= Event_INVALID) { return NULL; }
+    struct List_Ifaces* out = ee->listTable[ev];
+    if (!out) {
+        out = ee->listTable[ev] = List_Ifaces_new(ee->alloc);
+    }
+    return out;
+}
+
+int incoming(struct Interface_Two* trickIf, struct Message* msg)
+{
+    struct EventEmitter_pvt* ee = Identity_containerOf(trickIf, struct EventEmitter_pvt, trickIf);
+    Assert_true(msg->length >= 4);
+    Assert_true(!(msg->bytes % 4) && "alignment");
+    enum Event ev = Message_pop32(msg, NULL);
+    struct List_Ifaces* handlers = getHandlers(ee, ev, false);
+    if (!handlers) { return 0; }
+    for (int i = 0; i < handlers->length; i++) {
+        struct Message* messageClone = msg;
+        if (handlers->length > i+1) {
+            // if only one handler, no need to copy the message...
+            messageClone = Message_clone(msg, msg->allocator);
+        }
+        // We have to call this manually because we don't have an interface handy which is
+        // actually plumbed with this one.
+        Assert_true(e->iface.send);
+        e->iface.send(&e->iface, messageClone);
+    }
+    return 0;
+}
+
+void EventEmitter_regIface(struct EventEmitter* emitter, struct Interface_Two* iface, enum Event ev)
+{
+    struct EventEmitter_pvt* ee = Identity_check((struct EventEmitter_pvt*) emitter);
+    iface->connectedIf = ee->trickIf;
+    struct List_Ifaces* l = getHandlers(ee, ev, true);
+    if (!l) { return; }
+    List_Ifaces_add(l, iface);
+}
+
+struct EventEmitter* EventEmitter_new(struct Allocator* alloc)
+{
+    struct EventEmitter_pvt* ee = Allocator_calloc(alloc, sizeof(struct EventEmitter_pvt), 1);
+    ee->alloc = alloc;
+    ee->trickIf.send = incoming;
+    Identity_set(br);
+    return &br->pub;
+}

+ 11 - 6
net/EventEmitter.h

@@ -15,18 +15,23 @@
 #ifndef EventEmitter_H
 #define EventEmitter_H
 
-enum EventEmitter_Events
-{
-    EventEmitter_Events_DISCOVERED_NODE,
-    EventEmitter_Events_
-}
+#include "interface/Interface.h"
+#include "memory/Allocator.h"
+#include "util/Event.h"
+#include "util/Linker.h"
+Linker_require("net/EventEmitter.c")
 
 struct EventEmitter
 {
     int unused;
 };
 
-EventEmitter_on(
+/**
+ * Register an interface to listen for (and fire) events.
+ * The same interface may be registered multiple times.
+ * If you only intend to fire events, just register with Event_INVALID.
+ */
+void EventEmitter_regIface(struct EventEmitter* ee, struct Interface_Two* iface, enum Event ev);
 
 struct EventEmitter* EventEmitter_new(struct Allocator* alloc);
 

+ 6 - 0
util/Identity.h

@@ -57,4 +57,10 @@
     #define Identity_ncheck(pointer) Identity_check(pointer)
 #endif
 
+#define Identity_containerOf(ptr, type, member) \
+    (__extension__ ({                                                          \
+	    const __typeof__(((type*)0)->member)*__mptr = (ptr);                   \
+	    Identity_check( (type*)((char*)__mptr - offsetof(type, member)) );     \
+    })
+
 #endif