1 // SPDX-License-Identifier: GPL-2.0 OR MIT
3 * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
13 #include <sys/socket.h>
18 #include "containers.h"
23 #define COMMENT_CHAR '#'
25 static const char *get_value(const char *line, const char *key)
27 size_t linelen = strlen(line);
28 size_t keylen = strlen(key);
30 if (keylen >= linelen)
33 if (strncasecmp(line, key, keylen))
39 static inline bool parse_port(uint16_t *port, uint32_t *flags, const char *value)
42 struct addrinfo *resolved;
43 struct addrinfo hints = {
44 .ai_family = AF_UNSPEC,
45 .ai_socktype = SOCK_DGRAM,
46 .ai_protocol = IPPROTO_UDP,
47 .ai_flags = AI_PASSIVE
51 fprintf(stderr, "Unable to parse empty port\n");
55 ret = getaddrinfo(NULL, value, &hints, &resolved);
57 fprintf(stderr, "%s: `%s'\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), value);
62 if (resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) {
63 *port = ntohs(((struct sockaddr_in *)resolved->ai_addr)->sin_port);
65 } else if (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6)) {
66 *port = ntohs(((struct sockaddr_in6 *)resolved->ai_addr)->sin6_port);
69 fprintf(stderr, "Neither IPv4 nor IPv6 address found: `%s'\n", value);
71 freeaddrinfo(resolved);
73 *flags |= WGDEVICE_HAS_LISTEN_PORT;
77 static inline bool parse_fwmark(uint32_t *fwmark, uint32_t *flags, const char *value)
83 if (!strcasecmp(value, "off")) {
85 *flags |= WGDEVICE_HAS_FWMARK;
89 if (!char_is_digit(value[0]))
92 if (strlen(value) > 2 && value[0] == '0' && value[1] == 'x')
95 ret = strtoul(value, &end, base);
96 if (*end || ret > UINT32_MAX)
100 *flags |= WGDEVICE_HAS_FWMARK;
103 fprintf(stderr, "Fwmark is neither 0/off nor 0-0xffffffff: `%s'\n", value);
107 static inline bool parse_key(uint8_t key[static WG_KEY_LEN], const char *value)
109 if (!key_from_base64(key, value)) {
110 fprintf(stderr, "Key is not the correct length or format: `%s'\n", value);
111 memset(key, 0, WG_KEY_LEN);
117 static bool parse_keyfile(uint8_t key[static WG_KEY_LEN], const char *path)
121 char dst[WG_KEY_LEN_BASE64];
124 f = fopen(path, "r");
130 if (fread(dst, WG_KEY_LEN_BASE64 - 1, 1, f) != 1) {
131 /* If we're at the end and we didn't read anything, we're /dev/null or an empty file. */
132 if (!ferror(f) && feof(f) && !ftell(f)) {
133 memset(key, 0, WG_KEY_LEN);
138 fprintf(stderr, "Invalid length key in key file\n");
141 dst[WG_KEY_LEN_BASE64 - 1] = '\0';
143 while ((c = getc(f)) != EOF) {
144 if (!char_is_space(c)) {
145 fprintf(stderr, "Found trailing character in key file: `%c'\n", c);
149 if (ferror(f) && errno) {
153 ret = parse_key(key, dst);
160 static inline bool parse_ip(struct wgallowedip *allowedip, const char *value)
162 allowedip->family = AF_UNSPEC;
163 if (strchr(value, ':')) {
164 if (inet_pton(AF_INET6, value, &allowedip->ip6) == 1)
165 allowedip->family = AF_INET6;
167 if (inet_pton(AF_INET, value, &allowedip->ip4) == 1)
168 allowedip->family = AF_INET;
170 if (allowedip->family == AF_UNSPEC) {
171 fprintf(stderr, "Unable to parse IP address: `%s'\n", value);
177 static inline int parse_dns_retries(void)
180 char *retries = getenv("WG_ENDPOINT_RESOLUTION_RETRIES"), *end;
184 if (!strcmp(retries, "infinity"))
187 ret = strtoul(retries, &end, 10);
188 if (*end || ret > INT_MAX) {
189 fprintf(stderr, "Unable to parse WG_ENDPOINT_RESOLUTION_RETRIES: `%s'\n", retries);
195 static inline bool parse_endpoint(struct sockaddr *endpoint, const char *value)
197 char *mutable = strdup(value);
199 int ret, retries = parse_dns_retries();
200 struct addrinfo *resolved;
201 struct addrinfo hints = {
202 .ai_family = AF_UNSPEC,
203 .ai_socktype = SOCK_DGRAM,
204 .ai_protocol = IPPROTO_UDP
210 if (!strlen(value)) {
212 fprintf(stderr, "Unable to parse empty endpoint\n");
215 if (mutable[0] == '[') {
217 end = strchr(mutable, ']');
220 fprintf(stderr, "Unable to find matching brace of endpoint: `%s'\n", value);
224 if (*end++ != ':' || !*end) {
226 fprintf(stderr, "Unable to find port of endpoint: `%s'\n", value);
231 end = strrchr(mutable, ':');
232 if (!end || !*(end + 1)) {
234 fprintf(stderr, "Unable to find port of endpoint: `%s'\n", value);
240 #define min(a, b) ((a) < (b) ? (a) : (b))
241 for (unsigned int timeout = 1000000;; timeout = min(20000000, timeout * 6 / 5)) {
242 ret = getaddrinfo(begin, end, &hints, &resolved);
245 /* The set of return codes that are "permanent failures". All other possibilities are potentially transient.
247 * This is according to https://sourceware.org/glibc/wiki/NameResolver which states:
248 * "From the perspective of the application that calls getaddrinfo() it perhaps
249 * doesn't matter that much since EAI_FAIL, EAI_NONAME and EAI_NODATA are all
250 * permanent failure codes and the causes are all permanent failures in the
251 * sense that there is no point in retrying later."
253 * So this is what we do, except FreeBSD removed EAI_NODATA some time ago, so that's conditional.
255 if (ret == EAI_NONAME || ret == EAI_FAIL ||
259 (retries >= 0 && !retries--)) {
261 fprintf(stderr, "%s: `%s'\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), value);
264 fprintf(stderr, "%s: `%s'. Trying again in %.2f seconds...\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), value, timeout / 1000000.0);
268 if ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) ||
269 (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6)))
270 memcpy(endpoint, resolved->ai_addr, resolved->ai_addrlen);
272 freeaddrinfo(resolved);
274 fprintf(stderr, "Neither IPv4 nor IPv6 address found: `%s'\n", value);
277 freeaddrinfo(resolved);
282 static inline bool parse_persistent_keepalive(uint16_t *interval, uint32_t *flags, const char *value)
287 if (!strcasecmp(value, "off")) {
289 *flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
293 if (!char_is_digit(value[0]))
296 ret = strtoul(value, &end, 10);
297 if (*end || ret > 65535)
300 *interval = (uint16_t)ret;
301 *flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
304 fprintf(stderr, "Persistent keepalive interval is neither 0/off nor 1-65535: `%s'\n", value);
308 static bool validate_netmask(struct wgallowedip *allowedip)
313 switch (allowedip->family) {
316 ip = (uint32_t *)&allowedip->ip4;
320 ip = (uint32_t *)&allowedip->ip6;
323 return true; /* We don't know how to validate it, so say 'okay'. */
326 for (int i = last; i >= 0; --i) {
329 if (allowedip->cidr >= 32 * (i + 1))
331 if (allowedip->cidr > 32 * i)
332 mask >>= (allowedip->cidr - 32 * i);
333 if (ntohl(ip[i]) & mask)
340 static inline bool parse_allowedips(struct wgpeer *peer, struct wgallowedip **last_allowedip, const char *value)
342 struct wgallowedip *allowedip = *last_allowedip, *new_allowedip;
343 char *mask, *mutable = strdup(value), *sep, *saved_entry;
349 peer->flags |= WGPEER_REPLACE_ALLOWEDIPS;
350 if (!strlen(value)) {
355 while ((mask = strsep(&sep, ","))) {
359 saved_entry = strdup(mask);
360 ip = strsep(&mask, "/");
362 new_allowedip = calloc(1, sizeof(*new_allowedip));
363 if (!new_allowedip) {
370 if (!parse_ip(new_allowedip, ip)) {
378 if (!char_is_digit(mask[0]))
380 cidr = strtoul(mask, &end, 10);
381 if (*end || (cidr > 32 && new_allowedip->family == AF_INET) || (cidr > 128 && new_allowedip->family == AF_INET6))
383 } else if (new_allowedip->family == AF_INET)
385 else if (new_allowedip->family == AF_INET6)
389 new_allowedip->cidr = cidr;
391 if (!validate_netmask(new_allowedip))
392 fprintf(stderr, "Warning: AllowedIP has nonzero host part: %s/%s\n", ip, mask);
395 allowedip->next_allowedip = new_allowedip;
397 peer->first_allowedip = new_allowedip;
398 allowedip = new_allowedip;
402 *last_allowedip = allowedip;
408 fprintf(stderr, "AllowedIP is not in the correct format: `%s'\n", saved_entry);
413 static bool process_line(struct config_ctx *ctx, const char *line)
418 if (!strcasecmp(line, "[Interface]")) {
419 ctx->is_peer_section = false;
420 ctx->is_device_section = true;
423 if (!strcasecmp(line, "[Peer]")) {
424 struct wgpeer *new_peer = calloc(1, sizeof(struct wgpeer));
430 ctx->last_allowedip = NULL;
432 ctx->last_peer->next_peer = new_peer;
434 ctx->device->first_peer = new_peer;
435 ctx->last_peer = new_peer;
436 ctx->is_peer_section = true;
437 ctx->is_device_section = false;
438 ctx->last_peer->flags |= WGPEER_REPLACE_ALLOWEDIPS;
442 #define key_match(key) (value = get_value(line, key "="))
444 if (ctx->is_device_section) {
445 if (key_match("ListenPort"))
446 ret = parse_port(&ctx->device->listen_port, &ctx->device->flags, value);
447 else if (key_match("FwMark"))
448 ret = parse_fwmark(&ctx->device->fwmark, &ctx->device->flags, value);
449 else if (key_match("PrivateKey")) {
450 ret = parse_key(ctx->device->private_key, value);
452 ctx->device->flags |= WGDEVICE_HAS_PRIVATE_KEY;
455 } else if (ctx->is_peer_section) {
456 if (key_match("Endpoint"))
457 ret = parse_endpoint(&ctx->last_peer->endpoint.addr, value);
458 else if (key_match("PublicKey")) {
459 ret = parse_key(ctx->last_peer->public_key, value);
461 ctx->last_peer->flags |= WGPEER_HAS_PUBLIC_KEY;
462 } else if (key_match("AllowedIPs"))
463 ret = parse_allowedips(ctx->last_peer, &ctx->last_allowedip, value);
464 else if (key_match("PersistentKeepalive"))
465 ret = parse_persistent_keepalive(&ctx->last_peer->persistent_keepalive_interval, &ctx->last_peer->flags, value);
466 else if (key_match("PresharedKey")) {
467 ret = parse_key(ctx->last_peer->preshared_key, value);
469 ctx->last_peer->flags |= WGPEER_HAS_PRESHARED_KEY;
479 fprintf(stderr, "Line unrecognized: `%s'\n", line);
483 bool config_read_line(struct config_ctx *ctx, const char *input)
485 size_t len, cleaned_len = 0;
486 char *line, *comment;
489 /* This is what strchrnul is for, but that isn't portable. */
490 comment = strchr(input, COMMENT_CHAR);
492 len = comment - input;
496 line = calloc(len + 1, sizeof(char));
503 for (size_t i = 0; i < len; ++i) {
504 if (!char_is_space(input[i]))
505 line[cleaned_len++] = input[i];
509 ret = process_line(ctx, line);
513 free_wgdevice(ctx->device);
517 bool config_read_init(struct config_ctx *ctx, bool append)
519 memset(ctx, 0, sizeof(*ctx));
520 ctx->device = calloc(1, sizeof(*ctx->device));
526 ctx->device->flags |= WGDEVICE_REPLACE_PEERS | WGDEVICE_HAS_PRIVATE_KEY | WGDEVICE_HAS_FWMARK | WGDEVICE_HAS_LISTEN_PORT;
530 struct wgdevice *config_read_finish(struct config_ctx *ctx)
534 for_each_wgpeer(ctx->device, peer) {
535 if (!(peer->flags & WGPEER_HAS_PUBLIC_KEY)) {
536 fprintf(stderr, "A peer is missing a public key\n");
542 free_wgdevice(ctx->device);
546 static char *strip_spaces(const char *in)
552 out = calloc(t + 1, sizeof(char));
557 for (i = 0, l = 0; i < t; ++i) {
558 if (!char_is_space(in[i]))
564 struct wgdevice *config_read_cmd(const char *argv[], int argc)
566 struct wgdevice *device = calloc(1, sizeof(*device));
567 struct wgpeer *peer = NULL;
568 struct wgallowedip *allowedip = NULL;
575 if (!strcmp(argv[0], "listen-port") && argc >= 2 && !peer) {
576 if (!parse_port(&device->listen_port, &device->flags, argv[1]))
580 } else if (!strcmp(argv[0], "fwmark") && argc >= 2 && !peer) {
581 if (!parse_fwmark(&device->fwmark, &device->flags, argv[1]))
585 } else if (!strcmp(argv[0], "private-key") && argc >= 2 && !peer) {
586 if (!parse_keyfile(device->private_key, argv[1]))
588 device->flags |= WGDEVICE_HAS_PRIVATE_KEY;
591 } else if (!strcmp(argv[0], "peer") && argc >= 2) {
592 struct wgpeer *new_peer = calloc(1, sizeof(*new_peer));
600 peer->next_peer = new_peer;
602 device->first_peer = new_peer;
604 if (!parse_key(peer->public_key, argv[1]))
606 peer->flags |= WGPEER_HAS_PUBLIC_KEY;
609 } else if (!strcmp(argv[0], "remove") && argc >= 1 && peer) {
610 peer->flags |= WGPEER_REMOVE_ME;
613 } else if (!strcmp(argv[0], "endpoint") && argc >= 2 && peer) {
614 if (!parse_endpoint(&peer->endpoint.addr, argv[1]))
618 } else if (!strcmp(argv[0], "allowed-ips") && argc >= 2 && peer) {
619 char *line = strip_spaces(argv[1]);
623 if (!parse_allowedips(peer, &allowedip, line)) {
630 } else if (!strcmp(argv[0], "persistent-keepalive") && argc >= 2 && peer) {
631 if (!parse_persistent_keepalive(&peer->persistent_keepalive_interval, &peer->flags, argv[1]))
635 } else if (!strcmp(argv[0], "preshared-key") && argc >= 2 && peer) {
636 if (!parse_keyfile(peer->preshared_key, argv[1]))
638 peer->flags |= WGPEER_HAS_PRESHARED_KEY;
642 fprintf(stderr, "Invalid argument: %s\n", argv[0]);
648 free_wgdevice(device);