amneziawg-tools/src/config.c
Mark Puha 09db5f7cb3
Some checks failed
Linux / Build for Ubuntu (push) Has been cancelled
Linux / Build for Alpine (push) Has been cancelled
Windows / Build for Windows (push) Has been cancelled
Linux / GitHub Release (push) Has been cancelled
Windows / GitHub Release (push) Has been cancelled
fix: showconf
2025-08-17 12:10:36 +02:00

1036 lines
27 KiB
C

// SPDX-License-Identifier: GPL-2.0 OR MIT
/*
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/
#include <arpa/inet.h>
#include <limits.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <errno.h>
#include "config.h"
#include "containers.h"
#include "ipc.h"
#include "encoding.h"
#include "ctype.h"
#define COMMENT_CHAR '#'
// Keys that should be not stripped of whitespace
static const char *awg_special_handshake_keys[] = {
"I1", "I2", "I3", "I4", "I5",
"J1", "J2", "J3",
NULL
};
static const char *get_value(const char *line, const char *key)
{
size_t linelen = strlen(line);
size_t keylen = strlen(key);
if (keylen >= linelen)
return NULL;
if (strncasecmp(line, key, keylen))
return NULL;
return line + keylen;
}
static inline bool parse_port(uint16_t *port, uint32_t *flags, const char *value)
{
int ret;
struct addrinfo *resolved;
struct addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_DGRAM,
.ai_protocol = IPPROTO_UDP,
.ai_flags = AI_PASSIVE
};
if (!strlen(value)) {
fprintf(stderr, "Unable to parse empty port\n");
return false;
}
ret = getaddrinfo(NULL, value, &hints, &resolved);
if (ret) {
fprintf(stderr, "%s: `%s'\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), value);
return false;
}
ret = -1;
if (resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) {
*port = ntohs(((struct sockaddr_in *)resolved->ai_addr)->sin_port);
ret = 0;
} else if (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6)) {
*port = ntohs(((struct sockaddr_in6 *)resolved->ai_addr)->sin6_port);
ret = 0;
} else
fprintf(stderr, "Neither IPv4 nor IPv6 address found: `%s'\n", value);
freeaddrinfo(resolved);
if (!ret)
*flags |= WGDEVICE_HAS_LISTEN_PORT;
return ret == 0;
}
static inline bool parse_fwmark(uint32_t *fwmark, uint32_t *flags, const char *value)
{
unsigned long ret;
char *end;
int base = 10;
if (!strcasecmp(value, "off")) {
*fwmark = 0;
*flags |= WGDEVICE_HAS_FWMARK;
return true;
}
if (!char_is_digit(value[0]))
goto err;
if (strlen(value) > 2 && value[0] == '0' && value[1] == 'x')
base = 16;
ret = strtoul(value, &end, base);
if (*end || ret > UINT32_MAX)
goto err;
*fwmark = ret;
*flags |= WGDEVICE_HAS_FWMARK;
return true;
err:
fprintf(stderr, "Fwmark is neither 0/off nor 0-0xffffffff: `%s'\n", value);
return false;
}
static inline bool parse_key(uint8_t key[static WG_KEY_LEN], const char *value)
{
if (!key_from_base64(key, value)) {
fprintf(stderr, "Key is not the correct length or format: `%s'\n", value);
memset(key, 0, WG_KEY_LEN);
return false;
}
return true;
}
static bool parse_keyfile(uint8_t key[static WG_KEY_LEN], const char *path)
{
FILE *f;
int c;
char dst[WG_KEY_LEN_BASE64];
bool ret = false;
f = fopen(path, "r");
if (!f) {
perror("fopen");
return false;
}
if (fread(dst, WG_KEY_LEN_BASE64 - 1, 1, f) != 1) {
/* If we're at the end and we didn't read anything, we're /dev/null or an empty file. */
if (!ferror(f) && feof(f) && !ftell(f)) {
memset(key, 0, WG_KEY_LEN);
ret = true;
goto out;
}
fprintf(stderr, "Invalid length key in key file\n");
goto out;
}
dst[WG_KEY_LEN_BASE64 - 1] = '\0';
while ((c = getc(f)) != EOF) {
if (!char_is_space(c)) {
fprintf(stderr, "Found trailing character in key file: `%c'\n", c);
goto out;
}
}
if (ferror(f) && errno) {
perror("getc");
goto out;
}
ret = parse_key(key, dst);
out:
fclose(f);
return ret;
}
static inline bool parse_ip(struct wgallowedip *allowedip, const char *value)
{
allowedip->family = AF_UNSPEC;
if (strchr(value, ':')) {
if (inet_pton(AF_INET6, value, &allowedip->ip6) == 1)
allowedip->family = AF_INET6;
} else {
if (inet_pton(AF_INET, value, &allowedip->ip4) == 1)
allowedip->family = AF_INET;
}
if (allowedip->family == AF_UNSPEC) {
fprintf(stderr, "Unable to parse IP address: `%s'\n", value);
return false;
}
return true;
}
static inline int parse_dns_retries(void)
{
unsigned long ret;
char *retries = getenv("WG_ENDPOINT_RESOLUTION_RETRIES"), *end;
if (!retries)
return 15;
if (!strcmp(retries, "infinity"))
return -1;
ret = strtoul(retries, &end, 10);
if (*end || ret > INT_MAX) {
fprintf(stderr, "Unable to parse WG_ENDPOINT_RESOLUTION_RETRIES: `%s'\n", retries);
exit(1);
}
return (int)ret;
}
static inline bool parse_endpoint(struct sockaddr *endpoint, const char *value)
{
char *mutable = strdup(value);
char *begin, *end;
int ret, retries = parse_dns_retries();
struct addrinfo *resolved;
struct addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_DGRAM,
.ai_protocol = IPPROTO_UDP
};
if (!mutable) {
perror("strdup");
return false;
}
if (!strlen(value)) {
free(mutable);
fprintf(stderr, "Unable to parse empty endpoint\n");
return false;
}
if (mutable[0] == '[') {
begin = &mutable[1];
end = strchr(mutable, ']');
if (!end) {
free(mutable);
fprintf(stderr, "Unable to find matching brace of endpoint: `%s'\n", value);
return false;
}
*end++ = '\0';
if (*end++ != ':' || !*end) {
free(mutable);
fprintf(stderr, "Unable to find port of endpoint: `%s'\n", value);
return false;
}
} else {
begin = mutable;
end = strrchr(mutable, ':');
if (!end || !*(end + 1)) {
free(mutable);
fprintf(stderr, "Unable to find port of endpoint: `%s'\n", value);
return false;
}
*end++ = '\0';
}
#define min(a, b) ((a) < (b) ? (a) : (b))
for (unsigned int timeout = 1000000;; timeout = min(20000000, timeout * 6 / 5)) {
ret = getaddrinfo(begin, end, &hints, &resolved);
if (!ret)
break;
/* The set of return codes that are "permanent failures". All other possibilities are potentially transient.
*
* This is according to https://sourceware.org/glibc/wiki/NameResolver which states:
* "From the perspective of the application that calls getaddrinfo() it perhaps
* doesn't matter that much since EAI_FAIL, EAI_NONAME and EAI_NODATA are all
* permanent failure codes and the causes are all permanent failures in the
* sense that there is no point in retrying later."
*
* So this is what we do, except FreeBSD removed EAI_NODATA some time ago, so that's conditional.
*/
if (ret == EAI_NONAME || ret == EAI_FAIL ||
#ifdef EAI_NODATA
ret == EAI_NODATA ||
#endif
(retries >= 0 && !retries--)) {
free(mutable);
fprintf(stderr, "%s: `%s'\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), value);
return false;
}
fprintf(stderr, "%s: `%s'. Trying again in %.2f seconds...\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), value, timeout / 1000000.0);
usleep(timeout);
}
if ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) ||
(resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6)))
memcpy(endpoint, resolved->ai_addr, resolved->ai_addrlen);
else {
freeaddrinfo(resolved);
free(mutable);
fprintf(stderr, "Neither IPv4 nor IPv6 address found: `%s'\n", value);
return false;
}
freeaddrinfo(resolved);
free(mutable);
return true;
}
static inline bool parse_persistent_keepalive(uint16_t *interval, uint32_t *flags, const char *value)
{
unsigned long ret;
char *end;
if (!strcasecmp(value, "off")) {
*interval = 0;
*flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
return true;
}
if (!char_is_digit(value[0]))
goto err;
ret = strtoul(value, &end, 10);
if (*end || ret > 65535)
goto err;
*interval = (uint16_t)ret;
*flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
return true;
err:
fprintf(stderr, "Persistent keepalive interval is neither 0/off nor 1-65535: `%s'\n", value);
return false;
}
static bool validate_netmask(struct wgallowedip *allowedip)
{
uint32_t *ip;
int last;
switch (allowedip->family) {
case AF_INET:
last = 0;
ip = (uint32_t *)&allowedip->ip4;
break;
case AF_INET6:
last = 3;
ip = (uint32_t *)&allowedip->ip6;
break;
default:
return true; /* We don't know how to validate it, so say 'okay'. */
}
for (int i = last; i >= 0; --i) {
uint32_t mask = ~0;
if (allowedip->cidr >= 32 * (i + 1))
break;
if (allowedip->cidr > 32 * i)
mask >>= (allowedip->cidr - 32 * i);
if (ntohl(ip[i]) & mask)
return false;
}
return true;
}
static inline bool parse_allowedips(struct wgpeer *peer, struct wgallowedip **last_allowedip, const char *value)
{
struct wgallowedip *allowedip = *last_allowedip, *new_allowedip;
char *mask, *mutable = strdup(value), *sep, *saved_entry;
if (!mutable) {
perror("strdup");
return false;
}
peer->flags |= WGPEER_REPLACE_ALLOWEDIPS;
if (!strlen(value)) {
free(mutable);
return true;
}
sep = mutable;
while ((mask = strsep(&sep, ","))) {
unsigned long cidr;
char *end, *ip;
saved_entry = strdup(mask);
ip = strsep(&mask, "/");
new_allowedip = calloc(1, sizeof(*new_allowedip));
if (!new_allowedip) {
perror("calloc");
free(saved_entry);
free(mutable);
return false;
}
if (!parse_ip(new_allowedip, ip)) {
free(new_allowedip);
free(saved_entry);
free(mutable);
return false;
}
if (mask) {
if (!char_is_digit(mask[0]))
goto err;
cidr = strtoul(mask, &end, 10);
if (*end || (cidr > 32 && new_allowedip->family == AF_INET) || (cidr > 128 && new_allowedip->family == AF_INET6))
goto err;
} else if (new_allowedip->family == AF_INET)
cidr = 32;
else if (new_allowedip->family == AF_INET6)
cidr = 128;
else
goto err;
new_allowedip->cidr = cidr;
if (!validate_netmask(new_allowedip))
fprintf(stderr, "Warning: AllowedIP has nonzero host part: %s/%s\n", ip, mask);
if (allowedip)
allowedip->next_allowedip = new_allowedip;
else
peer->first_allowedip = new_allowedip;
allowedip = new_allowedip;
free(saved_entry);
}
free(mutable);
*last_allowedip = allowedip;
return true;
err:
free(new_allowedip);
free(mutable);
fprintf(stderr, "AllowedIP is not in the correct format: `%s'\n", saved_entry);
free(saved_entry);
return false;
}
static inline bool parse_awg_string(char **device_value, const char *name, const char *value) {
size_t len = strlen(value);
if (!len) {
*device_value = NULL;
return true;
}
if (len >= MAX_AWG_STRING_LEN) {
fprintf(stderr, "Unable to process string for: %s; longer than: %d\n", name, MAX_AWG_STRING_LEN);
return false;
}
*device_value = strdup(value);
if (*device_value == NULL) {
perror("strdup");
return false;
}
return true;
}
static inline bool parse_uint16(uint16_t *device_value, const char *name, const char *value) {
if (!strlen(value)) {
fprintf(stderr, "Unable to parse empty string\n");
return false;
}
char *end;
uint32_t ret;
ret = strtoul(value, &end, 10);
if (*end || ret > UINT16_MAX) {
fprintf(stderr, "Unable to parse %s: `%s'\n", name, value);
exit(1);
}
*device_value = (uint16_t)ret;
return true;
}
static inline bool parse_uint32(uint32_t *device_value, const char *name, const char *value) {
if (!strlen(value)) {
fprintf(stderr, "Unable to parse empty string\n");
return false;
}
char *end;
uint64_t ret;
ret = strtoul(value, &end, 10);
if (*end || ret > UINT32_MAX) {
fprintf(stderr, "Unable to parse %s: `%s'\n", name, value);
exit(1);
}
*device_value = (uint32_t)ret;
return true;
}
static inline bool parse_bool(bool *device_value, const char *name, const char *value) {
if (!strlen(value)) {
fprintf(stderr, "Unable to parse empty string\n");
return false;
}
if (!strcasecmp(value, "off")) {
*device_value = false;
return true;
}
if (!strcasecmp(value, "on")) {
*device_value = true;
return true;
}
if (!char_is_digit(value[0]))
goto err;
char *end;
uint32_t ret;
ret = strtoul(value, &end, 10);
if (*end) {
fprintf(stderr, "Unable to parse %s: `%s'\n", name, value);
exit(1);
}
*device_value = ret != 0;
return true;
err:
fprintf(stderr, "Boolean value is neither on/off nor 0/1: `%s'\n", value);
return false;
}
static bool process_line(struct config_ctx *ctx, const char *line)
{
const char *value;
bool ret = true;
if (!strcasecmp(line, "[Interface]")) {
ctx->is_peer_section = false;
ctx->is_device_section = true;
return true;
}
if (!strcasecmp(line, "[Peer]")) {
struct wgpeer *new_peer = calloc(1, sizeof(struct wgpeer));
if (!new_peer) {
perror("calloc");
return false;
}
ctx->last_allowedip = NULL;
if (ctx->last_peer)
ctx->last_peer->next_peer = new_peer;
else
ctx->device->first_peer = new_peer;
ctx->last_peer = new_peer;
ctx->is_peer_section = true;
ctx->is_device_section = false;
ctx->last_peer->flags |= WGPEER_REPLACE_ALLOWEDIPS;
return true;
}
#define key_match(key) (value = get_value(line, key "="))
if (ctx->is_device_section) {
if (key_match("ListenPort"))
ret = parse_port(&ctx->device->listen_port, &ctx->device->flags, value);
else if (key_match("FwMark"))
ret = parse_fwmark(&ctx->device->fwmark, &ctx->device->flags, value);
else if (key_match("PrivateKey")) {
ret = parse_key(ctx->device->private_key, value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_PRIVATE_KEY;
} else if (key_match("Jc")) {
ret = parse_uint16(&ctx->device->junk_packet_count, "Jc", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_JC;
} else if (key_match("Jmin")) {
ret = parse_uint16(&ctx->device->junk_packet_min_size, "Jmin", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_JMIN;
} else if (key_match("Jmax")) {
ret = parse_uint16(&ctx->device->junk_packet_max_size, "Jmax", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_JMAX;
} else if (key_match("S1")) {
ret = parse_uint16(&ctx->device->init_packet_junk_size, "S1", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_S1;
} else if (key_match("S2")) {
ret = parse_uint16(&ctx->device->response_packet_junk_size, "S2", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_S2;
} else if (key_match("S3")) {
ret = parse_uint16(&ctx->device->cookie_reply_packet_junk_size, "S3", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_S3;
} else if (key_match("S4")) {
ret = parse_uint16(&ctx->device->transport_packet_junk_size, "S4", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_S4;
} else if (key_match("H1")) {
ret = parse_awg_string(&ctx->device->init_packet_magic_header, "H1", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_H1;
} else if (key_match("H2")) {
ret = parse_awg_string(&ctx->device->response_packet_magic_header, "H2", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_H2;
} else if (key_match("H3")) {
ret = parse_awg_string(&ctx->device->underload_packet_magic_header, "H3", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_H3;
} else if (key_match("H4")) {
ret = parse_awg_string(&ctx->device->transport_packet_magic_header, "H4", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_H4;
} else if (key_match("I1")) {
ret = parse_awg_string(&ctx->device->i1, "I1", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_I1;
} else if (key_match("I2")) {
ret = parse_awg_string(&ctx->device->i2, "I2", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_I2;
} else if (key_match("I3")) {
ret = parse_awg_string(&ctx->device->i3, "I3", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_I3;
} else if (key_match("I4")) {
ret = parse_awg_string(&ctx->device->i4, "I4", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_I4;
} else if (key_match("I5")) {
ret = parse_awg_string(&ctx->device->i5, "I5", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_I5;
} else if (key_match("J1")) {
ret = parse_awg_string(&ctx->device->j1, "J1", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_J1;
} else if (key_match("J2")) {
ret = parse_awg_string(&ctx->device->j2, "J2", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_J2;
} else if (key_match("J3")) {
ret = parse_awg_string(&ctx->device->j3, "J3", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_J3;
} else if (key_match("Itime")) {
ret = parse_uint32(&ctx->device->itime, "Itime", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_ITIME;
} else {
goto error;
}
} else if (ctx->is_peer_section) {
if (key_match("Endpoint"))
ret = parse_endpoint(&ctx->last_peer->endpoint.addr, value);
else if (key_match("PublicKey")) {
ret = parse_key(ctx->last_peer->public_key, value);
if (ret)
ctx->last_peer->flags |= WGPEER_HAS_PUBLIC_KEY;
} else if (key_match("AllowedIPs"))
ret = parse_allowedips(ctx->last_peer, &ctx->last_allowedip, value);
else if (key_match("PersistentKeepalive"))
ret = parse_persistent_keepalive(&ctx->last_peer->persistent_keepalive_interval, &ctx->last_peer->flags, value);
else if (key_match("PresharedKey")) {
ret = parse_key(ctx->last_peer->preshared_key, value);
if (ret)
ctx->last_peer->flags |= WGPEER_HAS_PRESHARED_KEY;
} else if (key_match("AdvancedSecurity")) {
ret = parse_bool(&ctx->last_peer->awg, "AdvancedSecurity", value);
if (ret)
ctx->last_peer->flags |= WGPEER_HAS_AWG;
} else
goto error;
} else
goto error;
return ret;
#undef key_match
error:
fprintf(stderr, "Line unrecognized: `%s'\n", line);
return false;
}
bool config_read_line(struct config_ctx *ctx, const char *input)
{
size_t len, cleaned_len = 0;
char *line, *comment;
bool ret = true;
/* This is what strchrnul is for, but that isn't portable. */
comment = strchr(input, COMMENT_CHAR);
if (comment)
len = comment - input;
else
len = strlen(input);
line = calloc(len + 1, sizeof(char));
if (!line) {
perror("calloc");
ret = false;
goto out;
}
bool is_awg_special_handshake_key = false;
for (size_t i = 0; awg_special_handshake_keys[i] != NULL; i++) {
if (!strncasecmp(input, awg_special_handshake_keys[i], 2)) {
is_awg_special_handshake_key = true;
break;
}
}
if (is_awg_special_handshake_key) {
cleaned_len = clean_special_handshake_line(input, len, line);
} else {
for (size_t i = 0; i < len; ++i) {
if (!char_is_space(input[i])) {
line[cleaned_len++] = input[i];
}
}
}
if (!cleaned_len)
goto out;
ret = process_line(ctx, line);
out:
free(line);
if (!ret)
free_wgdevice(ctx->device);
return ret;
}
size_t clean_special_handshake_line(const char *input, size_t len, char *line)
{
size_t cleaned_len = 0, value_end = 0;
bool found_equals = false, found_value_start = false;
/* Remove preceding and trailing whitespaces before value
First pass: find the actual end of the value (trim trailing spaces) */
for (size_t i = len; i > 0; --i) {
if (!char_is_space(input[i - 1])) {
value_end = i;
break;
}
}
/* Second pass: clean according to KEY = VALUE rules */
for (size_t i = 0; i < value_end; ++i) {
if (!found_equals) {
/* Before '=': remove all whitespace */
if (input[i] == '=') {
line[cleaned_len++] = input[i];
found_equals = true;
} else if (!char_is_space(input[i])) {
line[cleaned_len++] = input[i];
}
} else if (!found_value_start) {
/* After '=' but before value: skip whitespace until first non-space */
if (!char_is_space(input[i])) {
line[cleaned_len++] = input[i];
found_value_start = true;
}
} else {
/* Within value: preserve all characters including spaces */
line[cleaned_len++] = input[i];
}
}
return cleaned_len;
}
bool config_read_init(struct config_ctx *ctx, bool append)
{
memset(ctx, 0, sizeof(*ctx));
ctx->device = calloc(1, sizeof(*ctx->device));
if (!ctx->device) {
perror("calloc");
return false;
}
if (!append)
ctx->device->flags |= WGDEVICE_REPLACE_PEERS | WGDEVICE_HAS_PRIVATE_KEY | WGDEVICE_HAS_FWMARK;
return true;
}
struct wgdevice *config_read_finish(struct config_ctx *ctx)
{
struct wgpeer *peer;
for_each_wgpeer(ctx->device, peer) {
if (!(peer->flags & WGPEER_HAS_PUBLIC_KEY)) {
fprintf(stderr, "A peer is missing a public key\n");
goto err;
}
}
return ctx->device;
err:
free_wgdevice(ctx->device);
return NULL;
}
static char *strip_spaces(const char *in)
{
char *out;
size_t t, l, i;
t = strlen(in);
out = calloc(t + 1, sizeof(char));
if (!out) {
perror("calloc");
return NULL;
}
for (i = 0, l = 0; i < t; ++i) {
if (!char_is_space(in[i]))
out[l++] = in[i];
}
return out;
}
struct wgdevice *config_read_cmd(const char *argv[], int argc)
{
struct wgdevice *device = calloc(1, sizeof(*device));
struct wgpeer *peer = NULL;
struct wgallowedip *allowedip = NULL;
if (!device) {
perror("calloc");
return false;
}
while (argc > 0) {
if (!strcmp(argv[0], "listen-port") && argc >= 2 && !peer) {
if (!parse_port(&device->listen_port, &device->flags, argv[1]))
goto error;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "fwmark") && argc >= 2 && !peer) {
if (!parse_fwmark(&device->fwmark, &device->flags, argv[1]))
goto error;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "private-key") && argc >= 2 && !peer) {
if (!parse_keyfile(device->private_key, argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_PRIVATE_KEY;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "jc") && argc >= 2 && !peer) {
if (!parse_uint16(&device->junk_packet_count, "jc", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_JC;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "jmin") && argc >= 2 && !peer) {
if (!parse_uint16(&device->junk_packet_min_size, "jmin", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_JMIN;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "jmax") && argc >= 2 && !peer) {
if (!parse_uint16(&device->junk_packet_max_size, "jmax", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_JMAX;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "s1") && argc >= 2 && !peer) {
if (!parse_uint16(&device->init_packet_junk_size, "s1", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_S1;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "s2") && argc >= 2 && !peer) {
if (!parse_uint16(&device->response_packet_junk_size, "s2", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_S2;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "s3") && argc >= 2 && !peer) {
if (!parse_uint16(&device->cookie_reply_packet_junk_size, "s3", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_S3;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "s4") && argc >= 2 && !peer) {
if (!parse_uint16(&device->transport_packet_junk_size, "s4", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_S4;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "h1") && argc >= 2 && !peer) {
if (!parse_awg_string(&device->init_packet_magic_header, "h1", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_H1;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "h2") && argc >= 2 && !peer) {
if (!parse_awg_string(&device->response_packet_magic_header, "h2", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_H2;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "h3") && argc >= 2 && !peer) {
if (!parse_awg_string(&device->underload_packet_magic_header, "h3", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_H3;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "h4") && argc >= 2 && !peer) {
if (!parse_awg_string(&device->transport_packet_magic_header, "h4", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_H4;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "i1") && argc >= 2 && !peer) {
if (!parse_awg_string(&device->i1, "i1", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_I1;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "i2") && argc >= 2 && !peer) {
if (!parse_awg_string(&device->i2, "i2", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_I2;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "i3") && argc >= 2 && !peer) {
if (!parse_awg_string(&device->i3, "i3", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_I3;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "i4") && argc >= 2 && !peer) {
if (!parse_awg_string(&device->i4, "i4", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_I4;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "i5") && argc >= 2 && !peer) {
if (!parse_awg_string(&device->i5, "i5", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_I5;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "j1") && argc >= 2 && !peer) {
if (!parse_awg_string(&device->j1, "j1", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_J1;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "j2") && argc >= 2 && !peer) {
if (!parse_awg_string(&device->j2, "j2", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_J2;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "j3") && argc >= 2 && !peer) {
if (!parse_awg_string(&device->j3, "j3", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_J3;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "itime") && argc >= 2 && !peer) {
if (!parse_uint32(&device->itime, "itime", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_ITIME;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "peer") && argc >= 2) {
struct wgpeer *new_peer = calloc(1, sizeof(*new_peer));
allowedip = NULL;
if (!new_peer) {
perror("calloc");
goto error;
}
if (peer)
peer->next_peer = new_peer;
else
device->first_peer = new_peer;
peer = new_peer;
if (!parse_key(peer->public_key, argv[1]))
goto error;
peer->flags |= WGPEER_HAS_PUBLIC_KEY;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "remove") && argc >= 1 && peer) {
peer->flags |= WGPEER_REMOVE_ME;
argv += 1;
argc -= 1;
} else if (!strcmp(argv[0], "endpoint") && argc >= 2 && peer) {
if (!parse_endpoint(&peer->endpoint.addr, argv[1]))
goto error;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "allowed-ips") && argc >= 2 && peer) {
char *line = strip_spaces(argv[1]);
if (!line)
goto error;
if (!parse_allowedips(peer, &allowedip, line)) {
free(line);
goto error;
}
free(line);
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "persistent-keepalive") && argc >= 2 && peer) {
if (!parse_persistent_keepalive(&peer->persistent_keepalive_interval, &peer->flags, argv[1]))
goto error;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "preshared-key") && argc >= 2 && peer) {
if (!parse_keyfile(peer->preshared_key, argv[1]))
goto error;
peer->flags |= WGPEER_HAS_PRESHARED_KEY;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "advanced-security") && argc >= 2 && peer) {
if (!parse_bool(&peer->awg, "AdvancedSecurity", argv[1]))
goto error;
peer->flags |= WGPEER_HAS_AWG;
argv += 2;
argc -= 2;
} else {
fprintf(stderr, "Invalid argument: %s\n", argv[0]);
goto error;
}
}
return device;
error:
free_wgdevice(device);
return false;
}