From 513a4b3cecefe4586dc2855f9da7d0fe086130dd Mon Sep 17 00:00:00 2001 From: Caleb James DeLisle Date: Thu, 19 Sep 2024 00:01:11 +0000 Subject: [PATCH] Unified Errors: Move JSON-benc to Rust, update bencoder, and remove Er.h entirely. --- Cargo.lock | 14 +- Cargo.toml | 2 + admin/angel/Core.c | 3 +- benc/Dict.h | 4 +- benc/List.h | 4 +- benc/Object.h | 14 +- .../json/JsonBencMessageReader.c | 319 +------- .../json/JsonBencMessageReader.h | 21 +- benc/serialization/json/JsonBencSerializer.c | 594 --------------- benc/serialization/json/JsonBencSerializer.h | 23 - .../test/JsonBencMessageReader_fuzz_test.c | 2 +- .../standard/BencMessageReader.c | 95 +-- .../standard/BencMessageReader.h | 6 +- client/cjdroute2.c | 43 +- exception/Er.c | 75 -- exception/Er.h | 66 -- exception/Er.js | 114 --- interface/ETHInterface_admin.c | 2 +- interface/UDPInterface.c | 2 +- interface/UDPInterface.h | 2 +- interface/tuntap/TUNInterface_freebsd.c | 2 +- interface/tuntap/TUNInterface_sunos.c | 4 +- rust/cjdns_sys/Cargo.toml | 2 + rust/cjdns_sys/Rffi.h | 7 + rust/cjdns_sys/cffi.h | 8 +- rust/cjdns_sys/src/cffi.rs | 81 ++- rust/cjdns_sys/src/crypto/crypto_auth.rs | 6 +- rust/cjdns_sys/src/rffi/benc.rs | 173 +++++ rust/cjdns_sys/src/rffi/mod.rs | 1 + rust/cjdns_sys/src/util/mod.rs | 1 + .../src/util/serialization/jsonbenc.rs | 683 ++++++++++++++++++ rust/cjdns_sys/src/util/serialization/mod.rs | 1 + util/Base10.c | 11 +- util/Base10.h | 4 +- util/platform/netdev/NetPlatform_win32.c | 5 +- util/test/Base10_test.c | 3 +- 36 files changed, 1110 insertions(+), 1287 deletions(-) delete mode 100644 benc/serialization/json/JsonBencSerializer.c delete mode 100644 benc/serialization/json/JsonBencSerializer.h delete mode 100644 exception/Er.c delete mode 100644 exception/Er.h delete mode 100644 exception/Er.js create mode 100644 rust/cjdns_sys/src/rffi/benc.rs create mode 100644 rust/cjdns_sys/src/util/serialization/jsonbenc.rs create mode 100644 rust/cjdns_sys/src/util/serialization/mod.rs diff --git a/Cargo.lock b/Cargo.lock index f5cbf643..9bad78a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -165,6 +165,16 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "bendy" +version = "0.3.2" +source = "git+https://github.com/cjdelisle/bendy-cjdns?tag=v0.3.2-cjdns#21849683c9e9b83b8d217d044c1da78ee573926b" +dependencies = [ + "failure", + "serde", + "serde_bytes", +] + [[package]] name = "bendy" version = "0.3.2" @@ -354,7 +364,7 @@ name = "cjdns-bencode" version = "0.1.0" source = "git+https://github.com/cjdelisle/cjdns-route-server?rev=9ea38312c9a849cf1a0be05332c9e4c96f49a758#9ea38312c9a849cf1a0be05332c9e4c96f49a758" dependencies = [ - "bendy", + "bendy 0.3.2 (git+https://github.com/CJDNS-Development-Team/bendy-cjdns?tag=v0.3.2-cjdns)", "hex", "serde", ] @@ -402,11 +412,13 @@ dependencies = [ "anyhow", "async-recursion", "async-trait", + "bendy 0.3.2 (git+https://github.com/cjdelisle/bendy-cjdns?tag=v0.3.2-cjdns)", "bindgen", "boringtun", "byteorder", "cbindgen", "cc", + "cjdns-bencode", "cjdns-bytes", "cjdns-crypto", "cjdns-keys", diff --git a/Cargo.toml b/Cargo.toml index b6d8ec7b..d8f8933e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,8 @@ cjdns-admin = { git = "https://github.com/cjdelisle/cjdns-route-server", rev = " cjdns-crypto = { git = "https://github.com/cjdelisle/cjdns-route-server", rev = "9ea38312c9a849cf1a0be05332c9e4c96f49a758", version = "0.1.0" } cjdns-keys = { git = "https://github.com/cjdelisle/cjdns-route-server", rev = "9ea38312c9a849cf1a0be05332c9e4c96f49a758", version = "0.1.0" } cjdns-bytes = { git = "https://github.com/cjdelisle/cjdns-route-server", rev = "9ea38312c9a849cf1a0be05332c9e4c96f49a758", version = "0.1.0" } +cjdns-bencode = { git = "https://github.com/cjdelisle/cjdns-route-server", rev = "9ea38312c9a849cf1a0be05332c9e4c96f49a758", version = "0.1.0" } +bendy = { git = "https://github.com/cjdelisle/bendy-cjdns", tag = "v0.3.2-cjdns", features = ["std", "serde"] } sodiumoxide = { git = "https://github.com/cjdelisle/sodiumoxide", rev = "9f6a18d40a4db253edfebac9f2ce5c22d09b1f47", version = "0.2", default-features = false, features = ["std"] } clap = { version = "4", features = ["derive"] } const_format = "0.2" diff --git a/admin/angel/Core.c b/admin/angel/Core.c index 3de688a8..07db0c48 100644 --- a/admin/angel/Core.c +++ b/admin/angel/Core.c @@ -469,7 +469,8 @@ int Core_main(int argc, char** argv) Log_debug(logger, "Finished getting pre-configuration from client"); struct Sockaddr_storage addr; Err_assert(AddrIface_popAddr(&addr, preConf)); - Dict* config = Er_assert(BencMessageReader_read(preConf, tempAlloc)); + Dict* config = NULL; + Err_assert(BencMessageReader_read(&config, preConf, tempAlloc)); String* privateKeyHex = Dict_getStringC(config, "privateKey"); Dict* adminConf = Dict_getDictC(config, "admin"); diff --git a/benc/Dict.h b/benc/Dict.h index c8318d3f..c204fa47 100644 --- a/benc/Dict.h +++ b/benc/Dict.h @@ -20,9 +20,9 @@ #include "util/Linker.h" Linker_require("benc/Dict.c") -struct Dict_Entry; +typedef struct Dict_Entry Dict_Entry_t; struct Dict_Entry { - struct Dict_Entry* next; + Dict_Entry_t* next; String* key; Object* val; }; diff --git a/benc/List.h b/benc/List.h index 3a77f2fd..25abf70c 100644 --- a/benc/List.h +++ b/benc/List.h @@ -20,9 +20,9 @@ #include "util/Linker.h" Linker_require("benc/List.c") -struct List_Item; +typedef struct List_Item List_Item_t; struct List_Item { - struct List_Item* next; + List_Item_t* next; Object* elem; }; diff --git a/benc/Object.h b/benc/Object.h index 1ba8c268..aaccfdcb 100644 --- a/benc/Object.h +++ b/benc/Object.h @@ -17,8 +17,6 @@ #include -#include "memory/Allocator.h" - // Dictionaries and lists are pointers to the head entry so that the head can change. typedef struct Dict_Entry* Dict; typedef struct List_Item* List; @@ -28,6 +26,8 @@ typedef struct String_s { } String; typedef String String_t; +typedef Dict Dict_t; +typedef List List_t; enum Object_Type { Object_INTEGER, @@ -41,14 +41,12 @@ typedef struct { enum Object_Type type; union { int64_t number; - String* string; - List* list; - Dict* dictionary; + String_t* string; + List_t* list; + Dict_t* dictionary; } as; } Object; -//#include "benc/List.h" -//#include "benc/Dict.h" - +typedef Object Object_t; #endif diff --git a/benc/serialization/json/JsonBencMessageReader.c b/benc/serialization/json/JsonBencMessageReader.c index e4f3759c..06be3bf9 100644 --- a/benc/serialization/json/JsonBencMessageReader.c +++ b/benc/serialization/json/JsonBencMessageReader.c @@ -14,323 +14,42 @@ */ #include "benc/serialization/json/JsonBencMessageReader.h" -#include "benc/List.h" -#include "benc/Dict.h" -#include "benc/String.h" -#include "exception/Er.h" +#include "benc/Object.h" +#include "exception/Err.h" #include "memory/Allocator.h" +#include "rust/cjdns_sys/RTypes.h" +#include "rust/cjdns_sys/Rffi.h" #include "wire/Message.h" -#include "util/Gcc.h" -#include "util/Hex.h" -#include "util/Base10.h" #include -struct Context { - Message_t* const msg; - struct Allocator* const alloc; - const bool lax; - int line; - uintptr_t beginningLastLine; -}; - -static int getColumn(struct Context* ctx) -{ - return (uintptr_t) Message_bytes(ctx->msg) - ctx->beginningLastLine; -} - -#define ERROR0(ctx, message) \ - return Er__raise(Gcc_SHORT_FILE, Gcc_LINE, ctx->alloc, \ - "Error parsing config (line %d column %d): " message, \ - ctx->line, getColumn(ctx)) -#define ERROR(ctx, message, ...) \ - return Er__raise(Gcc_SHORT_FILE, Gcc_LINE, ctx->alloc, \ - "Error parsing config (line %d column %d): " message, \ - ctx->line, getColumn(ctx), __VA_ARGS__) - -static Er_DEFUN(uint8_t peak(struct Context* ctx)) -{ - if (!Message_getLength(ctx->msg)) { ERROR0(ctx, "Out of content while reading"); } - Er_ret(Message_bytes(ctx->msg)[0]); -} - -static Er_DEFUN(void skip(struct Context* ctx, int num)) -{ - if (num > Message_getLength(ctx->msg)) { ERROR0(ctx, "Out of content while reading"); } - for (int i = 0; i < num; i++) { - if (Message_bytes(ctx->msg)[i] == '\n') { - ctx->beginningLastLine = (uintptr_t) &Message_bytes(ctx->msg)[i]; - ctx->line++; - } - } - Er(Er_fromErr(Message_eshift(ctx->msg, -num))); - Er_ret(); -} - -static Er_DEFUN(bool assertChar(struct Context* ctx, char chr, bool lax)) -{ - char c = Er(peak(ctx)); - if (c != chr) { - if (lax == true) { Er_ret(false); } - ERROR(ctx, "Expected char [%c] but got [%c]", chr, c); - } - Er_ret(true); -} - -static Er_DEFUN(void parseComment(struct Context* ctx)) -{ - Er(assertChar(ctx, '/', false)); - Er(skip(ctx, 1)); - uint8_t secondChar = Er(peak(ctx)); - if (secondChar != '/' && secondChar != '*') { ERROR(ctx, "Unexpected char [%c]", secondChar); } - bool lastCharSplat = false; - for (;;) { - Er(skip(ctx, 1)); - uint8_t chr = Er(peak(ctx)); - if (lastCharSplat && secondChar == '*' && chr == '/') { - // get rid of the trailing * - Er(skip(ctx, 1)); - } else if (secondChar == '/' && chr == '\n') { - } else { - lastCharSplat = (chr == '*'); - continue; - } - Er_ret(); - } -} - -static Er_DEFUN(void parseWhitespaceAndComments(struct Context* ctx)) -{ - for (;;) { - switch (Er(peak(ctx))) { - case '\n': - case ' ': - case '\r': - case '\t': - Er(skip(ctx, 1)); - continue; - - case '/': - Er(parseComment(ctx)); - continue; - - default: break; - } - Er_ret(); - } - ERROR0(ctx, "Reached end of message while parsing"); -} - -static Er_DEFUN(String* parseString(struct Context* ctx)) -{ - Er(assertChar(ctx, '"', false)); - int line = ctx->line; - uintptr_t beginningLastLine = ctx->beginningLastLine; - int msgLen = Message_getLength(ctx->msg); - - String* out = NULL; - uint32_t pos = 0; - #define PUSHOUT(chr) do { \ - if (out) { \ - Assert_true(out->len > pos); \ - out->bytes[pos] = (chr); \ - } \ - pos++; \ - } while (0) - // CHECKFILES_IGNORE expecting a ; - - for (;;) { - Er(skip(ctx, 1)); - uint8_t bchar = Er(peak(ctx)); - switch (bchar) { - case '"': { - Er(skip(ctx, 1)); - if (out) { Er_ret(out); } - // got the length, reset and then copy the string next cycle - ctx->line = line; - ctx->beginningLastLine = beginningLastLine; - Er(Er_fromErr(Message_eshift(ctx->msg, msgLen - Message_getLength(ctx->msg)))); - out = String_newBinary(NULL, pos, ctx->alloc); - pos = 0; - continue; - } - case '\0': - case '\n': { - ERROR0(ctx, "Unterminated string"); - } - case '\\': { - Er(skip(ctx, 1)); - uint8_t x = Er(peak(ctx)); - Er(skip(ctx, 1)); - if (x != 'x') { - ERROR0(ctx, "Char \\ only allowed if followed by x (as in \\xff)"); - } - uint8_t high = Er(peak(ctx)); - Er(skip(ctx, 1)); - uint8_t low = Er(peak(ctx)); - int byte = Hex_decodeByte(high, low); - if (byte < 0 || (byte > 255)) { ERROR0(ctx, "Invalid hex encoding"); } - PUSHOUT(byte); - continue; - } - default: { - PUSHOUT(bchar); - continue; - } - } - } - #undef PUSHOUT -} - -static Er_DEFUN(int64_t parseInteger(struct Context* ctx)) -{ - Er_ret( Er(Base10_read(ctx->msg)) ); -} - -static Er_DEFUN(Object* parseGeneric(struct Context* ctx)); - -static Er_DEFUN(List* parseList(struct Context* ctx)) -{ - Er(assertChar(ctx, '[', false)); - Er(skip(ctx, 1)); - struct List_Item* first = NULL; - struct List_Item* last = NULL; - for (int i = 0; ; i++) { - for (;;) { - Er(parseWhitespaceAndComments(ctx)); - // lax mode, allow ,, extra ,,, commas - if (!ctx->lax || Er(peak(ctx)) != ',') { break; } - Er(skip(ctx, 1)); - } - if (Er(peak(ctx)) == ']') { - Er(skip(ctx, 1)); - List* out = Allocator_malloc(ctx->alloc, sizeof(List)); - *out = first; - Er_ret(out); - } - if (i && Er(assertChar(ctx, ',', ctx->lax))) { - Er(skip(ctx, 1)); - Er(parseWhitespaceAndComments(ctx)); - } - struct List_Item* item = Allocator_calloc(ctx->alloc, sizeof(struct List_Item), 1); - item->elem = Er(parseGeneric(ctx)); - if (last) { - last->next = item; - } else { - first = item; - } - last = item; - } -} - -static Er_DEFUN(Dict* parseDict(struct Context* ctx)) -{ - Er(assertChar(ctx, '{', false)); - Er(skip(ctx, 1)); - struct Dict_Entry* last = NULL; - struct Dict_Entry* first = NULL; - for (int i = 0; ; i++) { - for (;;) { - Er(parseWhitespaceAndComments(ctx)); - if (!ctx->lax || Er(peak(ctx)) != ',') { break; } - Er(skip(ctx, 1)); - } - if (Er(peak(ctx)) == '}') { - Er(skip(ctx, 1)); - Dict* out = Allocator_malloc(ctx->alloc, sizeof(Dict)); - *out = first; - Er_ret(out); - } - if (i && Er(assertChar(ctx, ',', ctx->lax))) { - Er(skip(ctx, 1)); - Er(parseWhitespaceAndComments(ctx)); - } - struct Dict_Entry* entry = Allocator_calloc(ctx->alloc, sizeof(struct Dict_Entry), 1); - entry->key = Er(parseString(ctx)); - Er(parseWhitespaceAndComments(ctx)); - if (Er(assertChar(ctx, ':', ctx->lax))) { - Er(skip(ctx, 1)); - Er(parseWhitespaceAndComments(ctx)); - } - entry->val = Er(parseGeneric(ctx)); - if (last) { - last->next = entry; - } else { - first = entry; - } - last = entry; - } -} - -static Er_DEFUN(Object* parseGeneric(struct Context* ctx)) -{ - Object* out = Allocator_calloc(ctx->alloc, sizeof(Object), 1); - uint8_t c = Er(peak(ctx)); - switch (c) { - case '-': - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': { - out->type = Object_INTEGER; - out->as.number = Er(parseInteger(ctx)); - break; - } - case '[': { - out->type = Object_LIST; - out->as.list = Er(parseList(ctx)); - break; - } - case '{': { - out->type = Object_DICT; - out->as.dictionary = Er(parseDict(ctx)); - break; - } - case '"': { - out->type = Object_STRING; - out->as.string = Er(parseString(ctx)); - break; - } - default: - ERROR(ctx, "While looking for something to parse: " - "expected one of - 0 1 2 3 4 5 6 7 8 9 [ { \", found [%c]", c); - } - Er_ret(out); -} - -Er_DEFUN(Dict* JsonBencMessageReader_read( +Err_DEFUN JsonBencMessageReader_read( + Dict_t** out, Message_t* msg, struct Allocator* alloc, bool lax -)) { - struct Context ctx = { - .msg = msg, - .alloc = alloc, - .lax = lax, - .line = 1, - .beginningLastLine = (uintptr_t) Message_bytes(msg) - }; - Er_ret( Er(parseDict(&ctx)) ); +) { + return Rffi_Benc_decodeJson(out, msg, lax, alloc); +} + +Err_DEFUN JsonBencMessageReader_write( + Dict_t* input, + Message_t* msg, + struct Allocator* alloc +) { + return Rffi_Benc_encodeJson(input, msg, alloc); } const char* JsonBencMessageReader_readNoExcept( Message_t* msg, struct Allocator* alloc, - Dict** outPtr, + Dict_t** out, bool lax) { - struct Er_Ret* er = NULL; - Dict* out = Er_check(&er, JsonBencMessageReader_read(msg, alloc, lax)); + RTypes_Error_t* er = JsonBencMessageReader_read(out, msg, alloc, lax); if (er) { - return er->message; + return Rffi_printError(er, alloc); } - *outPtr = out; return NULL; } diff --git a/benc/serialization/json/JsonBencMessageReader.h b/benc/serialization/json/JsonBencMessageReader.h index 917f70d8..de30b320 100644 --- a/benc/serialization/json/JsonBencMessageReader.h +++ b/benc/serialization/json/JsonBencMessageReader.h @@ -15,8 +15,8 @@ #ifndef JsonBencMessageReader_H #define JsonBencMessageReader_H -#include "benc/Dict.h" -#include "exception/Er.h" +#include "benc/Object.h" +#include "exception/Err.h" #include "memory/Allocator.h" #include "wire/Message.h" #include "util/Linker.h" @@ -24,12 +24,23 @@ Linker_require("benc/serialization/json/JsonBencMessageReader.c") #include -Er_DEFUN(Dict* JsonBencMessageReader_read( +Err_DEFUN JsonBencMessageReader_read( + Dict_t** out, Message_t* msg, struct Allocator* alloc, bool lax -)); +); + +Err_DEFUN JsonBencMessageReader_write( + Dict_t* input, + Message_t* msg, + struct Allocator* alloc +); + const char* JsonBencMessageReader_readNoExcept( - Message_t* msg, struct Allocator* alloc, Dict** outPtr, bool lax); + Message_t* msg, + struct Allocator* alloc, + Dict** out, + bool lax); #endif diff --git a/benc/serialization/json/JsonBencSerializer.c b/benc/serialization/json/JsonBencSerializer.c deleted file mode 100644 index 1f45ada6..00000000 --- a/benc/serialization/json/JsonBencSerializer.c +++ /dev/null @@ -1,594 +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 . - */ -#include "memory/Allocator.h" -#include "io/Reader.h" -#include "io/Writer.h" -#include "benc/Dict.h" -#include "benc/List.h" -#include "benc/String.h" -#include "benc/serialization/BencSerializer.h" -#include "benc/serialization/json/JsonBencSerializer.h" -#include "util/Bits.h" -#include "util/Hex.h" - -#include -#include -#include -#include -#include - -static int32_t parseGeneric(struct Reader* reader, - struct Allocator* allocator, - Object** output); -static int32_t serializeGenericWithPadding(struct Writer* writer, - size_t padSpaceCount, - const Object* obj); - -/** What the name says. */ -static const char* thirtyTwoSpaces = " "; - -/** - * Write some number of spaces for padding. - * - * @param padSpaces the number of spaces to pad. - * @param padCounter an integer which is used for internal bookkeeping. - * @param writer where to write the padding. - */ -#define PAD(padSpaces, padCounter, writer) \ - padCounter = 0; \ - while (32 < padSpaces + padCounter) { \ - Writer_write(writer, thirtyTwoSpaces, 32); \ - padCounter += 32; \ - } \ - Writer_write(writer, thirtyTwoSpaces, padSpaces - padCounter) - -static inline int outOfContent() -{ - return -2; -} -#define OUT_OF_CONTENT_TO_READ outOfContent() - -static inline int unparsable() -{ - return -3; -} -#define UNPARSABLE unparsable() - -/** @see BencSerializer.h */ -static int32_t serializeString(struct Writer* writer, - const String* string) -{ - Writer_write(writer, "\"", 1); - size_t i; - uint8_t chr; - char buffer[5]; - for (i = 0; i < string->len; i++) { - chr = (uint8_t) string->bytes[i] & 0xFF; - /* Nonprinting chars, \ and " are hex'd */ - if (chr < 126 && chr > 31 && chr != '\\' && chr != '"') { - snprintf(buffer, 5, "%c", chr); - Writer_write(writer, buffer, 1); - } else { - snprintf(buffer, 5, "\\x%.2X", chr); - Writer_write(writer, buffer, 4); - } - } - return Writer_write(writer, "\"", 1); -} - -/** - * Read until 1 char after the target character. - */ -static inline int readUntil(uint8_t target, struct Reader* reader) -{ - uint8_t nextChar; - do { - if (Reader_read(reader, (char*)&nextChar, 1)) { - printf("Unexpected end of input while looking for '%c'\n",target); - return OUT_OF_CONTENT_TO_READ; - } - } while (nextChar != target); - return 0; -} - -static inline int parseString(struct Reader* reader, - struct Allocator* allocator, - String** output) -{ - #define BUFF_SZ (1<<8) - #define BUFF_MAX (1<<20) - - int curSize = BUFF_SZ; - struct Allocator* localAllocator = Allocator_child(allocator); - uint8_t* buffer = Allocator_malloc(localAllocator, curSize); - if (readUntil('"', reader) || Reader_read(reader, buffer, 1)) { - printf("Unterminated string\n"); - Allocator_free(localAllocator); - return OUT_OF_CONTENT_TO_READ; - } - for (int i = 0; i < BUFF_MAX - 1; i++) { - if (buffer[i] == '\\') { - // \x01 (skip the x) - Reader_skip(reader, 1); - uint8_t hex[2]; - if (Reader_read(reader, (char*)hex, 2)) { - printf("Unexpected end of input parsing escape sequence\n"); - Allocator_free(localAllocator); - return OUT_OF_CONTENT_TO_READ; - } - int byte = Hex_decodeByte(hex[0], hex[1]); - if (byte == -1) { - printf("Invalid escape \"%c%c\" after \"%.*s\"\n",hex[0],hex[1],i+1,buffer); - Allocator_free(localAllocator); - return UNPARSABLE; - } - buffer[i] = (uint8_t) byte; - } else if (buffer[i] == '"') { - *output = String_newBinary((char*)buffer, i, allocator); - Allocator_free(localAllocator); - return 0; - } - if (i == curSize - 1) { - curSize <<= 1; - buffer = Allocator_realloc(localAllocator, buffer, curSize); - } - if (Reader_read(reader, buffer + i + 1, 1)) { - if (i+1 <= 20) { - printf("Unterminated string \"%.*s\"\n", i+1, buffer); - } else { - printf("Unterminated string starting with \"%.*s...\"\n", 20, buffer); - } - Allocator_free(localAllocator); - return OUT_OF_CONTENT_TO_READ; - } - } - - printf("Maximum string length of %d bytes exceeded.\n",BUFF_SZ); - Allocator_free(localAllocator); - return UNPARSABLE; - - #undef BUFF_SZ - #undef BUFF_MAX -} - -/** @see BencSerializer.h */ -static int32_t serializeint64_t(struct Writer* writer, - int64_t integer) -{ - char buffer[32]; - Bits_memset(buffer, 0, 32); - - snprintf(buffer, 32, "%" PRId64, integer); - - return Writer_write(writer, buffer, CString_strlen(buffer)); -} - -/** @see BencSerializer.h */ -static int32_t parseint64_t(struct Reader* reader, - int64_t* output) -{ - uint8_t buffer[32]; - - for (int i = 0; i < 21; i++) { - int32_t status = Reader_read(reader, buffer + i, 0); - if (i == 0 && buffer[i] == '-' && status == 0) { - // It's just a negative number, no need to fail it. - continue; - } - if (buffer[i] < '0' || buffer[i] > '9' || status != 0 /* end of input */) { - buffer[i] = '\0'; - int64_t out = strtol((char*)buffer, NULL, 10); - // Failed parse causes 0 to be set. - if (out == 0 && buffer[0] != '0' && (buffer[0] != '-' || buffer[1] != '0')) { - printf("Failed to parse \"%s\": not a number\n",buffer); - return UNPARSABLE; - } - if ((out == INT64_MAX || out == INT64_MIN) && errno == ERANGE) { - printf("Failed to parse \"%s\": number too large/small\n",buffer); - return UNPARSABLE; - } - *output = out; - return 0; - } - Reader_skip(reader, 1); - } - - // Larger than the max possible int64. - buffer[22] = '\0'; - printf("Failed to parse \"%s\": number too large\n",buffer); - return UNPARSABLE; -} - -/** - * Serialize a bencoded list with padding at the beginning of each line. - * - * @param writer the place to write the output to. - * @param padSpaceCount the number of spaces to place at the beginning of each line. - * @param list the list to serialize - */ -static int32_t serializeListWithPadding(struct Writer* writer, - const size_t padSpaceCount, - const List* list) -{ - int padCounter; - - Writer_write(writer, "[\n", 2); - - const struct List_Item* entry = *list; - while (entry != NULL) { - PAD(padSpaceCount + 2, padCounter, writer); - serializeGenericWithPadding(writer, padSpaceCount + 2, entry->elem); - entry = entry->next; - if (entry != NULL) { - Writer_write(writer, ",\n", 2); - } - } - - Writer_write(writer, "\n", 1); - PAD(padSpaceCount, padCounter, writer); - return Writer_write(writer, "]", 1); -} - -/** @see BencSerializer.h */ -static int32_t serializeList(struct Writer* writer, - const List* list) -{ - return serializeListWithPadding(writer, 0, list); -} - -/** - * Parse a comment in with "slash splat" or double slash notation, - * leave the reader on the first character after the last end of comment mark. - */ -static inline int parseComment(struct Reader* reader) -{ - char chars[2]; - int ret = Reader_read(reader, &chars, 2); - if (ret) { - printf("Warning: expected comment\n"); - return OUT_OF_CONTENT_TO_READ; - } - if (chars[0] != '/') { - printf("Warning: expected a comment starting with '/', instead found '%c'\n",chars[0]); - return UNPARSABLE; - } - switch (chars[1]) { - case '*':; - do { - readUntil('*', reader); - } while (!(ret = Reader_read(reader, &chars, 1)) && chars[0] != '/'); - if (ret) { - printf("Unterminated multiline comment\n"); - return OUT_OF_CONTENT_TO_READ; - } - return 0; - case '/':; - return readUntil('\n', reader); - default: - printf("Warning: expected a comment starting with \"//\" or \"/*\", " - "instead found \"/%c\"\n",chars[1]); - return UNPARSABLE; - } -} - -/** @see BencSerializer.h */ -static int32_t parseList(struct Reader* reader, - struct Allocator* allocator, - List* output) -{ - char nextChar; - readUntil('[', reader); - - Object* element; - struct List_Item* thisEntry = NULL; - struct List_Item** lastEntryPointer = output; - int ret; - - for (;;) { - for (;;) { - if (Reader_read(reader, &nextChar, 0) != 0) { - printf("Unterminated list\n"); - return OUT_OF_CONTENT_TO_READ; - } - if (nextChar == '/') { - if ((ret = parseComment(reader)) != 0) { - return ret; - } - continue; - } - switch (nextChar) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '[': - case '{': - case '"': - break; - - case ']': - Reader_skip(reader, 1); - return 0; - - default: - // FIXME(gerard): silently skipping anything we don't understand - // might not be the best idea - Reader_skip(reader, 1); - continue; - } - break; - } - - if ((ret = parseGeneric(reader, allocator, &element)) != 0) { - return ret; - } - thisEntry = Allocator_malloc(allocator, sizeof(struct List_Item)); - thisEntry->elem = element; - thisEntry->next = NULL; - - // Read backwards so that the list reads out forward. - *lastEntryPointer = thisEntry; - lastEntryPointer = &(thisEntry->next); - } -} - -/** - * Serialize a bencoded dictionary with padding before each line. - * - * @param writer the place to write the output to. - * @param padSpaceCount the number of spaces to place at the beginning of each line. - * @param dictionary the dictionary to serialize. - */ -static int32_t serializeDictionaryWithPadding(struct Writer* writer, - size_t padSpaceCount, - const Dict* dictionary) -{ - int padCounter = 0; - Writer_write(writer, "{\n", 2); - const struct Dict_Entry* entry = *dictionary; - while (entry != NULL) { - PAD(padSpaceCount + 2, padCounter, writer); - serializeString(writer, entry->key); - Writer_write(writer, " : ", 3); - serializeGenericWithPadding(writer, padSpaceCount + 2, entry->val); - entry = entry->next; - if (entry != NULL) { - Writer_write(writer, ",\n", 2); - } - } - - Writer_write(writer, "\n", 1); - PAD(padSpaceCount, padCounter, writer); - return Writer_write(writer, "}", 1); -} - -/** @see BencSerializer.h */ -static int32_t serializeDictionary(struct Writer* writer, - const Dict* dictionary) -{ - return serializeDictionaryWithPadding(writer, 0, dictionary); -} - -/** @see BencSerializer.h */ -static int32_t parseDictionary(struct Reader* reader, - struct Allocator* allocator, - Dict* output) -{ - uint8_t nextChar; - readUntil('{', reader); - - String* key; - Object* value; - struct Dict_Entry* first = NULL; - struct Dict_Entry* last = NULL; - int ret = 0; - - for (;;) { - while (!ret) { - ret = Reader_read(reader, &nextChar, 0); - switch (nextChar) { - case '"': - break; - - case '}': - Reader_skip(reader, 1); - *output = first; - return 0; - - case '/': - parseComment(reader); - continue; - - default: - Reader_skip(reader, 1); - continue; - } - break; - } - if (ret) { - printf("Unterminated dictionary\n"); - return OUT_OF_CONTENT_TO_READ; - } - - // Get key and value. - if ((ret = parseString(reader, allocator, &key)) != 0) { - return ret; - } - - readUntil(':', reader); - - if ((ret = parseGeneric(reader, allocator, &value)) != 0) { - return ret; - } - - /* Allocate the entry. */ - struct Dict_Entry* entry = Allocator_calloc(allocator, sizeof(struct Dict_Entry), 1); - entry->key = key; - entry->val = value; - if (last) { - last->next = entry; - } else { - first = entry; - } - last = entry; - } -} - -static int32_t parseGeneric(struct Reader* reader, - struct Allocator* allocator, - Object** output) -{ - int ret = 0; - char firstChar; - - for (;;) { - ret = Reader_read(reader, &firstChar, 0); - switch (firstChar) { - case ' ': - case '\r': - case '\n': - case '\t': - Reader_skip(reader, 1); - continue; - - case '/':; - if ((ret = parseComment(reader)) != 0) { - return ret; - } - continue; - - default: - break; - } - if (ret) { - printf("Unexpected end of input\n"); - return OUT_OF_CONTENT_TO_READ; - } - break; - } - - Object* out = Allocator_malloc(allocator, sizeof(Object)); - - switch (firstChar) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9':; - // int64_t. Int is special because it is not a pointer but a int64_t. - int64_t bint; - if ((ret = parseint64_t(reader, &bint)) == UNPARSABLE) { - break; - } - out->type = Object_INTEGER; - out->as.number = bint; - break; - - case '[':; - // List. - List* list = Allocator_calloc(allocator, sizeof(List), 1); - ret = parseList(reader, allocator, list); - out->type = Object_LIST; - out->as.list = list; - break; - - case '{':; - // Dictionary - Dict* dict = Allocator_calloc(allocator, sizeof(Dict), 1); - ret = parseDictionary(reader, allocator, dict); - out->type = Object_DICT; - out->as.dictionary = dict; - break; - - case '"':; - // String - String* string = NULL; - ret = parseString(reader, allocator, &string); - out->type = Object_STRING; - out->as.string = string; - break; - - default: - printf("While looking for something to parse: " - "expected one of 0 1 2 3 4 5 6 7 8 9 [ { \", found '%c'\n", firstChar); - return UNPARSABLE; - } - - if (ret != 0) { - // Something went wrong while parsing. - return ret; - } - - *output = out; - return 0; -} - -/** - * Serialize a benc object into a json string with padding before each line. - * - * @param writer a Writer which to write the output to. - * @param number of pad spaces to place before each line. - * @param obj the object to serialize. - * @return -2 if the type of object cannot be determined, otherwise - * whatever is returned by the Writer. - */ -static int32_t serializeGenericWithPadding(struct Writer* writer, - size_t padSpaceCount, - const Object* obj) -{ - switch (obj->type) - { - case Object_STRING: - return serializeString(writer, obj->as.string); - case Object_DICT: - return serializeDictionaryWithPadding(writer, padSpaceCount, obj->as.dictionary); - case Object_LIST: - return serializeListWithPadding(writer, padSpaceCount, obj->as.list); - case Object_INTEGER: - return serializeint64_t(writer, obj->as.number); - default: - return -2; - } -} - -static const struct BencSerializer SERIALIZER = -{ - .serializeString = serializeString, - .parseString = parseString, - .serializeint64_t = serializeint64_t, - .parseint64_t = parseint64_t, - .serializeList = serializeList, - .parseList = parseList, - .serializeDictionary = serializeDictionary, - .parseDictionary = parseDictionary -}; - -const struct BencSerializer* JsonBencSerializer_get(void) -{ - return &SERIALIZER; -} - -#undef PAD diff --git a/benc/serialization/json/JsonBencSerializer.h b/benc/serialization/json/JsonBencSerializer.h deleted file mode 100644 index c1c15b6c..00000000 --- a/benc/serialization/json/JsonBencSerializer.h +++ /dev/null @@ -1,23 +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 . - */ -#ifndef JsonBencSerializer_H -#define JsonBencSerializer_H - -#include "benc/serialization/BencSerializer.h" -#include "util/Linker.h" -Linker_require("benc/serialization/json/JsonBencSerializer.c") - -const struct BencSerializer* JsonBencSerializer_get(void); -#endif diff --git a/benc/serialization/json/test/JsonBencMessageReader_fuzz_test.c b/benc/serialization/json/test/JsonBencMessageReader_fuzz_test.c index a56d0e48..80cc440e 100644 --- a/benc/serialization/json/test/JsonBencMessageReader_fuzz_test.c +++ b/benc/serialization/json/test/JsonBencMessageReader_fuzz_test.c @@ -20,7 +20,7 @@ void CJDNS_FUZZ_MAIN(void* vctx, Message_t* fuzz) { struct Allocator* alloc = (struct Allocator*) vctx; - Dict* out = NULL; + Dict_t* out = NULL; const char* res = JsonBencMessageReader_readNoExcept(fuzz, alloc, &out, true); if (res) { return; } res = JsonBencMessageReader_readNoExcept(fuzz, alloc, &out, false); diff --git a/benc/serialization/standard/BencMessageReader.c b/benc/serialization/standard/BencMessageReader.c index ea9f2dc1..b06e4901 100644 --- a/benc/serialization/standard/BencMessageReader.c +++ b/benc/serialization/standard/BencMessageReader.c @@ -18,102 +18,110 @@ #include "benc/Dict.h" #include "benc/List.h" #include "benc/serialization/standard/BencMessageReader.h" -#include "exception/Er.h" +#include "exception/Err.h" +#include "rust/cjdns_sys/RTypes.h" +#include "rust/cjdns_sys/Rffi.h" #include "wire/Message.h" #include "util/Base10.h" -static Er_DEFUN(Object* readGeneric(Message_t* msg, struct Allocator* alloc)); +static Err_DEFUN readGeneric(Object** outP, Message_t* msg, struct Allocator* alloc); -static Er_DEFUN(int64_t readInt(Message_t* msg, struct Allocator* alloc)) +static Err_DEFUN readInt(int64_t* outP, Message_t* msg, struct Allocator* alloc) { - int64_t num = Er(Base10_read(msg)); + int64_t num = -1; + Err(Base10_read(&num, msg)); uint8_t e = '\0'; - Er(Er_fromErr(Message_epop8(&e, msg))); + Err(Message_epop8(&e, msg)); if (e != 'e') { - Er_raise(Message_getAlloc(msg), "Int not terminated with 'e'"); + Err_raise(Message_getAlloc(msg), "Int not terminated with 'e'"); } - Er_ret(num); + *outP = num; + return NULL; } -static Er_DEFUN(String* readString(Message_t* msg, struct Allocator* alloc)) +static Err_DEFUN readString(String** outP, Message_t* msg, struct Allocator* alloc) { - int64_t len = Er(Base10_read(msg)); + int64_t len = -1; + Err(Base10_read(&len, msg)); if (len < 0) { - Er_raise(alloc, "Negative string length"); + Err_raise(alloc, "Negative string length"); } uint8_t flag = '\0'; - Er(Er_fromErr(Message_epop8(&flag, msg))); + Err(Message_epop8(&flag, msg)); if (flag != ':') { - Er_raise(alloc, "String not deliniated with a ':'"); + Err_raise(alloc, "String not deliniated with a ':'"); } if (len > Message_getLength(msg)) { - Er_raise(alloc, "String too long"); + Err_raise(alloc, "String too long"); } String* str = String_newBinary(NULL, len, alloc); - Er(Er_fromErr(Message_epop(msg, str->bytes, len))); - Er_ret(str); + Err(Message_epop(msg, str->bytes, len)); + *outP = str; + return NULL; } -static Er_DEFUN(List* readList(Message_t* msg, struct Allocator* alloc)) +static Err_DEFUN readList(List** outP, Message_t* msg, struct Allocator* alloc) { struct List_Item* last = NULL; for (;;) { uint8_t chr = '\0'; - Er(Er_fromErr(Message_epop8(&chr, msg))); + Err(Message_epop8(&chr, msg)); if (chr == 'e') { List* out = Allocator_malloc(alloc, sizeof(List)); *out = last; - Er_ret(out); + *outP = out; + return NULL; } - Er(Er_fromErr(Message_epush8(msg, chr))); + Err(Message_epush8(msg, chr)); struct List_Item* item = Allocator_malloc(alloc, sizeof(struct List_Item)); - item->elem = Er(readGeneric(msg, alloc)); + Err(readGeneric(&item->elem, msg, alloc)); item->next = last; last = item; } } -static Er_DEFUN(Dict* readDict(Message_t* msg, struct Allocator* alloc)) +static Err_DEFUN readDict(Dict** outP, Message_t* msg, struct Allocator* alloc) { struct Dict_Entry* last = NULL; for (;;) { uint8_t chr = '\0'; - Er(Er_fromErr(Message_epop8(&chr, msg))); + Err(Message_epop8(&chr, msg)); if (chr == 'e') { Dict* out = Allocator_malloc(alloc, sizeof(Dict)); *out = last; - Er_ret(out); + *outP = out; + return NULL; } - Er(Er_fromErr(Message_epush8(msg, chr))); + Err(Message_epush8(msg, chr)); struct Dict_Entry* entry = Allocator_malloc(alloc, sizeof(struct Dict_Entry)); - entry->key = Er(readString(msg, alloc)); - entry->val = Er(readGeneric(msg, alloc)); + Err(readString(&entry->key, msg, alloc)); + Err(readGeneric(&entry->val, msg, alloc)); entry->next = last; last = entry; } } -static Er_DEFUN(Object* readGeneric(Message_t* msg, struct Allocator* alloc)) +static Err_DEFUN readGeneric(Object** outP, Message_t* msg, struct Allocator* alloc) { uint8_t chr = '\0'; - Er(Er_fromErr(Message_epop8(&chr, msg))); + Err(Message_epop8(&chr, msg)); Object* out = Allocator_calloc(alloc, sizeof(Object), 1); switch (chr) { case 'l': { out->type = Object_LIST; - out->as.list = Er(readList(msg, alloc)); + Err(readList(&out->as.list, msg, alloc)); break; } case 'd': { out->type = Object_DICT; - out->as.dictionary = Er(readDict(msg, alloc)); + Err(readDict(&out->as.dictionary, msg, alloc)); break; } case 'i': { out->type = Object_INTEGER; - out->as.number = Er(readInt(msg, alloc)); + Err(readInt(&out->as.number, msg, alloc)); break; } case '0': @@ -127,34 +135,33 @@ static Er_DEFUN(Object* readGeneric(Message_t* msg, struct Allocator* alloc)) case '8': case '9': { out->type = Object_STRING; - Er(Er_fromErr(Message_epush8(msg, chr))); - out->as.string = Er(readString(msg, alloc)); + Err(Message_epush8(msg, chr)); + Err(readString(&out->as.string, msg, alloc)); break; } - default: Er_raise(alloc, "Unexpected character in message [%c]", (char)chr); + default: Err_raise(alloc, "Unexpected character in message [%c]", (char)chr); } - Er_ret(out); + *outP = out; + return NULL; } -Er_DEFUN(Dict* BencMessageReader_read(Message_t* msg, struct Allocator* alloc)) +Err_DEFUN BencMessageReader_read(Dict** outP, Message_t* msg, struct Allocator* alloc) { uint8_t d = '\0'; - Er(Er_fromErr(Message_epop8(&d, msg))); + Err(Message_epop8(&d, msg)); if (d != 'd') { - Er_raise(alloc, "Message does not begin with a 'd' to open the dictionary"); + Err_raise(alloc, "Message does not begin with a 'd' to open the dictionary"); } - Dict* out = Er(readDict(msg, alloc)); - Er_ret(out); + Err(readDict(outP, msg, alloc)); + return NULL; } const char* BencMessageReader_readNoExcept( Message_t* msg, struct Allocator* alloc, Dict** outPtr) { - struct Er_Ret* er = NULL; - Dict* out = Er_check(&er, BencMessageReader_read(msg, alloc)); + RTypes_Error_t* er = BencMessageReader_read(outPtr, msg, alloc); if (er) { - return er->message; + return Rffi_printError(er, alloc); } - *outPtr = out; return NULL; } diff --git a/benc/serialization/standard/BencMessageReader.h b/benc/serialization/standard/BencMessageReader.h index ef05e6ae..325e9b92 100644 --- a/benc/serialization/standard/BencMessageReader.h +++ b/benc/serialization/standard/BencMessageReader.h @@ -15,14 +15,14 @@ #ifndef BencMessageReader_H #define BencMessageReader_H -#include "benc/Dict.h" -#include "exception/Er.h" +#include "benc/Object.h" +#include "exception/Err.h" #include "memory/Allocator.h" #include "wire/Message.h" #include "util/Linker.h" Linker_require("benc/serialization/standard/BencMessageReader.c") -Er_DEFUN(Dict* BencMessageReader_read(Message_t* msg, struct Allocator* alloc)); +Err_DEFUN BencMessageReader_read(Dict** outP, Message_t* msg, struct Allocator* alloc); const char* BencMessageReader_readNoExcept( Message_t* msg, struct Allocator* alloc, Dict** outPtr); diff --git a/client/cjdroute2.c b/client/cjdroute2.c index dcfe87c6..1997ebc9 100644 --- a/client/cjdroute2.c +++ b/client/cjdroute2.c @@ -15,6 +15,7 @@ #include "exception/Err.h" #include "rust/cjdns_sys/RTypes.h" #include "util/events/Socket.h" +#include "wire/Message.h" #define _POSIX_C_SOURCE 200112L #include "client/AdminClient.h" #include "admin/angel/Core.h" @@ -25,7 +26,6 @@ #include "benc/Int.h" #include "benc/List.h" #include "benc/serialization/BencSerializer.h" -#include "benc/serialization/json/JsonBencSerializer.h" #include "benc/serialization/json/JsonBencMessageReader.h" #include "benc/serialization/standard/BencMessageReader.h" #include "benc/serialization/standard/BencMessageWriter.h" @@ -681,32 +681,34 @@ int cjdroute2_main(int argc, char** argv) // and if the old parser fails or the parsed content contains "version": 2, // fail to launch Message_t* confMsg = readToMsg(stdin, allocator); - struct Reader* confReader = ArrayReader_new(Message_bytes(confMsg), Message_getLength(confMsg), allocator); - Dict _config; - Dict* config = &_config; - const char* err = JsonBencMessageReader_readNoExcept(confMsg, allocator, &config, false); - if (!err) { - // If old version is specified, always use old parser so there is no possible error - int64_t* v = Dict_getIntC(config, "version"); - if (!v || *v < 2) { err = "using old parser"; } - } + Dict* config = NULL; + RTypes_Error_t* err = JsonBencMessageReader_read( + &config, + Message_clone(confMsg, allocator), + allocator, + false + ); if (err) { - if (JsonBencSerializer_get()->parseDictionary(confReader, allocator, &_config)) { - fprintf(stderr, "Failed to parse configuration.\n%s\n", err); - return -1; - } - int64_t* version = Dict_getIntC(config, "version"); - if (version && *version >= 2) { - fprintf(stderr, "Invalid cjdroute.conf\n%s\n", err); - return -1; + // Try again with lax parsing to check if version is < 2 + Err_assert(JsonBencMessageReader_read( + &config, + Message_clone(confMsg, allocator), + allocator, + true + )); + int64_t* ver = Dict_getIntC(config, "version"); + if (ver && *ver > 1) { + Err_assert(err); } } if (argc == 2 && CString_strcmp(argv[1], "--cleanconf") == 0) { // Slip a v2 in there because at this point, the conf file is definitely v2 valid Dict_putIntC(config, "version", 2, allocator); + struct Message* msg = Message_new(0, 10000, allocator); + Err_assert(JsonBencMessageReader_write(config, msg, allocator)); struct Writer* stdoutWriter = FileWriter_new(stdout, allocator); - JsonBencSerializer_get()->serializeDictionary(stdoutWriter, config); + stdoutWriter->write(stdoutWriter, Message_bytes(msg), Message_getLength(msg)); printf("\n"); return 0; } @@ -819,7 +821,8 @@ int cjdroute2_main(int argc, char** argv) Message_t* fromCoreMsg = NULL; Err_assert(InterfaceWaiter_waitForData(&fromCoreMsg, corePipe, eventBase, allocator)); - Dict* responseFromCore = Er_assert(BencMessageReader_read(fromCoreMsg, allocator)); + Dict* responseFromCore = NULL; + Err_assert(BencMessageReader_read(&responseFromCore, fromCoreMsg, allocator)); // --------------------- Close the Core Pipe --------------------- // Allocator_free(corePipeAlloc); diff --git a/exception/Er.c b/exception/Er.c deleted file mode 100644 index 90febcba..00000000 --- a/exception/Er.c +++ /dev/null @@ -1,75 +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 . - */ -#define _POSIX_C_SOURCE 200112L - -#include "exception/Er.h" -#include "util/CString.h" -#include "rust/cjdns_sys/Rffi.h" - -#include -#include -#include - -Gcc_USE_RET -struct Er_Ret* Er__raise(char* file, int line, struct Allocator* alloc, char* format, ...) -{ - va_list args; - va_start(args, format); - - if (alloc) { - int written = snprintf(NULL, 0, "%s:%d ", file, line); - Assert_true(written >= 0); - - va_list argsCopy; - va_copy(argsCopy, args); - int written2 = vsnprintf(NULL, 0, format, argsCopy); - Assert_true(written2 >= 0); - va_end(argsCopy); - - int len = written + written2 + 1; - - char* buf = Allocator_calloc(alloc, len, 1); - - snprintf(buf, len, "%s:%d ", file, line); - vsnprintf(&buf[written], len - written, format, args); - struct Er_Ret* res = Allocator_calloc(alloc, sizeof(struct Er_Ret), 1); - res->message = buf; - va_end(args); - return res; - } else { - fprintf(stderr, "%s:%d ", file, line); - vfprintf(stderr, format, args); - fprintf(stderr, "\n"); - } - abort(); - exit(100); -} - -void Er__assertFail(struct Er_Ret* er) -{ - if (!er) { return; } - Assert_failure("%s", er->message); -} - -Er_DEFUN(void Er_fromErr(RTypes_Error_t* err)) { - if (err) { - Allocator_t* alloc = Allocator_new(8192); - char* p = Rffi_printError(err, alloc); - struct Er_Ret* res = Allocator_calloc(alloc, sizeof(struct Er_Ret), 1); - res->message = p; - return res; - } - return NULL; -} \ No newline at end of file diff --git a/exception/Er.h b/exception/Er.h deleted file mode 100644 index 6bd9e3e8..00000000 --- a/exception/Er.h +++ /dev/null @@ -1,66 +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 . - */ -#ifndef Er_H -#define Er_H - -#include "exception/Err.h" -#include "memory/Allocator.h" -#include "rust/cjdns_sys/RTypes.h" -#include "util/Gcc.h" -#include "util/Js.h" - -#include "util/Linker.h" -Linker_require("exception/Er.c") - -struct Er_Ret -{ - const char* message; -}; - -Js({ this.Er_JS = require("../exception/Er.js").create(); }) - -#define Er_DEFUN(...) \ - Gcc_USE_RET Js_or({ return this.Er_JS.defun(Js_Q __VA_ARGS__ Js_Q) }, __VA_ARGS__) - -Gcc_PRINTF(4, 5) -Gcc_USE_RET -struct Er_Ret* Er__raise(char* file, int line, struct Allocator* alloc, char* format, ...); -#define Er_raise(...) \ - do { \ - struct Er_Ret* Er_ret = Er__raise(Gcc_SHORT_FILE, Gcc_LINE, __VA_ARGS__); \ - Js_or({ return 'return Er_ret;' }, Er__assertFail(Er_ret)); \ - } while (0) - // CHECKFILES_IGNORE missing ; - -#define Er(expr) Js_or({ return this.Er_JS.er(Js_Q expr Js_Q, Gcc_SHORT_FILE, Gcc_LINE); }, expr) - -#define Er_assert(expr) \ - Js_or({ return this.Er_JS.assert(Js_Q expr Js_Q, Gcc_SHORT_FILE, Gcc_LINE); }, expr) - -#define Er_check(ret, expr) \ - Js_or({ return this.Er_JS.check(#ret, Js_Q expr Js_Q, Gcc_SHORT_FILE, Gcc_LINE); }, expr) - -#define Er_ret(val) Js_or({ return this.Er_JS.ret(Js_Q val Js_Q); }, return val) - -static inline struct Er_Ret* Er_unwind(const char* file, int line, struct Er_Ret* ret) -{ - return ret; -} - -void Er__assertFail(struct Er_Ret* er); - -Er_DEFUN(void Er_fromErr(RTypes_Error_t* err)); - -#endif diff --git a/exception/Er.js b/exception/Er.js deleted file mode 100644 index 4814036c..00000000 --- a/exception/Er.js +++ /dev/null @@ -1,114 +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 . - */ -'use strict'; -const trim = (x) => { - if (x[0] !== ' ' || x[x.length-1] !== ' ') { - throw new Error("INTERNAL: Er input must begin " + - "with and end with a space, unrecognized [" + x + "]"); - } - return x.trim(); -}; - -const defun = (ctx, spec) => { - trim(spec); - if (spec.lastIndexOf(')') !== spec.length - 2) { - throw new Error("Er function spec must end " + - "with a ), unrecognized [" + spec + "]"); - } - let c = 1; - let i = spec.length - 3; - for (; c && i >= 0; i--) { - c += spec[i] === ')'; - c -= spec[i] === '('; - } - const args = spec.slice(i + 2, spec.length - 2); - const rettAndFunc = spec.slice(0, i + 1).trim(); - const func = rettAndFunc.replace(/^.*\s+([a-zA-Z_][a-zA-Z0-9_]*)$/, (all, a) => a); - //console.log('defun: ' + rettAndFunc + ' - ' + args); - if (func === rettAndFunc) { - throw new Error("Could not parse function [" + spec + "]"); - } - const rett = rettAndFunc.replace(/\s+[a-zA-Z_][a-zA-Z0-9_]*$/, '').trim(); - ctx.activeFunction = ctx.functions[func] = { rett: rett }; - if (rett === 'void') { - return 'struct Er_Ret* ' + func + '(' + args + ')'; - } else { - return 'struct Er_Ret* ' + func + '(' + rett + ' *Er_returnValP, ' + args + ')'; - } -}; - -const ret = (ctx, val) => { - val = trim(val); - if (ctx.activeFunction.rett === 'void') { - return 'return (struct Er_Ret*)0'; - } else { - return '*Er_returnValP = ' + val + '; return (struct Er_Ret*)0'; - } -}; - -const er = (ctx, assert, errOut, expr, file, line) => { - expr = trim(expr); - if (!/[a-zA-Z_][a-zA-Z0-9_]*\(.*\)$/.test(expr)) { - throw new Error("Er() expr must be in the form Er(funcName(arg1, arg2, ...)) " + - "in [" + expr + "]"); - } - const funcName = expr.slice(0, expr.indexOf('(')); - const f = ctx.functions[funcName]; - if (!f) { - throw new Error("Er() not a defined function [" + funcName + "] in [" + expr + "]"); - } - let ifret = `if (Er_ret) { return Er_unwind("${file}", ${line}, Er_ret); }`; - if (assert) { - ifret = ` - if (Er_ret) { - struct Er_Ret** Er_errOut = ${errOut ? errOut : '(struct Er_Ret**)0'}; - if (Er_errOut) { - *Er_errOut = Er_unwind("${file}", ${line}, Er_ret); - } else { - Er__assertFail(Er_unwind("${file}", ${line}, Er_ret)); - } - } - `; - } - if (f.rett === 'void') { - return `do { - struct Er_Ret* Er_ret = ${expr}; - ${ifret} - } while (0)`; - } else { - const args = expr.slice(expr.indexOf('(') + 1); - return `(__extension__({ - ${f.rett} Er_returnVal; - __builtin_memset(&Er_returnVal, 0, sizeof(Er_returnVal)); - struct Er_Ret* Er_ret = ${funcName}(&Er_returnVal, ${args}; - ${ifret} - Er_returnVal; - }))`; - } -}; - -module.exports.create = () => { - const ctx = { - activeFunction: undefined, - functions: {}, - }; - return { - defun: (spec) => defun(ctx, spec).replace(/\n/g, ' '), - ret: (val) => ret(ctx, val).replace(/\n/g, ' '), - er: (expr, file, line) => er(ctx, false, null, expr, file, line).replace(/\n/g, ' '), - assert: (expr, file, line) => er(ctx, true, null, expr, file, line).replace(/\n/g, ' '), - check: (out, expr, file, line) => er(ctx, true, out, expr, file, line).replace(/\n/g, ' '), - }; -}; diff --git a/interface/ETHInterface_admin.c b/interface/ETHInterface_admin.c index b738d168..2bb25aff 100644 --- a/interface/ETHInterface_admin.c +++ b/interface/ETHInterface_admin.c @@ -13,7 +13,7 @@ * along with this program. If not, see . */ #include "interface/ETHInterface_admin.h" -#include "exception/Er.h" +#include "exception/Err.h" #include "interface/ETHInterface.h" #include "benc/Int.h" #include "admin/Admin.h" diff --git a/interface/UDPInterface.c b/interface/UDPInterface.c index 29475b10..91c9468b 100644 --- a/interface/UDPInterface.c +++ b/interface/UDPInterface.c @@ -12,7 +12,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include "exception/Er.h" +#include "exception/Err.h" #include "rust/cjdns_sys/Rffi.h" #include "benc/StringList.h" #include "interface/UDPInterface.h" diff --git a/interface/UDPInterface.h b/interface/UDPInterface.h index 9c8a0634..c5ee8f34 100644 --- a/interface/UDPInterface.h +++ b/interface/UDPInterface.h @@ -15,7 +15,7 @@ #ifndef UDPInterface_H #define UDPInterface_H -#include "exception/Er.h" +#include "exception/Err.h" #include "interface/addressable/AddrIface.h" #include "benc/List.h" #include "util/events/EventBase.h" diff --git a/interface/tuntap/TUNInterface_freebsd.c b/interface/tuntap/TUNInterface_freebsd.c index 225403fd..f61d4302 100644 --- a/interface/tuntap/TUNInterface_freebsd.c +++ b/interface/tuntap/TUNInterface_freebsd.c @@ -48,7 +48,7 @@ Err_DEFUN TUNInterface_new(struct Iface** out, { char deviceFile[TUNInterface_IFNAMSIZ]; - if (isTapMode) { Er_raise(alloc, "tap mode not supported on this platform"); } + if (isTapMode) { Err_raise(alloc, "tap mode not supported on this platform"); } // We are on FreeBSD so we just need to read /dev/tunxx to create the tun interface if (interfaceName) { diff --git a/interface/tuntap/TUNInterface_sunos.c b/interface/tuntap/TUNInterface_sunos.c index 86e5e867..86aa5f92 100644 --- a/interface/tuntap/TUNInterface_sunos.c +++ b/interface/tuntap/TUNInterface_sunos.c @@ -94,7 +94,7 @@ Err_DEFUN TUNInterface_new(struct Iface** out, int isTapMode, EventBase_t* base, struct Log* logger, - struct Allocator* alloc)) + struct Allocator* alloc) { // tap mode is not supported at all by the sunos tun driver. if (isTapMode) { Err_raise(alloc, "tap mode not supported on this platform"); } @@ -139,7 +139,7 @@ Err_DEFUN TUNInterface_new(struct Iface** out, } else if (tunFd2 < 0) { error = "open(\"/dev/tun\") (opening for plumbing interface)"; } - Er_raise(alloc, "%s [%s]", error, strerror(err)); + Err_raise(alloc, "%s [%s]", error, strerror(err)); } struct lifreq ifr = { diff --git a/rust/cjdns_sys/Cargo.toml b/rust/cjdns_sys/Cargo.toml index 872f1e6c..12792d12 100644 --- a/rust/cjdns_sys/Cargo.toml +++ b/rust/cjdns_sys/Cargo.toml @@ -9,6 +9,8 @@ build = "build.rs" cjdns-crypto = { workspace = true } cjdns-keys = { workspace = true } cjdns-bytes = { workspace = true } +cjdns-bencode = { workspace = true } +bendy = { workspace = true } sodiumoxide = { workspace = true } hex = { workspace = true } log = { workspace = true } diff --git a/rust/cjdns_sys/Rffi.h b/rust/cjdns_sys/Rffi.h index f4a37ee2..272de4f2 100644 --- a/rust/cjdns_sys/Rffi.h +++ b/rust/cjdns_sys/Rffi.h @@ -269,4 +269,11 @@ RTypes_Error_t *Rffi_Seeder_listDnsSeeds(RTypes_Seeder_DnsSeeds_t **seeds_out, void Rffi_Seeder_new(Rffi_Seeder **seeder_out, Iface_t **iface_out, Allocator_t *alloc); +RTypes_Error_t *Rffi_Benc_decodeJson(Dict_t **out, + Message_t *msg, + bool lax_mode, + Allocator_t *alloc); + +RTypes_Error_t *Rffi_Benc_encodeJson(Dict_t *input, Message_t *msg, Allocator_t *alloc); + #endif /* rffi_H */ diff --git a/rust/cjdns_sys/cffi.h b/rust/cjdns_sys/cffi.h index d2e46844..3a7a2525 100644 --- a/rust/cjdns_sys/cffi.h +++ b/rust/cjdns_sys/cffi.h @@ -15,7 +15,9 @@ // This file is used to generate src/cffi.rs using bindgen -#include "benc/String.h" +#include "benc/Object.h" +#include "benc/List.h" +#include "benc/Dict.h" #include "interface/Iface.h" #include "interface/test/RustIface_test.h" #include "crypto/CryptoAuth.h" @@ -38,7 +40,9 @@ struct RBindings_Whitelist { Iface_t b; enum CryptoAuth_addUser_Res c; Message_t d; - String_t e; + List_Item_t e; + Dict_Entry_t ee; + Object_t eee; Log_t* f; enum RBindings_Version g; Sockaddr_t h; diff --git a/rust/cjdns_sys/src/cffi.rs b/rust/cjdns_sys/src/cffi.rs index ccde63ee..d1af8ff2 100644 --- a/rust/cjdns_sys/src/cffi.rs +++ b/rust/cjdns_sys/src/cffi.rs @@ -10,13 +10,10 @@ pub type __uint8_t = ::std::os::raw::c_uchar; pub type __uint16_t = ::std::os::raw::c_ushort; pub type __int32_t = ::std::os::raw::c_int; pub type __uint32_t = ::std::os::raw::c_uint; +pub type __int64_t = ::std::os::raw::c_long; pub type __uint64_t = ::std::os::raw::c_ulong; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct Allocator { - _unused: [u8; 0], -} -pub type Allocator_t = Allocator; +pub type Dict = *mut Dict_Entry; +pub type List = *mut List_Item; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct String_s { @@ -30,6 +27,74 @@ impl Default for String_s { } pub type String = String_s; pub type String_t = String; +pub type Dict_t = Dict; +pub type List_t = List; +#[repr(u32)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum Object_Type { + Object_INTEGER = 0, + Object_STRING = 1, + Object_LIST = 2, + Object_DICT = 3, + Object_UNPARSABLE = 4, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct Object { + pub type_: Object_Type, + pub as_: Object__bindgen_ty_1, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union Object__bindgen_ty_1 { + pub number: i64, + pub string: *mut String_t, + pub list: *mut List_t, + pub dictionary: *mut Dict_t, + _bindgen_union_align: u64, +} +impl Default for Object__bindgen_ty_1 { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +impl Default for Object { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +pub type Object_t = Object; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct Allocator { + _unused: [u8; 0], +} +pub type Allocator_t = Allocator; +pub type List_Item_t = List_Item; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct List_Item { + pub next: *mut List_Item_t, + pub elem: *mut Object, +} +impl Default for List_Item { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +pub type Dict_Entry_t = Dict_Entry; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct Dict_Entry { + pub next: *mut Dict_Entry_t, + pub key: *mut String, + pub val: *mut Object, +} +impl Default for Dict_Entry { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} pub type Iface_t = Iface; #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -423,7 +488,9 @@ pub struct RBindings_Whitelist { pub b: Iface_t, pub c: CryptoAuth_addUser_Res, pub d: Message_t, - pub e: String_t, + pub e: List_Item_t, + pub ee: Dict_Entry_t, + pub eee: Object_t, pub f: *mut Log_t, pub g: RBindings_Version, pub h: Sockaddr_t, diff --git a/rust/cjdns_sys/src/crypto/crypto_auth.rs b/rust/cjdns_sys/src/crypto/crypto_auth.rs index 655be6a8..bb7c4eeb 100644 --- a/rust/cjdns_sys/src/crypto/crypto_auth.rs +++ b/rust/cjdns_sys/src/crypto/crypto_auth.rs @@ -1818,9 +1818,9 @@ mod tests { fn fake_random(alloc: &mut Allocator) -> *mut cffi::Random_t { unsafe { - let fake_seed = cffi::DeterminentRandomSeed_new(alloc.c(), std::ptr::null_mut()); - let mut out: *mut cffi::Random_t = std::ptr::null_mut(); - let err = cffi::Random_newWithSeed(&out, alloc.c(), std::ptr::null_mut(), fake_seed); + // let fake_seed = cffi::DeterminentRandomSeed_new(alloc.c(), std::ptr::null_mut()); + // let mut out: *mut cffi::Random_t = std::ptr::null_mut(); + // let err = cffi::Random_newWithSeed(&out, alloc.c(), std::ptr::null_mut(), fake_seed); todo!() } } diff --git a/rust/cjdns_sys/src/rffi/benc.rs b/rust/cjdns_sys/src/rffi/benc.rs new file mode 100644 index 00000000..d46ff316 --- /dev/null +++ b/rust/cjdns_sys/src/rffi/benc.rs @@ -0,0 +1,173 @@ +use std::{borrow::Cow, collections::BTreeMap}; + +use anyhow::{anyhow, bail, Result}; +use bendy::value::Value; + +use crate::{ + cffi::{ + Allocator_t, + Dict_Entry_t, + Dict_t, + List_Item_t, + List_t, + Message_t, + Object_Type, + Object_t, + String_t, + }, + interface::wire::message, + rtypes::RTypes_Error_t, + util::serialization::jsonbenc, +}; + +use super::allocator; + +pub fn dict_to_c<'a>(alloc: *mut Allocator_t, v: &BTreeMap, Value<'a>>) -> *mut Dict_t { + let mut de = std::ptr::null_mut(); + for (k, v) in v.iter().rev() { + de = allocator::adopt(alloc, Dict_Entry_t{ + next: de, + key: string_to_c(alloc, &k[..]), + val: value_to_c(alloc, v), + }); + } + allocator::adopt(alloc, de) +} + +pub fn list_to_c<'a>(alloc: *mut Allocator_t, v: &Vec>) -> *mut List_t { + let mut list = std::ptr::null_mut(); + for v in v.iter().rev() { + list = allocator::adopt(alloc, List_Item_t{ + next: list, + elem: value_to_c(alloc, v), + }); + } + allocator::adopt(alloc, list) +} + +pub fn string_to_c<'a>(alloc: *mut Allocator_t, v: impl Into<&'a[u8]>) -> *mut String_t { + let v: &'a[u8] = v.into(); + let bytes = allocator::adopt(alloc, Vec::from(v)); + let bytes = unsafe { (*bytes)[..].as_mut_ptr() }; + allocator::adopt(alloc, String_t{ + len: v.len(), + bytes: bytes as _, + }) +} + +pub fn value_to_c<'a>(alloc: *mut Allocator_t, v: &Value<'a>) -> *mut Object_t { + let mut out = Object_t::default(); + match v { + Value::Bytes(s) => { + out.type_ = Object_Type::Object_STRING; + out.as_.string = string_to_c(alloc, &s[..]); + } + Value::Dict(d) => { + out.type_ = Object_Type::Object_DICT; + out.as_.dictionary = dict_to_c(alloc, d); + } + Value::List(l) => { + out.type_ = Object_Type::Object_LIST; + out.as_.list = list_to_c(alloc, l); + } + Value::Integer(i) => { + out.type_ = Object_Type::Object_INTEGER; + out.as_.number = *i; + } + } + allocator::adopt(alloc, out) +} + +pub fn string_from_c(string: *mut String_t) -> Result> { + let (len, ptr) = unsafe { ( (*string).len, (*string).bytes ) }; + let mut out = vec![0_u8;len]; + unsafe { + let buf = std::slice::from_raw_parts(ptr as *const u8, len); + out.copy_from_slice(buf); + } + Ok(Cow::Owned(out)) +} + +pub fn dict_from_c(dict: *mut Dict_t) -> Result, Value<'static>>> { + let mut out = Vec::new(); + unsafe { + let mut d = *dict; + while !d.is_null() { + let k = string_from_c((*d).key)?; + let v = value_from_c((*d).val)?; + out.push((k,v)); + d = (*d).next; + } + } + Ok(out.into_iter().rev().collect()) +} + +pub fn list_from_c(list: *mut List_t) -> Result>> { + let mut out: Vec> = Vec::new(); + unsafe { + let mut l = *list; + while !l.is_null() { + out.push(value_from_c( (*l).elem )?); + l = (*l).next; + } + } + out.reverse(); + Ok(out) +} + +pub fn value_from_c(obj: *mut Object_t) -> Result> { + match unsafe { (*obj).type_ } { + Object_Type::Object_STRING => Ok(Value::Bytes(string_from_c(unsafe { (*obj).as_.string })?)), + Object_Type::Object_DICT => Ok(Value::Dict(dict_from_c(unsafe { (*obj).as_.dictionary })?)), + Object_Type::Object_LIST => Ok(Value::List(list_from_c(unsafe { (*obj).as_.list })?)), + Object_Type::Object_INTEGER => Ok(Value::Integer(unsafe { (*obj).as_.number })), + t => bail!("value_from_c() invalid type: {}", t as u32), + } +} + +#[no_mangle] +pub extern "C" fn Rffi_Benc_decodeJson( + out: *mut *mut Dict_t, + msg: *mut Message_t, + lax_mode: bool, + alloc: *mut Allocator_t, +) -> *mut RTypes_Error_t { + let mut msg = message::Message::from_c_message(msg); + let val = match jsonbenc::parse(&mut msg, lax_mode) { + Ok(x) => x, + Err(e) => { + return allocator::adopt(alloc, RTypes_Error_t{ + e: Some(anyhow!("Error decoding json: {:?}", e)), + }); + } + }; + let d = match val { + Value::Dict(d) => dict_to_c(alloc, &d), + _ => { + return allocator::adopt(alloc, RTypes_Error_t{ e: Some(anyhow!("Json not a dict")) }); + } + }; + unsafe { + *out = d; + } + std::ptr::null_mut() +} + +#[no_mangle] +pub extern "C" fn Rffi_Benc_encodeJson( + input: *mut Dict_t, + msg: *mut Message_t, + alloc: *mut Allocator_t, +) -> *mut RTypes_Error_t { + let mut msg = message::Message::from_c_message(msg); + let input = match dict_from_c(input) { + Err(e) => { + return allocator::adopt(alloc, RTypes_Error_t{ e: Some(anyhow!("Error converting benc: {}", e)) }); + } + Ok(input) => input, + }; + if let Err(e) = jsonbenc::serialize(&mut msg, &Value::Dict(input)) { + return allocator::adopt(alloc, RTypes_Error_t{ e: Some(anyhow!("Error decoding json: {}", e)) }); + } + std::ptr::null_mut() +} \ No newline at end of file diff --git a/rust/cjdns_sys/src/rffi/mod.rs b/rust/cjdns_sys/src/rffi/mod.rs index cfffc995..3825d718 100644 --- a/rust/cjdns_sys/src/rffi/mod.rs +++ b/rust/cjdns_sys/src/rffi/mod.rs @@ -14,6 +14,7 @@ mod glock; mod base10; pub mod allocator; mod seeder; +mod benc; use anyhow::{bail, Result}; diff --git a/rust/cjdns_sys/src/util/mod.rs b/rust/cjdns_sys/src/util/mod.rs index 0158a12d..dbd5b485 100644 --- a/rust/cjdns_sys/src/util/mod.rs +++ b/rust/cjdns_sys/src/util/mod.rs @@ -5,6 +5,7 @@ pub mod sockaddr; pub mod identity; pub mod async_callable; pub mod callable; +pub mod serialization; pub mod events { use std::time::{SystemTime, UNIX_EPOCH}; diff --git a/rust/cjdns_sys/src/util/serialization/jsonbenc.rs b/rust/cjdns_sys/src/util/serialization/jsonbenc.rs new file mode 100644 index 00000000..997984ba --- /dev/null +++ b/rust/cjdns_sys/src/util/serialization/jsonbenc.rs @@ -0,0 +1,683 @@ +use bendy::value::Value; +use std::collections::BTreeMap; +use std::io::Read; +use std::borrow::Cow; +use cjdns_bytes::message::RWrite; +use std::io::Result as IoResult; + +#[derive(Debug)] +pub enum ParseError { + MalformedInput(String, usize), // Error with description and line number +} + +struct JsonParser { + reader: R, + current_line: usize, + lax_mode: bool, + last_chr: Option, +} + +fn format_chr(chr: u8) -> String { + char::from_u32(chr as _).map(|x|format!("{x}")).unwrap_or_else(||format!("CODE[{chr}]")) +} + +impl JsonParser { + fn new(reader: R, lax_mode: bool) -> Self { + JsonParser { + reader, + current_line: 1, + lax_mode, + last_chr: None, + } + } + + fn parse(&mut self) -> Result, ParseError> { + loop { + let byte = self.next_meaningful_char()?; + match byte { + b'{' => return self.parse_dict(), + b'[' => return self.parse_list(), + b'"' => return self.parse_string(), + b'0'..=b'9' | b'-' => return self.parse_int(byte), + x => { + return Err(ParseError::MalformedInput( + format!("Unexpected char {}", format_chr(x)), + self.current_line, + )); + } + } + } + } + + fn read_byte(&mut self) -> Result, ParseError> { + if let Some(lc) = self.last_chr.take() { + return Ok(Some(lc)); + } + let mut byte = [0; 1]; + match self.reader.read(&mut byte) { + Ok(0) => Ok(None), // End of stream + Ok(_) => Ok(Some(byte[0])), + Err(_) => Err(ParseError::MalformedInput("Error reading input".into(), self.current_line)), + } + } + + fn next_meaningful_char(&mut self) -> Result { + while let Some(byte) = self.read_byte()? { + match byte { + b'/' => self.handle_comment()?, + b' ' | b'\t' => {} + b'\n' => { self.current_line += 1 } + x => { + return Ok(x); + } + } + } + Err(ParseError::MalformedInput("Ran out of content while parsing".into(), self.current_line)) + } + + fn parse_dict(&mut self) -> Result, ParseError> { + let mut dict = BTreeMap::new(); + let mut need_comma = false; + loop { + let byte = self.next_meaningful_char()?; + + if need_comma && byte != b'}' && byte != b',' && !self.lax_mode { + return Err(ParseError::MalformedInput( + format!("Dict: Expecting ',' but got '{}'", format_chr(byte)), + self.current_line) + ); + } + match byte { + b'}' => { + if !dict.is_empty() && !need_comma && !self.lax_mode { + return Err(ParseError::MalformedInput( + format!("Dict: Unexpected trailing ','"), + self.current_line) + ); + } + return Ok(Value::Dict(dict)); + } + b'"' => { + let key = if let Value::Bytes(x) = self.parse_string()? { + x + } else { + unreachable!(); + }; + let mut need_colon = true; + let val = loop { + let byte = self.next_meaningful_char()?; + if need_colon && byte != b':' && !self.lax_mode { + return Err(ParseError::MalformedInput( + format!("Dict: Expecting ':' but got '{}'", format_chr(byte)), + self.current_line) + ); + } + match byte { + b':' => { + if !need_colon && !self.lax_mode { + return Err(ParseError::MalformedInput( + "Dict: Expected value but got duplicate ':'".into(), self.current_line)); + } + need_colon = false; + } + b'{' | b'[' | b'"' | b'0'..=b'9' | b'-' => { + self.last_chr = Some(byte); + break self.parse()?; + } + x => { + return Err(ParseError::MalformedInput( + format!("Dict: Unexpected character '{}', exppecting value", format_chr(x)), + self.current_line) + ); + } + } + }; + dict.insert(key, val); + need_comma = true; + } + b',' => { + if !need_comma && !self.lax_mode { + return Err(ParseError::MalformedInput("Dict: Unexpected comma".into(), self.current_line)); + } + need_comma = false; + } + x => { + return Err(ParseError::MalformedInput( + format!("Dict: Unexpected character '{}'", format_chr(x)), + self.current_line) + ); + } + } + } + } + + fn parse_list(&mut self) -> Result, ParseError> { + let mut list = vec![]; + let mut need_comma = false; + loop { + let byte = self.next_meaningful_char()?; + if need_comma && byte != b']' && byte != b',' && !self.lax_mode { + return Err(ParseError::MalformedInput( + format!("List: Expecting ',' but got '{}'", format_chr(byte)), + self.current_line) + ); + } + match byte { + b']' => { + if !list.is_empty() && !need_comma && !self.lax_mode { + return Err(ParseError::MalformedInput( + format!("List: Unexpected trailing ','"), + self.current_line) + ); + } + return Ok(Value::List(list)); + } + b',' => { + if !need_comma && !self.lax_mode { + if !need_comma && !self.lax_mode { + return Err(ParseError::MalformedInput("List: Unexpected comma".into(), self.current_line)); + } + } + need_comma = false; + } + b'{' | b'[' | b'"' | b'0'..=b'9' | b'-' => { + self.last_chr = Some(byte); + list.push(self.parse()?); + need_comma = true; + } + x => { + return Err(ParseError::MalformedInput( + format!("List: Unexpected character '{}'", format_chr(x)), + self.current_line) + ); + } + } + } + } + + fn parse_string(&mut self) -> Result, ParseError> { + let mut result = vec![]; + while let Some(byte) = self.read_byte()? { + match byte { + b'"' => break, + b'\\' => { + if let Some(escaped_byte) = self.read_byte()? { + match escaped_byte { + b'x' => { + let hex = self.read_two_hex_digits()?; + result.push(hex); + } + _ => { + return Err(ParseError::MalformedInput( + format!("String: Invalid escape sequence: '\\{}'", format_chr(byte)), + self.current_line, + )); + } + } + } + } + _ => result.push(byte), + } + } + Ok(Value::Bytes(Cow::Owned(result))) + } + + fn parse_int(&mut self, first_byte: u8) -> Result, ParseError> { + let mut num_str = String::new(); + num_str.push(first_byte as char); + while let Some(byte) = self.read_byte()? { + match byte { + b'0'..=b'9' | b'-' => num_str.push(byte as char), + x => { + self.last_chr = Some(x); + break; + }, + } + } + match num_str.parse::() { + Ok(num) => Ok(Value::Integer(num)), + Err(_) => Err(ParseError::MalformedInput("Invalid integer".into(), self.current_line)), + } + } + + fn handle_comment(&mut self) -> Result<(), ParseError> { + let next_byte = self.read_byte()?; + match next_byte { + Some(b'*') => self.consume_multiline_comment(), + Some(b'/') => self.consume_singleline_comment(), + _ => Ok(()), // Ignore invalid comment + } + } + + fn consume_singleline_comment(&mut self) -> Result<(), ParseError> { + while let Some(byte) = self.read_byte()? { + if byte == b'\n' { + self.current_line += 1; + break; + } + } + Ok(()) + } + + fn consume_multiline_comment(&mut self) -> Result<(), ParseError> { + while let Some(byte) = self.read_byte()? { + if byte == b'*' { + if let Some(b'/') = self.read_byte()? { + break; + } + } + } + Ok(()) + } + + fn read_two_hex_digits(&mut self) -> Result { + let mut hex = [0u8; 2]; + for i in 0..2 { + if let Some(byte) = self.read_byte()? { + hex[i] = byte; + } else { + return Err(ParseError::MalformedInput("Incomplete hex escape".into(), self.current_line)); + } + } + let hex_str = std::str::from_utf8(&hex).map_err(|_| ParseError::MalformedInput("Invalid hex escape".into(), self.current_line))?; + u8::from_str_radix(hex_str, 16).map_err(|_| ParseError::MalformedInput("Invalid hex digits".into(), self.current_line)) + } +} + +fn serialize_string(writer: &mut W, s: &[u8]) -> std::io::Result<()> { + writer.write_all(b"\"")?; + for &byte in s.iter().rev() { + if byte < 126 && byte > 31 && byte != b'\\' && byte != b'"' { + writer.write_all(&[byte])?; + } else { + let escaped = format!(r"\x{:02X}", byte); + writer.write_all(escaped.as_bytes())?; + } + } + writer.write_all(b"\"")?; + Ok(()) +} + +fn serialize_reverse<'a, W: RWrite>(writer: &mut W, obj: &Value<'a>, indent_level: usize) -> std::io::Result<()> { + let indent = " ".repeat(indent_level); + let indent2 = " ".repeat(indent_level + 1); + + match obj { + Value::Bytes(s) => serialize_string(writer, s)?, + Value::Integer(i) => { + let int_str = i.to_string(); + writer.write_all(&int_str.as_bytes())?; + } + Value::List(list) => { + writer.write_all(b"]")?; + if !list.is_empty() { + writer.write_all(indent.as_bytes())?; + writer.write_all(b"\n")?; + for (i, item) in list.iter().rev().enumerate() { + if i > 0 { + writer.write_all(b",")?; + } + serialize_reverse(writer, item, indent_level + 1)?; + writer.write_all(indent2.as_bytes())?; + writer.write_all(b"\n")?; + } + } + writer.write_all(b"[")?; + } + Value::Dict(dict) => { + writer.write_all(b"}")?; + if !dict.is_empty() { + writer.write_all(indent.as_bytes())?; + writer.write_all(b"\n")?; + for (i, (key, value)) in dict.iter().rev().enumerate() { + if i > 0 { + writer.write_all(b",\n")?; + } + serialize_reverse(writer, value, indent_level + 1)?; + writer.write_all(b": ")?; + serialize_string(writer, key)?; + writer.write_all(indent2.as_bytes())?; + } + writer.write_all(b"{\n")?; + } else { + writer.write_all(b"{")?; + } + } + } + + Ok(()) +} + +pub fn parse(reader: R, lax_mode: bool) -> Result, ParseError> { + JsonParser::new(reader, lax_mode).parse() +} + +pub fn serialize<'a, W: RWrite>(writer: &mut W, obj: &Value<'a>) -> IoResult<()> { + serialize_reverse(writer, obj, 0) +} + +#[cfg(test)] +mod test { + use anyhow::bail; + use bendy::value::Value; + + use crate::interface::wire::message::Message; + + use super::{parse, serialize}; + + #[test] + fn test() { + let conf = r#" +{ + // Private key: + // Your confidentiality and data integrity depend on this key, keep it secret! + "privateKey": "0b4eed1835d708a79914a8925e8d5fff706a5258dfb4826f56d4c29eaec7abc5", + + // This key corresponds to the public key and ipv6 address: + "publicKey": "f09rvtmr2s25942ygutztv8bf70ny8zd0kpffk8lmbzm40qty9k0.k", + "ipv6": "fcea:4775:eacd:7c3b:6fbc:c02b:43d6:53e1", + + // Anyone connecting and offering these passwords on connection will be allowed. + // + // WARNING: If a "login" parameter is passed, someone sniffing on the wire can + // sniff the packet and crack to find it. If the "login" is not passed + // then the hash of the 'password' is effectively the login, therefore + // that can be cracked. + // + "authorizedPasswords": [ + // Password is a unique string which is known to the client and server. + // User is an optional login name and will also be used to display the peer. + { "password": "dq1nlc6g46k9v5pd6hkrclpsz7807nl", "user": "default-login" } + + // More passwords should look like this. + // { "password": "86251h6k89u20rx9k0f4tlhwc6qkj5j", "user": "my-second-peer" }, + // { "password": "xzmnu69xqhff297pz1v3rnbhl034z1f", "user": "my-third-peer" }, + // { "password": "ksr4nftwznrsqw5z3t69dwn6v3rw6z4", "user": "my-fourth-peer" }, + + // Below is an example of your connection credentials + // that you can give to other people so they can connect + // to you using your default password (from above). + // The login field here yourself to your peer and the peerName field + // is the name the peer which will be displayed in peerStats + // Adding a unique password for each peer is advisable + // so that leaks can be isolated. + /* + "your.external.ip.goes.here:65288": { + "login": "default-login", + "password": "dq1nlc6g46k9v5pd6hkrclpsz7807nl", + "publicKey": "f09rvtmr2s25942ygutztv8bf70ny8zd0kpffk8lmbzm40qty9k0.k", + "peerName": "your-name-goes-here" + }, + */ + ], + + // Settings for administering and extracting information from your router. + // This interface provides functions which can be called through a UDP socket. + // See admin/Readme.md for more information about the API and try: + // ./tools/cexec + // For a list of functions which can be called. + // For example: ./tools/cexec 'memory()' + // will call a function which gets the core's current memory consumption. + // ./tools/cjdnslog + // is a tool which uses this admin interface to get logs from cjdns. + "admin": { + // Port to bind the admin RPC server to. + "bind": "127.0.0.1:11234", + + // Password for admin RPC server. + // This is a static password by default, so that tools like + // ./tools/cexec can use the API without you creating a + // config file at ~/.cjdnsadmin first. If you decide to + // expose the admin API to the network, change the password! + "password": "NONE" + }, + + // Interfaces to connect to the switch core. + "interfaces": { + // The interface which connects over UDP/IP based VPN tunnel. + "UDPInterface": [ + { + // Bind to this port. + "bind": "0.0.0.0:65288", + // Set the DSCP value for Qos. Default is 0. + // "dscp": 46, + + // Automatically connect to other nodes on the same LAN + // This works by binding a second port and sending beacons + // containing the main data port. + // beacon is a number between 0 and 2: + // 0 -> do not beacon nor connect to other nodes who beacon + // 1 -> quiet mode, accept beacons from other nodes only + // 2 -> send and accept beacons + // beaconDevices is a list which can contain names of devices such + // as eth0, as well as broadcast addresses to send to, such as + // 192.168.101.255, or the pseudo-name "all". + // in order to auto-peer, all cjdns nodes must use the same + // beaconPort. + "beacon": 2, + "beaconDevices": [ "all" ], + "beaconPort": 64512, + + // Nodes to connect to (IPv4 only). + "connectTo": { + // Add connection credentials here to join the network + // If you have several, don't forget the separating commas + // They should look like: + // "ipv4 address:port": { + // "login": "(optional) name your peer has for you" + // "password": "password to connect with", + // "publicKey": "remote node key.k", + // "peerName": "(optional) human-readable name for peer" + // }, + // Ask somebody who is already connected. + } + }, + { + // Bind to this port. + "bind": "[::]:65288", + // Set the DSCP value for Qos. Default is 0. + // "dscp": 46, + + // Nodes to connect to (IPv6 only). + "connectTo": { + // Add connection credentials here to join the network + // Ask somebody who is already connected. + } + } + ], + + // The interface which allows peering using layer-2 ethernet frames + "_disabled_ETHInterface": [ + // Alternatively bind to just one device and either beacon and/or + // connect to a specified MAC address + { + // Bind to this device (interface name, not MAC) + // "all" is a pseudo-name which will try to connect to all devices. + "bind": "all", + + // Auto-connect to other cjdns nodes on the same network. + // Options: + // + // 0 -- Disabled. + // + // 1 -- Accept beacons, this will cause cjdns to accept incoming + // beacon messages and try connecting to the sender. + // + // 2 -- Accept and send beacons, this will cause cjdns to broadcast + // messages on the local network which contain a randomly + // generated per-session password, other nodes which have this + // set to 1 or 2 will hear the beacon messages and connect + // automatically. + // + "beacon": 2, + + // Node(s) to connect to manually + // Note: does not work with "all" pseudo-device-name + "connectTo": { + // Credentials for connecting look similar to UDP credentials + // except they begin with the mac address, for example: + // "01:02:03:04:05:06":{"password":"a","publicKey":"b"} + } + } + ] + + }, + + // Configuration for the router. + "router": { + // DNS Seeds, these will be used to add peers automatically. + // The first seed in the list is trusted to provide the snode. + "dnsSeeds": [ + "seed.cjdns.fr" + ], + + // supernodes, if none are specified they'll be taken from your peers + "supernodes": [ + //"6743gf5tw80ExampleExampleExampleExamplevlyb23zfnuzv0.k", + ], + + // The interface which is used for connecting to the cjdns network. + "interface": { + // The type of interface (only TUNInterface is supported for now) + "type": "TUNInterface" + + // The name of a persistent TUN device to use. + // This for starting cjdroute as its own user. + // *MOST USERS DON'T NEED THIS* + //"tunDevice": "tun0" + }, + + // As an alternative to the TUN interface, you can create a socket interface + // which will create a UNIX socket which emits packets that would otherwise + // be sent through the TUN device. + // To enable this interface, change the name of the above TUN interface to + // "_disabled_interface" and change the name of this interface to + // simply "interface" + "_disabled_interface": { + "type": "SocketInterface", + + // The filesystem path to the socket to create or connect to. + "socketFullPath": "/var/run/cjdns.sock" + }, + + // System for tunneling IPv4 and ICANN IPv6 through cjdns. + // This is using the cjdns switch layer as a VPN carrier. + "ipTunnel": { + // Nodes allowed to connect to us. + // When a node with the given public key connects, give them the + // ip4 and/or ip6 addresses listed. + "allowedConnections": [ + // Give the client an address on 192.168.1.0/24, and an address + // it thinks has all of IPv6 behind it. + // ip4Prefix is the set of addresses which are routable from the tun + // for example, if you're advertizing a VPN into a company network + // which exists in 10.123.45.0/24 space, ip4Prefix should be 24 + // default is 32 for ipv4 and 128 for ipv6 + // so by default it will not install a route + // ip4Alloc is the block of addresses which are allocated to the + // for example if you want to issue 4 addresses to the client, those + // being 192.168.123.0 to 192.168.123.3, you would set this to 30 + // default is 32 for ipv4 and 128 for ipv6 (1 address) + // { + // "publicKey": "f64hfl7c4uxt6krmhPutTheRealAddressOfANodeHere7kfm5m0.k", + // "ip4Address": "192.168.1.24", + // "ip4Prefix": 0, + // "ip4Alloc": 32, + // "ip6Address": "2001:123:ab::10", + // "ip6Prefix": 0 + // "ip6Alloc": 64, + // }, + + // It's ok to only specify one address and prefix/alloc are optional. + // { + // "publicKey": "ydq8csdk8p8ThisIsJustAnExampleAddresstxuyqdf27hvn2z0.k", + // "ip4Address": "192.168.1.25", + // "ip4Prefix": 0, + // } + ], + + "outgoingConnections": [ + // Connect to one or more machines and ask them for IP addresses. + // "6743gf5tw80ExampleExampleExampleExamplevlyb23zfnuzv0.k", + // "pw9tfmr8pcrExampleExampleExampleExample8rhg1pgwpwf80.k", + // "g91lxyxhq0kExampleExampleExampleExample6t0mknuhw75l0.k" + ] + } + }, + + // Dropping permissions. + // In the event of a serious security exploit in cjdns, leak of confidential + // network traffic and/or keys is highly likely but the following rules are + // designed to prevent the attack from spreading to the system on which cjdns + // is running. + // Counter-intuitively, cjdns is *more* secure if it is started as root because + // non-root users do not have permission to use chroot or change usernames, + // limiting the effectiveness of the mitigations herein. + "security": [ + // Change the user id to sandbox the cjdns process after it starts. + // If keepNetAdmin is set to 0, IPTunnel will be unable to set IP addresses + // and ETHInterface will be unable to hot-add new interfaces + // Use { "setuser": 0 } to disable. + // Default: enabled with keepNetAdmin + { "setuser": "nobody", "keepNetAdmin": 1 }, + + // Chroot changes the filesystem root directory which cjdns sees, blocking it + // from accessing files outside of the chroot sandbox, if the user does not + // have permission to use chroot(), this will fail quietly. + // Use { "chroot": 0 } to disable. + // Default: enabled (using "/var/run") + { "chroot": "/var/run/" }, + + // Nofiles is a deprecated security feature which prevents cjdns from opening + // any files at all, using this will block setting of IP addresses and + // hot-adding ETHInterface devices but for users who do not need this, it + // provides a formidable sandbox. + // Default: disabled + { "nofiles": 0 }, + + // Noforks will prevent cjdns from spawning any new processes or threads, + // this prevents many types of exploits from attacking the wider system. + // Default: enabled + { "noforks": 1 }, + + // The client sets up the core using a sequence of RPC calls, the responses + // to these calls are verified but in the event that the client crashes + // setup of the core completes, it could leave the core in an insecure state + // This call constitutes the client telling the core that the security rules + // have been fully applied and the core may run. Without it, the core will + // exit within a few seconds with return code 232. + // Default: enabled + { "setupComplete": 1 } + ], + + // Logging + "logging": { + // Uncomment to have cjdns log to stdout rather than making logs available + // via the admin socket. + // "logTo": "stdout" + }, + + // If set to non-zero, cjdns will not fork to the background. + // Recommended for use in conjunction with "logTo":"stdout". + "noBackground": 0, + + // Path for admin control pipe: + // If you pass only a filename then cjdns will guess the full path + // On unix the default path is /tmp/ + // On windows: \\.\pipe\ + "pipe": "cjdroute.sock", + + // This is to make the configuration be parsed in strict mode, which allows + // it to be edited externally using cjdnsconf. + "version": 2 +} + "#; + let mut msg = Message::new(20000); + msg.push_bytes(conf.as_bytes()).unwrap(); + let res = parse(msg, false).unwrap(); + let _ = match res { + bendy::value::Value::Dict(d) => d, + _ => panic!("Wrong type"), + }; + } +} \ No newline at end of file diff --git a/rust/cjdns_sys/src/util/serialization/mod.rs b/rust/cjdns_sys/src/util/serialization/mod.rs new file mode 100644 index 00000000..71da1622 --- /dev/null +++ b/rust/cjdns_sys/src/util/serialization/mod.rs @@ -0,0 +1 @@ +pub mod jsonbenc; \ No newline at end of file diff --git a/util/Base10.c b/util/Base10.c index 1cef7ec6..15f6a627 100644 --- a/util/Base10.c +++ b/util/Base10.c @@ -15,7 +15,7 @@ #include "util/Base10.h" #include "wire/Message.h" -#include "exception/Er.h" +#include "exception/Err.h" #include "util/CString.h" #include "rust/cjdns_sys/Rffi.h" @@ -40,17 +40,18 @@ Err_DEFUN Base10_write(Message_t* msg, int64_t num) return NULL; } -Er_DEFUN(int64_t Base10_read(Message_t* msg)) +Err_DEFUN Base10_read(int64_t* outP, Message_t* msg) { int64_t numOut = 0; uint32_t bytes = 0; int out = Rffi_parseBase10( Message_bytes(msg), Message_getLength(msg), &numOut, &bytes); if (out != 0) { - Er_raise(Message_getAlloc(msg), "Error reading base10: %d", out); + Err_raise(Message_getAlloc(msg), "Error reading base10: %d", out); } - Er(Er_fromErr(Message_eshift(msg, -bytes))); - Er_ret(numOut); + Err(Message_eshift(msg, -bytes)); + *outP = numOut; + return NULL; } int Base10_fromString(uint8_t* str, int64_t* numOut) diff --git a/util/Base10.h b/util/Base10.h index f15728fd..ff60db7e 100644 --- a/util/Base10.h +++ b/util/Base10.h @@ -15,7 +15,7 @@ #ifndef Base10_H #define Base10_H -#include "exception/Er.h" +#include "exception/Err.h" #include "wire/Message.h" #include "util/Linker.h" Linker_require("util/Base10.c") @@ -23,7 +23,7 @@ Linker_require("util/Base10.c") #include Err_DEFUN Base10_write(Message_t* msg, int64_t num); -Er_DEFUN(int64_t Base10_read(Message_t* msg)); +Err_DEFUN Base10_read(int64_t* outP, Message_t* msg); int Base10_fromString(uint8_t* str, int64_t* numOut); #endif diff --git a/util/platform/netdev/NetPlatform_win32.c b/util/platform/netdev/NetPlatform_win32.c index c1f9c562..8d2dd656 100644 --- a/util/platform/netdev/NetPlatform_win32.c +++ b/util/platform/netdev/NetPlatform_win32.c @@ -109,7 +109,7 @@ static Err_DEFUN setupRoute(const char* deviceName, void WINAPI InitializeIpForwardEntry(PMIB_IPFORWARD_ROW2 Row); MIB_IPFORWARD_ROW2 row = { - .InterfaceLuid = Er(getLuid(deviceName, alloc)), + .InterfaceLuid = 0, .ValidLifetime = WSA_INFINITE, .PreferredLifetime = WSA_INFINITE, .Metric = 0xffffffff, @@ -129,6 +129,7 @@ static Err_DEFUN setupRoute(const char* deviceName, .Age = 0, .Origin = 0 }; + Err(getLuid(&row.InterfaceLuid deviceName, alloc)); if (addrFam == AF_INET6) { Bits_memcpy(&row.DestinationPrefix.Prefix.Ipv6.sin6_addr, addrBytes, 15); @@ -176,7 +177,7 @@ Err_DEFUN NetPlatform_addAddress(const char* interfaceName, .OnLinkPrefixLength = 0xFF }; - ipRow.InterfaceLuid = Er(getLuid(interfaceName, tempAlloc)); + Err(getLuid(&ipRow.InterfaceLuid, interfaceName, tempAlloc)); ipRow.Address.si_family = addrFam; if (addrFam == AF_INET6) { diff --git a/util/test/Base10_test.c b/util/test/Base10_test.c index f5ad76be..679dd40e 100644 --- a/util/test/Base10_test.c +++ b/util/test/Base10_test.c @@ -45,7 +45,8 @@ int main() Assert_true(Message_getLength(msg) == (int)CString_strlen(buff)); Assert_true(!Bits_memcmp(Message_bytes(msg), buff, Message_getLength(msg))); - int64_t read = Er_assert(Base10_read(msg)); + int64_t read = -1; + Err_assert(Base10_read(&read, msg)); Assert_true(read == num); Assert_true(Message_getLength(msg) == 0); }