gluon/package/gluon-mesh-vpn-wireguard/src/respondd.c
2023-01-09 22:37:19 +01:00

210 lines
5.1 KiB
C

/* SPDX-FileCopyrightText: 2020, Leonardo Mörlein <me@irrelefant.net> */
/* SPDX-FileCopyrightText: 2016, Matthias Schiffer <mschiffer@universe-factory.net> */
/* SPDX-License-Identifier: BSD-2-Clause */
#include <respondd.h>
#include <json-c/json.h>
#include <libgluonutil.h>
#include <uci.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "libubus.h"
static struct json_object * stdout_read(const char *cmd, const char *skip, bool oneword) {
FILE *f = popen(cmd, "r");
if (!f)
return NULL;
char *line = NULL;
size_t len = 0;
size_t skiplen = strlen(skip);
ssize_t read_chars = getline(&line, &len, f);
pclose(f);
if (read_chars < 1) {
free(line);
return NULL;
}
if (line[read_chars-1] == '\n')
line[read_chars-1] = '\0';
const char *content = line;
if (strncmp(content, skip, skiplen) == 0)
content += skiplen;
if (oneword) {
for (int i = 0; i < len; i++) {
if (isspace(line[i])) {
line[i] = 0;
}
}
}
struct json_object *ret = gluonutil_wrap_string(content);
free(line);
return ret;
}
static struct json_object * get_wireguard_public_key(void) {
return stdout_read("exec /lib/gluon/mesh-vpn/wireguard_pubkey.sh", "", false);
}
static struct json_object * get_wireguard_version(void) {
return stdout_read("exec wg -v", "wireguard-tools ", true);
}
static bool wireguard_enabled(void) {
bool enabled = true;
struct uci_context *ctx = uci_alloc_context();
if (!ctx)
goto disabled_nofree;
ctx->flags &= ~UCI_FLAG_STRICT;
struct uci_package *p;
if (uci_load(ctx, "network", &p))
goto disabled;
struct uci_section *s = uci_lookup_section(ctx, p, "wg_mesh");
if (!s)
goto disabled;
const char *disabled_str = uci_lookup_option_string(ctx, s, "disabled");
if (!disabled_str || !strcmp(disabled_str, "1"))
enabled = false;
disabled:
uci_free_context(ctx);
disabled_nofree:
return enabled;
}
static bool get_pubkey_privacy(void) {
bool ret = true;
struct json_object *site = NULL;
site = gluonutil_load_site_config();
if (!site)
goto end;
struct json_object *mesh_vpn;
if (!json_object_object_get_ex(site, "mesh_vpn", &mesh_vpn))
goto end;
struct json_object *pubkey_privacy;
if (!json_object_object_get_ex(mesh_vpn, "pubkey_privacy", &pubkey_privacy))
goto end;
ret = json_object_get_boolean(pubkey_privacy);
end:
json_object_put(site);
return ret;
}
static struct json_object * get_wireguard(void) {
bool wg_enabled = wireguard_enabled();
struct json_object *ret = json_object_new_object();
json_object_object_add(ret, "version", get_wireguard_version());
json_object_object_add(ret, "enabled", json_object_new_boolean(wg_enabled));
if (wg_enabled && !get_pubkey_privacy())
json_object_object_add(ret, "public_key", get_wireguard_public_key());
return ret;
}
static struct json_object * respondd_provider_nodeinfo(void) {
struct json_object *ret = json_object_new_object();
struct json_object *software = json_object_new_object();
json_object_object_add(software, "wireguard", get_wireguard());
json_object_object_add(ret, "software", software);
return ret;
}
static json_object *blobmsg_attr2json(struct blob_attr *attr, int type)
{
int len = blobmsg_data_len(attr);
struct blobmsg_data *data = blobmsg_data(attr);
struct blob_attr *inner_attr;
json_object *res = NULL;
switch(type) {
case BLOBMSG_TYPE_STRING:
return gluonutil_wrap_string(blobmsg_get_string(attr));
case BLOBMSG_TYPE_BOOL:
return json_object_new_boolean(blobmsg_get_bool(attr));
case BLOBMSG_TYPE_INT16:
return json_object_new_double(blobmsg_get_u16(attr));
case BLOBMSG_TYPE_INT32:
return json_object_new_double(blobmsg_get_u32(attr));
case BLOBMSG_TYPE_INT64:
return json_object_new_double(blobmsg_get_u64(attr));
case BLOBMSG_TYPE_DOUBLE:
return json_object_new_double(blobmsg_get_double(attr));
case BLOBMSG_TYPE_TABLE:
res = json_object_new_object();
__blob_for_each_attr(inner_attr, data, len) {
json_object_object_add(res, blobmsg_name(inner_attr), blobmsg_attr2json(inner_attr, blobmsg_type(inner_attr)));
};
break;
case BLOBMSG_TYPE_ARRAY:
res = json_object_new_array();
__blob_for_each_attr(inner_attr, data, len) {
json_object_array_add(res, blobmsg_attr2json(inner_attr, blobmsg_type(inner_attr)));
}
break;
}
return res;
}
static void cb_wgpeerselector_vpn(struct ubus_request *req, int type, struct blob_attr *msg)
{
json_object_object_add(req->priv, "mesh_vpn", blobmsg_attr2json(msg, type));
}
static struct json_object * respondd_provider_statistics(void) {
struct json_object *ret = json_object_new_object();
struct ubus_context *ctx = ubus_connect(NULL);
uint32_t ubus_path_id;
if (!ctx) {
fprintf(stderr, "Error in gluon-mesh-vpn-wireguard.so: Failed to connect to ubus.\n");
goto err;
}
if (ubus_lookup_id(ctx, "wgpeerselector.wg_mesh", &ubus_path_id)) {
goto err;
}
ubus_invoke(ctx, ubus_path_id, "status", NULL, cb_wgpeerselector_vpn, ret, 1000);
err:
if (ctx)
ubus_free(ctx);
return ret;
}
const struct respondd_provider_info respondd_providers[] = {
{"nodeinfo", respondd_provider_nodeinfo},
{"statistics", respondd_provider_statistics},
{}
};