Parcourir la source

Changes in order to enable changing of TUN device on the fly

Message.h
  * Introduce associatedFd to a message for sharing file descriptors between processes
  * Ability to push and pop numbers in any byte order
Seccomp_test.c
  * Refactoring to work with changes to Pipe.h
Process_test.c
  * Refactoring to work with changes to Pipe.h
  * Test passing file descriptors between processes to make sure it works
Sockaddr.h
  * Ability to create a synthetic sockaddr which encapsulates a uint32_t handle
UDPAddrIface.h
  * Introduce UDPAddrIface_getFd
Pipe.h
  * Pipe_named()
    * Now uses fullName rather than path + name, as did Pipe_namedConnect()
    * Requires a logger as an argument
    * Now Pipe_named() only connects to a unix socket server, never creates one
  * Pipe_namedConnect() removed
  * Pipe_forFiles()
    * Changed to Pipe_forFd() which accepts only one fd and is easier to implement
    * Has an argument 'ipc' to determine whether the fd being opened can be used to pass file descriptors between processes
    * Requires a logger as an argument
  * Pipe_exists() added which checks if a unix socket exists
PipeServer.h - introduced a new object which manages a unix socket server
  * Acts as an AddrIface similar to UDPAddrIface when messages come in from different clients of the pipe server
Seccomp.c -> Allow accept4 syscall which is used by PipeServer to accept a request from a new client
CString.c -> Add CString_strdup() which clones a string using an allocator
Bits.h -> Add get16 and put16 which allow read and write of potentially unaligned numbers
ReachabilityCollector.c -> Reduce logging
make.js -> Enable diagnostic colors on GCC always
TUNMessageType.h -> Introduced an enum of types of TUN devices used by linux/bsd/android/sunos
TUNInterface_*.c -> Switch from Pipe_forFiles() to Pipe_forFd()
SocketInterface.h -> Removed attemptToCreate which is nolonger possible
BSDMessageWrapper.c -> Don't return Error_NONE which is technically zero but being returned by a function which should return a pointer
AddrIfaceMuxer.h -> New interface which muxes multiple AddrIfaces into a single one, allowing Admin to be reachable via UDP and also socket
AddrIface.h -> Ability to push and pop Sockaddr to/from Message
UDPInterface.h -> UDPInterface_getFd() get the file descriptor underlying this interface
UDPInterface_admin.c -> New RPC: UDPInterface_getFd() for getting the file descriptor of a UDP interface
Iface.h -> improved documentation
FramingIface.c -> Support associatedFd
Janitor.c -> Reduce logging
Configurator.c
  * Stop supporting tunfd because this should be done through RPC calls
  * Stop supporting socketAttemptToCreate because this is removed
cjdroute2.c
  * Do not generate conf files with tunfd
  * Do not generate conf files with socketAttemptToCreate
  * Use a standardized socket path ( /tmp/cjdroute.sock ) rather than a randomized path
  * Wait for Core to create the socket server rather than creating it in the client
Core.c
  * Allow multiple calls to Core_initTun() or Core_initTunFd()
  * Remove FileNo_admin
  * Remove socketAttemptToCreate from Core_initSocket
  * Create a PipeServer rather than connecting to the client
  * After preconfiguration is complete, launch a muxer and plug in the UDPInterface as well as the socket interface
Caleb James DeLisle il y a 4 ans
Parent
commit
8e00a34c7a
45 fichiers modifiés avec 1151 ajouts et 970 suppressions
  1. 85 31
      admin/Admin.c
  2. 82 45
      admin/angel/Core.c
  3. 4 20
      client/Configurator.c
  4. 69 27
      client/cjdroute2.c
  5. 6 6
      dht/dhtcore/Janitor.c
  6. 11 5
      interface/FramingIface.c
  7. 5 0
      interface/Iface.h
  8. 6 0
      interface/UDPInterface.c
  9. 2 0
      interface/UDPInterface.h
  10. 16 0
      interface/UDPInterface_admin.c
  11. 18 0
      interface/addressable/AddrIface.h
  12. 128 0
      interface/addressable/AddrIfaceMuxer.c
  13. 14 13
      interface/addressable/AddrIfaceMuxer.h
  14. 0 47
      interface/addressable/UDPAddrInterface.h_
  15. 2 2
      interface/tuntap/BSDMessageTypeWrapper.c
  16. 1 2
      interface/tuntap/SocketInterface.c
  17. 0 1
      interface/tuntap/SocketInterface.h
  18. 1 1
      interface/tuntap/TUNInterface_darwin.c
  19. 1 1
      interface/tuntap/TUNInterface_freebsd.c
  20. 1 1
      interface/tuntap/TUNInterface_linux.c
  21. 1 1
      interface/tuntap/TUNInterface_netbsd.c
  22. 1 1
      interface/tuntap/TUNInterface_openbsd.c
  23. 1 1
      interface/tuntap/TUNInterface_sunos.c
  24. 23 0
      interface/tuntap/TUNMessageType.h
  25. 4 0
      node_build/make.js
  26. 3 1
      subnode/ReachabilityCollector.c
  27. 12 0
      util/Bits.h
  28. 9 0
      util/CString.c
  29. 3 0
      util/CString.h
  30. 5 0
      util/Seccomp.c
  31. 2 1
      util/Seccomp.h
  32. 17 24
      util/events/Pipe.h
  33. 21 23
      util/events/PipeServer.h
  34. 2 0
      util/events/UDPAddrIface.h
  35. 0 278
      util/events/libuv/FileNo.c
  36. 0 110
      util/events/libuv/FileNo_admin.c
  37. 109 238
      util/events/libuv/Pipe.c
  38. 253 0
      util/events/libuv/PipeServer.c
  39. 14 22
      util/events/libuv/Pipe_pvt.h
  40. 10 0
      util/events/libuv/UDPAddrIface.c
  41. 19 0
      util/platform/Sockaddr.c
  42. 20 1
      util/platform/Sockaddr.h
  43. 62 26
      util/test/Process_test.c
  44. 38 22
      util/test/Seccomp_test.c
  45. 70 19
      wire/Message.h

+ 85 - 31
admin/Admin.c

@@ -108,9 +108,6 @@ struct Admin_pvt
     /** non-zero if this session able to receive asynchronous messages. */
     int asyncEnabled;
 
-    /** Length of addresses of clients which communicate with admin. */
-    uint32_t addrLen;
-
     struct Message* tempSendMsg;
 
     Identity
@@ -127,12 +124,14 @@ static void sendMessage(struct Message* message, struct Sockaddr* dest, struct A
 static void sendBenc(Dict* message,
                      struct Sockaddr* dest,
                      struct Allocator* alloc,
-                     struct Admin_pvt* admin)
+                     struct Admin_pvt* admin,
+                     int fd)
 {
     Message_reset(admin->tempSendMsg);
     BencMessageWriter_write(message, admin->tempSendMsg, NULL);
     struct Message* msg = Message_new(0, admin->tempSendMsg->length + 32, alloc);
     Message_push(msg, admin->tempSendMsg->bytes, admin->tempSendMsg->length, NULL);
+    Message_setAssociatedFd(msg, fd);
     sendMessage(msg, dest, admin);
 }
 
@@ -167,51 +166,59 @@ static void clearExpiredAddresses(void* vAdmin)
     Log_debug(admin->logger, "Cleared [%d] expired sessions", count);
 }
 
-/**
- * public function to send responses
- */
-int Admin_sendMessage(Dict* message, String* txid, struct Admin* adminPub)
+static int sendMessage0(Dict* message, String* txid, struct Admin* adminPub, int fd)
 {
     struct Admin_pvt* admin = Identity_check((struct Admin_pvt*) adminPub);
     if (!admin) {
         return 0;
     }
-    Identity_check(admin);
-    Assert_true(txid && txid->len >= admin->addrLen);
-
-    struct Sockaddr_storage addr;
-    Bits_memcpy(&addr, txid->bytes, admin->addrLen);
+    Assert_true(txid && txid->len >= sizeof(struct Sockaddr));
+    uint16_t addrLen = 0;
+    Bits_memcpy(&addrLen, txid->bytes, 2);
+    Assert_true(txid->len >= addrLen);
+
+    struct Allocator* alloc = NULL;
+    if (admin->currentRequest) {
+        alloc = admin->currentRequest->alloc;
+    } else {
+        alloc = Allocator_child(admin->allocator);
+    }
+    struct Sockaddr* addr = Sockaddr_clone((struct Sockaddr*) txid->bytes, alloc);
 
     // if this is an async call, check if we've got any input from that client.
     // if the client is nresponsive then fail the call so logs don't get sent
     // out forever after a disconnection.
     if (!admin->currentRequest) {
-        struct Sockaddr* addrPtr = (struct Sockaddr*) &addr.addr;
-        int index = Map_LastMessageTimeByAddr_indexForKey(&addrPtr, &admin->map);
+        int index = Map_LastMessageTimeByAddr_indexForKey(&addr, &admin->map);
         uint64_t now = Time_currentTimeMilliseconds(admin->eventBase);
         if (index < 0 || checkAddress(admin, index, now)) {
             return Admin_sendMessage_CHANNEL_CLOSED;
         }
     }
 
-    struct Allocator* alloc = Allocator_child(admin->allocator);
-
     // Bounce back the user-supplied txid.
-    String* userTxid =
-        String_newBinary(&txid->bytes[admin->addrLen], txid->len - admin->addrLen, alloc);
-    if (txid->len > admin->addrLen) {
+    if (txid->len > addr->addrLen) {
+        String* userTxid =
+            String_newBinary(&txid->bytes[addr->addrLen], txid->len - addr->addrLen, alloc);
         Dict_putString(message, TXID, userTxid, alloc);
     }
 
-    sendBenc(message, &addr.addr, alloc, admin);
+    sendBenc(message, addr, alloc, admin, fd);
 
     Dict_remove(message, TXID);
 
-    Allocator_free(alloc);
+    if (!admin->currentRequest) {
+        Allocator_free(alloc);
+    }
 
     return 0;
 }
 
+int Admin_sendMessage(Dict* message, String* txid, struct Admin* adminPub)
+{
+    return sendMessage0(message, txid, adminPub, -1);
+}
+
 static inline bool authValid(Dict* message, struct Message* messageBytes, struct Admin_pvt* admin)
 {
     String* cookieStr = Dict_getStringC(message, "cookie");
@@ -431,7 +438,9 @@ static void handleMessage(struct Message* message,
     if (err) {
         Log_warn(admin->logger,
                  "Unparsable data from [%s] content: [%s] error: [%s]",
-                 Sockaddr_print(src, alloc), message->bytes, err);
+                 Sockaddr_print(src, alloc),
+                 Hex_print(message->bytes, message->length, alloc),
+                 err);
         return;
     }
 
@@ -451,15 +460,11 @@ static void handleMessage(struct Message* message,
 static Iface_DEFUN receiveMessage(struct Message* message, struct Iface* iface)
 {
     struct Admin_pvt* admin = Identity_containerOf(iface, struct Admin_pvt, iface);
-
-    Assert_ifParanoid(message->length >= (int)admin->addrLen);
-    struct Sockaddr_storage addrStore = { .addr = { .addrLen = 0 } };
-    Message_pop(message, &addrStore, admin->addrLen, NULL);
-
     struct Allocator* alloc = Allocator_child(admin->allocator);
-    admin->currentRequest = message;
+    struct Sockaddr* addrPtr = AddrIface_popAddr(message, NULL);
 
-    handleMessage(message, &addrStore.addr, alloc, admin);
+    admin->currentRequest = message;
+    handleMessage(message, Sockaddr_clone(addrPtr, alloc), alloc, admin);
 
     admin->currentRequest = NULL;
     Allocator_free(alloc);
@@ -511,6 +516,51 @@ void Admin_registerFunctionWithArgCount(char* name,
     }
 }
 
+static void importFd(Dict* args, void* vAdmin, String* txid, struct Allocator* requestAlloc)
+{
+    struct Admin_pvt* admin = Identity_check((struct Admin_pvt*) vAdmin);
+    int fd = admin->currentRequest->associatedFd;
+    Dict* res = Dict_new(requestAlloc);
+    char* error = "none";
+    if (fd < 0) {
+        if (Defined(win32)) {
+            error = "Admin_importFd() does not support win32";
+        } else {
+            error = "file descriptor was not attached to message";
+        }
+    } else {
+        Dict_putIntC(res, "fd", fd, requestAlloc);
+    }
+    Dict_putStringCC(res, "error", error, requestAlloc);
+    Admin_sendMessage(res, txid, &admin->pub);
+}
+
+static void exportFd(Dict* args, void* vAdmin, String* txid, struct Allocator* requestAlloc)
+{
+    struct Admin_pvt* admin = Identity_check((struct Admin_pvt*) vAdmin);
+    int64_t* fd_p = Dict_getIntC(args, "fd");
+    if (!fd_p || *fd_p < 0) {
+        Dict* res = Dict_new(requestAlloc);
+        Dict_putStringCC(res, "error", "invalid fd", requestAlloc);
+        Admin_sendMessage(res, txid, &admin->pub);
+        return;
+    }
+    int fd = *fd_p;
+    Dict* res = Dict_new(requestAlloc);
+    char* error = "none";
+    if (fd < 0) {
+        if (Defined(win32)) {
+            error = "Admin_exportFd() does not support win32";
+        } else {
+            error = "file descriptor was not attached to message";
+        }
+    } else {
+        Dict_putIntC(res, "fd", fd, requestAlloc);
+    }
+    Dict_putStringCC(res, "error", error, requestAlloc);
+    Admin_sendMessage(res, txid, &admin->pub);
+}
+
 struct Admin* Admin_new(struct AddrIface* ai,
                         struct Log* logger,
                         struct EventBase* eventBase,
@@ -522,7 +572,6 @@ struct Admin* Admin_new(struct AddrIface* ai,
     admin->allocator = alloc;
     admin->logger = logger;
     admin->eventBase = eventBase;
-    admin->addrLen = ai->addr->addrLen;
     admin->map.allocator = alloc;
     admin->iface.send = receiveMessage;
     Iface_plumb(&admin->iface, &ai->iface);
@@ -537,6 +586,11 @@ struct Admin* Admin_new(struct AddrIface* ai,
         ((struct Admin_FunctionArg[]) {
             { .name = "page", .required = 0, .type = "Int" }
         }), &admin->pub);
+    Admin_registerFunction("Admin_importFd", importFd, admin, true, NULL, &admin->pub);
+    Admin_registerFunction("Admin_exportFd", exportFd, admin, true,
+        ((struct Admin_FunctionArg[]) {
+        { .name = "fd", .required = 1, .type = "Int" }
+    }), &admin->pub);
 
     return &admin->pub;
 }

+ 82 - 45
admin/angel/Core.c

@@ -42,6 +42,8 @@
 #endif
 #include "net/InterfaceController_admin.h"
 #include "interface/addressable/PacketHeaderToUDPAddrIface.h"
+#include "interface/addressable/AddrIfaceMuxer.h"
+#include "interface/tuntap/TUNMessageType.h"
 #include "interface/ASynchronizer.h"
 #include "memory/Allocator.h"
 #include "memory/MallocAllocator.h"
@@ -55,8 +57,8 @@
 #include "tunnel/IpTunnel_admin.h"
 #include "tunnel/RouteGen_admin.h"
 #include "util/events/EventBase.h"
-#include "util/events/libuv/FileNo_admin.h"
 #include "util/events/Pipe.h"
+#include "util/events/PipeServer.h"
 #include "util/events/Timeout.h"
 #include "util/Hex.h"
 #include "util/log/FileWriterLog.h"
@@ -121,6 +123,10 @@ struct Context
     struct IpTunnel* ipTunnel;
     struct EncodingScheme* encodingScheme;
     struct GlobalConfig* globalConf;
+
+    struct Iface* tunDevice;
+    struct Allocator* tunAlloc;
+
     Identity
 };
 
@@ -150,19 +156,25 @@ static void sendResponse(String* error,
 }
 
 static void initSocket2(String* socketFullPath,
-                          bool attemptToCreate,
                           struct Context* ctx,
                           uint8_t addressPrefix,
                           struct Except* eh)
 {
     Log_debug(ctx->logger, "Initializing socket: %s;", socketFullPath->bytes);
 
-    struct Iface* rawSocketIf = SocketInterface_new(
-        socketFullPath->bytes, attemptToCreate, ctx->base, ctx->logger, NULL, ctx->alloc);
+    if (ctx->tunDevice) {
+        Iface_unplumb(ctx->tunDevice, &ctx->nc->tunAdapt->tunIf);
+        Allocator_free(ctx->tunAlloc);
+    }
+    ctx->tunAlloc = Allocator_child(ctx->alloc);
 
-    struct SocketWrapper* sw = SocketWrapper_new(ctx->alloc, ctx->logger);
+    struct Iface* rawSocketIf = SocketInterface_new(
+        socketFullPath->bytes, ctx->base, ctx->logger, NULL, ctx->tunAlloc);
+    struct SocketWrapper* sw = SocketWrapper_new(ctx->tunAlloc, ctx->logger);
     Iface_plumb(&sw->externalIf, rawSocketIf);
-    Iface_plumb(&sw->internalIf, &ctx->nc->tunAdapt->tunIf);
+
+    ctx->tunDevice = &sw->internalIf;
+    Iface_plumb(ctx->tunDevice, &ctx->nc->tunAdapt->tunIf);
 
     SocketWrapper_addAddress(&sw->externalIf, ctx->nc->myAddress->ip6.bytes, ctx->logger,
                                 eh, ctx->alloc);
@@ -180,49 +192,71 @@ static void initTunnel2(String* desiredDeviceName,
     char assignedTunName[TUNInterface_IFNAMSIZ];
     char* desiredName = (desiredDeviceName) ? desiredDeviceName->bytes : NULL;
 
-    struct Iface* tun = TUNInterface_new(
-        desiredName, assignedTunName, 0, ctx->base, ctx->logger, eh, ctx->alloc);
+    if (ctx->tunDevice) {
+        Iface_unplumb(&ctx->nc->tunAdapt->tunIf, ctx->tunDevice);
+        Allocator_free(ctx->tunAlloc);
+    }
+    ctx->tunAlloc = Allocator_child(ctx->alloc);
+    ctx->tunDevice = TUNInterface_new(
+        desiredName, assignedTunName, 0, ctx->base, ctx->logger, eh, ctx->tunAlloc);
 
-    Iface_plumb(tun, &ctx->nc->tunAdapt->tunIf);
+    Iface_plumb(ctx->tunDevice, &ctx->nc->tunAdapt->tunIf);
 
     GlobalConfig_setTunName(ctx->globalConf, String_CONST(assignedTunName));
 
     struct Sockaddr* myAddr =
-        Sockaddr_fromBytes(ctx->nc->myAddress->ip6.bytes, Sockaddr_AF_INET6, ctx->alloc);
+        Sockaddr_fromBytes(ctx->nc->myAddress->ip6.bytes, Sockaddr_AF_INET6, ctx->tunAlloc);
     myAddr->prefix = addressPrefix;
     myAddr->flags |= Sockaddr_flags_PREFIX;
     NetDev_addAddress(assignedTunName, myAddr, ctx->logger, eh);
     NetDev_setMTU(assignedTunName, DEFAULT_MTU, ctx->logger, eh);
 }
 
+static void initTunFd0(
+    struct Context* ctx,
+    Dict* args,
+    String* txid,
+    struct Except* eh,
+    struct Allocator* requestAlloc)
+{
+    int64_t* tunfd = Dict_getIntC(args, "tunfd");
+    int64_t* tuntype = Dict_getIntC(args, "type");
+    if (!tunfd || *tunfd < 0) {
+        String* error = String_printf(requestAlloc, "Invalid tunfd");
+        sendResponse(error, ctx->admin, txid, requestAlloc);
+        return;
+    }
+    int fileno = *tunfd;
+    int type = (*tuntype) ? *tuntype : TUNMessageType_guess();
+
+    struct Allocator* tunAlloc = Allocator_child(ctx->alloc);
+    struct Pipe* p = Pipe_forFd(fileno, false, ctx->base, eh, ctx->logger, tunAlloc);
+    struct Iface* iface = NULL;
+    if (type == TUNMessageType_NONE) {
+        struct AndroidWrapper* aw = AndroidWrapper_new(tunAlloc, ctx->logger);
+        Iface_plumb(&aw->externalIf, &p->iface);
+        iface = &aw->internalIf;
+    } else {
+        iface = &p->iface;
+    }
+
+    if (ctx->tunDevice) {
+        Iface_unplumb(&ctx->nc->tunAdapt->tunIf, ctx->tunDevice);
+        Allocator_free(ctx->tunAlloc);
+    }
+    ctx->tunAlloc = tunAlloc;
+    ctx->tunDevice = iface;
+    Iface_plumb(&p->iface, &ctx->nc->tunAdapt->tunIf);
+
+    sendResponse(String_CONST("none"), ctx->admin, txid, requestAlloc);
+}
+
 static void initTunfd(Dict* args, void* vcontext, String* txid, struct Allocator* requestAlloc)
 {
     struct Context* ctx = Identity_check((struct Context*) vcontext);
     struct Jmp jmp;
     Jmp_try(jmp) {
-        int64_t* tunfd = Dict_getIntC(args, "tunfd");
-        int64_t* tuntype = Dict_getIntC(args, "type");
-        if (!tunfd || *tunfd < 0) {
-            String* error = String_printf(requestAlloc, "Invalid tunfd");
-            sendResponse(error, ctx->admin, txid, requestAlloc);
-            return;
-        }
-        int fileno = *tunfd;
-        int type = (*tuntype) ? *tuntype : FileNo_Type_NORMAL;
-        Log_debug(ctx->logger, "Initializing TUN device from file [%d] type [%s]",
-            fileno, (type == FileNo_Type_ANDROID) ? "android" : "normal");
-        struct Pipe* p = Pipe_forFiles(
-            fileno, fileno, ctx->base, &jmp.handler, ctx->logger, ctx->alloc);
-        Log_debug(ctx->logger, "Pipe created");
-        p->logger = ctx->logger;
-        if (type == FileNo_Type_ANDROID) {
-            struct AndroidWrapper* aw = AndroidWrapper_new(ctx->alloc, ctx->logger);
-            Iface_plumb(&aw->externalIf, &p->iface);
-            Iface_plumb(&aw->internalIf, &ctx->nc->tunAdapt->tunIf);
-        } else {
-            Iface_plumb(&p->iface, &ctx->nc->tunAdapt->tunIf);
-        }
-        sendResponse(String_CONST("none"), ctx->admin, txid, requestAlloc);
+        initTunFd0(ctx, args, txid, &jmp.handler, requestAlloc);
     } Jmp_catch {
         Log_debug(ctx->logger, "Failed to create pipe [%s]", jmp.message);
         String* error = String_printf(requestAlloc, "Failed to configure tunnel [%s]", jmp.message);
@@ -255,9 +289,7 @@ static void initSocket(Dict* args, void* vcontext, String* txid, struct Allocato
     struct Jmp jmp;
     Jmp_try(jmp) {
         String* socketFullPath = Dict_getStringC(args, "socketFullPath");
-        bool socketAttemptToCreate = *Dict_getIntC(args, "socketAttemptToCreate");
-        initSocket2(socketFullPath, socketAttemptToCreate, ctx, AddressCalc_ADDRESS_PREFIX_BITS,
-            &jmp.handler);
+        initSocket2(socketFullPath, ctx, AddressCalc_ADDRESS_PREFIX_BITS, &jmp.handler);
     } Jmp_catch {
         String* error = String_printf(requestAlloc, "Failed to configure socket [%s]",
             jmp.message);
@@ -336,7 +368,6 @@ void Core_init(struct Allocator* alloc,
 #ifdef HAS_ETH_INTERFACE
     ETHInterface_admin_register(eventBase, alloc, logger, admin, nc->ifController);
 #endif
-    FileNo_admin_register(admin, alloc, eventBase, logger, eh);
 
     SupernodeHunter_admin_register(spf->snh, admin, alloc);
     ReachabilityCollector_admin_register(spf->rc, admin, alloc);
@@ -381,7 +412,7 @@ void Core_init(struct Allocator* alloc,
     Admin_registerFunction("Core_initSocket", initSocket, ctx, true,
         ((struct Admin_FunctionArg[]) {
             { .name = "socketFullPath", .required = 1, .type = "String" },
-            { .name = "socketAttemptToCreate", .required = 1, .type = "Int" }
+            { .name = "socketAttemptToCreate", .required = 0, .type = "Int" }
         }), admin);
 }
 
@@ -389,7 +420,7 @@ int Core_main(int argc, char** argv)
 {
     struct Except* eh = NULL;
 
-    if (argc != 4) {
+    if (argc != 3) {
         Except_throw(eh, "This is internal to cjdns and shouldn't started manually.");
     }
 
@@ -408,12 +439,13 @@ int Core_main(int argc, char** argv)
     Allocator_setCanary(alloc, (unsigned long)Random_uint64(rand));
 
     struct Allocator* tempAlloc = Allocator_child(alloc);
-    struct Pipe* clientPipe = Pipe_named(argv[2], argv[3], eventBase, eh, tempAlloc);
-    clientPipe->logger = logger;
+    // Not using tempalloc because we're going to keep this pipe around for admin
+    struct PipeServer* clientPipe = PipeServer_named(argv[2], eventBase, eh, logger, alloc);
     Log_debug(logger, "Getting pre-configuration from client");
     struct Message* preConf =
-        InterfaceWaiter_waitForData(&clientPipe->iface, eventBase, tempAlloc, eh);
+        InterfaceWaiter_waitForData(&clientPipe->iface.iface, eventBase, tempAlloc, eh);
     Log_debug(logger, "Finished getting pre-configuration from client");
+    struct Sockaddr* addr = Sockaddr_clone(AddrIface_popAddr(preConf, eh), tempAlloc);
     Dict* config = BencMessageReader_read(preConf, tempAlloc, eh);
 
     String* privateKeyHex = Dict_getStringC(config, "privateKey");
@@ -448,8 +480,13 @@ int Core_main(int argc, char** argv)
     // --------------------- Bind Admin UDP --------------------- //
     struct UDPAddrIface* udpAdmin = UDPAddrIface_new(eventBase, &bindAddr.addr, alloc, eh, logger);
 
+    // ---- Setup a muxer so we can get admin from socket or UDP ---- //
+    struct AddrIfaceMuxer* muxer = AddrIfaceMuxer_new(logger, alloc);
+    Iface_plumb(&udpAdmin->generic.iface, AddrIfaceMuxer_registerIface(muxer, alloc));
+    Iface_plumb(&clientPipe->iface.iface, AddrIfaceMuxer_registerIface(muxer, alloc));
+
     // --------------------- Setup Admin --------------------- //
-    struct Admin* admin = Admin_new(&udpAdmin->generic, logger, eventBase, pass);
+    struct Admin* admin = Admin_new(&muxer->iface, logger, eventBase, pass);
 
     // --------------------- Setup the Logger --------------------- //
     Dict* logging = Dict_getDictC(config, "logging");
@@ -474,11 +511,11 @@ int Core_main(int argc, char** argv)
     // This always times out because the angel doesn't respond.
     struct Message* clientResponse = Message_new(0, 512, tempAlloc);
     BencMessageWriter_write(&response, clientResponse, eh);
-    Iface_CALL(clientPipe->iface.send, clientResponse, &clientPipe->iface);
+    AddrIface_pushAddr(clientResponse, addr, eh);
+    Iface_CALL(clientPipe->iface.iface.send, clientResponse, &clientPipe->iface.iface);
 
     Allocator_free(tempAlloc);
 
-
     Core_init(alloc, logger, eventBase, privateKey, admin, rand, eh, NULL, false);
     EventBase_beginLoop(eventBase);
     return 0;

+ 4 - 20
client/Configurator.c

@@ -306,19 +306,7 @@ static void tunInterface(Dict* ifaceConf, struct Allocator* tempAlloc, struct Co
 
     Dict* args = Dict_new(tempAlloc);
     if (tunfd && device) {
-        Dict_putStringC(args, "path", device, tempAlloc);
-        Dict_putStringC(args, "type",
-                       String_new(tunfd->bytes, tempAlloc), tempAlloc);
-        Dict* res = NULL;
-        rpcCall0(String_CONST("FileNo_import"), args, ctx, tempAlloc, &res, false);
-        if (res) {
-            Dict* args = Dict_new(tempAlloc);
-            int64_t* tunfd = Dict_getIntC(res, "tunfd");
-            int64_t* type = Dict_getIntC(res, "type");
-            Dict_putIntC(args, "tunfd", *tunfd, tempAlloc);
-            Dict_putIntC(args, "type", *type, tempAlloc);
-            rpcCall0(String_CONST("Core_initTunfd"), args, ctx, tempAlloc, NULL, false);
-        }
+        Log_warn(ctx->logger, "tunfd is nolonger used, see Admin_importFd() and Core_initTunFd()");
     } else {
         if (device) {
             Dict_putStringC(args, "desiredTunName", device, tempAlloc);
@@ -344,16 +332,12 @@ static void socketInterface(Dict* ifaceConf, struct Allocator* tempAlloc, struct
         exit(1);
     }
 
-    // false by default
-    int64_t attemptToCreate = 0;
-
-    int64_t* socketAttemptToCreate = Dict_getIntC(ifaceConf, "socketAttemptToCreate");
-    if (socketAttemptToCreate && *socketAttemptToCreate) {
-        attemptToCreate = 1;
+    if (Dict_getIntC(ifaceConf, "socketAttemptToCreate")) {
+        Log_warn(ctx->logger, "SocketInterface \"socketAttemptToCreate\" nolonger has any "
+            "effect, you must create the socket from the outside");
     }
 
     Dict_putStringC(args, "socketFullPath", socketFullPath, tempAlloc);
-    Dict_putIntC(args, "socketAttemptToCreate", attemptToCreate, tempAlloc);
     rpcCall0(String_CONST("Core_initSocket"), args, ctx, tempAlloc, NULL, true);
 }
 

+ 69 - 27
client/cjdroute2.c

@@ -12,6 +12,7 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
+#define _POSIX_C_SOURCE 200112L
 #include "client/AdminClient.h"
 #include "admin/angel/Core.h"
 #include "admin/angel/InterfaceWaiter.h"
@@ -58,9 +59,17 @@
 #include <stdint.h>
 #include <stdio.h>
 #include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <stdlib.h>
 
 #define DEFAULT_TUN_DEV "tun0"
 
+#ifndef CJD_PACKAGE_VERSION
+    #define CJD_PACKAGE_VERSION "unknown"
+#endif
+
 static int genconf(struct Random* rand, bool eth)
 {
     uint8_t password[32];
@@ -265,11 +274,7 @@ static int genconf(struct Random* rand, bool eth)
            "        // 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"
-           "            // The type of tunfd (only \"android\" for now)\n"
-           "            // If \"android\" here, the tunDevice should be used as the pipe path\n"
-           "            // to transfer the tun file description.\n"
-           "            // \"tunfd\" : \"android\"\n");
+           "            \"type\": \"TUNInterface\"\n");
 #ifndef __APPLE__
     printf("\n"
            "            // The name of a persistent TUN device to use.\n"
@@ -290,10 +295,6 @@ static int genconf(struct Random* rand, bool eth)
            "\n"
            "            // The filesystem path to the socket to create or connect to.\n"
            "            \"socketFullPath\": \"/var/run/cjdns.sock\",\n"
-           "\n"
-           "            // If non-zero, we will attempt to create the socket file if it doesn't\n"
-           "            // exist, otherwise we will simply try to connect to an existing socket\n"
-           "            \"socketAttemptToCreate\": 1\n"
            "        },\n"
            "\n");
     printf("        // System for tunneling IPv4 and ICANN IPv6 through cjdns.\n"
@@ -566,6 +567,27 @@ static struct Message* readToMsg(FILE* f, struct Allocator* alloc)
     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("TEMP");
+        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;
+}
+
 int main(int argc, char** argv)
 {
     #ifdef Log_KEYS
@@ -713,26 +735,26 @@ int main(int argc, char** argv)
 
     // --------------------- Setup Pipes to Angel --------------------- //
     struct Allocator* corePipeAlloc = Allocator_child(allocator);
-    char corePipeName[64] = "client-core-";
-    Random_base32(rand, (uint8_t*)corePipeName+CString_strlen(corePipeName), 31);
-    String* pipePath = Dict_getStringC(config, "pipe");
-    String* path = String_CONST(Pipe_PATH);
-    if (!pipePath) {
-        pipePath = path;
-    }
-    if (!Defined(win32) && access(pipePath->bytes, W_OK)) {
-        Except_throw(eh, "Pipe directory not writable: [%s]",pipePath->bytes);
+    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));
+        }
     }
 
-    Assert_ifParanoid(EventBase_eventCount(eventBase) == 0);
-    struct Pipe* corePipe = Pipe_named(pipePath->bytes, corePipeName, eventBase,
-                                       eh, corePipeAlloc);
-    Assert_ifParanoid(EventBase_eventCount(eventBase) == 2);
-    corePipe->logger = logger;
-
-    char* args[] = { "core", pipePath->bytes, corePipeName, NULL };
+    char* args[] = { "core", pipePath->bytes, NULL };
 
-    // --------------------- Spawn Angel --------------------- //
+    // --------------------- Spawn Core --------------------- //
     String* privateKey = Dict_getStringC(config, "privateKey");
 
     char* corePath = Process_getPath(allocator);
@@ -747,6 +769,26 @@ int main(int argc, char** argv)
     }
     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 (Pipe_exists(pipePath->bytes, eh)) {
+            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 = Pipe_named(pipePath->bytes, eventBase, eh, logger, allocator);
+
     // --------------------- Pre-Configure Core ------------------------- //
     Dict* preConf = Dict_new(allocator);
     Dict* adminPreConf = Dict_new(allocator);
@@ -788,7 +830,7 @@ int main(int argc, char** argv)
                      adminBind->bytes);
     }
 
-    Assert_ifParanoid(EventBase_eventCount(eventBase) == 1);
+    //Assert_ifParanoid(EventBase_eventCount(eventBase) == 1);
 
     // --------------------- Configuration ------------------------- //
     Configurator_config(config,

+ 6 - 6
dht/dhtcore/Janitor.c

@@ -634,7 +634,7 @@ static void maintanenceCycle(void* vcontext)
         // Ping a random link from a random node.
 
     } else {
-        Log_debug(janitor->logger, "Could not find anything to do");
+        //Log_debug(janitor->logger, "Could not find anything to do");
     }
 
     // Try to ping the existing node we have heard from least recently.
@@ -658,11 +658,11 @@ static void maintanenceCycle(void* vcontext)
         //return;
     }
 
-    Log_debug(janitor->logger,
-              "Global Mean Response Time: %u nodes [%d] links [%d]",
-              RouterModule_globalMeanResponseTime(janitor->routerModule),
-              janitor->nodeStore->nodeCount,
-              janitor->nodeStore->linkCount);
+    // Log_debug(janitor->logger,
+    //           "Global Mean Response Time: %u nodes [%d] links [%d]",
+    //           RouterModule_globalMeanResponseTime(janitor->routerModule),
+    //           janitor->nodeStore->nodeCount,
+    //           janitor->nodeStore->linkCount);
 
     if (now > janitor->timeOfNextGlobalMaintainence) {
         //search(addr.ip6.bytes, janitor);

+ 11 - 5
interface/FramingIface.c

@@ -60,8 +60,12 @@ static struct Message* mergeMessage(struct FramingIface_pvt* fi, struct Message*
 
     struct Message* out = Message_new(0, length + REQUIRED_PADDING, fi->frameAlloc);
     Message_push(out, last->bytes, last->length, NULL);
+    out->associatedFd = last->associatedFd;
     for (part = fi->frameParts; part; part = part->next) {
         Message_push(out, part->msg->bytes, part->msg->length, NULL);
+        if (!out->associatedFd) {
+            out->associatedFd = part->msg->associatedFd;
+        }
     }
 
     Assert_true(length <= out->length);
@@ -74,7 +78,7 @@ static Iface_DEFUN receiveMessage(struct Message* msg, struct Iface* streamIf)
 
     if (fi->bytesRemaining > fi->maxMessageSize) {
         // Oversize message
-Assert_true(0);
+        Assert_ifTesting(0);
         return NULL;
     }
 
@@ -93,7 +97,7 @@ Assert_true(0);
         }
         fi->bytesRemaining -= msg->length;
         Allocator_adopt(fi->frameAlloc, msg->alloc);
-        struct MessageList* parts = Allocator_malloc(fi->frameAlloc, sizeof(struct MessageList));
+        struct MessageList* parts = Allocator_calloc(fi->frameAlloc, sizeof(struct MessageList), 1);
         parts->msg = msg;
         parts->next = fi->frameParts;
         fi->frameParts = parts;
@@ -114,7 +118,7 @@ Assert_true(0);
         fi->bytesRemaining = Endian_bigEndianToHost32(fi->header.length_be);
         if (fi->bytesRemaining > fi->maxMessageSize) {
             // oversize
-Assert_true(0);
+            Assert_ifTesting(0);
             return NULL;
         }
 
@@ -125,6 +129,7 @@ Assert_true(0);
         } else if (fi->bytesRemaining < (uint32_t)msg->length) {
             struct Allocator* alloc = Allocator_child(msg->alloc);
             struct Message* m = Message_new(fi->bytesRemaining, REQUIRED_PADDING, alloc);
+            m->associatedFd = msg->associatedFd;
             Bits_memcpy(m->bytes, msg->bytes, fi->bytesRemaining);
             Message_shift(msg, -fi->bytesRemaining, NULL);
             fi->bytesRemaining = 0;
@@ -135,15 +140,16 @@ Assert_true(0);
         } else {
             fi->frameAlloc = Allocator_child(fi->alloc);
             struct Message* m = Allocator_calloc(fi->frameAlloc, sizeof(struct Message), 1);
+            m->associatedFd = msg->associatedFd;
             m->capacity = m->length = msg->length + 4;
-            m->bytes = Allocator_malloc(fi->frameAlloc, m->length);
+            m->bytes = Allocator_calloc(fi->frameAlloc, m->length, 1);
             m->alloc = fi->frameAlloc;
             Message_shift(m, -m->length, NULL);
             Message_push(m, msg->bytes, msg->length, NULL);
             Message_push(m, fi->header.bytes, 4, NULL);
 
             fi->bytesRemaining -= msg->length;
-            fi->frameParts = Allocator_malloc(fi->frameAlloc, sizeof(struct MessageList));
+            fi->frameParts = Allocator_calloc(fi->frameAlloc, sizeof(struct MessageList), 1);
             fi->frameParts->msg = m;
             fi->frameParts->next = NULL;
         }

+ 5 - 0
interface/Iface.h

@@ -138,6 +138,11 @@ static inline Iface_DEFUN Iface_next(struct Iface* iface, struct Message* msg)
  * it directly.
  * If you are calling from a Iface_Callback function, you must not use this function to call
  * the next function with the message passed to you. In that case just call the function directly.
+ *
+ * Why is there code using this weird function rather than simply calling Iface_send() ?
+ * Iface_send() only works when you send to *your* iface which is plumbed to the iface that you want
+ * the data to go to, this macro will be used when you want to bypass the need to create a local
+ * iface and plumb it to the remote one just in order to send one message.
  */
 #ifdef PARANOIA
     #define Iface_CALL(func, msg, ...) \

+ 6 - 0
interface/UDPInterface.c

@@ -313,4 +313,10 @@ int UDPInterface_setDSCP(struct UDPInterface* udpif, uint8_t dscp)
     if (res) { return res; }
     if (ctx->bcastIf) { return UDPAddrIface_setDSCP(ctx->bcastIf, dscp); }
     return 0;
+}
+
+int UDPInterface_getFd(struct UDPInterface* udpif)
+{
+    struct UDPInterface_pvt* ctx = Identity_check((struct UDPInterface_pvt*) udpif);
+    return UDPAddrIface_getFd(ctx->commIf);
 }

+ 2 - 0
interface/UDPInterface.h

@@ -114,4 +114,6 @@ List* UDPInterface_getBroadcastAddrs(struct UDPInterface* udpif, struct Allocato
  */
 int UDPInterface_setDSCP(struct UDPInterface* udpif, uint8_t dscp);
 
+int UDPInterface_getFd(struct UDPInterface* udpif);
+
 #endif

+ 16 - 0
interface/UDPInterface_admin.c

@@ -315,6 +315,17 @@ static void beacon(Dict* args, void* vcontext, String* txid, struct Allocator* r
     Admin_sendMessage(&out, txid, ctx->admin);
 }
 
+static void getFd(Dict* args, void* vcontext, String* txid, struct Allocator* requestAlloc)
+{
+    struct Context* ctx = Identity_check((struct Context*) vcontext);
+    struct UDPInterface* udpif = getIface(ctx, args, txid, requestAlloc, NULL);
+    int fd = UDPInterface_getFd(udpif);
+    Dict* out = Dict_new(requestAlloc);
+    Dict_putIntC(out, "fd", fd, requestAlloc);
+    Dict_putStringCC(out, "error", "none", requestAlloc);
+    Admin_sendMessage(out, txid, ctx->admin);
+}
+
 void UDPInterface_admin_register(struct EventBase* base,
                                  struct Allocator* alloc,
                                  struct Log* logger,
@@ -375,4 +386,9 @@ void UDPInterface_admin_register(struct EventBase* base,
             { .name = "interfaceNumber", .required = 0, .type = "Int" },
             { .name = "state", .required = 0, .type = "Int" }
         }), admin);
+
+    Admin_registerFunction("UDPInterface_getFd", getFd, ctx, true,
+        ((struct Admin_FunctionArg[]) {
+            { .name = "interfaceNumber", .required = 0, .type = "Int" },
+        }), admin);
 }

+ 18 - 0
interface/addressable/AddrIface.h

@@ -17,6 +17,8 @@
 
 #include "interface/Iface.h"
 #include "util/platform/Sockaddr.h"
+#include "exception/Except.h"
+#include "wire/Message.h"
 
 /**
  * An AddrInterface, short for "Adderssable Interface" is an interface which
@@ -38,4 +40,20 @@ struct AddrIface
     struct Allocator* alloc;
 };
 
+static inline void AddrIface_pushAddr(
+    struct Message* msg,
+    struct Sockaddr* addr,
+    struct Except* eh)
+{
+    Message_push(msg, addr, addr->addrLen, eh);
+}
+
+static inline struct Sockaddr* AddrIface_popAddr(struct Message* msg, struct Except* eh)
+{
+    struct Sockaddr* out = (struct Sockaddr*) msg->bytes;
+    uint16_t len = Message_pop16h(msg, eh);
+    Message_pop(msg, NULL, len - 2, eh);
+    return out;
+}
+
 #endif

+ 128 - 0
interface/addressable/AddrIfaceMuxer.c

@@ -0,0 +1,128 @@
+/* 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 <https://www.gnu.org/licenses/>.
+ */
+#include "interface/addressable/AddrIfaceMuxer.h"
+#include "memory/Allocator.h"
+#include "util/platform/Sockaddr.h"
+#include "util/Assert.h"
+#include "util/Identity.h"
+#include "wire/Message.h"
+
+#include "util/Hex.h"
+
+struct AddrIfaceMuxer_pvt;
+struct AddrIfaceMuxer_Iface {
+    struct Iface iface;
+    struct AddrIfaceMuxer_pvt* muxer;
+    struct Allocator* alloc;
+    struct Sockaddr addr;
+    Identity
+};
+
+#define Map_NAME Ifaces
+#define Map_ENABLE_HANDLES
+#define Map_VALUE_TYPE struct AddrIfaceMuxer_Iface*
+#include "util/Map.h"
+
+struct AddrIfaceMuxer_pvt
+{
+    struct AddrIfaceMuxer pub;
+    struct Map_Ifaces ifaces;
+    struct Log* log;
+    Identity
+};
+
+static Iface_DEFUN incomingFromAddrIf(struct Message* msg, struct Iface* addrIf)
+{
+    struct AddrIfaceMuxer_pvt* ctx =
+        Identity_containerOf(addrIf, struct AddrIfaceMuxer_pvt, pub.iface.iface);
+
+    struct Sockaddr* addr = AddrIface_popAddr(msg, NULL);
+    if (addr->addrLen > sizeof(struct Sockaddr)) {
+        // There's another address packed under this one
+        struct Sockaddr* subaddr = &addr[1];
+        Assert_true(subaddr->addrLen == addr->addrLen - sizeof(struct Sockaddr));
+        // Move the needle back to include it
+        Message_shift(msg, subaddr->addrLen, NULL);
+        addr->addrLen -= subaddr->addrLen;
+    }
+    uint32_t handle = Sockaddr_addrHandle(addr);
+    int idx = Map_Ifaces_indexForHandle(handle, &ctx->ifaces);
+    if (idx < 0) {
+        Log_info(ctx->log, "DROP message to nonexistant iface [0x%x]", handle);
+        return NULL;
+    }
+    return Iface_next(&ctx->ifaces.values[idx]->iface, msg);
+}
+
+static Iface_DEFUN incomingFromInputIf(struct Message* msg, struct Iface* inputIf)
+{
+    struct AddrIfaceMuxer_Iface* cli =
+        Identity_containerOf(inputIf, struct AddrIfaceMuxer_Iface, iface);
+    struct AddrIfaceMuxer_pvt* ctx = Identity_check(cli->muxer);
+    if (msg->length < (int)sizeof(struct Sockaddr)) {
+        Log_info(ctx->log, "DROP runt");
+        return NULL;
+    }
+
+    uint16_t addrLen = Bits_get16(msg->bytes);
+    AddrIface_pushAddr(msg, &cli->addr, NULL);
+
+    // After pushing the address, tweak the length
+    Bits_put16(msg->bytes, cli->addr.addrLen + addrLen);
+
+    return Iface_next(&ctx->pub.iface.iface, msg);
+}
+
+static int removeIfaceOnFree(struct Allocator_OnFreeJob* job)
+{
+    struct AddrIfaceMuxer_Iface* cli = Identity_check((struct AddrIfaceMuxer_Iface*)job->userData);
+    struct AddrIfaceMuxer_pvt* m = Identity_check(cli->muxer);
+    uint32_t handle = Sockaddr_addrHandle(&cli->addr);
+    int idx = Map_Ifaces_indexForHandle(handle, &m->ifaces);
+    if (idx > -1) {
+        Map_Ifaces_remove(idx, &m->ifaces);
+    }
+    return 0;
+}
+
+struct Iface* AddrIfaceMuxer_registerIface(struct AddrIfaceMuxer* muxer, struct Allocator* alloc)
+{
+    struct AddrIfaceMuxer_pvt* m = Identity_check((struct AddrIfaceMuxer_pvt*) muxer);
+    struct AddrIfaceMuxer_Iface* cli =
+        Allocator_calloc(alloc, sizeof(struct AddrIfaceMuxer_Iface), 1);
+    cli->iface.send = incomingFromInputIf;
+    cli->muxer = m;
+    cli->alloc = alloc;
+    Identity_set(cli);
+    int idx = Map_Ifaces_put(&cli, &m->ifaces);
+    uint32_t handle = m->ifaces.handles[idx];
+    Sockaddr_addrFromHandle(&cli->addr, handle);
+    Allocator_onFree(alloc, removeIfaceOnFree, cli);
+    return &cli->iface;
+}
+
+struct AddrIfaceMuxer* AddrIfaceMuxer_new(struct Log* log, struct Allocator* alloc)
+{
+    struct AddrIfaceMuxer_pvt* ctx = Allocator_clone(alloc, (&(struct AddrIfaceMuxer_pvt) {
+        .pub = {
+            .iface.iface.send = incomingFromAddrIf,
+            .iface.alloc = alloc,
+        },
+        .log = log,
+        .ifaces = { .allocator = alloc },
+    }));
+    Identity_set(ctx);
+    return &ctx->pub;
+}

+ 14 - 13
util/events/libuv/FileNo_admin.h → interface/addressable/AddrIfaceMuxer.h

@@ -12,26 +12,27 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
-#ifndef FileNo_admin_H
-#define FileNo_admin_H
+#ifndef AddrIfaceMuxer_H
+#define AddrIfaceMuxer_H
 
-#include "admin/Admin.h"
+#include "interface/addressable/AddrIface.h"
 #include "memory/Allocator.h"
 #include "util/log/Log.h"
-#include "util/events/EventBase.h"
-#include "util/events/FileNo.h"
 #include "util/Linker.h"
-Linker_require("util/events/libuv/FileNo_admin.c");
+Linker_require("interface/addressable/AddrIfaceMuxer.c");
 
-struct FileNo_admin
+struct AddrIfaceMuxer
 {
-    void* userData;
+    struct AddrIface iface;
 };
 
-void FileNo_admin_register(struct Admin* admin,
-                           struct Allocator* alloc,
-                           struct EventBase* base,
-                           struct Log* logger,
-                           struct Except* eh);
+/**
+ * Register an interface with the muxer.
+ * After registering, you will have the interface which you can then interact with.
+ * To unregister, free the allocator which is provided in this function.
+ */
+struct Iface* AddrIfaceMuxer_registerIface(struct AddrIfaceMuxer* muxer, struct Allocator* alloc);
+
+struct AddrIfaceMuxer* AddrIfaceMuxer_new(struct Log* log, struct Allocator* alloc);
 
 #endif

+ 0 - 47
interface/addressable/UDPAddrInterface.h_

@@ -1,47 +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 <https://www.gnu.org/licenses/>.
- */
-#ifndef UDPAddrInterface_H
-#define UDPAddrInterface_H
-
-#include "exception/Except.h"
-#include "interface/Interface.h"
-#include "interface/addressable/AddrInterface.h"
-#include "memory/Allocator.h"
-#include "util/events/EventBase.h"
-#include "util/log/Log.h"
-#include "util/Linker.h"
-// TODO(cjd): Move this into util/events
-Linker_require("util/events/libuv/UDPAddrInterface.c")
-
-#define UDPAddrInterface_PADDING_AMOUNT 512
-#define UDPAddrInterface_BUFFER_CAP 3496
-
-/** Maximum number of bytes to hold in queue before dropping packets. */
-#define UDPAddrInterface_MAX_QUEUE 16384
-
-/**
- * @param base the event loop context.
- * @param bindAddr the address/port to bind to.
- * @param allocator the memory allocator for this message.
- * @param exHandler the handler to deal with whatever exception arises.
- * @param logger
- * @return a new UDPInterfaceBase.
- */
-struct AddrInterface* UDPAddrInterface_new(struct EventBase* base,
-                                           struct Sockaddr* bindAddr,
-                                           struct Allocator* allocator,
-                                           struct Except* exHandler,
-                                           struct Log* logger);
-#endif

+ 2 - 2
interface/tuntap/BSDMessageTypeWrapper.c

@@ -42,7 +42,7 @@ static Iface_DEFUN receiveMessage(struct Message* msg, struct Iface* wireSide)
     struct BSDMessageTypeWrapper_pvt* ctx =
         Identity_containerOf(wireSide, struct BSDMessageTypeWrapper_pvt, pub.wireSide);
 
-    if (msg->length < 4) { return Error_NONE; }
+    if (msg->length < 4) { return NULL; }
 
     uint16_t afType_be = ((uint16_t*) msg->bytes)[1];
     uint16_t ethertype = 0;
@@ -53,7 +53,7 @@ static Iface_DEFUN receiveMessage(struct Message* msg, struct Iface* wireSide)
     } else {
         Log_debug(ctx->logger, "Message of unhandled aftype [0x%04x]",
                   Endian_bigEndianToHost16(afType_be));
-        return Error_NONE;
+        return NULL;
     }
     ((uint16_t*) msg->bytes)[0] = 0;
     ((uint16_t*) msg->bytes)[1] = ethertype;

+ 1 - 2
interface/tuntap/SocketInterface.c

@@ -19,7 +19,6 @@
 #include "util/events/Pipe.h"
 
 struct Iface* SocketInterface_new(const char* socketFullPath,
-                                    bool attemptToCreate,
                                     struct EventBase* base,
                                     struct Log* logger,
                                     struct Except* eh,
@@ -27,7 +26,7 @@ struct Iface* SocketInterface_new(const char* socketFullPath,
 {
     Log_info(logger, "Initializing socket: %s;", socketFullPath);
 
-    struct Pipe* p = Pipe_namedConnect(socketFullPath, attemptToCreate, base, eh, alloc);
+    struct Pipe* p = Pipe_named(socketFullPath, base, eh, logger, alloc);
 
     return &p->iface;
 }

+ 0 - 1
interface/tuntap/SocketInterface.h

@@ -35,7 +35,6 @@ Linker_require("interface/tuntap/SocketInterface.c");
  * @return a Interface.
  */
 struct Iface* SocketInterface_new(const char* socketFullPath,
-                                    bool attemptToCreate,
                                     struct EventBase* base,
                                     struct Log* logger,
                                     struct Except* eh,

+ 1 - 1
interface/tuntap/TUNInterface_darwin.c

@@ -112,7 +112,7 @@ struct Iface* TUNInterface_new(const char* interfaceName,
         Except_throw(eh, "getting utun interface name [%s]", strerror(err));
     }
 
-    struct Pipe* p = Pipe_forFiles(tunFd, tunFd, base, eh, logger, alloc);
+    struct Pipe* p = Pipe_forFd(tunFd, false, base, eh, logger, alloc);
 
     struct BSDMessageTypeWrapper* bmtw = BSDMessageTypeWrapper_new(alloc, logger);
     Iface_plumb(&p->iface, &bmtw->wireSide);

+ 1 - 1
interface/tuntap/TUNInterface_freebsd.c

@@ -113,7 +113,7 @@ struct Iface* TUNInterface_new(const char* interfaceName,
         Except_throw(eh, "%s [%s]", error, strerror(err));
     }
 
-    struct Pipe* p = Pipe_forFiles(tunFd, tunFd, base, eh, logger, alloc);
+    struct Pipe* p = Pipe_forFd(tunFd, false, base, eh, logger, alloc);
 
     struct BSDMessageTypeWrapper* bmtw = BSDMessageTypeWrapper_new(alloc, logger);
     Iface_plumb(&p->iface, &bmtw->wireSide);

+ 1 - 1
interface/tuntap/TUNInterface_linux.c

@@ -74,7 +74,7 @@ struct Iface* TUNInterface_new(const char* interfaceName,
         CString_safeStrncpy(assignedInterfaceName, ifRequest.ifr_name, maxNameSize);
     }
 
-    struct Pipe* p = Pipe_forFiles(fileno, fileno, base, eh, logger, alloc);
+    struct Pipe* p = Pipe_forFd(fileno, false, base, eh, logger, alloc);
 
     return &p->iface;
 }

+ 1 - 1
interface/tuntap/TUNInterface_netbsd.c

@@ -88,7 +88,7 @@ struct Iface* TUNInterface_new(const char* interfaceName,
             snprintf(assignedInterfaceName, TUNInterface_IFNAMSIZ, "tun%d", ppa);
         }
     }
-    struct Pipe* p = Pipe_forFiles(tunFd, tunFd, base, eh, logger, alloc);
+    struct Pipe* p = Pipe_forFd(tunFd, false, base, eh, logger, alloc);
 
     struct BSDMessageTypeWrapper* bmtw = BSDMessageTypeWrapper_new(alloc, logger);
     Iface_plumb(&p->iface, &bmtw->wireSide);

+ 1 - 1
interface/tuntap/TUNInterface_openbsd.c

@@ -76,7 +76,7 @@ struct Iface* TUNInterface_new(const char* interfaceName,
             snprintf(assignedInterfaceName, TUNInterface_IFNAMSIZ, "tun%d", ppa);
         }
     }
-    struct Pipe* p = Pipe_forFiles(tunFd, tunFd, base, eh, logger, alloc);
+    struct Pipe* p = Pipe_forFd(tunFd, false, base, eh, logger, alloc);
 
     struct BSDMessageTypeWrapper* bmtw = BSDMessageTypeWrapper_new(alloc, logger);
     Iface_plumb(&p->iface, &bmtw->wireSide);

+ 1 - 1
interface/tuntap/TUNInterface_sunos.c

@@ -184,7 +184,7 @@ struct Iface* TUNInterface_new(const char* interfaceName,
 
     close(ipFd);
 
-    struct Pipe* p = Pipe_forFiles(tunFd, tunFd, base, eh, logger, alloc);
+    struct Pipe* p = Pipe_forFd(tunFd, false, base, eh, logger, alloc);
 
     struct TUNInterface_Illumos_pvt* ctx =
         Allocator_clone(alloc, (&(struct TUNInterface_Illumos_pvt) {

+ 23 - 0
interface/tuntap/TUNMessageType.h

@@ -15,6 +15,7 @@
 #ifndef TUNMessageType_H
 #define TUNMessageType_H
 
+#include "util/Defined.h"
 #include "wire/Message.h"
 
 static inline void TUNMessageType_push(struct Message* message,
@@ -32,4 +33,26 @@ static inline uint16_t TUNMessageType_pop(struct Message* message, struct Except
     return ((uint16_t*) message->bytes)[-1];
 }
 
+enum TUNMessageType {
+    // Ethertype header, used by linux
+    TUNMessageType_ETHERTYPE = 0,
+
+    // No header, used by android and sunos, ipv4 and ipv6 only
+    TUNMessageType_NONE  = 1,
+
+    // address family header, used by BSD
+    TUNMessageType_AF  = 2,
+};
+
+static inline enum TUNMessageType TUNMessageType_guess()
+{
+    if (Defined(android) || Defined(sunos)) {
+        return TUNMessageType_NONE;
+    } else if (Defined(linux) || Defined(win32)) {
+        return TUNMessageType_ETHERTYPE;
+    } else {
+        return TUNMessageType_AF;
+    }
+}
+
 #endif

+ 4 - 0
node_build/make.js

@@ -144,6 +144,10 @@ Builder.configure({
             '-Wno-error'
         );
         builder.config.cflags.slice(builder.config.cflags.indexOf('-Werror'), 1);
+    } else {
+        builder.config.cflags.push(
+            '-fdiagnostics-color=always'
+        )
     }
 
     // Install any user-defined CFLAGS. Necessary if you are messing about with building cnacl

+ 3 - 1
subnode/ReachabilityCollector.c

@@ -274,7 +274,9 @@ static void mkNextRequest(struct ReachabilityCollector_pvt* rcp)
         if (pi->pub.querying && !pi->waitForResponse) { break; }
     }
     if (!pi || !pi->pub.querying) {
-        Log_debug(rcp->log, "All [%u] peers have been queried", rcp->piList->length);
+        if (rcp->piList->length > 0) {
+            Log_debug(rcp->log, "All [%u] peers have been queried", rcp->piList->length);
+        }
         return;
     }
     if (pi->waitForResponse) {

+ 12 - 0
util/Bits.h

@@ -143,4 +143,16 @@ static inline void* Bits__memcpy(void* out,
 
 void* Bits_memmem(const void* haystack, size_t haystackLen, const void* needle, size_t needleLen);
 
+static inline uint16_t Bits_get16(uint8_t* bytes)
+{
+    uint16_t x = 0;
+    Bits_memcpy(&x, bytes, 2);
+    return x;
+}
+
+static inline void Bits_put16(uint8_t* bytes, uint16_t num)
+{
+    Bits_memcpy(bytes, &num, 2);
+}
+
 #endif

+ 9 - 0
util/CString.c

@@ -13,6 +13,7 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 #include "util/CString.h"
+#include "util/Bits.h"
 
 #include <string.h>
 
@@ -67,3 +68,11 @@ char* CString_safeStrncpy(char* restrict dest, const char *restrict src, size_t
     dest[n - 1] = '\0';
     return ret;
 }
+
+char* CString_strdup(const char* string, struct Allocator* alloc)
+{
+    int len = CString_strlen(string);
+    char* out = Allocator_calloc(alloc, len+1, 1);
+    Bits_memcpy(out, string, len);
+    return out;
+}

+ 3 - 0
util/CString.h

@@ -15,6 +15,7 @@
 #ifndef CString_H
 #define CString_H
 
+#include "memory/Allocator.h"
 #include "util/Gcc.h"
 #include "util/Linker.h"
 Linker_require("util/CString.c");
@@ -55,4 +56,6 @@ char* CString_strcpy(char* restrict dest, const char* restrict src);
 Gcc_NONNULL(1,2)
 char* CString_safeStrncpy(char* restrict dest, const char *restrict src, size_t n);
 
+char* CString_strdup(const char* string, struct Allocator* alloc);
+
 #endif

+ 5 - 0
util/Seccomp.c

@@ -343,6 +343,11 @@ static struct sock_fprog* mkFilter(struct Allocator* alloc, struct Except* eh)
         IFEQ(__NR_madvise, success),
         #endif
 
+        // accept() for PipeServer
+        #ifdef __NR_accept4
+        IFEQ(__NR_accept4, success),
+        #endif
+
         RET(SECCOMP_RET_TRAP),
 
         LABEL(socket),

+ 2 - 1
util/Seccomp.h

@@ -18,8 +18,9 @@
 #include "exception/Except.h"
 #include "memory/Allocator.h"
 #include "util/log/Log.h"
+#include "util/Js.h"
 
-<?js require("../util/Seccomp.js").detect(this.async, file, builder); ?>
+Js({ require("../util/Seccomp.js").detect(this.async, file, builder); })
 
 void Seccomp_dropPermissions(struct Allocator* tempAlloc, struct Log* logger, struct Except* eh);
 

+ 17 - 24
util/events/Pipe.h

@@ -15,10 +15,11 @@
 #ifndef Pipe_H
 #define Pipe_H
 
-#include "memory/Allocator.h"
 #include "exception/Except.h"
 #include "interface/Iface.h"
+#include "memory/Allocator.h"
 #include "util/events/EventBase.h"
+#include "util/log/Log.h"
 #include "util/Linker.h"
 Linker_require("util/events/libuv/Pipe.c");
 
@@ -31,28 +32,24 @@ struct Pipe
 {
     struct Iface iface;
 
-    /** the name as provided by the user eg: "foo" */
-    const char* const name;
-
     /** The name of the file eg: "/tmp/cjdns_pipe_foo" */
     const char* const fullName;
 
-    /** A pointer to the platform dependent file descriptor or handle. */
-    void* fd;
-
     void* userData;
 
-    struct EventBase* const base;
-
     Pipe_callback onConnection;
     Pipe_callback onClose;
-
-    struct Log* logger;
 };
 
 #define Pipe_PADDING_AMOUNT 512
 #define Pipe_BUFFER_CAP 4000
 
+#ifdef win32
+    #define Pipe_PATH_SEP "\\"
+#else
+    #define Pipe_PATH_SEP "/"
+#endif
+
 #ifndef Pipe_PATH
     #ifdef win32
         #define Pipe_PATH "\\\\.\\pipe"
@@ -63,23 +60,19 @@ struct Pipe
     #endif
 #endif
 
-struct Pipe* Pipe_named(const char* path,
-                        const char* name,
+struct Pipe* Pipe_named(const char* fullPath,
                         struct EventBase* eb,
                         struct Except* eh,
+                        struct Log* log,
                         struct Allocator* userAlloc);
 
-struct Pipe* Pipe_namedConnect(const char* fullPath,
-                               bool attemptToCreate,
-                               struct EventBase* eb,
-                               struct Except* eh,
-                               struct Allocator* userAlloc);
+struct Pipe* Pipe_forFd(int fd,
+                        bool ipc,
+                        struct EventBase* eb,
+                        struct Except* eh,
+                        struct Log* log,
+                        struct Allocator* userAlloc);
 
-struct Pipe* Pipe_forFiles(int inFd,
-                           int outFd,
-                           struct EventBase* eb,
-                           struct Except* eh,
-                           struct Log* logger,
-                           struct Allocator* userAlloc);
+bool Pipe_exists(const char* fullPath, struct Except* eh);
 
 #endif

+ 21 - 23
util/events/FileNo.h → util/events/PipeServer.h

@@ -12,40 +12,38 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
-#ifndef FileNo_H
-#define FileNo_H
+#ifndef PipeServer_H
+#define PipeServer_H
 
 #include "memory/Allocator.h"
 #include "exception/Except.h"
+#include "interface/addressable/AddrIface.h"
 #include "util/events/EventBase.h"
 #include "util/Linker.h"
-#include "util/log/Log.h"
-Linker_require("util/events/libuv/FileNo.c");
+Linker_require("util/events/libuv/PipeServer.c");
 
-enum FileNo_Type {
-    /** Normal tunfd type, no need other wrapper */
-    FileNo_Type_NORMAL = 0,
+#include <stdbool.h>
 
-    /** Android tunfd, need AndroidWrapper */
-    FileNo_Type_ANDROID
-};
+struct PipeServer;
+typedef void (* PipeServer_callback)(struct PipeServer* p, struct Sockaddr* client);
 
-struct FileNo_Promise;
-struct FileNo_Promise
+struct PipeServer
 {
-    void (* callback)(struct FileNo_Promise* promise,
-                      int fileno);
+    struct AddrIface iface;
+
+    /** The name of the file eg: "/tmp/cjdns_pipe_foo" */
+    const char* const fullName;
+
     void* userData;
-    struct Allocator* alloc;
-};
 
-#define FileNo_PADDING_AMOUNT   512
-#define FileNo_BUFFER_CAP       4000
+    PipeServer_callback onConnection;
+    PipeServer_callback onDisconnection;
+};
 
-struct FileNo_Promise* FileNo_import(const char* path,
-                                     struct EventBase* eb,
-                                     struct Except* eh,
-                                     struct Log* logger,
-                                     struct Allocator* alloc);
+struct PipeServer* PipeServer_named(const char* fullPath,
+                                    struct EventBase* eb,
+                                    struct Except* eh,
+                                    struct Log* log,
+                                    struct Allocator* userAlloc);
 
 #endif

+ 2 - 0
util/events/UDPAddrIface.h

@@ -55,4 +55,6 @@ int UDPAddrIface_setDSCP(struct UDPAddrIface* iface, uint8_t dscp);
 
 int UDPAddrIface_setBroadcast(struct UDPAddrIface* iface, bool enable);
 
+int UDPAddrIface_getFd(struct UDPAddrIface*);
+
 #endif

+ 0 - 278
util/events/libuv/FileNo.c

@@ -1,278 +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 <https://www.gnu.org/licenses/>.
- */
-#include "util/events/libuv/UvWrapper.h"
-#include "benc/String.h"
-#include "memory/Allocator.h"
-#include "util/events/FileNo.h"
-#include "util/events/libuv/EventBase_pvt.h"
-#include "util/log/Log.h"
-#include "util/Identity.h"
-
-#include <inttypes.h>
-#include <stdio.h>
-
-// This structure used to be public and accessable.
-struct FileNo
-{
-    /** The name of the file eg: "/tmp/cjdns_fileno_foo" */
-    const char* const pipePath;
-
-    void* userData;
-
-    enum FileNo_Type type;
-
-    struct EventBase* const base;
-
-    struct Allocator* alloc;
-
-    struct Log* logger;
-};
-
-struct FileNoContext
-{
-    struct FileNo_Promise pub;
-    struct FileNo* fileno;
-
-    Identity
-};
-
-struct FileNo_pvt
-{
-    struct FileNo pub;
-
-    uv_pipe_t server;
-    uv_pipe_t peer;
-
-    /** Job to close the handles when the allocator is freed */
-    struct Allocator_OnFreeJob* closeHandlesOnFree;
-
-    int isActive;
-
-    Identity
-};
-
-static void onClose(uv_handle_t* handle)
-{
-    struct FileNo_pvt* fileno = Identity_check((struct FileNo_pvt*)handle->data);
-    handle->data = NULL;
-    if (fileno->closeHandlesOnFree && !fileno->server.data && !fileno->peer.data) {
-        Allocator_onFreeComplete((struct Allocator_OnFreeJob*) fileno->closeHandlesOnFree);
-    }
-}
-
-#if FileNo_PADDING_AMOUNT < 8
-    #error
-#endif
-#define ALLOC(buff) (((struct Allocator**) &(buff[-(8 + (((uintptr_t)buff) % 8))]))[0])
-
-static void incoming(uv_pipe_t* stream,
-                     ssize_t nread,
-                     const uv_buf_t* buf,
-                     uv_handle_type pending)
-{
-    // Grab out the allocator which was placed there by allocate()
-    struct Allocator* alloc = buf->base ? ALLOC(buf->base) : NULL;
-
-    // not always true, something with reusing handles
-    //Assert_true(pending == UV_UNKNOWN_HANDLE);
-
-    if (nread < 0) {
-        uv_close((uv_handle_t*) stream, onClose);
-#ifndef win32
-    } else {
-        struct FileNo_pvt* fileno = Identity_check((struct FileNo_pvt*) stream->data);
-        if (fileno->peer.accepted_fd != -1) {
-            struct FileNoContext* fctx = (struct FileNoContext*) fileno->pub.userData;
-            Log_info(fileno->pub.logger, "Got TUN file descriptor [%d] cb exists [%d]",
-                fileno->peer.accepted_fd, fctx->pub.callback != NULL);
-            if (fctx->pub.callback) {
-                fctx->pub.callback(&fctx->pub, fileno->peer.accepted_fd);
-            }
-        }
-#endif
-    }
-
-    if (alloc) {
-        Allocator_free(alloc);
-    }
-}
-
-static void allocate(uv_handle_t* handle, size_t size, uv_buf_t* buf)
-{
-    struct FileNo_pvt* pipe = Identity_check((struct FileNo_pvt*) handle->data);
-    size = FileNo_BUFFER_CAP;
-    size_t fullSize = size + FileNo_PADDING_AMOUNT;
-
-    struct Allocator* child = Allocator_child(pipe->pub.alloc);
-    char* buff = Allocator_malloc(child, fullSize);
-    buff += FileNo_PADDING_AMOUNT;
-
-    ALLOC(buff) = child;
-
-    buf->base = buff;
-    buf->len = size;
-}
-
-static void connected(uv_connect_t* req, int status)
-{
-    uv_stream_t* link = req->handle;
-    struct FileNo_pvt* fileno = Identity_check((struct FileNo_pvt*) link->data);
-    Log_debug(fileno->pub.logger, "FileNo [%s] established connection", fileno->pub.pipePath);
-
-    int ret;
-    if (status) {
-        Log_info(fileno->pub.logger, "uv_pipe_connect() failed for pipe [%s] [%s]",
-                 fileno->pub.pipePath, uv_strerror(status) );
-        uv_close((uv_handle_t*) &fileno->peer, onClose);
-
-    } else if ((ret = uv_read2_start((uv_stream_t*)&fileno->peer, allocate, incoming))) {
-        Log_info(fileno->pub.logger, "uv_read2_start() failed for pipe [%s] [%s]",
-                 fileno->pub.pipePath, uv_strerror(ret));
-        uv_close((uv_handle_t*) &fileno->peer, onClose);
-
-    } else {
-        fileno->isActive = 1;
-    }
-}
-
-static void listenCallback(uv_stream_t* server, int status)
-{
-    struct FileNo_pvt* fileno = Identity_check((struct FileNo_pvt*) server->data);
-    if (fileno->isActive) {
-        // first connection wins.
-        return;
-    }
-    if (status == -1) {
-        Log_info(fileno->pub.logger, "failed to accept pipe connection [%s] [%s]",
-                 fileno->pub.pipePath, uv_strerror(status) );
-        return;
-    }
-
-    Log_info(fileno->pub.logger, "Try accept pipe connection [%s]",
-            fileno->pub.pipePath);
-
-    int ret = uv_accept(server, (uv_stream_t*) &fileno->peer);
-    if (ret) {
-        Log_warn(fileno->pub.logger, "uv_accept() failed: pipe [%s] [%s]",
-                 fileno->pub.pipePath, uv_strerror(ret) );
-        uv_close((uv_handle_t*) &fileno->peer, onClose);
-    } else {
-        uv_connect_t req = { .handle = (uv_stream_t*) &fileno->peer };
-        connected(&req, 0);
-    }
-}
-
-static int closeHandlesOnFree(struct Allocator_OnFreeJob* job)
-{
-    struct FileNo_pvt* fileno = Identity_check((struct FileNo_pvt*)job->userData);
-    fileno->closeHandlesOnFree = job;
-    int skip = 2;
-    if (fileno->server.data) {
-        uv_close((uv_handle_t*) &fileno->server, NULL);
-        skip--;
-    }
-    if (fileno->peer.data) {
-        uv_close((uv_handle_t*) &fileno->peer, NULL);
-        skip--;
-    }
-    if (skip == 2) {
-        return 0;
-    }
-    return Allocator_ONFREE_ASYNC;
-}
-
-static struct FileNo* FileNo_new(const char* path,
-                                 struct EventBase* eb,
-                                 struct Except* eh,
-                                 struct Log* logger,
-                                 struct Allocator* userAlloc)
-{
-    struct EventBase_pvt* ctx = EventBase_privatize(eb);
-    struct Allocator* alloc = Allocator_child(userAlloc);
-    String* pathStr = String_new(path, alloc);
-
-    struct FileNo_pvt* out = Allocator_clone(alloc, (&(struct FileNo_pvt) {
-        .pub = {
-            .pipePath = pathStr->bytes,
-            .base = eb,
-            .logger = logger,
-            .alloc = alloc
-        }
-    }));
-
-    int ret = uv_pipe_init(ctx->loop, &out->peer, 1);
-    if (ret) {
-        Except_throw(eh, "uv_pipe_init() failed [%s]", uv_strerror(ret));
-    }
-
-    ret = uv_pipe_init(ctx->loop, &out->server, 1);
-    if (ret) {
-        Except_throw(eh, "uv_pipe_init() failed [%s]", uv_strerror(ret));
-    }
-
-    Allocator_onFree(alloc, closeHandlesOnFree, out);
-
-    out->peer.data = out;
-    out->server.data = out;
-    Identity_set(out);
-
-    uv_fs_t req;
-    uv_fs_unlink(out->peer.loop, &req, out->pub.pipePath, NULL);
-
-    ret = uv_pipe_bind(&out->server, out->pub.pipePath);
-    if (!ret) {
-        ret = uv_listen((uv_stream_t*) &out->server, 1, listenCallback);
-        if (ret) {
-            Except_throw(eh, "uv_listen() failed [%s] for pipe [%s]",
-                         uv_strerror(ret), out->pub.pipePath);
-        }
-
-        return &out->pub;
-    }
-
-    Except_throw(eh, "uv_pipe_bind() failed [%s] for pipe [%s]",
-                 uv_strerror(ret), out->pub.pipePath);
-
-    return &out->pub;
-}
-
-static struct FileNo_Promise* FileNo_newFile(const char* path,
-                                             struct EventBase* eb,
-                                             struct Except* eh,
-                                             struct Log* logger,
-                                             struct Allocator* alloc)
-{
-    struct FileNo* fn = FileNo_new(path, eb, eh, logger, alloc);
-    struct FileNoContext* fctx = Allocator_clone(fn->alloc, (&(struct FileNoContext) {
-                .pub = {
-                    .alloc = fn->alloc
-                },
-                .fileno = fn
-            }));
-    Identity_set(fctx);
-
-    fn->userData = fctx;
-
-    return &fctx->pub;
-}
-
-struct FileNo_Promise* FileNo_import(const char* path,
-                                     struct EventBase* eb,
-                                     struct Except* eh,
-                                     struct Log* logger,
-                                     struct Allocator* alloc)
-{
-    return FileNo_newFile(path, eb, eh, logger, alloc);
-}

+ 0 - 110
util/events/libuv/FileNo_admin.c

@@ -1,110 +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 <https://www.gnu.org/licenses/>.
- */
-#include "admin/Admin.h"
-#include "benc/Dict.h"
-#include "benc/List.h"
-#include "benc/String.h"
-#include "util/log/Log.h"
-#include "util/Identity.h"
-#include "util/events/FileNo.h"
-#include "util/events/libuv/FileNo_admin.h"
-#include "util/events/Time.h"
-#include "util/events/Timeout.h"
-
-struct Context
-{
-    struct FileNo_admin pub;
-    struct Admin* admin;
-    struct Allocator* alloc;
-    struct Except* eh;
-    struct EventBase* base;
-    struct Log* logger;
-    Identity
-};
-
-struct FileNoRequest
-{
-    String* txid;
-    struct FileNo_Promise* fp;
-    struct Context* ctx;
-    struct FileNo* fileno;
-    enum FileNo_Type type;
-    Identity
-};
-
-static void onResponse(struct FileNo_Promise* promise, int tunfd)
-{
-    struct FileNoRequest* fr= Identity_check((struct FileNoRequest*)promise->userData);
-    Dict* resp = Dict_new(promise->alloc);
-
-    Dict_putIntC(resp, "tunfd", tunfd, promise->alloc);
-    Dict_putIntC(resp, "type", fr->type, promise->alloc);
-    Dict_putStringCC(resp, "error", "none", promise->alloc);
-    Admin_sendMessage(resp, fr->txid, fr->ctx->admin);
-}
-
-static void import(Dict* args, void* vcontext, String* txid, struct Allocator* requestAlloc)
-{
-    struct Context* ctx = Identity_check((struct Context*) vcontext);
-    String* path = Dict_getStringC(args, "path");
-    String* type = Dict_getStringC(args, "type");
-    char* err = NULL;
-
-    if (Defined(win32)) {
-        err = "Do not support win32";
-        Dict errDict = Dict_CONST(String_CONST("error"), String_OBJ(String_CONST(err)), NULL);
-        Admin_sendMessage(&errDict, txid, ctx->admin);
-        return;
-    }
-
-    int fdtype = FileNo_Type_NORMAL;
-    if (type && !CString_strcmp(type->bytes, "android")) {
-        fdtype = FileNo_Type_ANDROID;
-    }
-
-    struct FileNo_Promise* fp = FileNo_import(path->bytes, ctx->base, ctx->eh, ctx->logger,
-                                              ctx->alloc);
-    struct FileNoRequest* fr = Allocator_calloc(fp->alloc, sizeof(struct FileNoRequest), 1);
-    Identity_set(fr);
-    fr->txid = String_clone(txid, fp->alloc);
-    fr->fp = fp;
-    fr->type = fdtype;
-    fr->ctx = ctx;
-
-    fp->userData = fr;
-    fp->callback = onResponse;
-}
-
-void FileNo_admin_register(struct Admin* admin,
-                           struct Allocator* alloc,
-                           struct EventBase* base,
-                           struct Log* logger,
-                           struct Except* eh)
-{
-    struct Context* ctx = Allocator_clone(alloc, (&(struct Context) {
-        .admin = admin,
-        .eh = eh,
-        .alloc = alloc,
-        .logger = logger,
-        .base = base
-    }));
-    Identity_set(ctx);
-
-    Admin_registerFunction("FileNo_import", import, ctx, true,
-        ((struct Admin_FunctionArg[]) {
-            { .name = "path", .required = 1, .type = "String" },
-            { .name = "type", .required = 0, .type = "String" }
-        }), admin);
-}

+ 109 - 238
util/events/libuv/Pipe.c

@@ -15,6 +15,7 @@
 #include "util/events/libuv/UvWrapper.h"
 #include "memory/Allocator.h"
 #include "util/events/Pipe.h"
+#include "util/events/libuv/Pipe_pvt.h"
 #include "util/events/libuv/EventBase_pvt.h"
 #include "util/log/Log.h"
 #include "util/Identity.h"
@@ -26,6 +27,8 @@
 #include <inttypes.h>
 #include <libgen.h>
 #include <stdio.h>
+#include <sys/stat.h>
+#include <string.h>
 
 struct Pipe_WriteRequest_pvt;
 
@@ -33,7 +36,6 @@ struct Pipe_pvt
 {
     struct Pipe pub;
 
-    uv_pipe_t server;
     uv_pipe_t peer;
 
     /** Job to close the handles when the allocator is freed */
@@ -42,11 +44,8 @@ struct Pipe_pvt
     /** Job which blocks the freeing until the callback completes */
     struct Allocator_OnFreeJob* blockFreeInsideCallback;
 
-    /**
-     * If the output pipe is same as the input, this points to peer.
-     * Otherwise it points to server which is reused.
-     */
-    uv_pipe_t* out;
+    // true if we can pass file descriptors through this pipe
+    bool ipc;
 
     /** 1 when the pipe becomes active. */
     int isActive;
@@ -61,6 +60,8 @@ struct Pipe_pvt
 
     struct Allocator* alloc;
 
+    struct Log* log;
+
     Identity
 };
 
@@ -76,7 +77,7 @@ static void sendMessageCallback(uv_write_t* uvReq, int error)
 {
     struct Pipe_WriteRequest_pvt* req = Identity_check((struct Pipe_WriteRequest_pvt*) uvReq);
     if (error) {
-        Log_info(req->pipe->pub.logger, "Failed to write to pipe [%s] [%s]",
+        Log_info(req->pipe->log, "Failed to write to pipe [%s] [%s]",
                  req->pipe->pub.fullName, uv_strerror(error) );
     }
     req->pipe->queueLen -= req->msg->length;
@@ -84,7 +85,7 @@ static void sendMessageCallback(uv_write_t* uvReq, int error)
     Allocator_free(req->alloc);
 }
 
-static uint8_t sendMessage2(struct Pipe_WriteRequest_pvt* req)
+static void sendMessage2(struct Pipe_WriteRequest_pvt* req)
 {
     struct Pipe_pvt* pipe = req->pipe;
     struct Message* m = req->msg;
@@ -93,16 +94,32 @@ static uint8_t sendMessage2(struct Pipe_WriteRequest_pvt* req)
         { .base = (char*)m->bytes, .len = m->length }
     };
 
-    int ret = uv_write(&req->uvReq, (uv_stream_t*) pipe->out, buffers, 1, sendMessageCallback);
+    int ret = -1;
+    if (pipe->ipc && m->associatedFd) {
+        int fd = Message_getAssociatedFd(m);
+        uv_stream_t* fake_handle = Allocator_calloc(req->alloc, sizeof(uv_stream_t), 1);
+        fake_handle->io_watcher.fd = fd;
+        fake_handle->type = UV_TCP;
+        ret = uv_write2(
+            &req->uvReq,
+            (uv_stream_t*) &pipe->peer,
+            buffers,
+            1,
+            fake_handle,
+            sendMessageCallback);
+        Log_debug(pipe->log, "Sending message with fd [%d]", fd);
+    } else {
+        ret = uv_write(&req->uvReq, (uv_stream_t*) &pipe->peer, buffers, 1, sendMessageCallback);
+    }
     if (ret) {
-        Log_info(pipe->pub.logger, "Failed writing to pipe [%s] [%s]",
+        Log_info(pipe->log, "Failed writing to pipe [%s] [%s]",
                  pipe->pub.fullName, uv_strerror(ret) );
         Allocator_free(req->alloc);
-        return Error_UNDELIVERABLE;
+        return;
     }
     pipe->queueLen += m->length;
 
-    return Error_NONE;
+    return;
 }
 
 static Iface_DEFUN sendMessage(struct Message* m, struct Iface* iface)
@@ -130,17 +147,13 @@ static Iface_DEFUN sendMessage(struct Message* m, struct Iface* iface)
     Identity_set(req);
 
     if (pipe->isActive) {
-        Log_debug(pipe->pub.logger, "Pipe [%s] sending a message len [%d]",
-            pipe->pub.name, (int)m->length);
         sendMessage2(req);
     } else {
         if (!pipe->bufferedRequest) {
-            Log_debug(pipe->pub.logger, "Pipe [%s] buffering a message",
-                pipe->pub.name);
+            Log_debug(pipe->log, "Buffering a message");
             pipe->bufferedRequest = req;
         } else {
-            Log_debug(pipe->pub.logger, "Pipe [%s] appending to the buffered message",
-                pipe->pub.name);
+            Log_debug(pipe->log, "Appending to the buffered message");
             uint8_t* buff =
                 Allocator_malloc(reqAlloc, m->length + pipe->bufferedRequest->msg->length);
             Bits_memcpy(buff,
@@ -155,7 +168,7 @@ static Iface_DEFUN sendMessage(struct Message* m, struct Iface* iface)
             pipe->bufferedRequest = req;
         }
     }
-    return Error_NONE;
+    return NULL;
 }
 
 /** Asynchronous allocator freeing. */
@@ -163,7 +176,7 @@ static void onClose(uv_handle_t* handle)
 {
     struct Pipe_pvt* pipe = Identity_check((struct Pipe_pvt*)handle->data);
     handle->data = NULL;
-    if (pipe->closeHandlesOnFree && !pipe->server.data && !pipe->peer.data) {
+    if (pipe->closeHandlesOnFree && !pipe->peer.data) {
         Allocator_onFreeComplete((struct Allocator_OnFreeJob*) pipe->closeHandlesOnFree);
     }
 }
@@ -184,18 +197,15 @@ static void incoming(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf)
     Assert_true(!alloc || alloc->fileName == pipe->alloc->fileName);
 
     if (nread < 0) {
-        Log_debug(pipe->pub.logger, "Pipe closed [%s]", pipe->pub.fullName);
         if (pipe->pub.onClose) {
             pipe->pub.onClose(&pipe->pub, 0);
         }
-        uv_close((uv_handle_t*) stream, onClose);
 
     } else if (nread == 0) {
         // This is common.
-        //Log_debug(pipe->pub.logger, "Pipe 0 length read [%s]", pipe->pub.fullName);
+        //Log_debug(pipe->log, "Pipe 0 length read [%s]", pipe->pub.fullName);
 
     } else {
-        Log_debug(pipe->pub.logger, "Pipe read [%s] [%d]", pipe->pub.fullName, (int) nread);
         Assert_true(alloc);
         struct Message* m = Allocator_calloc(alloc, sizeof(struct Message), 1);
         m->length = nread;
@@ -203,6 +213,11 @@ static void incoming(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf)
         m->capacity = buf->len;
         m->bytes = (uint8_t*)buf->base;
         m->alloc = alloc;
+        if (pipe->ipc) {
+            #ifndef win32
+                Message_setAssociatedFd(m, stream->accepted_fd);
+            #endif
+        }
         Iface_send(&pipe->pub.iface, m);
     }
 
@@ -216,6 +231,11 @@ static void incoming(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf)
     }
 }
 
+static void incoming2(uv_pipe_t* stream, ssize_t nread, const uv_buf_t* buf, uv_handle_type _)
+{
+    incoming((uv_stream_t*)stream, nread, buf);
+}
+
 static void allocate(uv_handle_t* handle, size_t size, uv_buf_t* buf)
 {
     struct Pipe_pvt* pipe = Identity_check((struct Pipe_pvt*) handle->data);
@@ -232,20 +252,29 @@ static void allocate(uv_handle_t* handle, size_t size, uv_buf_t* buf)
     buf->len = size;
 }
 
+static int startPipe(struct Pipe_pvt* pipe)
+{
+    if (pipe->ipc) {
+        return uv_read2_start((uv_stream_t*)&pipe->peer, allocate, incoming2);
+    } else {
+        return uv_read_start((uv_stream_t*)&pipe->peer, allocate, incoming);
+    }
+}
+
 static void connected(uv_connect_t* req, int status)
 {
     uv_stream_t* link = req->handle;
     struct Pipe_pvt* pipe = Identity_check((struct Pipe_pvt*) link->data);
-    Log_debug(pipe->pub.logger, "Pipe [%s] established connection", pipe->pub.fullName);
+    Log_debug(pipe->log, "Pipe [%s] established connection", pipe->pub.fullName);
 
     int ret;
     if (status) {
-        Log_info(pipe->pub.logger, "uv_pipe_connect() failed for pipe [%s] [%s]",
+        Log_info(pipe->log, "uv_pipe_connect() failed for pipe [%s] [%s]",
                  pipe->pub.fullName, uv_strerror(status) );
         uv_close((uv_handle_t*) &pipe->peer, onClose);
 
-    } else if ((ret = uv_read_start((uv_stream_t*)&pipe->peer, allocate, incoming))) {
-        Log_info(pipe->pub.logger, "uv_read_start() failed for pipe [%s] [%s]",
+    } else if ((ret = startPipe(pipe))) {
+        Log_info(pipe->log, "uv_read_start() failed for pipe [%s] [%s]",
                  pipe->pub.fullName, uv_strerror(ret));
         uv_close((uv_handle_t*) &pipe->peer, onClose);
 
@@ -258,52 +287,13 @@ static void connected(uv_connect_t* req, int status)
 
         // If there's anything buffered then send it.
         if (pipe->bufferedRequest) {
-            Log_debug(pipe->pub.logger, "Sending buffered message");
+            Log_debug(pipe->log, "Sending buffered message");
             sendMessage2(pipe->bufferedRequest);
             pipe->bufferedRequest = NULL;
         }
     }
 }
 
-static void listenCallback(uv_stream_t* server, int status)
-{
-    struct Pipe_pvt* pipe = Identity_check((struct Pipe_pvt*) server->data);
-    if (pipe->isActive) {
-        // first connection wins.
-        return;
-    }
-    if (status == -1) {
-        Log_info(pipe->pub.logger, "failed to accept pipe connection [%s] [%s]",
-                 pipe->pub.fullName, uv_strerror(status) );
-
-        if (pipe->pub.onConnection) {
-            pipe->pub.onConnection(&pipe->pub, status);
-        }
-        return;
-    }
-
-    int ret = uv_accept(server, (uv_stream_t*) &pipe->peer);
-    if (ret) {
-        Log_warn(pipe->pub.logger, "uv_accept() failed: pipe [%s] [%s]",
-                 pipe->pub.fullName, uv_strerror(ret) );
-        if (pipe->pub.onConnection) {
-            pipe->pub.onConnection(&pipe->pub, -1);
-        }
-        uv_close((uv_handle_t*) &pipe->peer, onClose);
-    } else {
-        uv_connect_t req = { .handle = (uv_stream_t*) &pipe->peer };
-        connected(&req, 0);
-    }
-
-    uv_close((uv_handle_t*) &pipe->server, onClose);
-
-    #ifndef win32
-        // get rid of the pipe after it has been connected.
-        uv_fs_t req;
-        uv_fs_unlink(pipe->peer.loop, &req, pipe->pub.fullName, NULL);
-    #endif
-}
-
 static int blockFreeInsideCallback(struct Allocator_OnFreeJob* job)
 {
     struct Pipe_pvt* pipe = Identity_check((struct Pipe_pvt*)job->userData);
@@ -318,240 +308,121 @@ static int closeHandlesOnFree(struct Allocator_OnFreeJob* job)
 {
     struct Pipe_pvt* pipe = Identity_check((struct Pipe_pvt*)job->userData);
     pipe->closeHandlesOnFree = job;
-    int skip = 2;
-    if (pipe->server.data) {
-        uv_close((uv_handle_t*) &pipe->server, onClose);
-        skip--;
-    }
     if (pipe->peer.data) {
         uv_close((uv_handle_t*) &pipe->peer, onClose);
-        skip--;
-    }
-    if (skip == 2) {
-        return 0;
-    }
-    return Allocator_ONFREE_ASYNC;
-}
-
-static struct Pipe_pvt* newPipe(struct EventBase* eb,
-                                const char* path,
-                                const char* name,
-                                struct Except* eh,
-                                struct Allocator* userAlloc)
-{
-    struct EventBase_pvt* ctx = EventBase_privatize(eb);
-    struct Allocator* alloc = Allocator_child(userAlloc);
-
-    char prefix[32] = {0};
-    if (Defined(win32)) {
-        Bits_memcpy(prefix, "\\cjdns_pipe_", CString_strlen("\\cjdns_pipe_"));
-    } else {
-        Bits_memcpy(prefix, "/cjdns_pipe_", CString_strlen("/cjdns_pipe_"));
-    }
-    char* cname = Allocator_malloc(alloc, (path ? CString_strlen(path) : 0) +
-                                   CString_strlen(prefix) + CString_strlen(name) + 1);
-    int pos = 0;
-    if (path) {
-        Bits_memcpy(cname, path, CString_strlen(path));
-        pos += CString_strlen(path);
-    }
-    Bits_memcpy(cname + pos, prefix, CString_strlen(prefix));
-    pos += CString_strlen(prefix);
-    Bits_memcpy(cname + pos, name, CString_strlen(name) + 1);
-
-    struct Pipe_pvt* out = Allocator_clone(alloc, (&(struct Pipe_pvt) {
-        .pub = {
-            .iface = {
-                .send = sendMessage
-            },
-            .fullName = cname,
-            .name = &cname[pos],
-            .base = eb
-        },
-        .alloc = alloc
-    }));
-
-    int ret;
-
-    ret = uv_pipe_init(ctx->loop, &out->peer, 0);
-    if (ret) {
-        Except_throw(eh, "uv_pipe_init() failed [%s]", uv_strerror(ret));
-    }
-
-    ret = uv_pipe_init(ctx->loop, &out->server, 0);
-    if (ret) {
-        Except_throw(eh, "uv_pipe_init() failed [%s]", uv_strerror(ret));
+        return Allocator_ONFREE_ASYNC;
     }
-
-    #ifdef win32
-        out->pub.fd = &out->peer.handle;
-    #else
-        out->pub.fd = &out->peer.io_watcher.fd;
-    #endif
-
-    Allocator_onFree(alloc, closeHandlesOnFree, out);
-    Allocator_onFree(alloc, blockFreeInsideCallback, out);
-
-    out->peer.data = out;
-    out->server.data = out;
-    out->out = &out->peer;
-    Identity_set(out);
-
-    return out;
+    return 0;
 }
 
 static struct Pipe_pvt* newPipeAny(struct EventBase* eb,
                                   const char* fullPath,
+                                  bool ipc,
+                                  struct Log* log,
                                   struct Except* eh,
                                   struct Allocator* userAlloc)
 {
     struct EventBase_pvt* ctx = EventBase_privatize(eb);
     struct Allocator* alloc = Allocator_child(userAlloc);
 
-    char* name = NULL;
-    if (fullPath) {
-        name = basename(String_new(fullPath, alloc)->bytes);
-    }
-
     struct Pipe_pvt* out = Allocator_clone(alloc, (&(struct Pipe_pvt) {
         .pub = {
             .iface = {
                 .send = sendMessage
             },
-            .fullName = fullPath,
-            .name = name,
-            .base = eb
+            .fullName = (fullPath) ? CString_strdup(fullPath, alloc) : NULL,
         },
-        .alloc = alloc
+        .alloc = alloc,
+        .log = log,
+        .ipc = ipc,
     }));
 
-    int ret;
-
-    ret = uv_pipe_init(ctx->loop, &out->peer, 0);
-    if (ret) {
-        Except_throw(eh, "uv_pipe_init() failed [%s]", uv_strerror(ret));
-    }
-
-    ret = uv_pipe_init(ctx->loop, &out->server, 0);
+    int ret = uv_pipe_init(ctx->loop, &out->peer, ipc);
     if (ret) {
         Except_throw(eh, "uv_pipe_init() failed [%s]", uv_strerror(ret));
     }
 
-    #ifdef win32
-        out->pub.fd = &out->peer.handle;
-    #else
-        out->pub.fd = &out->peer.io_watcher.fd;
-    #endif
-
     Allocator_onFree(alloc, closeHandlesOnFree, out);
     Allocator_onFree(alloc, blockFreeInsideCallback, out);
 
     out->peer.data = out;
-    out->server.data = out;
-    out->out = &out->peer;
     Identity_set(out);
 
     return out;
 }
 
-struct Pipe* Pipe_forFiles(int inFd,
-                           int outFd,
-                           struct EventBase* eb,
-                           struct Except* eh,
-                           struct Log* logger,
-                           struct Allocator* userAlloc)
+struct Pipe* Pipe_forFd(int fd,
+                        bool ipc,
+                        struct EventBase* eb,
+                        struct Except* eh,
+                        struct Log* log,
+                        struct Allocator* userAlloc)
 {
     char buff[32] = {0};
-    snprintf(buff, 31, "forFiles(%d,%d)", inFd, outFd);
+    snprintf(buff, 31, "forFd(%d)", fd);
 
-    struct Pipe_pvt* out = newPipe(eb, NULL, buff, eh, userAlloc);
-    out->pub.logger = logger;
+    struct Pipe_pvt* out = newPipeAny(eb, buff, ipc, log, eh, userAlloc);
 
-    int ret = uv_pipe_open(&out->peer, inFd);
+    int ret = uv_pipe_open(&out->peer, fd);
     if (ret) {
         Except_throw(eh, "uv_pipe_open(inFd) failed [%s]",
                      uv_strerror(ret));
     }
 
-    if (inFd != outFd) {
-        out->out = &out->server;
-        ret = uv_pipe_open(out->out, outFd);
-        if (ret) {
-            Except_throw(eh, "uv_pipe_open(outFd) failed [%s]",
-                         uv_strerror(ret));
-        }
-    }
-
     uv_connect_t req = { .handle = (uv_stream_t*) &out->peer };
     connected(&req, 0);
 
     return &out->pub;
 }
 
-struct Pipe* Pipe_named(const char* path,
-                        const char* name,
+struct Pipe* Pipe_named(const char* fullPath,
                         struct EventBase* eb,
                         struct Except* eh,
+                        struct Log* log,
                         struct Allocator* userAlloc)
 {
-    struct Pipe_pvt* out = newPipe(eb, path, name, eh, userAlloc);
-    int ret;
+    struct Pipe_pvt* out = newPipeAny(eb, fullPath, true, log, eh, userAlloc);
 
-    // Attempt to create pipe.
-    ret = uv_pipe_bind(&out->server, out->pub.fullName);
-    if (!ret) {
-        ret = uv_listen((uv_stream_t*) &out->server, 1, listenCallback);
-        if (ret) {
-            Except_throw(eh, "uv_listen() failed [%s] for pipe [%s]",
-                         uv_strerror(ret), out->pub.fullName);
-        }
-        return &out->pub;
-    }
+    uv_connect_t* req = Allocator_malloc(out->alloc, sizeof(uv_connect_t));
+    req->data = out;
+    uv_pipe_connect(req, &out->peer, out->pub.fullName, connected);
 
-    if (ret == UV_EADDRINUSE) {
-        // Pipe exists, connect to it.
-        uv_connect_t* req = Allocator_malloc(out->alloc, sizeof(uv_connect_t));
-        req->data = out;
-        uv_pipe_connect(req, &out->peer, out->pub.fullName, connected);
-        return &out->pub;
+    int err = (&out->peer)->delayed_error;
+    if (err != 0) {
+        Except_throw(eh, "uv_pipe_connect() failed [%s] for pipe [%s]",
+                     uv_strerror(err), out->pub.fullName);
     }
 
-    Except_throw(eh, "uv_pipe_bind() failed [%s] for pipe [%s]",
-                 uv_strerror(ret), out->pub.fullName);
-
     return &out->pub;
 }
 
-struct Pipe* Pipe_namedConnect(const char* fullPath,
-                               bool attemptToCreate,
+struct Pipe* Pipe_serverAccept(uv_pipe_t* server,
+                               const char* pipeName,
                                struct EventBase* eb,
                                struct Except* eh,
+                               struct Log* log,
                                struct Allocator* userAlloc)
 {
-    struct Pipe_pvt* out = newPipeAny(eb, fullPath, eh, userAlloc);
-
-    if (attemptToCreate) {
-        // Attempt to create pipe.
-        int ret = uv_pipe_bind(&out->server, out->pub.fullName);
-        if (!ret) {
-            ret = uv_listen((uv_stream_t*) &out->server, 1, listenCallback);
-            if (ret) {
-                Except_throw(eh, "uv_listen() failed [%s] for pipe [%s]",
-                             uv_strerror(ret), out->pub.fullName);
-            }
-            return &out->pub;
-        }
-    }
-
-    uv_connect_t* req = Allocator_malloc(out->alloc, sizeof(uv_connect_t));
-    req->data = out;
-    uv_pipe_connect(req, &out->peer, out->pub.fullName, connected);
-
-    int err = (&out->peer)->delayed_error;
-    if (err != 0) {
-        Except_throw(eh, "uv_pipe_connect() failed [%s] for pipe [%s]",
-                     uv_strerror(err), out->pub.fullName);
+    struct Pipe_pvt* out = newPipeAny(eb, NULL, true, log, eh, userAlloc);
+    int ret = uv_accept((uv_stream_t*) server, (uv_stream_t*) &out->peer);
+    if (ret) {
+        uv_close((uv_handle_t*) &out->peer, onClose);
+        Except_throw(eh, "uv_accept() failed: pipe [%s] [%s]",
+            pipeName, uv_strerror(ret) );
+    } else {
+        uv_connect_t req = { .handle = (uv_stream_t*) &out->peer };
+        connected(&req, 0);
     }
-
     return &out->pub;
 }
+
+bool Pipe_exists(const char* path, struct Except* eh)
+{
+    struct stat st;
+    if (stat(path, &st)) {
+        if (errno == ENOENT) { return false; }
+        Except_throw(eh, "Failed stat(%s) [%s]", path, strerror(errno));
+    } else {
+        return (st.st_mode & S_IFMT) == S_IFSOCK;
+    }
+}

+ 253 - 0
util/events/libuv/PipeServer.c

@@ -0,0 +1,253 @@
+/* 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 <https://www.gnu.org/licenses/>.
+ */
+#include "util/events/libuv/UvWrapper.h"
+
+#include "benc/String.h"
+#include "exception/Jmp.h"
+#include "memory/Allocator.h"
+#include "util/events/Pipe.h"
+#include "util/events/PipeServer.h"
+#include "util/events/libuv/Pipe_pvt.h"
+#include "util/events/libuv/EventBase_pvt.h"
+#include "util/log/Log.h"
+#include "util/Identity.h"
+#include "util/CString.h"
+#include "wire/Message.h"
+#include "wire/Error.h"
+
+#include <inttypes.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+struct PipeServer_pvt;
+struct Client
+{
+    struct Iface iface;
+    struct Pipe* pipe;
+    struct PipeServer_pvt* psp;
+    struct Allocator* alloc;
+
+    struct Sockaddr addr;
+    Identity
+};
+
+#define Map_NAME Clients
+#define Map_ENABLE_HANDLES
+#define Map_VALUE_TYPE struct Client*
+#include "util/Map.h"
+
+struct PipeServer_pvt
+{
+    struct PipeServer pub;
+
+    uv_pipe_t server;
+
+    struct Map_Clients clients;
+
+    struct Allocator_OnFreeJob* closeHandlesOnFree;
+
+    struct Allocator* alloc;
+
+    struct EventBase_pvt* base;
+
+    struct Log* log;
+
+    uint32_t nextHandle;
+
+    Identity
+};
+
+static Iface_DEFUN sendMessage(struct Message* m, struct Iface* iface)
+{
+    struct PipeServer_pvt* psp = Identity_check((struct PipeServer_pvt*) iface);
+    struct Sockaddr* addr = AddrIface_popAddr(m, NULL);
+    uint32_t handle = Sockaddr_addrHandle(addr);
+    int idx = Map_Clients_indexForHandle(handle, &psp->clients);
+    if (idx < 0) {
+        Log_warn(psp->log, "Attempted to send a message to client [0x%x] which is gone", handle);
+        return NULL;
+    }
+    struct Client* cli = psp->clients.values[idx];
+    return Iface_next(&cli->iface, m);
+}
+
+static Iface_DEFUN incomingFromClient(struct Message* msg, struct Iface* iface)
+{
+    struct Client* cli = Identity_containerOf(iface, struct Client, iface);
+    struct PipeServer_pvt* psp = Identity_check(cli->psp);
+    AddrIface_pushAddr(msg, &cli->addr, NULL);
+    return Iface_next(&psp->pub.iface.iface, msg);
+}
+
+/** Asynchronous allocator freeing. */
+static void onClose(uv_handle_t* handle)
+{
+    struct PipeServer_pvt* psp = Identity_check((struct PipeServer_pvt*)handle->data);
+    handle->data = NULL;
+    if (psp->closeHandlesOnFree && !psp->server.data) {
+        Allocator_onFreeComplete((struct Allocator_OnFreeJob*) psp->closeHandlesOnFree);
+    }
+}
+
+static struct Pipe* getPipe(struct PipeServer_pvt* psp, struct Allocator* alloc)
+{
+    struct Jmp j;
+    Jmp_try(j) {
+        return Pipe_serverAccept(
+            &psp->server, psp->pub.fullName, &psp->base->pub, &j.handler, psp->log, alloc);
+    } Jmp_catch {
+        Log_warn(psp->log, "failed to connect to client on pipe [%s] [%s]",
+                 psp->pub.fullName, j.message);
+    }
+    return NULL;
+}
+
+static int removeClientOnFree(struct Allocator_OnFreeJob* job)
+{
+    struct Client* cli = Identity_check((struct Client*)job->userData);
+    struct PipeServer_pvt* psp = Identity_check(cli->psp);
+    uint32_t handle = Sockaddr_addrHandle(&cli->addr);
+    int idx = Map_Clients_indexForHandle(handle, &psp->clients);
+    if (idx > -1) {
+        Map_Clients_remove(idx, &psp->clients);
+    }
+    return 0;
+}
+
+static void pipeOnClose(struct Pipe* p, int status)
+{
+    struct Client* cli = Identity_check((struct Client*) p->userData);
+    struct PipeServer_pvt* psp = Identity_check(cli->psp);
+    if (psp->pub.onDisconnection) {
+        psp->pub.onDisconnection(&psp->pub, &cli->addr);
+    }
+    Allocator_free(cli->alloc);
+}
+
+static void listenCallback(uv_stream_t* server, int status)
+{
+    uv_pipe_t* pServer = (uv_pipe_t*) server;
+    struct PipeServer_pvt* psp = Identity_containerOf(pServer, struct PipeServer_pvt, server);
+    if (status == -1) {
+        Log_info(psp->log, "failed to accept pipe connection [%s] [%s]",
+                 psp->pub.fullName, uv_strerror(status));
+
+        return;
+    }
+    struct Allocator* pipeAlloc = Allocator_child(psp->alloc);
+    struct Pipe* p = getPipe(psp, pipeAlloc);
+    if (p == NULL) {
+        Allocator_free(pipeAlloc);
+        return;
+    }
+    struct Client* cli = Allocator_calloc(pipeAlloc, sizeof(struct Client), 1);
+    cli->iface.send = incomingFromClient;
+    Iface_plumb(&cli->iface, &p->iface);
+    cli->alloc = pipeAlloc;
+    cli->pipe = p;
+    cli->psp = psp;
+    p->userData = cli;
+    Identity_set(cli);
+    int idx = Map_Clients_put(&cli, &psp->clients);
+    uint32_t handle = psp->clients.handles[idx];
+    Sockaddr_addrFromHandle(&cli->addr, handle);
+    {
+        // assertions
+        Assert_true(handle == Sockaddr_addrHandle(&cli->addr));
+        //printf("Handle is %x index is %d\n", handle, idx);
+        int idx2 = Map_Clients_indexForHandle(handle, &psp->clients);
+        Assert_true(idx2 == idx);
+    }
+    Allocator_onFree(pipeAlloc, removeClientOnFree, cli);
+    if (psp->pub.onConnection) {
+        psp->pub.onConnection(&psp->pub, &cli->addr);
+    }
+    cli->pipe->onClose = pipeOnClose;
+}
+
+static int closeHandlesOnFree(struct Allocator_OnFreeJob* job)
+{
+    struct PipeServer_pvt* psp = Identity_check((struct PipeServer_pvt*)job->userData);
+    psp->closeHandlesOnFree = job;
+    if (psp->server.data) {
+        uv_close((uv_handle_t*) &psp->server, onClose);
+        return Allocator_ONFREE_ASYNC;
+    }
+    return 0;
+}
+
+static struct PipeServer_pvt* newPipeAny(struct EventBase* eb,
+                                  const char* fullPath,
+                                  struct Except* eh,
+                                  struct Log* log,
+                                  struct Allocator* userAlloc)
+{
+    struct EventBase_pvt* ctx = EventBase_privatize(eb);
+    struct Allocator* alloc = Allocator_child(userAlloc);
+
+    struct PipeServer_pvt* psp = Allocator_clone(alloc, (&(struct PipeServer_pvt) {
+        .pub = {
+            .iface = {
+                .iface = {
+                    .send = sendMessage
+                },
+                .alloc = alloc,
+            },
+            .fullName = CString_strdup(fullPath, alloc),
+        },
+        .clients = {
+            .allocator = alloc
+        },
+        .base = ctx,
+        .alloc = alloc,
+        .log = log,
+    }));
+
+    int ret = uv_pipe_init(ctx->loop, &psp->server, 0);
+    if (ret) {
+        Except_throw(eh, "uv_pipe_init() failed [%s]", uv_strerror(ret));
+    }
+
+    Allocator_onFree(alloc, closeHandlesOnFree, psp);
+
+    psp->server.data = psp;
+    //out->out = &out->peer;
+    Identity_set(psp);
+
+    return psp;
+}
+
+struct PipeServer* PipeServer_named(const char* fullPath,
+                                    struct EventBase* eb,
+                                    struct Except* eh,
+                                    struct Log* log,
+                                    struct Allocator* userAlloc)
+{
+    struct PipeServer_pvt* out = newPipeAny(eb, fullPath, eh, log, userAlloc);
+
+    int ret = uv_pipe_bind(&out->server, out->pub.fullName);
+    if (ret) {
+        Except_throw(eh, "uv_pipe_bind() failed [%s] for pipe [%s]",
+            uv_strerror(ret), out->pub.fullName);
+    }
+    ret = uv_listen((uv_stream_t*) &out->server, 1, listenCallback);
+    if (ret) {
+        Except_throw(eh, "uv_listen() failed [%s] for pipe [%s]",
+                        uv_strerror(ret), out->pub.fullName);
+    }
+    return &out->pub;
+}

+ 14 - 22
interface/addressable/AddrInterface.h_ → util/events/libuv/Pipe_pvt.h

@@ -12,28 +12,20 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
-#ifndef AddrInterface_H
-#define AddrInterface_H
+#ifndef Pipe_pvt_H
+#define Pipe_pvt_H
 
-#include "interface/Interface.h"
-#include "util/platform/Sockaddr.h"
+#include "util/events/libuv/UvWrapper.h"
+#include "util/events/Pipe.h"
+#include "util/Linker.h"
+Linker_require("util/events/libuv/Pipe.c");
 
-/**
- * An AddrInterface, short for "Adderssable Interface" is an interface which
- * sends and accepts an address as the header of the messages sent to and
- * recieved from it.
- */
-struct AddrInterface
-{
-    /** As a generic interface. */
-    struct Iface generic;
-
-    /**
-     * The address of this node if applicable to the implementation.
-     * addr->addrLen will always tell how many of the first bytes of each
-     * message are reserved for the address.
-     */
-    struct Sockaddr* addr;
-};
+#include <stdbool.h>
 
-#endif
+struct Pipe* Pipe_serverAccept(uv_pipe_t* server,
+                               const char* pipeName,
+                               struct EventBase* eb,
+                               struct Except* eh,
+                               struct Log* log,
+                               struct Allocator* userAlloc);
+#endif

+ 10 - 0
util/events/libuv/UDPAddrIface.c

@@ -252,6 +252,16 @@ int UDPAddrIface_setDSCP(struct UDPAddrIface* iface, uint8_t dscp)
     return res;
 }
 
+int UDPAddrIface_getFd(struct UDPAddrIface* iface)
+{
+    struct UDPAddrIface_pvt* context = Identity_check((struct UDPAddrIface_pvt*) iface);
+    int out = -1;
+    #ifndef win32
+        out = context->uvHandle.io_watcher.fd;
+    #endif
+    return out;
+}
+
 int UDPAddrIface_setBroadcast(struct UDPAddrIface* iface, bool enable)
 {
     struct UDPAddrIface_pvt* context = Identity_check((struct UDPAddrIface_pvt*) iface);

+ 19 - 0
util/platform/Sockaddr.c

@@ -326,3 +326,22 @@ int Sockaddr_compare(const struct Sockaddr* a, const struct Sockaddr* b)
 {
     return Bits_memcmp(a, b, a->addrLen);
 }
+
+uint32_t Sockaddr_addrHandle(const struct Sockaddr* addr)
+{
+    if (addr->addrLen != sizeof(struct Sockaddr) || addr->type != Sockaddr_HANDLE) {
+        return Sockaddr_addrHandle_INVALID;
+    }
+    uint32_t handle;
+    Bits_memcpy(&handle, &((uint8_t*)addr)[4], 4);
+    return handle;
+}
+
+void Sockaddr_addrFromHandle(struct Sockaddr* addr, uint32_t handle)
+{
+    Assert_true(handle != Sockaddr_addrHandle_INVALID);
+    Bits_memset(addr, 0, sizeof(struct Sockaddr));
+    addr->type = Sockaddr_HANDLE;
+    addr->addrLen = sizeof(struct Sockaddr);
+    Bits_memcpy(&((uint8_t*)addr)[4], &handle, 4);
+}

+ 20 - 1
util/platform/Sockaddr.h

@@ -29,7 +29,11 @@ struct Sockaddr
 
     #define Sockaddr_flags_BCAST  1
     #define Sockaddr_flags_PREFIX (1<<1)
-    uint16_t flags;
+    uint8_t flags;
+
+    #define Sockaddr_PLATFORM 0
+    #define Sockaddr_HANDLE 1
+    uint8_t type;
 
     /** Only applies if flags & Sockaddr_flags_PREFIX is true. */
     uint8_t prefix;
@@ -56,6 +60,21 @@ extern const struct Sockaddr* const Sockaddr_LOOPBACK_le;
 
 extern const struct Sockaddr* const Sockaddr_LOOPBACK6;
 
+/**
+ * Parse an internal form of Sockaddr which is used to represent a uint32_t handle.
+ * If the length of the address is not equal to sizeof(struct Sockaddr) or the type is not
+ * Sockaddr_HANDLE then this returns Sockaddr_addrHandle_INVALID.
+ */
+#define Sockaddr_addrHandle_INVALID 0xffffffffu
+uint32_t Sockaddr_addrHandle(const struct Sockaddr* addr);
+
+/**
+ * Create a handle sockaddr from a numeric handle, if handle is equal to
+ * Sockaddr_addrHandle_INVALID then this will trigger an assertion.
+ */
+void Sockaddr_addrFromHandle(struct Sockaddr* addr, uint32_t handle);
+
+
 int Sockaddr_getPrefix(struct Sockaddr* addr);
 
 /**

+ 62 - 26
util/test/Process_test.c

@@ -12,8 +12,10 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
+#include "benc/String.h"
 #include "crypto/random/Random.h"
 #include "util/events/EventBase.h"
+#include "util/events/PipeServer.h"
 #include "util/events/Pipe.h"
 #include "util/events/Timeout.h"
 #include "memory/Allocator.h"
@@ -26,9 +28,12 @@
 #include "wire/Message.h"
 #include "wire/Error.h"
 
+#include <errno.h>
+#include <fcntl.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <string.h>
 
 #define MESSAGE  "IT WORKS!"
 #define MESSAGEB "INDEED"
@@ -38,34 +43,36 @@ struct Context {
     struct Allocator* alloc;
     struct EventBase* base;
     struct Log* log;
+
+    // Parent only
+    int fd;
+
+    // child only
+    char* name;
     Identity
 };
 
-static void onConnectionParent(struct Pipe* p, int status)
+static void onConnectionParent(struct PipeServer* p, struct Sockaddr* addr)
 {
-    Assert_true(!status);
     struct Context* c = Identity_check((struct Context*) p->userData);
-
     struct Allocator* alloc = Allocator_child(c->alloc);
-    uint8_t* bytes = Allocator_calloc(alloc, CString_strlen(MESSAGE) + 1, 1);
-    Bits_memcpy(bytes, MESSAGE, CString_strlen(MESSAGE));
-    struct Message* m = Allocator_clone(alloc, (&(struct Message) {
-        .length = CString_strlen(MESSAGE),
-        .padding = 0,
-        .capacity = CString_strlen(MESSAGE),
-        .alloc = alloc,
-        .bytes = bytes
-    }));
-    printf("Parent sending message [%s]\n", bytes);
-    Iface_send(&c->iface, m);
+    struct Message* msg = Message_new(0, 256, alloc);
+    Message_push(msg, MESSAGE, CString_strlen(MESSAGE) + 1, NULL);
+    AddrIface_pushAddr(msg, addr, NULL);
+    if (!Defined(win32)) {
+        Message_setAssociatedFd(msg, c->fd);
+    }
+    printf("Parent sending message [%s] len [%d]\n", MESSAGE, msg->length);
+    Iface_send(&c->iface, msg);
     Allocator_free(alloc);
 }
 
 static Iface_DEFUN receiveMessageParent(struct Message* msg, struct Iface* iface)
 {
     struct Context* c = Identity_check((struct Context*) iface);
-    Assert_true(msg->length == (int)CString_strlen(MESSAGEB));
-    Assert_true(!Bits_memcmp(msg->bytes, MESSAGEB, CString_strlen(MESSAGEB)));
+    AddrIface_popAddr(msg, NULL);
+    Assert_true(msg->length == (int)CString_strlen(MESSAGEB)+1);
+    Assert_true(!Bits_memcmp(msg->bytes, MESSAGEB, CString_strlen(MESSAGEB)+1));
     Allocator_free(c->alloc);
     return NULL;
 }
@@ -86,8 +93,26 @@ static Iface_DEFUN receiveMessageChild(struct Message* msg, struct Iface* iface)
     struct Context* c = Identity_check((struct Context*) iface);
     struct Message* m = Message_clone(msg, c->alloc);
     printf("Child received message\n");
-    Assert_true(m->length == (int)CString_strlen(MESSAGE));
-    Assert_true(!Bits_memcmp(m->bytes, MESSAGE, CString_strlen(MESSAGE)));
+    Assert_true(m->length == (int)CString_strlen(MESSAGE)+1);
+    Assert_true(!Bits_memcmp(m->bytes, MESSAGE, CString_strlen(MESSAGE)+1));
+
+    if (!Defined(win32)) {
+        int fd = Message_getAssociatedFd(msg);
+        if (lseek(fd, 0, SEEK_SET) < 0) {
+            printf("lseek(%d) failed: errno %s\n", fd, strerror(errno));
+            Assert_failure("lseek()");
+        }
+        uint8_t* buf = Allocator_calloc(msg->alloc, 2048, 1);
+        if (read(fd, buf, 1024) < 0) {
+            printf("read(%d) failed: errno %s\n", fd, strerror(errno));
+            Assert_failure("read()");
+        }
+        if (CString_strncmp(buf, c->name, 1024)) {
+            printf("want: %s\n"
+                   "got:  %s", c->name, buf);
+            Assert_failure("file content is wrong");
+        }
+    }
 
     Message_shift(m, -((int)CString_strlen(MESSAGE)), NULL);
     Message_push(m, MESSAGEB, CString_strlen(MESSAGEB), NULL);
@@ -102,11 +127,11 @@ static Iface_DEFUN receiveMessageChild(struct Message* msg, struct Iface* iface)
 
 static void child(char* name, struct Context* ctx)
 {
-    struct Pipe* pipe = Pipe_named(Pipe_PATH, name, ctx->base, NULL, ctx->alloc);
-    pipe->logger = ctx->log;
+    struct Pipe* pipe = Pipe_named(name, ctx->base, NULL, ctx->log, ctx->alloc);
     pipe->onConnection = onConnectionChild;
     pipe->userData = ctx;
     ctx->iface.send = receiveMessageChild;
+    ctx->name = name;
     Iface_plumb(&ctx->iface, &pipe->iface);
     Timeout_setTimeout(timeout, NULL, 2000, ctx->base, ctx->alloc);
     EventBase_beginLoop(ctx->base);
@@ -130,14 +155,23 @@ int main(int argc, char** argv)
     }
 
     struct Random* rand = Random_new(alloc, log, NULL);
-    char name[32] = {0};
-    Random_base32(rand, (uint8_t*)name, 31);
+    char randName[32] = {0};
+    Random_base32(rand, (uint8_t*)randName, 31);
+    String* name = String_printf(alloc, "%s%scjdns-test-%s", Pipe_PATH, Pipe_PATH_SEP, randName);
+    if (!Defined(win32)) {
+        String* textName =
+            String_printf(alloc, "%s%scjdns-test-%s.txt", Pipe_PATH, Pipe_PATH_SEP, randName);
+        int fd = open(textName->bytes, O_CREAT | O_TRUNC | O_RDWR);
+        Assert_true(fd >= 0);
+        Assert_true(write(fd, name->bytes, name->len) == ((ssize_t)name->len));
+        ctx->fd = fd;
+        unlink(textName->bytes);
+    }
 
-    struct Pipe* pipe = Pipe_named(Pipe_PATH, name, eb, NULL, alloc);
-    pipe->logger = log;
+    struct PipeServer* pipe = PipeServer_named(name->bytes, eb, NULL, log, alloc);
     pipe->userData = ctx;
     pipe->onConnection = onConnectionParent;
-    Iface_plumb(&ctx->iface, &pipe->iface);
+    Iface_plumb(&ctx->iface, &pipe->iface.iface);
 
     char* path = Process_getPath(alloc);
 
@@ -153,12 +187,14 @@ int main(int argc, char** argv)
         Assert_true(path[0] == '/');
     #endif
 
-    char* args[] = { "Process_test", "child", name, NULL };
+    char* args[] = { "Process_test", "child", name->bytes, NULL };
 
     Assert_true(!Process_spawn(path, args, eb, alloc, NULL));
 
     Timeout_setTimeout(timeout, NULL, 2000, eb, alloc);
 
     EventBase_beginLoop(eb);
+
+    unlink(name->bytes);
     return 0;
 }

+ 38 - 22
util/test/Seccomp_test.c

@@ -12,17 +12,21 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
+#include "benc/String.h"
 #include "util/log/FileWriterLog.h"
 #include "memory/Allocator.h"
 #include "memory/MallocAllocator.h"
 #include "util/Seccomp.h"
 #include "util/events/EventBase.h"
 #include "util/events/Process.h"
+#include "util/events/PipeServer.h"
 #include "util/events/Pipe.h"
 #include "util/events/Timeout.h"
 #include "util/CString.h"
 #include "crypto/random/Random.h"
 
+#include <unistd.h>
+
 struct Context
 {
     struct Iface iface;
@@ -36,25 +40,32 @@ static void childComplete(void* vEventBase)
     EventBase_endLoop((struct EventBase*)vEventBase);
 }
 
-static void onConnectionChild(struct Pipe* p, int status)
+struct ChildCtx
+{
+    struct EventBase* base;
+    struct Log* log;
+    struct Pipe* pipe;
+    struct Allocator* alloc;
+    Identity
+};
+
+static void onConnectionChild(struct Pipe* pipe, int status)
 {
-    // hax
-    struct Allocator* alloc = (struct Allocator*) p->userData;
-    struct Log* logger = p->logger;
+    struct ChildCtx* child = Identity_check((struct ChildCtx*) pipe->userData);
 
-    Seccomp_dropPermissions(alloc, logger, NULL);
+    Seccomp_dropPermissions(child->alloc, child->log, NULL);
     Assert_true(Seccomp_isWorking());
 
-    struct Message* ok = Message_new(0, 512, alloc);
+    struct Message* ok = Message_new(0, 512, child->alloc);
     Message_push(ok, "OK", 3, NULL);
 
     struct Iface iface = { .send = NULL };
-    Iface_plumb(&p->iface, &iface);
+    Iface_plumb(&pipe->iface, &iface);
     Iface_send(&iface, ok);
 
     // just set a timeout long enough that we're pretty sure the parent will get the message
     // before we quit.
-    Timeout_setInterval(childComplete, p->base, 10, p->base, alloc);
+    Timeout_setInterval(childComplete, child->base, 10, child->base, child->alloc);
 }
 
 static void timeout(void* vNULL)
@@ -64,14 +75,16 @@ static void timeout(void* vNULL)
 
 static int child(char* pipeName, struct Allocator* alloc, struct Log* logger)
 {
-    struct EventBase* eb = EventBase_new(alloc);
-    struct Pipe* pipe = Pipe_named(Pipe_PATH, pipeName, eb, NULL, alloc);
-    pipe->onConnection = onConnectionChild;
-    pipe->logger = logger;
-    pipe->userData = alloc;
-
-    Timeout_setTimeout(timeout, eb, 2000, eb, alloc);
-    EventBase_beginLoop(eb);
+    struct ChildCtx* ctx = Allocator_calloc(alloc, sizeof(struct ChildCtx), 1);
+    ctx->base = EventBase_new(alloc);
+    ctx->alloc = alloc;
+    ctx->log = logger;
+    ctx->pipe = Pipe_named(pipeName, ctx->base, NULL, logger, alloc);
+    ctx->pipe->onConnection = onConnectionChild;
+    ctx->pipe->userData = ctx;
+    Identity_set(ctx);
+    Timeout_setTimeout(timeout, ctx->base, 2000, ctx->base, alloc);
+    EventBase_beginLoop(ctx->base);
 
     return 0;
 }
@@ -79,6 +92,8 @@ static int child(char* pipeName, struct Allocator* alloc, struct Log* logger)
 static Iface_DEFUN receiveMessageParent(struct Message* msg, struct Iface* iface)
 {
     struct Context* ctx = Identity_check((struct Context*) iface);
+    // PipeServer pushes a uint32 identifier of the client who sent the message
+    AddrIface_popAddr(msg, NULL);
     Assert_true(msg->length == 3);
     Assert_true(!Bits_memcmp(msg->bytes, "OK", 3));
     EventBase_endLoop(ctx->eventBase);
@@ -102,8 +117,9 @@ int main(int argc, char** argv)
 
     struct EventBase* eb = EventBase_new(alloc);
     struct Random* rand = Random_new(alloc, logger, NULL);
-    char name[32] = {0};
-    Random_base32(rand, (uint8_t*)name, 31);
+    char randName[32] = {0};
+    Random_base32(rand, (uint8_t*)randName, 31);
+    String* name = String_printf(alloc, "%s%scjdns-test-%s", Pipe_PATH, Pipe_PATH_SEP, randName);
 
     struct Context* ctx = Allocator_calloc(alloc, sizeof(struct Context), 1);
     Identity_set(ctx);
@@ -111,17 +127,17 @@ int main(int argc, char** argv)
     ctx->iface.send = receiveMessageParent;
     ctx->eventBase = eb;
 
-    struct Pipe* pipe = Pipe_named(Pipe_PATH, name, eb, NULL, alloc);
-    pipe->logger = logger;
-    Iface_plumb(&ctx->iface, &pipe->iface);
+    struct PipeServer* pipe = PipeServer_named(name->bytes, eb, NULL, logger, alloc);
+    Iface_plumb(&ctx->iface, &pipe->iface.iface);
 
     char* path = Process_getPath(alloc);
-    char* args[] = { "Seccomp_test", "child", name, NULL };
+    char* args[] = { "Seccomp_test", "child", name->bytes, NULL };
 
     Assert_true(!Process_spawn(path, args, eb, alloc, NULL));
 
     Timeout_setTimeout(timeout, NULL, 2000, eb, alloc);
 
     EventBase_beginLoop(eb);
+    unlink(name->bytes);
     return 0;
 }

+ 70 - 19
wire/Message.h

@@ -37,6 +37,13 @@ struct Message
     /** Amount of bytes of storage space available in the message. */
     int32_t capacity;
 
+    /**
+     * When sending/receiving a Message on a unix socket, a file descriptor to attach.
+     * Caviat: In order to maintain backward compatibility with a Message which is
+     * allocated using calloc, file descriptor 0 is referred to by -1
+     */
+    int associatedFd;
+
     #ifdef PARANOIA
         /** This is used inside of Iface.h to support Iface_next() */
         struct Iface* currentIface;
@@ -68,6 +75,28 @@ static inline struct Message* Message_new(uint32_t messageLength,
     return out;
 }
 
+static inline void Message_setAssociatedFd(struct Message* msg, int fd)
+{
+    if (fd == -1) {
+        msg->associatedFd = 0;
+    } else if (fd == 0) {
+        msg->associatedFd = -1;
+    } else {
+        msg->associatedFd = fd;
+    }
+}
+
+static inline int Message_getAssociatedFd(struct Message* msg)
+{
+    if (msg->associatedFd == -1) {
+        return 0;
+    } else if (msg->associatedFd == 0) {
+        return -1;
+    } else {
+        return msg->associatedFd;
+    }
+}
+
 static inline struct Message* Message_clone(struct Message* toClone, struct Allocator* alloc)
 {
     Assert_true(toClone->capacity >= toClone->length);
@@ -153,31 +182,53 @@ static inline void Message_pop(struct Message* restrict msg,
     }
 }
 
-#define Message_popGeneric(size) \
-    static inline uint ## size ## _t Message_pop ## size (struct Message* msg, struct Except* eh) \
+#define Message_pushH(size) \
+    static inline void Message_push ## size ## h                                          \
+        (struct Message* msg, uint ## size ## _t dat, struct Except* eh)                  \
+    {                                                                                     \
+        Message_push(msg, &dat, (size)/8, eh);                                            \
+    }
+#define Message_popH(size) \
+    static inline uint ## size ## _t Message_pop ## size ## h \
+        (struct Message* msg, struct Except* eh) \
     {                                                                                             \
         uint ## size ## _t out;                                                                   \
         Message_pop(msg, &out, (size)/8, eh);                                                     \
-        return Endian_bigEndianToHost ## size (out);                                              \
+        return out;                                                                               \
     }
 
-Message_popGeneric(8)
-Message_popGeneric(16)
-Message_popGeneric(32)
-Message_popGeneric(64)
-
-
-#define Message_pushGeneric(size) \
-    static inline void Message_push ## size                                               \
-        (struct Message* msg, uint ## size ## _t dat, struct Except* eh)                  \
-    {                                                                                     \
-        uint ## size ## _t x = Endian_hostToBigEndian ## size (dat);                      \
-        Message_push(msg, &x, (size)/8, eh);                                              \
+#define Message_pushBE(size) \
+    static inline void Message_push ## size \
+            (struct Message* msg, uint ## size ## _t dat, struct Except* eh)                      \
+    {                                                                                             \
+        Message_push ## size ## h(msg, Endian_hostToBigEndian ## size (dat), eh);                 \
+    }
+#define Message_pushLE(size) \
+    static inline void Message_push ## size ## le \
+            (struct Message* msg, uint ## size ## _t dat, struct Except* eh)                      \
+    {                                                                                             \
+        Message_push ## size ## h(msg, Endian_hostToLittleEndian ## size (dat), eh);              \
+    }
+#define Message_popBE(size) \
+    static inline uint ## size ## _t Message_pop ## size (struct Message* msg, struct Except* eh) \
+    {                                                                                             \
+        return Endian_bigEndianToHost ## size (Message_pop ## size ## h(msg, eh));                \
     }
+#define Message_popLE(size) \
+    static inline uint ## size ## _t Message_pop ## size ## le \
+        (struct Message* msg, struct Except* eh) \
+    {                                                                                             \
+        return Endian_littleEndianToHost ## size (Message_pop ## size ## h(msg, eh));             \
+    }
+
+#define Message_pushPop(size) \
+    Message_pushH(size) Message_popH(size) \
+    Message_pushBE(size) Message_popBE(size) \
+    Message_pushLE(size) Message_popLE(size)
 
-Message_pushGeneric(8)
-Message_pushGeneric(16)
-Message_pushGeneric(32)
-Message_pushGeneric(64)
+Message_pushPop(8)
+Message_pushPop(16)
+Message_pushPop(32)
+Message_pushPop(64)
 
 #endif