[WIP] wg: Support human-readable peer names

TODO:
* Refactor the inflatable_buffer code from ipc.c and use it for both
  the wgpeer_name array and the names.
* ugly_print, dumb_print
* Make wgpeer_names part of wgdevice?
* Check for dupes?
* Test!

Signed-off-by: Luis Ressel <aranea@aixah.de>
This commit is contained in:
Luis Ressel 2019-03-25 14:53:09 +01:00
parent 9a0fc56cdb
commit f50ef1676f
3 changed files with 185 additions and 4 deletions

139
src/tools/peer_names.c Normal file
View File

@ -0,0 +1,139 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 Luis Ressel <aranea@aixah.de>. All Rights Reserved.
*/
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "containers.h"
#include "encoding.h"
#include "peer_names.h"
static bool parse_peer_name(char *line, struct wgpeer_name *slot)
{
char *name;
size_t len;
name = strpbrk(line, " \t");
if (!name)
return false;
*name = '\0';
if (!key_from_base64(slot->public_key, line))
return false;
len = strlen(++name);
if (name[len - 1] == '\n')
name[len - 1] = '\0';
slot->name = strdup(name);
return !!slot->name;
}
static int peer_name_cmp(const void *a, const void *b)
{
const struct wgpeer_name *x = a, *y = b;
return memcmp(x->public_key, y->public_key, WG_KEY_LEN);
}
bool peer_names_open(struct wgpeer_names *names, struct wgdevice *device)
{
size_t max_peers = 1, peers = 0, buf_len = 0, path_len;
struct wgpeer_name *arr, *arr2;
char *buf = NULL, *path;
FILE *f;
path = getenv("WG_PEER_NAMES");
if (!path || !path[0]) {
errno = 0;
return false;
}
path_len = strlen(path);
if (path[path_len - 1] == '/') {
size_t dev_len = strlen(device->name);
char *path2 = malloc(path_len + dev_len + 1);
if (!path2)
return false;
memcpy(path2, path, path_len);
memcpy(path2 + path_len, device->name, dev_len + 1);
f = fopen(path2, "r");
free(path2);
} else
f = fopen(path, "r");
if (!f)
return false;
arr = malloc(sizeof(struct wgpeer_name));
if (!arr)
return false;
while (getline(&buf, &buf_len, f) >= 0) {
if (parse_peer_name(buf, &arr[peers]) && (++peers == max_peers)) {
// TODO: Pulled this overflow check out of my ass, gotta revisit it later
if (SIZE_MAX / (2 * sizeof(struct wgpeer_name)) < max_peers) {
errno = ENOMEM;
return false;
}
max_peers *= 2;
arr2 = realloc(arr, max_peers * sizeof(struct wgpeer_name));
if (!arr2) {
free(arr);
return false;
}
arr = arr2;
}
}
free(buf);
if (!peers)
free(arr);
if ((arr2 = realloc(arr, peers * sizeof(struct wgpeer_name))))
arr = arr2;
qsort(arr, peers, sizeof(struct wgpeer_name), peer_name_cmp);
names->len = peers;
names->arr = arr;
return true;
}
void peer_names_free(struct wgpeer_names *names)
{
if (!names)
return;
for (size_t i = 0; i < names->len; ++i)
free(names->arr[i].name);
if (names->len)
free(names->arr);
}
char *peer_names_get(struct wgpeer_names *names, uint8_t key[static WG_KEY_LEN])
{
size_t r, l = 0;
if (!names || !names->len)
return NULL;
r = names->len - 1;
while (l <= r) {
size_t m = l + (r - l) / 2;
int cmp = memcmp(key, names->arr[m].public_key, WG_KEY_LEN);
if (!cmp)
return names->arr[m].name;
else if (cmp > 0)
l = m + 1;
else if (!m)
break;
else
r = m - 1;
}
return NULL;
}

29
src/tools/peer_names.h Normal file
View File

@ -0,0 +1,29 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 Luis Ressel <aranea@aixah.de>. All Rights Reserved.
*/
#ifndef PEER_NAMES_H
#define PEER_NAMES_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "containers.h"
struct wgpeer_name {
char *name;
uint8_t public_key[WG_KEY_LEN];
};
struct wgpeer_names {
size_t len;
struct wgpeer_name *arr;
};
bool peer_names_open(struct wgpeer_names *names, struct wgdevice *device);
void peer_names_free(struct wgpeer_names *names);
char *peer_names_get(struct wgpeer_names *names, uint8_t key[static WG_KEY_LEN]);
#endif // PEER_NAMES_H

View File

@ -22,6 +22,7 @@
#include "ipc.h"
#include "terminal.h"
#include "encoding.h"
#include "peer_names.h"
#include "subcommands.h"
static int peer_cmp(const void *first, const void *second)
@ -205,8 +206,9 @@ static void show_usage(void)
fprintf(stderr, "Usage: %s %s { <interface> | all | interfaces } [public-key | private-key | listen-port | fwmark | peers | preshared-keys | endpoints | allowed-ips | latest-handshakes | transfer | persistent-keepalive | dump]\n", PROG_NAME, COMMAND_NAME);
}
static void pretty_print(struct wgdevice *device)
static void pretty_print(struct wgdevice *device, struct wgpeer_names *names)
{
char *name;
struct wgpeer *peer;
struct wgallowedip *allowedip;
@ -226,6 +228,8 @@ static void pretty_print(struct wgdevice *device)
}
for_each_wgpeer(device, peer) {
terminal_printf(TERMINAL_FG_YELLOW TERMINAL_BOLD "peer" TERMINAL_RESET ": " TERMINAL_FG_YELLOW "%s" TERMINAL_RESET "\n", key(peer->public_key));
if ((name = peer_names_get(names, peer->public_key)))
terminal_printf(" " TERMINAL_BOLD "name" TERMINAL_RESET ": %s\n", name);
if (peer->flags & WGPEER_HAS_PRESHARED_KEY)
terminal_printf(" " TERMINAL_BOLD "preshared key" TERMINAL_RESET ": %s\n", masked_key(peer->preshared_key));
if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6)
@ -398,6 +402,7 @@ int show_main(int argc, char *argv[])
interface = interfaces;
for (size_t len = 0; (len = strlen(interface)); interface += len + 1) {
struct wgdevice *device = NULL;
struct wgpeer_names names;
if (ipc_get_device(&device, interface) < 0) {
fprintf(stderr, "Unable to access interface %s: %s\n", interface, strerror(errno));
@ -410,9 +415,12 @@ int show_main(int argc, char *argv[])
break;
}
} else {
pretty_print(device);
if (!peer_names_open(&names, device) && errno)
perror("peer_names_open");
pretty_print(device, &names);
if (strlen(interface + len + 1))
printf("\n");
peer_names_free(&names);
}
free_wgdevice(device);
ret = 0;
@ -438,6 +446,7 @@ int show_main(int argc, char *argv[])
show_usage();
else {
struct wgdevice *device = NULL;
struct wgpeer_names names;
if (ipc_get_device(&device, argv[1]) < 0) {
perror("Unable to access interface");
@ -446,8 +455,12 @@ int show_main(int argc, char *argv[])
if (argc == 3) {
if (!ugly_print(device, argv[2], false))
ret = 1;
} else
pretty_print(device);
} else {
if (!peer_names_open(&names, device) && errno)
perror("peer_names_open");
pretty_print(device, &names);
peer_names_free(&names);
}
free_wgdevice(device);
}
return ret;