]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/wireguard-tools/setconf.c
unbound: Vendor import 1.20.0
[FreeBSD/FreeBSD.git] / contrib / wireguard-tools / setconf.c
1 // SPDX-License-Identifier: GPL-2.0 OR MIT
2 /*
3  * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
4  */
5
6 #include <stddef.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10
11 #include "containers.h"
12 #include "config.h"
13 #include "ipc.h"
14 #include "subcommands.h"
15
16 struct pubkey_origin {
17         uint8_t *pubkey;
18         bool from_file;
19 };
20
21 static int pubkey_cmp(const void *first, const void *second)
22 {
23         const struct pubkey_origin *a = first, *b = second;
24         int ret = memcmp(a->pubkey, b->pubkey, WG_KEY_LEN);
25         if (ret)
26                 return ret;
27         return a->from_file - b->from_file;
28 }
29
30 static bool sync_conf(struct wgdevice *file)
31 {
32         struct wgdevice *runtime;
33         struct wgpeer *peer;
34         struct pubkey_origin *pubkeys;
35         size_t peer_count = 0, i = 0;
36
37         if (!file->first_peer)
38                 return true;
39
40         for_each_wgpeer(file, peer)
41                 ++peer_count;
42
43         if (ipc_get_device(&runtime, file->name) != 0) {
44                 perror("Unable to retrieve current interface configuration");
45                 return false;
46         }
47
48         if (!runtime->first_peer) {
49                 free_wgdevice(runtime);
50                 return true;
51         }
52
53         file->flags &= ~WGDEVICE_REPLACE_PEERS;
54
55         for_each_wgpeer(runtime, peer)
56                 ++peer_count;
57
58         pubkeys = calloc(peer_count, sizeof(*pubkeys));
59         if (!pubkeys) {
60                 free_wgdevice(runtime);
61                 perror("Public key allocation");
62                 return false;
63         }
64
65         for_each_wgpeer(file, peer) {
66                 pubkeys[i].pubkey = peer->public_key;
67                 pubkeys[i].from_file = true;
68                 ++i;
69         }
70         for_each_wgpeer(runtime, peer) {
71                 pubkeys[i].pubkey = peer->public_key;
72                 pubkeys[i].from_file = false;
73                 ++i;
74         }
75         qsort(pubkeys, peer_count, sizeof(*pubkeys), pubkey_cmp);
76
77         for (i = 0; i < peer_count; ++i) {
78                 if (pubkeys[i].from_file)
79                         continue;
80                 if (i == peer_count - 1 || !pubkeys[i + 1].from_file || memcmp(pubkeys[i].pubkey, pubkeys[i + 1].pubkey, WG_KEY_LEN)) {
81                         peer = calloc(1, sizeof(struct wgpeer));
82                         if (!peer) {
83                                 free_wgdevice(runtime);
84                                 free(pubkeys);
85                                 perror("Peer allocation");
86                                 return false;
87                         }
88                         peer->flags = WGPEER_REMOVE_ME;
89                         memcpy(peer->public_key, pubkeys[i].pubkey, WG_KEY_LEN);
90                         peer->next_peer = file->first_peer;
91                         file->first_peer = peer;
92                         if (!file->last_peer)
93                                 file->last_peer = peer;
94                 }
95         }
96         free_wgdevice(runtime);
97         free(pubkeys);
98         return true;
99 }
100
101 int setconf_main(int argc, const char *argv[])
102 {
103         struct wgdevice *device = NULL;
104         struct config_ctx ctx;
105         FILE *config_input = NULL;
106         char *config_buffer = NULL;
107         size_t config_buffer_len = 0;
108         int ret = 1;
109
110         if (argc != 3) {
111                 fprintf(stderr, "Usage: %s %s <interface> <configuration filename>\n", PROG_NAME, argv[0]);
112                 return 1;
113         }
114
115         config_input = fopen(argv[2], "r");
116         if (!config_input) {
117                 perror("fopen");
118                 return 1;
119         }
120         if (!config_read_init(&ctx, !strcmp(argv[0], "addconf"))) {
121                 fclose(config_input);
122                 return 1;
123         }
124         while (getline(&config_buffer, &config_buffer_len, config_input) >= 0) {
125                 if (!config_read_line(&ctx, config_buffer)) {
126                         fprintf(stderr, "Configuration parsing error\n");
127                         goto cleanup;
128                 }
129         }
130         device = config_read_finish(&ctx);
131         if (!device) {
132                 fprintf(stderr, "Invalid configuration\n");
133                 goto cleanup;
134         }
135         strncpy(device->name, argv[1], IFNAMSIZ - 1);
136         device->name[IFNAMSIZ - 1] = '\0';
137
138         if (!strcmp(argv[0], "syncconf")) {
139                 if (!sync_conf(device))
140                         goto cleanup;
141         }
142
143         if (ipc_set_device(device) != 0) {
144                 perror("Unable to modify interface");
145                 goto cleanup;
146         }
147
148         ret = 0;
149
150 cleanup:
151         if (config_input)
152                 fclose(config_input);
153         free(config_buffer);
154         free_wgdevice(device);
155         return ret;
156 }