0
0
mirror of https://github.com/cjdelisle/cjdns synced 2025-10-06 08:42:43 +02:00
Files
cjdns/interface/UDPInterface_admin.c
2024-09-28 14:43:16 +00:00

429 lines
16 KiB
C

/* 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 "benc/Dict.h"
#include "benc/Int.h"
#include "admin/Admin.h"
#include "memory/Allocator.h"
#include "net/InterfaceController.h"
#include "rust/cjdns_sys/RTypes.h"
#include "rust/cjdns_sys/Rffi.h"
#include "util/events/EventBase.h"
#include "util/platform/Sockaddr.h"
#include "crypto/Key.h"
#include "interface/UDPInterface_admin.h"
#include "interface/UDPInterface.h"
#include "util/Identity.h"
#include "util/version/Version.h"
#define ArrayList_TYPE struct UDPInterface
#define ArrayList_NAME UDPInterface
#include "util/ArrayList.h"
struct Context
{
EventBase_t* eventBase;
struct Allocator* alloc;
struct Log* logger;
struct Admin* admin;
struct ArrayList_UDPInterface* ifaces;
struct InterfaceController* ic;
struct GlobalConfig* globalConf;
Identity
};
static struct UDPInterface* getIface(struct Context* ctx,
Dict* args,
String* txid,
struct Allocator* requestAlloc,
uint32_t* ifNumP)
{
int64_t* interfaceNumber = Dict_getIntC(args, "interfaceNumber");
uint32_t ifNum = (interfaceNumber) ? ((uint32_t) *interfaceNumber) : 0;
if (ifNumP) { *ifNumP = ifNum; }
struct UDPInterface* udpif = ArrayList_UDPInterface_get(ctx->ifaces, ifNum);
if (!udpif) {
Dict* out = Dict_new(requestAlloc);
Dict_putStringCC(out, "error", "no such interface for interfaceNumber", requestAlloc);
Admin_sendMessage(out, txid, ctx->admin);
}
return udpif;
}
static void beginConnection(Dict* args,
void* vcontext,
String* txid,
struct Allocator* requestAlloc)
{
struct Context* ctx = Identity_check((struct Context*) vcontext);
uint32_t ifNum = 0;
struct UDPInterface* udpIf = getIface(ctx, args, txid, requestAlloc, &ifNum);
if (!udpIf) { return; }
String* password = Dict_getStringC(args, "password");
String* login = Dict_getStringC(args, "login");
String* publicKey = Dict_getStringC(args, "publicKey");
String* address = Dict_getStringC(args, "address");
String* peerName = Dict_getStringC(args, "peerName");
int64_t* versionP = Dict_getIntC(args, "version");
int version = Version_DEFAULT_ASSUMPTION;
if (versionP) { version = *versionP; }
char* error = NULL;
Log_debug(ctx->logger, "Peering with [%s]", publicKey->bytes);
struct Sockaddr_storage ss;
uint8_t pkBytes[32];
int ret;
if ((ret = Key_parse(publicKey, pkBytes, NULL))) {
error = Key_parse_strerror(ret);
} else if (Sockaddr_parse(address->bytes, &ss)) {
error = "unable to parse ip address and port.";
} else if (Sockaddr_getFamily(&ss.addr) != Sockaddr_getFamily(udpIf->generic.addr)) {
error = "different address type than this socket is bound to.";
} else {
struct Sockaddr* addr = &ss.addr;
char* addrPtr = NULL;
int addrLen = Sockaddr_getAddress(&ss.addr, &addrPtr);
Assert_true(addrLen > 0);
struct Allocator* tempAlloc = Allocator_child(ctx->alloc);
if (Bits_isZero(addrPtr, addrLen)) {
// unspec'd address, convert to loopback
if (Sockaddr_getFamily(addr) == Sockaddr_AF_INET) {
addr = Sockaddr_clone(Sockaddr_LOOPBACK, tempAlloc);
} else if (Sockaddr_getFamily(addr) == Sockaddr_AF_INET6) {
addr = Sockaddr_clone(Sockaddr_LOOPBACK6, tempAlloc);
} else {
Assert_failure("Sockaddr which is not AF_INET nor AF_INET6");
}
Sockaddr_setPort(addr, Sockaddr_getPort(&ss.addr));
}
int ret = InterfaceController_bootstrapPeer(
ctx->ic, ifNum, pkBytes, addr, password, login, peerName, version);
Allocator_free(tempAlloc);
if (ret) {
switch(ret) {
case InterfaceController_bootstrapPeer_BAD_IFNUM:
// Should never happen, should be caught in getIface()
error = "interface deregistered";
break;
case InterfaceController_bootstrapPeer_BAD_KEY:
error = "invalid cjdns public key.";
break;
case InterfaceController_bootstrapPeer_OUT_OF_SPACE:
error = "no more space to register with the switch.";
break;
default:
error = "unknown error";
break;
}
} else {
error = "none";
}
}
Dict* out = Dict_new(requestAlloc);
Dict_putStringCC(out, "error", error, requestAlloc);
Admin_sendMessage(out, txid, ctx->admin);
}
static struct UDPInterface* setupLibuvUDP(struct Context* ctx,
struct Sockaddr* addr,
uint16_t beaconPort,
uint8_t dscp,
String* txid,
struct Allocator* alloc)
{
struct UDPInterface* udpIf = NULL;
RTypes_Error_t* er = UDPInterface_new(
&udpIf,
ctx->eventBase,
addr,
beaconPort,
alloc,
ctx->logger,
ctx->globalConf);
if (er) {
Dict* out = Dict_new(alloc);
const char* emsg = Rffi_printError(er, alloc);
Dict_putStringCC(out, "error", emsg, alloc);
Admin_sendMessage(out, txid, ctx->admin);
Allocator_free(alloc);
return NULL;
} else if (dscp) {
if (UDPInterface_setDSCP(udpIf, dscp)) {
Log_warn(ctx->logger, "Set DSCP failed");
}
}
return udpIf;
}
static void newInterface2(struct Context* ctx,
struct Sockaddr* addr,
uint8_t dscp,
String* txid,
struct Allocator* requestAlloc,
uint16_t beaconPort)
{
struct Allocator* const alloc = Allocator_child(ctx->alloc);
struct UDPInterface* udpif = setupLibuvUDP(ctx, addr, beaconPort, dscp, txid, alloc);
if (!udpif) { return; }
int af = Sockaddr_getFamily(addr);
String* name = String_printf(requestAlloc, "UDP/IPv%d/%s",
(af == Sockaddr_AF_INET ? 4 : 6),
Sockaddr_print(addr, requestAlloc));
struct InterfaceController_Iface* ici =
InterfaceController_newIface(ctx->ic, name, alloc);
ici->af = af;
Iface_plumb(&ici->addrIf, udpif->generic.iface);
ArrayList_UDPInterface_put(ctx->ifaces, ici->ifNum, udpif);
Dict* out = Dict_new(requestAlloc);
Dict_putStringCC(out, "error", "none", requestAlloc);
Dict_putIntC(out, "interfaceNumber", ici->ifNum, requestAlloc);
char* printedAddr = Sockaddr_print(udpif->generic.addr, requestAlloc);
Dict_putStringCC(out,
"bindAddress",
printedAddr,
requestAlloc);
Admin_sendMessage(out, txid, ctx->admin);
}
static void newInterface(Dict* args, void* vcontext, String* txid, struct Allocator* requestAlloc)
{
struct Context* ctx = Identity_check((struct Context*) vcontext);
String* bindAddress = Dict_getStringC(args, "bindAddress");
int64_t* dscpValue = Dict_getIntC(args, "dscp");
uint8_t dscp = dscpValue ? ((uint8_t) *dscpValue) : 0;
int64_t* beaconPort_p = Dict_getIntC(args, "beaconPort");
uint16_t beaconPort = beaconPort_p ? ((uint16_t) *beaconPort_p) : 0;
struct Sockaddr_storage addr;
if (Sockaddr_parse((bindAddress) ? bindAddress->bytes : "0.0.0.0", &addr)) {
Dict out = Dict_CONST(
String_CONST("error"), String_OBJ(String_CONST("Failed to parse address")), NULL
);
Admin_sendMessage(&out, txid, ctx->admin);
return;
}
newInterface2(ctx, &addr.addr, dscp, txid, requestAlloc, beaconPort);
}
static void listDevices(Gcc_UNUSED Dict* args, void* vcontext, String* txid, struct Allocator* requestAlloc)
{
struct Context* ctx = Identity_check((struct Context*) vcontext);
Dict* out = Dict_new(requestAlloc);
List* list = NULL;
RTypes_Error_t* er = UDPInterface_listDevices(&list, requestAlloc);
if (er) {
const char* emsg = Rffi_printError(er, requestAlloc);
Dict_putStringCC(out, "error", emsg, requestAlloc);
} else {
Dict_putListC(out, "ret", list, requestAlloc);
}
Admin_sendMessage(out, txid, ctx->admin);
}
static void setBroadcastDevices(
Dict* args, void* vcontext, String* txid, struct Allocator* requestAlloc)
{
struct Context* ctx = Identity_check((struct Context*) vcontext);
struct UDPInterface* udpif = getIface(ctx, args, txid, requestAlloc, NULL);
if (!udpif) { return; }
UDPInterface_setBroadcastDevices(udpif, Dict_getListC(args, "devices"));
Dict* out = Dict_new(requestAlloc);
Dict_putStringCC(out, "error", "none", requestAlloc);
Admin_sendMessage(out, txid, ctx->admin);
}
static void getBroadcastDevices(
Dict* args, void* vcontext, String* txid, struct Allocator* requestAlloc)
{
struct Context* ctx = Identity_check((struct Context*) vcontext);
struct UDPInterface* udpif = getIface(ctx, args, txid, requestAlloc, NULL);
if (!udpif) { return; }
Dict* out = Dict_new(requestAlloc);
Dict_putStringCC(out, "error", "none", requestAlloc);
List* devices = UDPInterface_getBroadcastDevices(udpif, requestAlloc);
Dict_putListC(out, "devices", devices, requestAlloc);
Admin_sendMessage(out, txid, ctx->admin);
}
static void getBroadcastAddrs(
Dict* args, void* vcontext, String* txid, struct Allocator* requestAlloc)
{
struct Context* ctx = Identity_check((struct Context*) vcontext);
struct UDPInterface* udpif = getIface(ctx, args, txid, requestAlloc, NULL);
if (!udpif) { return; }
Dict* out = Dict_new(requestAlloc);
Dict_putStringCC(out, "error", "none", requestAlloc);
List* addrs = UDPInterface_getBroadcastAddrs(udpif, requestAlloc);
Dict_putListC(out, "addrs", addrs, requestAlloc);
Admin_sendMessage(out, txid, ctx->admin);
}
static void beacon(Dict* args, void* vcontext, String* txid, struct Allocator* requestAlloc)
{
int64_t* stateP = Dict_getIntC(args, "state");
int64_t* ifNumP = Dict_getIntC(args, "interfaceNumber");
uint32_t ifNum = (ifNumP) ? ((uint32_t) *ifNumP) : 0;
uint32_t state = (stateP) ? ((uint32_t) *stateP) : 0xffffffff;
struct Context* ctx = Identity_check((struct Context*) vcontext);
char* error = NULL;
int ret = InterfaceController_beaconState(ctx->ic, ifNum, state);
if (ret == InterfaceController_beaconState_NO_SUCH_IFACE) {
error = "invalid interfaceNumber";
} else if (ret == InterfaceController_beaconState_INVALID_STATE) {
error = "invalid state";
} else if (ret) {
error = "internal";
}
if (error) {
Dict* out = Dict_new(requestAlloc);
Dict_putStringCC(out, "error", error, requestAlloc);
Admin_sendMessage(out, txid, ctx->admin);
return;
}
char* stateStr = "disabled";
if (state == InterfaceController_beaconState_newState_ACCEPT) {
stateStr = "accepting";
} else if (state == InterfaceController_beaconState_newState_SEND) {
stateStr = "sending and accepting";
}
Dict out = Dict_CONST(
String_CONST("error"), String_OBJ(String_CONST("none")), Dict_CONST(
String_CONST("state"), Int_OBJ(state), Dict_CONST(
String_CONST("stateName"), String_OBJ(String_CONST(stateStr)), NULL
)));
Admin_sendMessage(&out, txid, ctx->admin);
}
static void getFd(Dict* args, void* vcontext, String* txid, struct Allocator* requestAlloc)
{
struct Context* ctx = Identity_check((struct Context*) vcontext);
struct UDPInterface* udpif = getIface(ctx, args, txid, requestAlloc, NULL);
if (!udpif) {
return;
}
int fd = UDPInterface_getFd(udpif);
Dict* out = Dict_new(requestAlloc);
Dict_putIntC(out, "fd", fd, requestAlloc);
Dict_putStringCC(out, "error", "none", requestAlloc);
Admin_sendMessage(out, txid, ctx->admin);
}
static void workerStates(Dict* args, void* vcontext, String* txid, struct Allocator* requestAlloc) {
struct Context* ctx = Identity_check((struct Context*) vcontext);
struct UDPInterface* udpif = getIface(ctx, args, txid, requestAlloc, NULL);
Object_t* ws = NULL;
RTypes_Error_t* err = UDPInterface_workerStates(&ws, udpif, requestAlloc);
Dict* out = Dict_new(requestAlloc);
if (err) {
char* ers = Rffi_printError(err, requestAlloc);
Dict_putStringCC(out, "error", ers, requestAlloc);
} else {
Dict_putStringCC(out, "error", "none", requestAlloc);
Dict_putObject(out, String_CONST("workers"), ws, requestAlloc);
}
Admin_sendMessage(out, txid, ctx->admin);
}
void UDPInterface_admin_register(EventBase_t* base,
struct Allocator* alloc,
struct Log* logger,
struct Admin* admin,
struct InterfaceController* ic,
struct GlobalConfig* globalConf)
{
struct Context* ctx = Allocator_clone(alloc, (&(struct Context) {
.eventBase = base,
.alloc = alloc,
.logger = logger,
.admin = admin,
.ic = ic,
.globalConf = globalConf
}));
Identity_set(ctx);
ctx->ifaces = ArrayList_UDPInterface_new(alloc);
Admin_registerFunction("UDPInterface_new", newInterface, ctx, true,
((struct Admin_FunctionArg[]) {
{ .name = "bindAddress", .required = 0, .type = "String" },
{ .name = "dscp", .required = 0, .type = "Int" },
{ .name = "beaconPort", .required = 0, .type = "Int" }
}), admin);
Admin_registerFunction("UDPInterface_beginConnection", beginConnection, ctx, true,
((struct Admin_FunctionArg[]) {
{ .name = "interfaceNumber", .required = 0, .type = "Int" },
{ .name = "password", .required = 0, .type = "String" },
{ .name = "publicKey", .required = 1, .type = "String" },
{ .name = "address", .required = 1, .type = "String" },
{ .name = "login", .required = 0, .type = "String" },
{ .name = "peerName", .required = 0, .type = "String" },
{ .name = "version", .required = 0, .type = "Int" },
}), admin);
Admin_registerFunctionNoArgs("UDPInterface_listDevices", listDevices, ctx, true, admin);
Admin_registerFunction("UDPInterface_setBroadcastDevices", setBroadcastDevices, ctx, true,
((struct Admin_FunctionArg[]) {
{ .name = "interfaceNumber", .required = 0, .type = "Int" },
{ .name = "devices", .required = 1, .type = "List" }
}), admin);
Admin_registerFunction("UDPInterface_getBroadcastDevices", getBroadcastDevices, ctx, true,
((struct Admin_FunctionArg[]) {
{ .name = "interfaceNumber", .required = 0, .type = "Int" }
}), admin);
Admin_registerFunction("UDPInterface_getBroadcastAddrs", getBroadcastAddrs, ctx, true,
((struct Admin_FunctionArg[]) {
{ .name = "interfaceNumber", .required = 0, .type = "Int" }
}), admin);
Admin_registerFunction("UDPInterface_beacon", beacon, ctx, true,
((struct Admin_FunctionArg[]) {
{ .name = "interfaceNumber", .required = 0, .type = "Int" },
{ .name = "state", .required = 0, .type = "Int" }
}), admin);
Admin_registerFunction("UDPInterface_getFd", getFd, ctx, true,
((struct Admin_FunctionArg[]) {
{ .name = "interfaceNumber", .required = 0, .type = "Int" },
}), admin);
Admin_registerFunction("UDPInterface_workerStates", workerStates, ctx, true,
((struct Admin_FunctionArg[]) {
{ .name = "interfaceNumber", .required = 0, .type = "Int" },
}), admin);
}