mirror of
https://github.com/cjdelisle/cjdns
synced 2025-10-05 16:22:54 +02:00
Unified Errors: Move JSON-benc to Rust, update bencoder, and remove Er.h entirely.
This commit is contained in:
14
Cargo.lock
generated
14
Cargo.lock
generated
@@ -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",
|
||||
|
@@ -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"
|
||||
|
@@ -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");
|
||||
|
@@ -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;
|
||||
};
|
||||
|
@@ -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;
|
||||
};
|
||||
|
||||
|
@@ -17,8 +17,6 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#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
|
||||
|
@@ -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 <stdbool.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@@ -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 <stdbool.h>
|
||||
|
||||
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
|
||||
|
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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 <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
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
|
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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
|
@@ -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);
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#define _POSIX_C_SOURCE 200112L
|
||||
|
||||
#include "exception/Er.h"
|
||||
#include "util/CString.h"
|
||||
#include "rust/cjdns_sys/Rffi.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
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;
|
||||
}
|
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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
|
114
exception/Er.js
114
exception/Er.js
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
'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, ' '),
|
||||
};
|
||||
};
|
@@ -13,7 +13,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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"
|
||||
|
@@ -12,7 +12,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "exception/Er.h"
|
||||
#include "exception/Err.h"
|
||||
#include "rust/cjdns_sys/Rffi.h"
|
||||
#include "benc/StringList.h"
|
||||
#include "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"
|
||||
|
@@ -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) {
|
||||
|
@@ -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 = {
|
||||
|
@@ -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 }
|
||||
|
@@ -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 */
|
||||
|
@@ -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;
|
||||
|
@@ -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,
|
||||
|
@@ -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!()
|
||||
}
|
||||
}
|
||||
|
173
rust/cjdns_sys/src/rffi/benc.rs
Normal file
173
rust/cjdns_sys/src/rffi/benc.rs
Normal file
@@ -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<Cow<'a, [u8]>, 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<Value<'a>>) -> *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<Cow<'static, [u8]>> {
|
||||
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<BTreeMap<Cow<'static, [u8]>, 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<Vec<Value<'static>>> {
|
||||
let mut out: Vec<Value<'_>> = 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<Value<'static>> {
|
||||
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()
|
||||
}
|
@@ -14,6 +14,7 @@ mod glock;
|
||||
mod base10;
|
||||
pub mod allocator;
|
||||
mod seeder;
|
||||
mod benc;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
|
||||
|
@@ -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};
|
||||
|
683
rust/cjdns_sys/src/util/serialization/jsonbenc.rs
Normal file
683
rust/cjdns_sys/src/util/serialization/jsonbenc.rs
Normal file
@@ -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<R: Read> {
|
||||
reader: R,
|
||||
current_line: usize,
|
||||
lax_mode: bool,
|
||||
last_chr: Option<u8>,
|
||||
}
|
||||
|
||||
fn format_chr(chr: u8) -> String {
|
||||
char::from_u32(chr as _).map(|x|format!("{x}")).unwrap_or_else(||format!("CODE[{chr}]"))
|
||||
}
|
||||
|
||||
impl<R: Read> JsonParser<R> {
|
||||
fn new(reader: R, lax_mode: bool) -> Self {
|
||||
JsonParser {
|
||||
reader,
|
||||
current_line: 1,
|
||||
lax_mode,
|
||||
last_chr: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse(&mut self) -> Result<Value<'static>, 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<Option<u8>, 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<u8, ParseError> {
|
||||
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<Value<'static>, 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<Value<'static>, 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<Value<'static>, 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<Value<'static>, 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::<i64>() {
|
||||
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<u8, ParseError> {
|
||||
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<W: RWrite>(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<R: Read>(reader: R, lax_mode: bool) -> Result<Value<'static>, 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"),
|
||||
};
|
||||
}
|
||||
}
|
1
rust/cjdns_sys/src/util/serialization/mod.rs
Normal file
1
rust/cjdns_sys/src/util/serialization/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod jsonbenc;
|
@@ -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)
|
||||
|
@@ -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 <stdint.h>
|
||||
|
||||
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
|
||||
|
@@ -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) {
|
||||
|
@@ -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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user