Browse Source

Standardized the Rust Iface, Rust Allocator interop and Rust/C FFI

Caleb James DeLisle 3 years ago
parent
commit
a3b7f328d9

+ 213 - 0
Cargo.lock

@@ -35,6 +35,12 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "autocfg"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+
 [[package]]
 name = "bindgen"
 version = "0.55.1"
@@ -65,6 +71,25 @@ version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
 
+[[package]]
+name = "cbindgen"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1df6a11bba1d7cab86c166cecf4cf8acd7d02b7b65924d81b33d27197f22ee35"
+dependencies = [
+ "clap",
+ "heck",
+ "indexmap",
+ "log",
+ "proc-macro2",
+ "quote",
+ "serde",
+ "serde_json",
+ "syn",
+ "tempfile",
+ "toml",
+]
+
 [[package]]
 name = "cc"
 version = "1.0.60"
@@ -98,6 +123,7 @@ version = "0.1.0"
 dependencies = [
  "anyhow",
  "bindgen",
+ "cbindgen",
  "cc",
  "sodiumoxide",
 ]
@@ -148,12 +174,38 @@ dependencies = [
  "termcolor",
 ]
 
+[[package]]
+name = "getrandom"
+version = "0.1.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
+dependencies = [
+ "cfg-if 0.1.10",
+ "libc",
+ "wasi",
+]
+
 [[package]]
 name = "glob"
 version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
 
+[[package]]
+name = "hashbrown"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
+
+[[package]]
+name = "heck"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
+dependencies = [
+ "unicode-segmentation",
+]
+
 [[package]]
 name = "hermit-abi"
 version = "0.1.17"
@@ -172,6 +224,22 @@ dependencies = [
  "quick-error",
 ]
 
+[[package]]
+name = "indexmap"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "itoa"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
+
 [[package]]
 name = "lazy_static"
 version = "1.4.0"
@@ -262,6 +330,12 @@ version = "0.3.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33"
 
+[[package]]
+name = "ppv-lite86"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
+
 [[package]]
 name = "privatetopublic"
 version = "0.1.0"
@@ -300,6 +374,47 @@ dependencies = [
  "proc-macro2",
 ]
 
+[[package]]
+name = "rand"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
+dependencies = [
+ "getrandom",
+ "libc",
+ "rand_chacha",
+ "rand_core",
+ "rand_hc",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rand_hc"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
+dependencies = [
+ "rand_core",
+]
+
 [[package]]
 name = "randombytes"
 version = "0.1.0"
@@ -307,6 +422,12 @@ dependencies = [
  "cjdns_sys",
 ]
 
+[[package]]
+name = "redox_syscall"
+version = "0.1.57"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
+
 [[package]]
 name = "regex"
 version = "1.4.2"
@@ -325,12 +446,27 @@ version = "0.6.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
 
+[[package]]
+name = "remove_dir_all"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
+dependencies = [
+ "winapi",
+]
+
 [[package]]
 name = "rustc-hash"
 version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
 
+[[package]]
+name = "ryu"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
+
 [[package]]
 name = "same-file"
 version = "1.0.6"
@@ -340,6 +476,37 @@ dependencies = [
  "winapi-util",
 ]
 
+[[package]]
+name = "serde"
+version = "1.0.117"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.117"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.59"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
 [[package]]
 name = "shlex"
 version = "0.1.1"
@@ -368,6 +535,31 @@ dependencies = [
  "cjdns_sys",
 ]
 
+[[package]]
+name = "syn"
+version = "1.0.52"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c1e438504729046a5cfae47f97c30d6d083c7d91d94603efdae3477fc070d4c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-xid",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
+dependencies = [
+ "cfg-if 0.1.10",
+ "libc",
+ "rand",
+ "redox_syscall",
+ "remove_dir_all",
+ "winapi",
+]
+
 [[package]]
 name = "termcolor"
 version = "1.1.2"
@@ -402,6 +594,21 @@ dependencies = [
  "lazy_static",
 ]
 
+[[package]]
+name = "toml"
+version = "0.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75cf45bb0bef80604d001caaec0d09da99611b3c0fd39d3080468875cdb65645"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
+
 [[package]]
 name = "unicode-width"
 version = "0.1.8"
@@ -437,6 +644,12 @@ dependencies = [
  "winapi-util",
 ]
 
+[[package]]
+name = "wasi"
+version = "0.9.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
+
 [[package]]
 name = "which"
 version = "3.1.1"

+ 31 - 0
HACKING.md

@@ -10,6 +10,37 @@ Small patches are easy to include and large ones are hard to validate. Consider
 breaking your evil plans up into a bunch of nice little changes which are easy
 to understand and prove safe.
 
+## Rust interop
+Cjdns codebase contains both C and Rust code. If you are planning on implementing
+new functionality, it is better to do it in Rust because it simplifies code review
+and maintainability.
+
+### Dependencies - ask first
+If you're planning to add *any* new dependencies to cjdns, ask before you add them.
+Binary size is important, cjdns runs on some small devices and pull requests adding
+large and/or needless dependencies will be rejected. If you want to include a new
+dependency, have a very good reason and ask before you do the work.
+
+### FFI
+There are two kinds of foreign function interface in cjdns: cffi and rffi. Cffi is
+Rust code which is generated from C code and Rffi is C code generated from Rust.
+
+* `rust/cjdns_sys/src/cffi.rs` is generated from `rust/cjdns_sys/cffi.h`
+* `rust/cjdns_sys/Rffi.h` is generated from `rust/cjdns_sys/src/rffi.rs`
+
+Normally these files are checked in to git and do not change, but if you need to add
+a C function to be called from Rust, or a Rust function to be called from C, you need
+to edit the relevant file and then rebuild cjdns_sys with generate-cffi and/or
+generate-rffi feature.
+
+```
+cd rust/cjdns_sys
+cargo build --features generate-rffi
+cargo build --features generate-cffi
+```
+
+Running these will update the generated files.
+
 Minutiae:
 ---------
 

+ 4 - 4
admin/angel/Core.c

@@ -36,7 +36,6 @@
 #include "interface/tuntap/TUNInterface.h"
 #include "interface/tuntap/SocketInterface.h"
 #include "interface/tuntap/SocketWrapper.h"
-#include "interface/tuntap/AndroidWrapper.h"
 #include "interface/UDPInterface_admin.h"
 #ifdef HAS_ETH_INTERFACE
 #include "interface/ETHInterface_admin.h"
@@ -51,6 +50,7 @@
 #include "memory/Allocator_admin.h"
 #include "net/SwitchPinger_admin.h"
 #include "net/UpperDistributor_admin.h"
+#include "rust/cjdns_sys/Rffi.h"
 
 #define NumberCompress_OLD_CODE
 #include "switch/NumberCompress.h"
@@ -241,9 +241,9 @@ static void initTunfd(Dict* args, void* vcontext, String* txid, struct Allocator
     }
     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;
+        Rffi_IfWrapper_t aw = Rffi_android_create(tunAlloc);
+        Iface_plumb(aw.external, &p->iface);
+        iface = aw.internal;
     } else {
         iface = &p->iface;
     }

+ 10 - 8
interface/RustIface.h → interface/Iface.c

@@ -12,14 +12,16 @@
  * 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 RustIface_H
-#define RustIface_H
-
+#include "interface/Iface.h"
 #include "interface/Iface.h"
 #include "memory/Allocator.h"
-#include "util/Linker.h"
-Linker_require("interface/RustIface.c")
-
-struct Iface* RustIface_wrap(void* rustIf, struct Allocator* alloc);
+#include "util/Identity.h"
 
-#endif
+// This needs to be in a C file in order to be accessible from Rust
+Iface_DEFUN Iface_incomingFromRust(struct Message* message, struct Iface* thisInterface)
+{
+    if (!thisInterface->connectedIf) {
+        return Error(INTERNAL);
+    }
+    return Iface_send(thisInterface, message);
+}

+ 7 - 5
interface/Iface.h

@@ -20,9 +20,8 @@
 #include "wire/Error.h"
 #include "wire/Message.h"
 #include "util/Defined.h"
-
-// Needed to make sure the rustIface code is available to the linker
-#include "interface/RustIface.h"
+#include "util/Linker.h"
+Linker_require("interface/Iface.c")
 
 struct Iface;
 
@@ -34,7 +33,7 @@ typedef struct Error_s (* Iface_Callback)(struct Message* message, struct Iface*
 
 #define Iface_DEFUN __attribute__ ((warn_unused_result)) struct Error_s
 
-struct Iface
+typedef struct Iface
 {
     /** Send a message through this interface. */
     Iface_Callback send;
@@ -46,7 +45,10 @@ struct Iface
 
     /** Interface to which this one is connected (if connected) */
     struct Iface* connectedIf;
-};
+} Iface_t;
+
+// This needs to be in a C file in order to be accessible from Rust
+Iface_DEFUN Iface_incomingFromRust(struct Message* message, struct Iface* thisInterface);
 
 /**
  * Send a message to an Iface.

+ 0 - 55
interface/RustIface.c

@@ -1,55 +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 "interface/RustIface.h"
-#include "interface/Iface.h"
-#include "memory/Allocator.h"
-#include "util/Identity.h"
-
-// struct RustIface {
-//     struct Iface pub;
-//     void* rustIf;
-//     Identity
-// };
-
-// struct Error_s iface_send_from_c(struct Message* msg, void* iface);
-// void iface_wrap_rust_iface(void* rust_if, struct Iface* c_if);
-
-// static Iface_DEFUN send(struct Message* message, struct Iface* thisInterface)
-// {
-//     struct RustIface* iface = Identity_check((struct RustIface*) thisInterface);
-//     return iface_send_from_c(message, iface->rustIf);
-// }
-
-// static Iface_DEFUN recv(struct Message* message, struct Iface* thisInterface)
-// {
-//     return Error(NONE);
-// }
-
-Iface_DEFUN RustIface_incomingFromRust(struct Message* message, struct Iface* thisInterface)
-{
-    if (!thisInterface->connectedIf) {
-        return Error(INTERNAL);
-    }
-    return Iface_send(thisInterface, message);
-}
-
-// struct Iface* RustIface_wrap(void* rustIf, struct Allocator* alloc)
-// {
-//     struct RustIface* out = Allocator_calloc(alloc, sizeof(struct Iface), 1);
-//     Identity_set(out);
-//     out->rustIf = rustIf;
-//     return NULL;
-// }

+ 44 - 10
interface/test/RustIface_test.c

@@ -14,12 +14,13 @@
  */
 
 #include "memory/Allocator.h"
-#include "rust/cjdns_sys/bindings.c"
+#include "rust/cjdns_sys/Rffi.h"
 #include "interface/Iface.h"
 #include "memory/MallocAllocator.h"
 #include "util/Identity.h"
 #include "wire/Message.h"
 #include "util/Assert.h"
+#include "interface/test/RustIface_test.h"
 
 #include <stdio.h>
 
@@ -31,10 +32,30 @@ struct Context
     Identity
 };
 
-static Iface_DEFUN sendInside(struct Message* msg, struct Iface* inside)
+static int incomingCount = 0;
+static int outgoingCount = 0;
+static int dropped = 0;
+
+void RustIface_gotIncoming()
 {
-    printf("sendInside called\n");
-    struct Context* ctx = Identity_containerOf(inside, struct Context, inside);
+    incomingCount++;
+}
+
+void RustIface_gotOutgoing()
+{
+    outgoingCount++;
+}
+
+void RustIface_dropped()
+{
+    dropped++;
+}
+
+static Iface_DEFUN sendOutside(struct Message* msg, struct Iface* outside)
+{
+    printf("sendOutside called\n");
+    Assert_true(incomingCount == 1 && outgoingCount == 1 && dropped == 0);
+    struct Context* ctx = Identity_containerOf(outside, struct Context, outside);
     uint32_t top = Er_assert(Message_epop32be(msg));
     Assert_true(top == 0x00000800);
     Assert_true(!(ctx->received & 1));
@@ -42,15 +63,22 @@ static Iface_DEFUN sendInside(struct Message* msg, struct Iface* inside)
     return Error(NONE);
 }
 
-static Iface_DEFUN sendOutside(struct Message* msg, struct Iface* outside)
+static Iface_DEFUN sendInside(struct Message* msg, struct Iface* inside)
 {
-    printf("sendOutside called\n");
-    struct Context* ctx = Identity_containerOf(outside, struct Context, outside);
+    printf("sendInside called\n");
+    Assert_true(incomingCount == 1 && outgoingCount == 0 && dropped == 0);
+    struct Context* ctx = Identity_containerOf(inside, struct Context, inside);
     Assert_true(!(ctx->received & 1<<1));
     ctx->received |= 1<<1;
-    return Iface_next(outside, msg);
+    return Iface_next(inside, msg);
 }
 
+// Message goes:
+// 1. Rust incoming
+// 2. sendInside -> bounces the message back with Iface_next()
+// 3. Rust outgoing
+// 4. sendOutside -> eats the message with Error(NONE)
+
 int main()
 {
     struct Allocator* alloc = MallocAllocator_new(20000);
@@ -58,15 +86,21 @@ int main()
     Identity_set(ctx);
     ctx->outside.send = sendOutside;
     ctx->inside.send = sendInside;
-    struct CCountWrapper wrapper = countwrapper_create(alloc);
+    Rffi_IfWrapper_t wrapper = Rffi_testwrapper_create(alloc);
     Iface_plumb(&ctx->inside, wrapper.internal);
     Iface_plumb(&ctx->outside, wrapper.external);
 
+    Assert_true(incomingCount == 0 && outgoingCount == 0 && dropped == 0);
+
     struct Message* msg = Message_new(256, 256, alloc);
     Er_assert(Message_epush32be(msg, 0x00000800));
-    Iface_send(&ctx->inside, msg);
+    Iface_send(&ctx->outside, msg);
 
     Assert_true(ctx->received == 3);
+    Assert_true(incomingCount == 1 && outgoingCount == 1 && dropped == 0);
     Allocator_free(alloc);
+    // Expect RustIface_dropped() to be called 3 times because there are
+    // 3 different structures which are dropped
+    Assert_true(incomingCount == 1 && outgoingCount == 1 && dropped == 3);
     return 0;
 }

+ 6 - 10
rust/cjdns_sys/bindings.c → interface/test/RustIface_test.h

@@ -12,13 +12,9 @@
  * 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 "memory/Allocator.h"
-#include "interface/Iface.h"
-
-
-struct CCountWrapper {
-    struct Iface* internal;
-    struct Iface* external;
-};
-struct CCountWrapper countwrapper_create(struct Allocator* alloc);
+#ifndef RUSTIFACE_H
+#define RUSTIFACE_H
+void RustIface_gotIncoming(void);
+void RustIface_gotOutgoing(void);
+void RustIface_dropped(void);
+#endif

+ 2 - 2
memory/Allocator.h

@@ -98,7 +98,7 @@ struct Allocator_OnFreeJob
  *
  * The function pointers in the allocator structure are best called through the associated macros.
  */
-struct Allocator
+typedef struct Allocator
 {
     /** The name of the file where this allocator was created. */
     const char* fileName;
@@ -108,7 +108,7 @@ struct Allocator
 
     /** Non-zero if allocator is currently freeing. */
     int isFreeing;
-};
+} Allocator_t;
 
 struct Allocator_Allocation
 {

+ 2 - 2
node_build/make.js

@@ -426,12 +426,12 @@ Builder.configure({
 
     builder.buildLibrary('crypto/random/randombytes.c');
 
-    builder.buildLibrary('rust/cjdns_sys/bindings.c');
+    builder.buildLibrary('rust/cjdns_sys/cffi.h');
 
     builder.lintFiles(function (fileName, file, callback) {
         if (/dependencies/.test(fileName) ||
             /crypto\/sign/.test(fileName) ||
-            /bindings.c/.test(fileName)
+            /.ffi\.h/.test(fileName)
         ) {
             callback('', false);
             return;

+ 3 - 1
rust/cjdns_sys/Cargo.toml

@@ -12,7 +12,9 @@ anyhow = "1.0"
 [build_dependencies]
 cc = "1.0"
 anyhow = "1.0"
+cbindgen = { version = "0.15", optional = true }
 bindgen = { version = "0.55", optional = true }
 
 [features]
-generate-bindings = ["bindgen"]
+generate-rffi = ["cbindgen"]
+generate-cffi = ["bindgen"]

+ 21 - 0
rust/cjdns_sys/Rffi.h

@@ -0,0 +1,21 @@
+#ifndef rffi_H
+#define rffi_H
+
+// This file is generated from src/rffi.rs using cbindgen
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include "cffi.h"
+
+typedef struct {
+  Iface_t *internal;
+  Iface_t *external;
+} Rffi_IfWrapper_t;
+
+Rffi_IfWrapper_t Rffi_testwrapper_create(Allocator_t *a);
+
+Rffi_IfWrapper_t Rffi_android_create(Allocator_t *a);
+
+#endif /* rffi_H */

+ 0 - 547
rust/cjdns_sys/bindings.rs

@@ -1,547 +0,0 @@
-/* automatically generated by rust-bindgen 0.55.1 */
-extern "C" {
-    pub fn Assert_failure(format: *const ::std::os::raw::c_char, ...);
-}
-
-pub type Allocator_OnFreeCallback = ::std::option::Option<
-    unsafe extern "C" fn(job: *mut Allocator_OnFreeJob) -> ::std::os::raw::c_int,
->;
-#[repr(C)]
-#[derive(Debug, Copy, Clone)]
-pub struct Allocator_OnFreeJob {
-    pub callback: Allocator_OnFreeCallback,
-    pub userData: *mut ::std::os::raw::c_void,
-}
-#[test]
-fn bindgen_test_layout_Allocator_OnFreeJob() {
-    assert_eq!(
-        ::std::mem::size_of::<Allocator_OnFreeJob>(),
-        16usize,
-        concat!("Size of: ", stringify!(Allocator_OnFreeJob))
-    );
-    assert_eq!(
-        ::std::mem::align_of::<Allocator_OnFreeJob>(),
-        8usize,
-        concat!("Alignment of ", stringify!(Allocator_OnFreeJob))
-    );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<Allocator_OnFreeJob>())).callback as *const _ as usize },
-        0usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(Allocator_OnFreeJob),
-            "::",
-            stringify!(callback)
-        )
-    );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<Allocator_OnFreeJob>())).userData as *const _ as usize },
-        8usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(Allocator_OnFreeJob),
-            "::",
-            stringify!(userData)
-        )
-    );
-}
-#[repr(C)]
-#[derive(Debug, Copy, Clone)]
-pub struct Allocator {
-    pub fileName: *const ::std::os::raw::c_char,
-    pub lineNum: ::std::os::raw::c_int,
-    pub isFreeing: ::std::os::raw::c_int,
-}
-#[test]
-fn bindgen_test_layout_Allocator() {
-    assert_eq!(
-        ::std::mem::size_of::<Allocator>(),
-        16usize,
-        concat!("Size of: ", stringify!(Allocator))
-    );
-    assert_eq!(
-        ::std::mem::align_of::<Allocator>(),
-        8usize,
-        concat!("Alignment of ", stringify!(Allocator))
-    );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<Allocator>())).fileName as *const _ as usize },
-        0usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(Allocator),
-            "::",
-            stringify!(fileName)
-        )
-    );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<Allocator>())).lineNum as *const _ as usize },
-        8usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(Allocator),
-            "::",
-            stringify!(lineNum)
-        )
-    );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<Allocator>())).isFreeing as *const _ as usize },
-        12usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(Allocator),
-            "::",
-            stringify!(isFreeing)
-        )
-    );
-}
-#[repr(C)]
-#[derive(Debug, Copy, Clone)]
-pub struct Allocator_Allocation {
-    pub size: usize,
-}
-#[test]
-fn bindgen_test_layout_Allocator_Allocation() {
-    assert_eq!(
-        ::std::mem::size_of::<Allocator_Allocation>(),
-        8usize,
-        concat!("Size of: ", stringify!(Allocator_Allocation))
-    );
-    assert_eq!(
-        ::std::mem::align_of::<Allocator_Allocation>(),
-        8usize,
-        concat!("Alignment of ", stringify!(Allocator_Allocation))
-    );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<Allocator_Allocation>())).size as *const _ as usize },
-        0usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(Allocator_Allocation),
-            "::",
-            stringify!(size)
-        )
-    );
-}
-extern "C" {
-    pub fn Allocator_getChild(
-        alloc: *mut Allocator,
-        childNumber: ::std::os::raw::c_int,
-    ) -> *mut Allocator;
-}
-extern "C" {
-    pub fn Allocator_getAllocation(
-        alloc: *mut Allocator,
-        allocNum: ::std::os::raw::c_int,
-    ) -> *mut Allocator_Allocation;
-}
-extern "C" {
-    pub fn Allocator__malloc(
-        allocator: *mut Allocator,
-        length: ::std::os::raw::c_ulong,
-        fileName: *const ::std::os::raw::c_char,
-        lineNum: ::std::os::raw::c_int,
-    ) -> *mut ::std::os::raw::c_void;
-}
-extern "C" {
-    pub fn Allocator__calloc(
-        alloc: *mut Allocator,
-        length: ::std::os::raw::c_ulong,
-        count: ::std::os::raw::c_ulong,
-        fileName: *const ::std::os::raw::c_char,
-        lineNum: ::std::os::raw::c_int,
-    ) -> *mut ::std::os::raw::c_void;
-}
-extern "C" {
-    pub fn Allocator__realloc(
-        allocator: *mut Allocator,
-        original: *const ::std::os::raw::c_void,
-        size: ::std::os::raw::c_ulong,
-        fileName: *const ::std::os::raw::c_char,
-        lineNum: ::std::os::raw::c_int,
-    ) -> *mut ::std::os::raw::c_void;
-}
-extern "C" {
-    pub fn Allocator__clone(
-        allocator: *mut Allocator,
-        toClone: *const ::std::os::raw::c_void,
-        length: ::std::os::raw::c_ulong,
-        fileName: *const ::std::os::raw::c_char,
-        lineNum: ::std::os::raw::c_int,
-    ) -> *mut ::std::os::raw::c_void;
-}
-extern "C" {
-    pub fn Allocator__child(
-        alloc: *mut Allocator,
-        fileName: *const ::std::os::raw::c_char,
-        lineNum: ::std::os::raw::c_int,
-    ) -> *mut Allocator;
-}
-extern "C" {
-    pub fn Allocator__free(
-        alloc: *mut Allocator,
-        file: *const ::std::os::raw::c_char,
-        line: ::std::os::raw::c_int,
-    );
-}
-extern "C" {
-    pub fn Allocator__onFree(
-        alloc: *mut Allocator,
-        callback: Allocator_OnFreeCallback,
-        context: *mut ::std::os::raw::c_void,
-        file: *const ::std::os::raw::c_char,
-        line: ::std::os::raw::c_int,
-    ) -> *mut Allocator_OnFreeJob;
-}
-extern "C" {
-    pub fn Allocator_cancelOnFree(toRemove: *mut Allocator_OnFreeJob) -> ::std::os::raw::c_int;
-}
-extern "C" {
-    pub fn Allocator_onFreeComplete(onFreeJob: *mut Allocator_OnFreeJob);
-}
-extern "C" {
-    pub fn Allocator__adopt(
-        parentAlloc: *mut Allocator,
-        alloc: *mut Allocator,
-        fileName: *const ::std::os::raw::c_char,
-        lineNum: ::std::os::raw::c_int,
-    );
-}
-extern "C" {
-    pub fn Allocator__disown(
-        parentAlloc: *mut Allocator,
-        allocToDisown: *mut Allocator,
-        fileName: *const ::std::os::raw::c_char,
-        lineNum: ::std::os::raw::c_int,
-    );
-}
-extern "C" {
-    pub fn Allocator_setCanary(alloc: *mut Allocator, value: usize);
-}
-extern "C" {
-    pub fn Allocator_bytesAllocated(allocator: *mut Allocator) -> ::std::os::raw::c_ulong;
-}
-extern "C" {
-    pub fn Allocator_snapshot(alloc: *mut Allocator, includeAllocations: ::std::os::raw::c_int);
-}
-pub type Allocator_Provider = ::std::option::Option<
-    unsafe extern "C" fn(
-        ctx: *mut ::std::os::raw::c_void,
-        original: *mut Allocator_Allocation,
-        size: ::std::os::raw::c_ulong,
-        group: *mut Allocator,
-    ) -> *mut ::std::os::raw::c_void,
->;
-extern "C" {
-    pub fn Allocator_new(
-        sizeLimit: ::std::os::raw::c_ulong,
-        provider: Allocator_Provider,
-        providerContext: *mut ::std::os::raw::c_void,
-        fileName: *const ::std::os::raw::c_char,
-        lineNum: ::std::os::raw::c_int,
-    ) -> *mut Allocator;
-}
-pub const Error_e_Error_NONE: Error_e = 0;
-pub const Error_e_Error_MALFORMED_ADDRESS: Error_e = 1;
-pub const Error_e_Error_FLOOD: Error_e = 2;
-pub const Error_e_Error_LINK_LIMIT_EXCEEDED: Error_e = 3;
-pub const Error_e_Error_OVERSIZE_MESSAGE: Error_e = 4;
-pub const Error_e_Error_RUNT: Error_e = 5;
-pub const Error_e_Error_AUTHENTICATION: Error_e = 6;
-pub const Error_e_Error_INVALID: Error_e = 7;
-pub const Error_e_Error_UNDELIVERABLE: Error_e = 8;
-pub const Error_e_Error_LOOP_ROUTE: Error_e = 9;
-pub const Error_e_Error_RETURN_PATH_INVALID: Error_e = 10;
-pub const Error_e_Error_UNHANDLED: Error_e = 11;
-pub const Error_e_Error_OVERFLOW: Error_e = 12;
-pub const Error_e_Error_INTERNAL: Error_e = 13;
-pub type Error_e = ::std::os::raw::c_uint;
-#[repr(C)]
-#[derive(Debug, Copy, Clone)]
-pub struct Error_s {
-    pub e: Error_e,
-}
-#[test]
-fn bindgen_test_layout_Error_s() {
-    assert_eq!(
-        ::std::mem::size_of::<Error_s>(),
-        4usize,
-        concat!("Size of: ", stringify!(Error_s))
-    );
-    assert_eq!(
-        ::std::mem::align_of::<Error_s>(),
-        4usize,
-        concat!("Alignment of ", stringify!(Error_s))
-    );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<Error_s>())).e as *const _ as usize },
-        0usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(Error_s),
-            "::",
-            stringify!(e)
-        )
-    );
-}
-#[repr(C)]
-#[derive(Debug, Copy, Clone)]
-pub struct Er_Ret {
-    pub message: *const ::std::os::raw::c_char,
-}
-#[test]
-fn bindgen_test_layout_Er_Ret() {
-    assert_eq!(
-        ::std::mem::size_of::<Er_Ret>(),
-        8usize,
-        concat!("Size of: ", stringify!(Er_Ret))
-    );
-    assert_eq!(
-        ::std::mem::align_of::<Er_Ret>(),
-        8usize,
-        concat!("Alignment of ", stringify!(Er_Ret))
-    );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<Er_Ret>())).message as *const _ as usize },
-        0usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(Er_Ret),
-            "::",
-            stringify!(message)
-        )
-    );
-}
-extern "C" {
-    pub fn Er__raise(
-        file: *mut ::std::os::raw::c_char,
-        line: ::std::os::raw::c_int,
-        alloc: *mut Allocator,
-        format: *mut ::std::os::raw::c_char,
-        ...
-    ) -> *mut Er_Ret;
-}
-extern "C" {
-    pub fn Er__assertFail(er: *mut Er_Ret);
-}
-pub type size_t = ::std::os::raw::c_ulong;
-pub type wchar_t = ::std::os::raw::c_int;
-extern "C" {
-    pub fn Bits_log2x64_stupid(number: u64) -> ::std::os::raw::c_int;
-}
-extern "C" {
-    pub fn Bits_memmem(
-        haystack: *const ::std::os::raw::c_void,
-        haystackLen: size_t,
-        needle: *const ::std::os::raw::c_void,
-        needleLen: size_t,
-    ) -> *mut ::std::os::raw::c_void;
-}
-#[repr(C)]
-#[derive(Debug, Copy, Clone)]
-pub struct Message {
-    pub length: i32,
-    pub padding: i32,
-    pub bytes: *mut u8,
-    pub capacity: i32,
-    pub associatedFd: ::std::os::raw::c_int,
-    pub currentIface: *mut Iface,
-    pub alloc: *mut Allocator,
-}
-#[test]
-fn bindgen_test_layout_Message() {
-    assert_eq!(
-        ::std::mem::size_of::<Message>(),
-        40usize,
-        concat!("Size of: ", stringify!(Message))
-    );
-    assert_eq!(
-        ::std::mem::align_of::<Message>(),
-        8usize,
-        concat!("Alignment of ", stringify!(Message))
-    );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<Message>())).length as *const _ as usize },
-        0usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(Message),
-            "::",
-            stringify!(length)
-        )
-    );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<Message>())).padding as *const _ as usize },
-        4usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(Message),
-            "::",
-            stringify!(padding)
-        )
-    );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<Message>())).bytes as *const _ as usize },
-        8usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(Message),
-            "::",
-            stringify!(bytes)
-        )
-    );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<Message>())).capacity as *const _ as usize },
-        16usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(Message),
-            "::",
-            stringify!(capacity)
-        )
-    );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<Message>())).associatedFd as *const _ as usize },
-        20usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(Message),
-            "::",
-            stringify!(associatedFd)
-        )
-    );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<Message>())).currentIface as *const _ as usize },
-        24usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(Message),
-            "::",
-            stringify!(currentIface)
-        )
-    );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<Message>())).alloc as *const _ as usize },
-        32usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(Message),
-            "::",
-            stringify!(alloc)
-        )
-    );
-}
-extern "C" {
-    pub fn Message_new(
-        messageLength: u32,
-        amountOfPadding: u32,
-        alloc: *mut Allocator,
-    ) -> *mut Message;
-}
-extern "C" {
-    pub fn Message_setAssociatedFd(msg: *mut Message, fd: ::std::os::raw::c_int);
-}
-extern "C" {
-    pub fn Message_getAssociatedFd(msg: *mut Message) -> ::std::os::raw::c_int;
-}
-extern "C" {
-    pub fn Message_clone(toClone: *mut Message, alloc: *mut Allocator) -> *mut Message;
-}
-extern "C" {
-    pub fn Message_copyOver(output: *mut Message, input: *mut Message, allocator: *mut Allocator);
-}
-pub type Iface_Callback = ::std::option::Option<
-    unsafe extern "C" fn(message: *mut Message, thisInterface: *mut Iface) -> Error_s,
->;
-#[repr(C)]
-#[derive(Debug, Copy, Clone)]
-pub struct Iface {
-    pub send: Iface_Callback,
-    pub currentMsg: *mut Message,
-    pub connectedIf: *mut Iface,
-}
-#[test]
-fn bindgen_test_layout_Iface() {
-    assert_eq!(
-        ::std::mem::size_of::<Iface>(),
-        24usize,
-        concat!("Size of: ", stringify!(Iface))
-    );
-    assert_eq!(
-        ::std::mem::align_of::<Iface>(),
-        8usize,
-        concat!("Alignment of ", stringify!(Iface))
-    );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<Iface>())).send as *const _ as usize },
-        0usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(Iface),
-            "::",
-            stringify!(send)
-        )
-    );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<Iface>())).currentMsg as *const _ as usize },
-        8usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(Iface),
-            "::",
-            stringify!(currentMsg)
-        )
-    );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<Iface>())).connectedIf as *const _ as usize },
-        16usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(Iface),
-            "::",
-            stringify!(connectedIf)
-        )
-    );
-}
-#[repr(C)]
-#[derive(Debug, Copy, Clone)]
-pub struct CCountWrapper {
-    pub internal: *mut Iface,
-    pub external: *mut Iface,
-}
-#[test]
-fn bindgen_test_layout_CCountWrapper() {
-    assert_eq!(
-        ::std::mem::size_of::<CCountWrapper>(),
-        16usize,
-        concat!("Size of: ", stringify!(CCountWrapper))
-    );
-    assert_eq!(
-        ::std::mem::align_of::<CCountWrapper>(),
-        8usize,
-        concat!("Alignment of ", stringify!(CCountWrapper))
-    );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<CCountWrapper>())).internal as *const _ as usize },
-        0usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(CCountWrapper),
-            "::",
-            stringify!(internal)
-        )
-    );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<CCountWrapper>())).external as *const _ as usize },
-        8usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(CCountWrapper),
-            "::",
-            stringify!(external)
-        )
-    );
-}
-extern "C" {
-    pub fn countwrapper_create(alloc: *mut Allocator) -> CCountWrapper;
-}

+ 35 - 10
rust/cjdns_sys/build.rs

@@ -21,6 +21,26 @@ fn cfiles<P: AsRef<Path>>(out: &mut Vec<PathBuf>, path: P) -> Result<()> {
 }
 
 fn main() -> Result<()> {
+    // Generate C bindings from rust
+    #[cfg(feature = "generate-rffi")]
+    {
+        println!("Generating rffi");
+        let mut conf = cbindgen::Config::default();
+        conf.language = cbindgen::Language::C;
+        conf.autogen_warning =
+            Some("// This file is generated from src/rffi.rs using cbindgen".to_owned());
+        conf.style = cbindgen::Style::Type;
+        conf.include_guard = Some("rffi_H".to_owned());
+        conf.includes = vec!["cffi.h".to_owned()];
+        cbindgen::Builder::new()
+            .with_src("./src/rffi.rs")
+            .with_config(conf)
+            .generate()
+            .expect("Unable to generate rffi")
+            .write_to_file("Rffi.h");
+        println!("Generating rffi done");
+    }
+
     let ret = Command::new("/bin/sh")
         .current_dir("../../")
         .env("MAINJS", "./node_build/make.js")
@@ -79,21 +99,26 @@ fn main() -> Result<()> {
     }
     build.compile("cjdns_sys");
 
-    #[cfg(feature = "generate-bindings")]
+    // Generate rust bindings from C
+    #[cfg(feature = "generate-cffi")]
     {
         let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
         bindgen::Builder::default()
-            .header(
-                out_path
-                    .join("rust_cjdns_sys_bindings_c.i")
-                    .to_str()
-                    .unwrap(),
-            )
+            .header(out_path.join("rust_cjdns_sys_cffi_h.i").to_str().unwrap())
             .generate_comments(false)
+            .layout_tests(false)
+            .default_enum_style(bindgen::EnumVariation::Rust {
+                non_exhaustive: false,
+            })
+            .raw_line("#![allow(non_snake_case)]")
+            .raw_line("#![allow(dead_code)]")
+            .raw_line("#![allow(non_camel_case_types)]")
+            .whitelist_function(".*")
+            .whitelist_type("RBindings_Whitelist")
             .generate()
-            .expect("Unable to generate bindings")
-            .write_to_file(out_path.join("bindings.rs"))
-            .expect("Couldn't write bindings!");
+            .expect("Unable to generate rbindings")
+            .write_to_file("src/cffi.rs")
+            .expect("Couldn't write rbindings");
     }
     Ok(())
 }

+ 30 - 0
rust/cjdns_sys/cffi.h

@@ -0,0 +1,30 @@
+/* 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/>.
+ */
+
+// This file is used to generate src/cffi.rs using bindgen
+
+#include "memory/Allocator.h"
+#include "interface/Iface.h"
+#include "interface/test/RustIface_test.h"
+
+// This structure is guaranteed to be present in the generated rust code
+// Also all functions in the above headers will be present.
+// Any types which are not transitively included in either this structure
+// or in one of the functions will not be generated. This prevents generating
+// a bunch of platform-specific trash like uint_fast8_t etc.
+struct RBindings_Whitelist {
+    Allocator_t a;
+    Iface_t b;
+};

+ 260 - 0
rust/cjdns_sys/src/cffi.rs

@@ -0,0 +1,260 @@
+/* automatically generated by rust-bindgen 0.55.1 */
+
+#![allow(non_snake_case)]
+#![allow(dead_code)]
+#![allow(non_camel_case_types)]
+
+extern "C" {
+    pub fn Assert_failure(format: *const ::std::os::raw::c_char, ...);
+}
+pub type Allocator_OnFreeCallback = ::std::option::Option<
+    unsafe extern "C" fn(job: *mut Allocator_OnFreeJob) -> ::std::os::raw::c_int,
+>;
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct Allocator_OnFreeJob {
+    pub callback: Allocator_OnFreeCallback,
+    pub userData: *mut ::std::os::raw::c_void,
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct Allocator {
+    pub fileName: *const ::std::os::raw::c_char,
+    pub lineNum: ::std::os::raw::c_int,
+    pub isFreeing: ::std::os::raw::c_int,
+}
+pub type Allocator_t = Allocator;
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct Allocator_Allocation {
+    pub size: usize,
+}
+extern "C" {
+    pub fn Allocator_getChild(
+        alloc: *mut Allocator,
+        childNumber: ::std::os::raw::c_int,
+    ) -> *mut Allocator;
+}
+extern "C" {
+    pub fn Allocator_getAllocation(
+        alloc: *mut Allocator,
+        allocNum: ::std::os::raw::c_int,
+    ) -> *mut Allocator_Allocation;
+}
+extern "C" {
+    pub fn Allocator__malloc(
+        allocator: *mut Allocator,
+        length: ::std::os::raw::c_ulong,
+        fileName: *const ::std::os::raw::c_char,
+        lineNum: ::std::os::raw::c_int,
+    ) -> *mut ::std::os::raw::c_void;
+}
+extern "C" {
+    pub fn Allocator__calloc(
+        alloc: *mut Allocator,
+        length: ::std::os::raw::c_ulong,
+        count: ::std::os::raw::c_ulong,
+        fileName: *const ::std::os::raw::c_char,
+        lineNum: ::std::os::raw::c_int,
+    ) -> *mut ::std::os::raw::c_void;
+}
+extern "C" {
+    pub fn Allocator__realloc(
+        allocator: *mut Allocator,
+        original: *const ::std::os::raw::c_void,
+        size: ::std::os::raw::c_ulong,
+        fileName: *const ::std::os::raw::c_char,
+        lineNum: ::std::os::raw::c_int,
+    ) -> *mut ::std::os::raw::c_void;
+}
+extern "C" {
+    pub fn Allocator__clone(
+        allocator: *mut Allocator,
+        toClone: *const ::std::os::raw::c_void,
+        length: ::std::os::raw::c_ulong,
+        fileName: *const ::std::os::raw::c_char,
+        lineNum: ::std::os::raw::c_int,
+    ) -> *mut ::std::os::raw::c_void;
+}
+extern "C" {
+    pub fn Allocator__child(
+        alloc: *mut Allocator,
+        fileName: *const ::std::os::raw::c_char,
+        lineNum: ::std::os::raw::c_int,
+    ) -> *mut Allocator;
+}
+extern "C" {
+    pub fn Allocator__free(
+        alloc: *mut Allocator,
+        file: *const ::std::os::raw::c_char,
+        line: ::std::os::raw::c_int,
+    );
+}
+extern "C" {
+    pub fn Allocator__onFree(
+        alloc: *mut Allocator,
+        callback: Allocator_OnFreeCallback,
+        context: *mut ::std::os::raw::c_void,
+        file: *const ::std::os::raw::c_char,
+        line: ::std::os::raw::c_int,
+    ) -> *mut Allocator_OnFreeJob;
+}
+extern "C" {
+    pub fn Allocator_cancelOnFree(toRemove: *mut Allocator_OnFreeJob) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn Allocator_onFreeComplete(onFreeJob: *mut Allocator_OnFreeJob);
+}
+extern "C" {
+    pub fn Allocator__adopt(
+        parentAlloc: *mut Allocator,
+        alloc: *mut Allocator,
+        fileName: *const ::std::os::raw::c_char,
+        lineNum: ::std::os::raw::c_int,
+    );
+}
+extern "C" {
+    pub fn Allocator__disown(
+        parentAlloc: *mut Allocator,
+        allocToDisown: *mut Allocator,
+        fileName: *const ::std::os::raw::c_char,
+        lineNum: ::std::os::raw::c_int,
+    );
+}
+extern "C" {
+    pub fn Allocator_setCanary(alloc: *mut Allocator, value: usize);
+}
+extern "C" {
+    pub fn Allocator_bytesAllocated(allocator: *mut Allocator) -> ::std::os::raw::c_ulong;
+}
+extern "C" {
+    pub fn Allocator_snapshot(alloc: *mut Allocator, includeAllocations: ::std::os::raw::c_int);
+}
+pub type Allocator_Provider = ::std::option::Option<
+    unsafe extern "C" fn(
+        ctx: *mut ::std::os::raw::c_void,
+        original: *mut Allocator_Allocation,
+        size: ::std::os::raw::c_ulong,
+        group: *mut Allocator,
+    ) -> *mut ::std::os::raw::c_void,
+>;
+extern "C" {
+    pub fn Allocator_new(
+        sizeLimit: ::std::os::raw::c_ulong,
+        provider: Allocator_Provider,
+        providerContext: *mut ::std::os::raw::c_void,
+        fileName: *const ::std::os::raw::c_char,
+        lineNum: ::std::os::raw::c_int,
+    ) -> *mut Allocator;
+}
+#[repr(u32)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum Error_e {
+    Error_NONE = 0,
+    Error_MALFORMED_ADDRESS = 1,
+    Error_FLOOD = 2,
+    Error_LINK_LIMIT_EXCEEDED = 3,
+    Error_OVERSIZE_MESSAGE = 4,
+    Error_RUNT = 5,
+    Error_AUTHENTICATION = 6,
+    Error_INVALID = 7,
+    Error_UNDELIVERABLE = 8,
+    Error_LOOP_ROUTE = 9,
+    Error_RETURN_PATH_INVALID = 10,
+    Error_UNHANDLED = 11,
+    Error_OVERFLOW = 12,
+    Error_INTERNAL = 13,
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct Error_s {
+    pub e: Error_e,
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct Er_Ret {
+    pub message: *const ::std::os::raw::c_char,
+}
+extern "C" {
+    pub fn Er__raise(
+        file: *mut ::std::os::raw::c_char,
+        line: ::std::os::raw::c_int,
+        alloc: *mut Allocator,
+        format: *mut ::std::os::raw::c_char,
+        ...
+    ) -> *mut Er_Ret;
+}
+extern "C" {
+    pub fn Er__assertFail(er: *mut Er_Ret);
+}
+pub type size_t = ::std::os::raw::c_ulong;
+extern "C" {
+    pub fn Bits_log2x64_stupid(number: u64) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn Bits_memmem(
+        haystack: *const ::std::os::raw::c_void,
+        haystackLen: size_t,
+        needle: *const ::std::os::raw::c_void,
+        needleLen: size_t,
+    ) -> *mut ::std::os::raw::c_void;
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct Message {
+    pub length: i32,
+    pub padding: i32,
+    pub bytes: *mut u8,
+    pub capacity: i32,
+    pub associatedFd: ::std::os::raw::c_int,
+    pub currentIface: *mut Iface,
+    pub alloc: *mut Allocator,
+}
+extern "C" {
+    pub fn Message_new(
+        messageLength: u32,
+        amountOfPadding: u32,
+        alloc: *mut Allocator,
+    ) -> *mut Message;
+}
+extern "C" {
+    pub fn Message_setAssociatedFd(msg: *mut Message, fd: ::std::os::raw::c_int);
+}
+extern "C" {
+    pub fn Message_getAssociatedFd(msg: *mut Message) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    pub fn Message_clone(toClone: *mut Message, alloc: *mut Allocator) -> *mut Message;
+}
+extern "C" {
+    pub fn Message_copyOver(output: *mut Message, input: *mut Message, allocator: *mut Allocator);
+}
+pub type Iface_Callback = ::std::option::Option<
+    unsafe extern "C" fn(message: *mut Message, thisInterface: *mut Iface) -> Error_s,
+>;
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct Iface {
+    pub send: Iface_Callback,
+    pub currentMsg: *mut Message,
+    pub connectedIf: *mut Iface,
+}
+pub type Iface_t = Iface;
+extern "C" {
+    pub fn Iface_incomingFromRust(message: *mut Message, thisInterface: *mut Iface) -> Error_s;
+}
+extern "C" {
+    pub fn RustIface_gotIncoming();
+}
+extern "C" {
+    pub fn RustIface_gotOutgoing();
+}
+extern "C" {
+    pub fn RustIface_dropped();
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct RBindings_Whitelist {
+    pub a: Allocator_t,
+    pub b: Iface_t,
+}

+ 87 - 0
rust/cjdns_sys/src/external/interface/cif.rs

@@ -0,0 +1,87 @@
+use crate::cffi::{self, Allocator, Error_e, Error_s, Message};
+use crate::external::interface::iface::{self, IfRecv, Iface, IfacePvt};
+use crate::external::memory::allocator;
+use anyhow::{bail, Result};
+
+// TODO: this is not even the tiniest big thread safe
+struct CRecv {
+    c_iface: *mut cffi::Iface,
+}
+impl IfRecv for CRecv {
+    fn recv(&self, m: &mut Message) -> Result<()> {
+        let ers = unsafe { cffi::Iface_incomingFromRust(m as *mut Message, self.c_iface) };
+        match ers.e {
+            Error_e::Error_NONE => Ok(()),
+            _ => {
+                bail!("Error from C code {}", ers.e as usize);
+            }
+        }
+    }
+}
+
+#[repr(C)]
+struct CIface {
+    cif: cffi::Iface,
+
+    // Additional "private" fields
+    id_tag: u32,
+    rif: IfacePvt,
+}
+impl Drop for CIface {
+    fn drop(&mut self) {
+        println!("CIface dropped");
+    }
+}
+
+// This is an assertion to make sure we're being passed something legit from C
+const IFACE_IDENT: u32 = 0xdeadbeef;
+
+unsafe extern "C" fn from_c(msg: *mut cffi::Message, iface_p: *mut cffi::Iface) -> Error_s {
+    let iface = (iface_p as *mut CIface).as_ref().unwrap();
+
+    // We're getting called from C with a ffi::Iface which is supposed to be one of ours
+    // i.e. a CIface, but at the end of the day, we're doing a dirty cast here so lets
+    // check that this field id_tag is equal to the value we set it to initially.
+    assert!(iface.id_tag == IFACE_IDENT);
+
+    match iface.rif.send(msg.as_mut().unwrap()) {
+        // TODO: we need better error handling
+        Ok(_) => Error_s {
+            e: Error_e::Error_NONE,
+        },
+        Err(_) => Error_s {
+            e: Error_e::Error_INTERNAL,
+        },
+    }
+}
+
+/// Create a new Iface / cffi::Iface pair which are plumbed together
+/// After calling this, you will be able to plumb your iface of choice to the Iface
+/// and then pass the cffi::Iface to C code to be plumbed to another C Iface and
+/// messages passed to the C Iface will come out in your Iface
+pub fn new<T: Into<String>>(alloc: *mut Allocator, name: T) -> (Iface, *mut cffi::Iface) {
+    //pub fn new(alloc: *mut Allocator) -> (, Iface) {
+    let (mut iface, iface_pvt) = iface::new(name);
+    let out = allocator::adopt(
+        alloc,
+        CIface {
+            cif: cffi::Iface {
+                send: Some(from_c),
+                currentMsg: 0 as *mut cffi::Message,
+                connectedIf: 0 as *mut cffi::Iface,
+            },
+            id_tag: IFACE_IDENT,
+            rif: iface_pvt,
+        },
+    );
+    let c_iface = (&mut out.cif) as *mut cffi::Iface;
+    iface.set_receiver(CRecv { c_iface });
+    (iface, c_iface)
+}
+
+/// Helper function which just creates a C-iface to wrap an Iface
+pub fn wrap(alloc: *mut Allocator, mut iface: Iface) -> *mut cffi::Iface {
+    let (mut ext, cext) = new(alloc, format!("cif::wrap({})", &iface.name));
+    iface.plumb(&mut ext).unwrap();
+    cext
+}

+ 169 - 115
rust/cjdns_sys/src/external/interface/iface.rs

@@ -1,17 +1,18 @@
 //! Network interface from C part of the project.
 
+use crate::cffi::Message;
 use anyhow::{bail, Result};
-use std::ffi::c_void;
-use std::sync::Arc;
 use std::sync::RwLock;
+use std::sync::{Arc, Weak};
+use std::time::{SystemTime, UNIX_EPOCH};
 
-use crate::external::memory::allocator;
-use crate::ffi::{Allocator, Error_e_Error_INTERNAL, Error_e_Error_NONE, Error_s, Message};
-
+/// This is the trait which you need to implement in order to implement
+/// a cjdns Iface.
 pub trait IfRecv {
     fn recv(&self, m: &mut Message) -> Result<()>;
 }
 
+// Receiver which just always causes an error, default if none other is registered
 struct DefaultRecv();
 impl IfRecv for DefaultRecv {
     fn recv(&self, _: &mut Message) -> Result<()> {
@@ -19,139 +20,192 @@ impl IfRecv for DefaultRecv {
     }
 }
 
-#[derive(Clone)]
-pub struct IfaceRecv {
+/// This is the private (internal) half of a cjdns Iface.
+/// When you create a new iface, this half is "yours" and you use it
+/// to send messages through your interface. The other half is given
+/// to the function that called your module so that it can "plumb" your
+/// module to another module to connect them together.
+pub struct IfacePvt {
+    // Name of the Iface
     name: String,
-    r: Arc<RwLock<Box<dyn IfRecv>>>,
+
+    // Receiver of iface we are plumbed to, when we send a message, it goes here
+    peer_recv: Arc<RwLock<Option<Box<dyn IfRecv>>>>,
+}
+impl IfacePvt {
+    /// This method is typically called from inside of a IfRecv::recv()
+    /// method, it allows you to pass a message on to whichever iface might
+    /// be plumbed to yours.
+    pub fn send(&self, m: &mut Message) -> Result<()> {
+        match &*self.peer_recv.read().unwrap() {
+            Some(s) => s.recv(m),
+            None => bail!("No connected iface for {}", self.name),
+        }
+    }
 }
 
-#[derive(Clone)]
-pub struct Iface {
-    ir: IfaceRecv,
-    s: Arc<RwLock<Option<IfaceRecv>>>,
+fn kinda_random() -> u64 {
+    let t = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
+    t.as_secs() + t.subsec_nanos() as u64
 }
 
+/// Create a new cjdns Iface
+/// An Iface comprises two parts, the Iface and the IfacePvt
+/// The Iface is the "public" component which allows different
+/// modules to be inter-connected to one another. The IfacePvt allows
+/// a module to send messages to the Iface with-which it is plumbed.
+///
+/// After creating a new Iface, you will want to store the IfacePvt
+/// inside of your structure (the one which implements IfRecv trait)
+/// so that you can send messages, then after that, you can register
+/// tour IfRecv trait with the Iface.
+pub fn new<T: Into<String>>(name: T) -> (Iface, IfacePvt) {
+    let a = Arc::new(RwLock::new(None));
+    let n: String = name.into();
+    (
+        Iface {
+            name: n.clone(),
+            id: kinda_random(),
+            peer_id: 0,
+            peer_recv: Arc::downgrade(&a),
+            our_recv: None,
+        },
+        IfacePvt {
+            name: n,
+            peer_recv: a,
+        },
+    )
+}
+
+/// This is the public-facing part of an Iface, it is able to be plumbed
+/// to another iface.
+pub struct Iface {
+    /// Name of the iface
+    pub name: String,
+
+    /// Unique id of this interface, used to prevent the wrong
+    /// iface being unplumbed
+    id: u64,
+
+    /// Id of the iface we are plumbed to, if we are
+    peer_id: u64,
+
+    /// Our receiver, which is placed with the Ext so that it can be taken by the peer
+    /// This is None if we're plumbed
+    our_recv: Option<Box<dyn IfRecv>>,
+
+    /// Receiver of iface we are plumbed to, None unless we are plumbed
+    peer_recv: Weak<RwLock<Option<Box<dyn IfRecv>>>>,
+}
 impl Iface {
+    /// Get the name of the Iface
     pub fn name(&self) -> &str {
-        &self.ir.name
-    }
-    pub fn new<T: Into<String>>(name: T) -> Iface {
-        Iface {
-            ir: IfaceRecv {
-                name: name.into(),
-                r: Arc::new(RwLock::new(Box::new(DefaultRecv {}))),
-            },
-            s: Arc::new(RwLock::new(None)),
-        }
-    }
-    pub fn send(&self, m: &mut Message) -> Result<()> {
-        let s_l = self.s.read().unwrap();
-        match &*s_l {
-            Some(s) => s.r.read().unwrap().recv(m),
-            None => bail!("No connected iface for {}", self.ir.name),
-        }
+        &self.name
     }
-    pub fn set_receiver<T: 'static + IfRecv>(&self, ir: T) {
-        let mut irr_l = self.ir.r.write().unwrap();
-        *irr_l = Box::new(ir);
+
+    /// Set the IfRecv of this iface. This will typically be called by the module which
+    /// creates the Iface before returning the Iface to it's caller.
+    pub fn set_receiver<T: 'static + IfRecv>(&mut self, ir: T) {
+        self.our_recv = Some(Box::new(ir));
     }
-    pub fn plumb(&self, other: &Iface) -> Result<()> {
-        let mut s_l = self.s.write().unwrap();
-        if s_l.is_some() {
+
+    fn get_peer_recv(&self, oname: &str) -> Result<Arc<RwLock<Option<Box<dyn IfRecv>>>>> {
+        if let Some(o) = self.peer_recv.upgrade() {
+            Ok(o)
+        } else {
             bail!(
-                "Plumbing: {} to {}, {} is already plumbed",
-                self.ir.name,
-                other.ir.name,
-                self.ir.name
+                "Plumbing: {} to {}, {} already dropped",
+                self.name,
+                oname,
+                self.name
             );
         }
-        let mut other_l = other.s.write().unwrap();
-        if other_l.is_some() {
+    }
+    fn check_our_recv_some(&self, oname: &str) -> Result<()> {
+        if self.our_recv.is_some() {
+            Ok(())
+        } else {
             bail!(
-                "Plumbing: {} to {}, {} is already plumbed",
-                self.ir.name,
-                other.ir.name,
-                other.ir.name
+                "Plumbing: {} to {}, {} already plumbed",
+                self.name,
+                oname,
+                self.name
             );
         }
-        s_l.replace(other.ir.clone());
-        other_l.replace(self.ir.clone());
+    }
+
+    /// Connect two Ifaces together, this will typically be done by
+    /// Code which is interconnecting different modules with one another.
+    pub fn plumb(&mut self, other: &mut Iface) -> Result<()> {
+        let spr = self.get_peer_recv(&other.name)?;
+        let opr = other.get_peer_recv(&self.name)?;
+
+        let mut spr_l = spr.write().unwrap();
+        let mut opr_l = opr.write().unwrap();
+
+        self.check_our_recv_some(&other.name)?;
+        other.check_our_recv_some(&self.name)?;
+
+        assert!(spr_l.replace(other.our_recv.take().unwrap()).is_none());
+        assert!(opr_l.replace(self.our_recv.take().unwrap()).is_none());
+        assert!(self.peer_id == 0);
+        assert!(other.peer_id == 0);
+        self.peer_id = other.id;
+        other.peer_id = self.id;
         Ok(())
     }
-    pub fn cif(self, alloc: *mut Allocator) -> *mut crate::ffi::Iface {
-        let out = allocator::adopt(
-            alloc,
-            CIface {
-                cif: crate::ffi::Iface {
-                    send: Some(from_c),
-                    currentMsg: 0 as *mut crate::ffi::Message,
-                    connectedIf: 0 as *mut crate::ffi::Iface,
-                },
-                id_tag: IFACE_IDENT,
-                rif: self,
-            },
-        );
-        {
-            let mut s_l = out.rif.s.write().unwrap();
-            s_l.replace(IfaceRecv {
-                name: format!("{} recv from C", out.rif.ir.name),
-                r: Arc::new(RwLock::new(Box::new(CRecv {
-                    c_iface: out as *const CIface,
-                }))),
-            });
-        }
-        (&mut out.cif) as *mut crate::ffi::Iface
+
+    /// Tell whether the Iface is currently plumbed to another iface or not.
+    pub fn is_plumbed(&self) -> bool {
+        // When we're plumbed, our our_recv is taken away and given to our peer
+        self.our_recv.is_none()
     }
-}
 
-extern "C" {
-    pub fn RustIface_incomingFromRust(msg: *mut Message, iface: *mut c_void) -> Error_s;
-}
-struct CRecv {
-    c_iface: *const CIface,
-}
-impl IfRecv for CRecv {
-    fn recv(&self, m: &mut Message) -> Result<()> {
-        let ers = unsafe {
-            let ifr = self.c_iface.as_ref().unwrap();
-            let cfr_p = &ifr.cif as *const crate::ffi::Iface;
-            RustIface_incomingFromRust(m as *mut Message, cfr_p as *mut c_void)
-        };
-        match ers.e {
-            crate::ffi::Error_e_Error_NONE => Ok(()),
-            _ => {
-                bail!("Error from C code {}", ers.e as usize);
-            }
+    fn check_our_recv_none(&self, oname: &str) -> Result<()> {
+        if self.our_recv.is_none() {
+            Ok(())
+        } else {
+            bail!(
+                "Unplumbing: {} from {}, {} already plumbed",
+                self.name,
+                oname,
+                self.name
+            );
         }
     }
-}
 
-#[repr(C)]
-pub struct CIface {
-    cif: crate::ffi::Iface,
+    /// Disconnect two ifaces, the two ifaces which are being disconnected
+    /// must have been plumbed to eachother, otherwise this method returns
+    /// an error.
+    pub fn unplumb(&mut self, other: &mut Iface) -> Result<()> {
+        let spr = self.get_peer_recv(&other.name)?;
+        let opr = other.get_peer_recv(&self.name)?;
 
-    // Additional "private" fields
-    id_tag: u32,
-    rif: Iface,
-}
+        let mut spr_l = spr.write().unwrap();
+        let mut opr_l = opr.write().unwrap();
 
-// This is an assertion to make sure we're being passed something legit from C
-const IFACE_IDENT: u32 = 0xdeadbeef;
-
-pub unsafe extern "C" fn from_c(
-    msg: *mut crate::ffi::Message,
-    iface_p: *mut crate::ffi::Iface,
-) -> Error_s {
-    let iface = (iface_p as *mut CIface).as_ref().unwrap();
-    assert!(iface.id_tag == IFACE_IDENT);
-    let recv = iface.rif.ir.r.read().unwrap();
-    match recv.recv(msg.as_mut().unwrap()) {
-        // TODO: we need better error handling
-        Ok(_) => Error_s {
-            e: Error_e_Error_NONE,
-        },
-        Err(_) => Error_s {
-            e: Error_e_Error_INTERNAL,
-        },
+        self.check_our_recv_none(&other.name)?;
+        other.check_our_recv_none(&self.name)?;
+
+        if self.id != other.peer_id || other.id != self.peer_id {
+            bail!(
+                concat!(
+                    "Unplumbing: {} from {}, id mismatch:",
+                    "self: {} other: {}, self.peer_id: {} other.peer_id: {}"
+                ),
+                &self.name,
+                &other.name,
+                self.id,
+                other.id,
+                self.peer_id,
+                other.peer_id
+            );
+        }
+
+        assert!(other.our_recv.replace(spr_l.take().unwrap()).is_none());
+        assert!(self.our_recv.replace(opr_l.take().unwrap()).is_none());
+        self.peer_id = 0;
+        other.peer_id = 0;
+        Ok(())
     }
 }

+ 30 - 6
rust/cjdns_sys/src/external/memory/allocator.rs

@@ -1,10 +1,34 @@
 //! Memory allocator from C part of the project.
 
-/// External opaque C allocator type. Should be used in a form of a pointer only.
-use crate::ffi::Allocator;
+use crate::cffi::{Allocator, Allocator_OnFreeJob, Allocator__onFree};
+use std::any::Any;
+use std::os::raw::{c_char, c_int, c_void};
 
-/// TODO: This needs to hook Allocator_onFree() in order to
-/// un-leak the memory when the allocator is freed up
-pub fn adopt<T: 'static>(_alloc: *mut Allocator, t: T) -> &'static mut T {
-    Box::leak(Box::new(t))
+unsafe extern "C" fn drop_on_free(job_p: *mut Allocator_OnFreeJob) -> c_int {
+    let job = job_p.as_ref().unwrap();
+    Box::from_raw(job.userData as *mut Box<dyn std::any::Any>);
+    0 as c_int
+}
+
+/// "Adopt" a structure in Rust and transfer logical ownership to an Allocator.
+/// The structure that is adopted is placed in a box which is then leaked
+/// so that rust will nolonger care about the memory. But Allocator__onFree()
+/// is called to setup a callback when the allocator is freed.
+/// When the onFree callback is called, this code re-constructs the leaked box
+/// and then drops it, causing Rust to properly drop all of the memory which
+/// is related to the adopted structure, including any Arc, String, Vec or other
+/// heap objects which are owned by it.
+pub fn adopt<T: 'static>(alloc: *mut Allocator, t: T) -> &'static mut T {
+    let bat: Box<dyn Any> = Box::new(t);
+    let out: *mut Box<dyn Any> = Box::into_raw(Box::new(bat));
+    unsafe {
+        Allocator__onFree(
+            alloc,
+            Some(drop_on_free),
+            out as *mut c_void,
+            b"<rust>\0".as_ptr() as *const c_char,
+            0 as c_int,
+        );
+        out.as_mut().unwrap().downcast_mut().unwrap()
+    }
 }

+ 0 - 10
rust/cjdns_sys/src/ffi.rs

@@ -1,10 +0,0 @@
-#![allow(non_upper_case_globals)]
-#![allow(non_snake_case)]
-#![allow(non_camel_case_types)]
-#![allow(dead_code)]
-
-#[cfg(feature = "generate-bindings")]
-include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
-
-#[cfg(not(feature = "generate-bindings"))]
-include!("../bindings.rs");

+ 0 - 86
rust/cjdns_sys/src/interface/countwrapper.rs

@@ -1,86 +0,0 @@
-//! Android wrapper
-//!
-//! Android VpnService is expect you to read/write packet from the tun device
-//! file description opened by system process rather than in the cjd process,
-//! this InterfaceWrapper handle this case.
-
-use crate::external::interface::iface::*;
-use crate::ffi::{Allocator, CCountWrapper, Message};
-
-use anyhow::Result;
-use std::sync::Arc;
-use std::sync::Mutex;
-
-#[derive(Default)]
-struct CountWrapperPvt {
-    incoming_count: usize,
-    outgoing_count: usize,
-}
-
-pub struct CountWrapperInt {
-    pvt: Arc<Mutex<CountWrapperPvt>>,
-    ext: Iface,
-}
-impl IfRecv for CountWrapperInt {
-    fn recv(&self, m: &mut Message) -> Result<()> {
-        {
-            let mut pvt_l = self.pvt.lock().unwrap();
-            pvt_l.outgoing_count += 1;
-            println!(
-                "Received outgoing message, total out {} in {}",
-                pvt_l.outgoing_count, pvt_l.incoming_count
-            );
-        }
-        self.ext.send(m)
-    }
-}
-
-pub struct CountWrapperExt {
-    pvt: Arc<Mutex<CountWrapperPvt>>,
-    int: Iface,
-}
-impl IfRecv for CountWrapperExt {
-    fn recv(&self, m: &mut Message) -> Result<()> {
-        {
-            let mut pvt_l = self.pvt.lock().unwrap();
-            pvt_l.incoming_count += 1;
-            println!(
-                "Received incoming message, total out {} in {}",
-                pvt_l.outgoing_count, pvt_l.incoming_count
-            );
-        }
-        self.int.send(m)
-    }
-}
-
-pub struct CountWrapper {
-    pub int: Iface,
-    pub ext: Iface,
-}
-
-impl Default for CountWrapper {
-    fn default() -> CountWrapper {
-        let pvt = Arc::new(Mutex::new(CountWrapperPvt::default()));
-        let int = Iface::new("CountWrapper int");
-        let ext = Iface::new("CountWrapper ext");
-
-        int.set_receiver(CountWrapperInt {
-            pvt: pvt.clone(),
-            ext: ext.clone(),
-        });
-        ext.set_receiver(CountWrapperExt {
-            pvt,
-            int: int.clone(),
-        });
-        CountWrapper { int, ext }
-    }
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn countwrapper_create(a: *mut Allocator) -> CCountWrapper {
-    let cw = CountWrapper::default();
-    CCountWrapper {
-        external: cw.ext.cif(a),
-        internal: cw.int.cif(a),
-    }
-}

+ 89 - 0
rust/cjdns_sys/src/interface/rustiface_test_wrapper.rs

@@ -0,0 +1,89 @@
+//! Test Wrapper
+//!
+//! This is used with RustIface_test.c to verify that the Rust/C Iface bridge works as expected.
+
+use crate::cffi::{self, Message};
+use crate::external::interface::iface::{self, IfRecv, Iface, IfacePvt};
+use anyhow::Result;
+use std::sync::Arc;
+use std::sync::Mutex;
+
+#[derive(Default)]
+struct TestWrapperPvt {
+    incoming_count: usize,
+    outgoing_count: usize,
+}
+impl Drop for TestWrapperPvt {
+    fn drop(&mut self) {
+        unsafe { cffi::RustIface_dropped() };
+        println!("TestWrapperPvt dropped");
+    }
+}
+
+pub struct TestWrapperInt {
+    pvt: Arc<Mutex<TestWrapperPvt>>,
+    ext_pvt: IfacePvt,
+}
+impl IfRecv for TestWrapperInt {
+    fn recv(&self, m: &mut Message) -> Result<()> {
+        {
+            let mut pvt_l = self.pvt.lock().unwrap();
+            pvt_l.outgoing_count += 1;
+            unsafe { cffi::RustIface_gotOutgoing() };
+            println!(
+                "Received outgoing message, total out {} in {}",
+                pvt_l.outgoing_count, pvt_l.incoming_count
+            );
+        }
+        self.ext_pvt.send(m)
+    }
+}
+impl Drop for TestWrapperInt {
+    fn drop(&mut self) {
+        unsafe { cffi::RustIface_dropped() };
+        println!("TestWrapperInt dropped");
+    }
+}
+
+pub struct TestWrapperExt {
+    pvt: Arc<Mutex<TestWrapperPvt>>,
+    int_pvt: IfacePvt,
+}
+impl IfRecv for TestWrapperExt {
+    fn recv(&self, m: &mut Message) -> Result<()> {
+        {
+            let mut pvt_l = self.pvt.lock().unwrap();
+            pvt_l.incoming_count += 1;
+            unsafe { cffi::RustIface_gotIncoming() };
+            println!(
+                "Received incoming message, total out {} in {}",
+                pvt_l.outgoing_count, pvt_l.incoming_count
+            );
+        }
+        self.int_pvt.send(m)
+    }
+}
+impl Drop for TestWrapperExt {
+    fn drop(&mut self) {
+        unsafe { cffi::RustIface_dropped() };
+        println!("TestWrapperExt dropped");
+    }
+}
+
+pub struct TestWrapper {
+    pub int: Iface,
+    pub ext: Iface,
+}
+impl Default for TestWrapper {
+    fn default() -> TestWrapper {
+        let pvt = Arc::new(Mutex::new(TestWrapperPvt::default()));
+        let (mut int, int_pvt) = iface::new("TestWrapper int");
+        let (mut ext, ext_pvt) = iface::new("TestWrapper ext");
+        int.set_receiver(TestWrapperInt {
+            pvt: pvt.clone(),
+            ext_pvt,
+        });
+        ext.set_receiver(TestWrapperExt { pvt, int_pvt });
+        TestWrapper { int, ext }
+    }
+}

+ 10 - 31
rust/cjdns_sys/src/interface/tuntap/android.rs

@@ -4,41 +4,28 @@
 //! file description opened by system process rather than in the cjd process,
 //! this InterfaceWrapper handle this case.
 
-use crate::external::interface::iface::*;
-use crate::ffi::Message;
-
+use crate::cffi::Message;
+use crate::external::interface::iface::{self, IfRecv, Iface, IfacePvt};
 use anyhow::Result;
-use std::sync::Arc;
-use std::sync::Mutex;
-
-#[derive(Default)]
-struct AndroidWrapperPvt {
-    incoming_count: usize,
-    outgoing_count: usize,
-}
 
 pub struct AndroidWrapperInt {
-    pvt: Arc<Mutex<AndroidWrapperPvt>>,
-    ext: Iface,
+    ext_pvt: IfacePvt,
 }
 impl IfRecv for AndroidWrapperInt {
     fn recv(&self, m: &mut Message) -> Result<()> {
-        self.pvt.lock().unwrap().outgoing_count += 1;
         // TODO: We need to pop bytes off of the message to make it Android compatible
-        self.ext.send(m)
+        self.ext_pvt.send(m)
     }
 }
 
 pub struct AndroidWrapperExt {
-    pvt: Arc<Mutex<AndroidWrapperPvt>>,
-    int: Iface,
+    int_pvt: IfacePvt,
 }
 impl IfRecv for AndroidWrapperExt {
     fn recv(&self, m: &mut Message) -> Result<()> {
-        self.pvt.lock().unwrap().incoming_count += 1;
         // TODO: We need to push bytes to the message
         //       because cjdns expects an ethertype at the beginning
-        self.int.send(m)
+        self.int_pvt.send(m)
     }
 }
 
@@ -49,18 +36,10 @@ pub struct AndroidWrapper {
 
 impl Default for AndroidWrapper {
     fn default() -> AndroidWrapper {
-        let pvt = Arc::new(Mutex::new(AndroidWrapperPvt::default()));
-        let int = Iface::new("AndroidWrapper int");
-        let ext = Iface::new("AndroidWrapper ext");
-
-        int.set_receiver(AndroidWrapperInt {
-            pvt: pvt.clone(),
-            ext: ext.clone(),
-        });
-        ext.set_receiver(AndroidWrapperExt {
-            pvt,
-            int: int.clone(),
-        });
+        let (mut int, int_pvt) = iface::new("AndroidWrapper int");
+        let (mut ext, ext_pvt) = iface::new("AndroidWrapper ext");
+        int.set_receiver(AndroidWrapperInt { ext_pvt });
+        ext.set_receiver(AndroidWrapperExt { int_pvt });
         AndroidWrapper { int, ext }
     }
 }

+ 4 - 2
rust/cjdns_sys/src/lib.rs

@@ -26,6 +26,7 @@ macro_rules! c_main {
 /// All the C implementations are gathered under this `external` module.
 pub mod external {
     pub mod interface {
+        pub mod cif;
         pub mod iface;
     }
     pub mod memory {
@@ -37,7 +38,8 @@ mod interface {
     pub mod tuntap {
         pub mod android;
     }
-    pub mod countwrapper;
+    pub mod rustiface_test_wrapper;
 }
 
-mod ffi;
+mod cffi;
+mod rffi;

+ 29 - 0
rust/cjdns_sys/src/rffi.rs

@@ -0,0 +1,29 @@
+use crate::cffi::{Allocator_t, Iface_t};
+use crate::external::interface::cif;
+
+// This file is used to generate cbindings.h using cbindgen
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct Rffi_IfWrapper_t {
+    pub internal: *mut Iface_t,
+    pub external: *mut Iface_t,
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Rffi_testwrapper_create(a: *mut Allocator_t) -> Rffi_IfWrapper_t {
+    let w = crate::interface::rustiface_test_wrapper::TestWrapper::default();
+    Rffi_IfWrapper_t {
+        external: cif::wrap(a, w.ext),
+        internal: cif::wrap(a, w.int),
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Rffi_android_create(a: *mut Allocator_t) -> Rffi_IfWrapper_t {
+    let w = crate::interface::tuntap::android::AndroidWrapper::default();
+    Rffi_IfWrapper_t {
+        external: cif::wrap(a, w.ext),
+        internal: cif::wrap(a, w.int),
+    }
+}