Fri Aug 28 17:17:53 2020 UTC ()
Show the time of the latest handshake properly formatted.


(tih)
diff -r1.4 -r1.5 src/usr.sbin/wgconfig/wgconfig.c

cvs diff -r1.4 -r1.5 src/usr.sbin/wgconfig/wgconfig.c (switch to unified diff)

--- src/usr.sbin/wgconfig/wgconfig.c 2020/08/21 17:51:31 1.4
+++ src/usr.sbin/wgconfig/wgconfig.c 2020/08/28 17:17:53 1.5
@@ -1,816 +1,820 @@ @@ -1,816 +1,820 @@
1/* $NetBSD: wgconfig.c,v 1.4 2020/08/21 17:51:31 martin Exp $ */ 1/* $NetBSD: wgconfig.c,v 1.5 2020/08/28 17:17:53 tih Exp $ */
2 2
3/* 3/*
4 * Copyright (C) Ryota Ozaki <ozaki.ryota@gmail.com> 4 * Copyright (C) Ryota Ozaki <ozaki.ryota@gmail.com>
5 * All rights reserved. 5 * All rights reserved.
6 * 6 *
7 * Redistribution and use in source and binary forms, with or without 7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions 8 * modification, are permitted provided that the following conditions
9 * are met: 9 * are met:
10 * 1. Redistributions of source code must retain the above copyright 10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer. 11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright 12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the 13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution. 14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the project nor the names of its contributors 15 * 3. Neither the name of the project nor the names of its contributors
16 * may be used to endorse or promote products derived from this software 16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission. 17 * without specific prior written permission.
18 * 18 *
19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE. 29 * SUCH DAMAGE.
30 */ 30 */
31 31
32#include <sys/cdefs.h> 32#include <sys/cdefs.h>
33__RCSID("$NetBSD: wgconfig.c,v 1.4 2020/08/21 17:51:31 martin Exp $"); 33__RCSID("$NetBSD: wgconfig.c,v 1.5 2020/08/28 17:17:53 tih Exp $");
34 34
35#include <sys/ioctl.h> 35#include <sys/ioctl.h>
36 36
37#include <net/if.h> 37#include <net/if.h>
38#include <net/if_wg.h> 38#include <net/if_wg.h>
39 39
40#include <arpa/inet.h> 40#include <arpa/inet.h>
41 41
42#include <stdio.h> 42#include <stdio.h>
43#include <stdlib.h> 43#include <stdlib.h>
44#include <string.h> 44#include <string.h>
45#include <err.h> 45#include <err.h>
46#include <unistd.h> 46#include <unistd.h>
47#include <errno.h> 47#include <errno.h>
48#include <resolv.h> 48#include <resolv.h>
49#include <util.h> 49#include <util.h>
50#include <netdb.h> 50#include <netdb.h>
51 51
52#include <prop/proplib.h> 52#include <prop/proplib.h>
53 53
54#define PROP_BUFFER_LEN 4096 54#define PROP_BUFFER_LEN 4096
55#define KEY_LEN 32 55#define KEY_LEN 32
56#define KEY_BASE64_LEN 44 56#define KEY_BASE64_LEN 44
57 57
58__dead static void 58__dead static void
59usage(void) 59usage(void)
60{ 60{
61 const char *progname = getprogname(); 61 const char *progname = getprogname();
62#define P(str) fprintf(stderr, "\t%s <interface> %s\n", progname, str) 62#define P(str) fprintf(stderr, "\t%s <interface> %s\n", progname, str)
63 63
64 fprintf(stderr, "Usage:\n"); 64 fprintf(stderr, "Usage:\n");
65 P("[show all]"); 65 P("[show all]");
66 P("show peer <peer name> [--show-preshared-key]"); 66 P("show peer <peer name> [--show-preshared-key]");
67 P("show private-key"); 67 P("show private-key");
68 P("set private-key <file path>"); 68 P("set private-key <file path>");
69 P("set listen-port <port>"); 69 P("set listen-port <port>");
70 P("add peer <peer name> <base64 public key>\n" 70 P("add peer <peer name> <base64 public key>\n"
71 "\t [--preshared-key=<file path>] [--endpoint=<ip>:<port>]\n" 71 "\t [--preshared-key=<file path>] [--endpoint=<ip>:<port>]\n"
72 "\t [--allowed-ips=<ip1>/<cidr1>[,<ip2>/<cidr2>]...]"); 72 "\t [--allowed-ips=<ip1>/<cidr1>[,<ip2>/<cidr2>]...]");
73 P("delete peer <peer name>"); 73 P("delete peer <peer name>");
74 74
75 exit(EXIT_FAILURE); 75 exit(EXIT_FAILURE);
76#undef P 76#undef P
77} 77}
78 78
79static const char * 79static const char *
80format_key(prop_object_t key_prop) 80format_key(prop_object_t key_prop)
81{ 81{
82 int error; 82 int error;
83 const void *key; 83 const void *key;
84 size_t key_len; 84 size_t key_len;
85 static char key_b64[KEY_BASE64_LEN + 1]; 85 static char key_b64[KEY_BASE64_LEN + 1];
86 86
87 if (key_prop == NULL) 87 if (key_prop == NULL)
88 return "(none)"; 88 return "(none)";
89 if (prop_object_type(key_prop) != PROP_TYPE_DATA) 89 if (prop_object_type(key_prop) != PROP_TYPE_DATA)
90 errx(EXIT_FAILURE, "invalid key"); 90 errx(EXIT_FAILURE, "invalid key");
91 91
92 key = prop_data_value(key_prop); 92 key = prop_data_value(key_prop);
93 key_len = prop_data_size(key_prop); 93 key_len = prop_data_size(key_prop);
94 if (key_len != KEY_LEN) 94 if (key_len != KEY_LEN)
95 errx(EXIT_FAILURE, "invalid key len: %zu", key_len); 95 errx(EXIT_FAILURE, "invalid key len: %zu", key_len);
96 error = b64_ntop(key, key_len, key_b64, KEY_BASE64_LEN + 1); 96 error = b64_ntop(key, key_len, key_b64, KEY_BASE64_LEN + 1);
97 if (error == -1) 97 if (error == -1)
98 errx(EXIT_FAILURE, "b64_ntop failed"); 98 errx(EXIT_FAILURE, "b64_ntop failed");
99 key_b64[KEY_BASE64_LEN] = '\0'; /* just in case */ 99 key_b64[KEY_BASE64_LEN] = '\0'; /* just in case */
100 100
101 return key_b64; 101 return key_b64;
102} 102}
103 103
104static const char * 104static const char *
105format_endpoint(prop_object_t endpoint_prop) 105format_endpoint(prop_object_t endpoint_prop)
106{ 106{
107 int error; 107 int error;
108 static char buf[INET6_ADDRSTRLEN]; 108 static char buf[INET6_ADDRSTRLEN];
109 struct sockaddr_storage sockaddr; 109 struct sockaddr_storage sockaddr;
110 const void *addr; 110 const void *addr;
111 size_t addr_len; 111 size_t addr_len;
112 112
113 if (prop_object_type(endpoint_prop) != PROP_TYPE_DATA) 113 if (prop_object_type(endpoint_prop) != PROP_TYPE_DATA)
114 errx(EXIT_FAILURE, "invalid endpoint"); 114 errx(EXIT_FAILURE, "invalid endpoint");
115 115
116 addr = prop_data_value(endpoint_prop); 116 addr = prop_data_value(endpoint_prop);
117 addr_len = prop_data_size(endpoint_prop); 117 addr_len = prop_data_size(endpoint_prop);
118 memcpy(&sockaddr, addr, addr_len); 118 memcpy(&sockaddr, addr, addr_len);
119 119
120 error = sockaddr_snprintf(buf, sizeof(buf), "%a:%p", 120 error = sockaddr_snprintf(buf, sizeof(buf), "%a:%p",
121 (struct sockaddr *)&sockaddr); 121 (struct sockaddr *)&sockaddr);
122 if (error == -1) 122 if (error == -1)
123 err(EXIT_FAILURE, "sockaddr_snprintf failed"); 123 err(EXIT_FAILURE, "sockaddr_snprintf failed");
124 124
125 return buf; 125 return buf;
126} 126}
127 127
128static void 128static void
129handle_allowed_ips(prop_dictionary_t peer, const char *prefix) 129handle_allowed_ips(prop_dictionary_t peer, const char *prefix)
130{ 130{
131 prop_object_t prop_obj; 131 prop_object_t prop_obj;
132 prop_array_t allowedips; 132 prop_array_t allowedips;
133 prop_object_iterator_t it; 133 prop_object_iterator_t it;
134 prop_dictionary_t allowedip; 134 prop_dictionary_t allowedip;
135 bool first = true; 135 bool first = true;
136 136
137 prop_obj = prop_dictionary_get(peer, "allowedips"); 137 prop_obj = prop_dictionary_get(peer, "allowedips");
138 if (prop_obj == NULL) 138 if (prop_obj == NULL)
139 return; 139 return;
140 if (prop_object_type(prop_obj) != PROP_TYPE_ARRAY) 140 if (prop_object_type(prop_obj) != PROP_TYPE_ARRAY)
141 errx(EXIT_FAILURE, "invalid allowedips"); 141 errx(EXIT_FAILURE, "invalid allowedips");
142 allowedips = prop_obj; 142 allowedips = prop_obj;
143 143
144 printf("%sallowed-ips: ", prefix); 144 printf("%sallowed-ips: ", prefix);
145 145
146 it = prop_array_iterator(allowedips); 146 it = prop_array_iterator(allowedips);
147 while ((prop_obj = prop_object_iterator_next(it)) != NULL) { 147 while ((prop_obj = prop_object_iterator_next(it)) != NULL) {
148 uint8_t family; 148 uint8_t family;
149 uint8_t cidr; 149 uint8_t cidr;
150 const void *addr; 150 const void *addr;
151 size_t addrlen, famaddrlen; 151 size_t addrlen, famaddrlen;
152 char ntopbuf[INET6_ADDRSTRLEN]; 152 char ntopbuf[INET6_ADDRSTRLEN];
153 const char *ntopret; 153 const char *ntopret;
154 154
155 if (prop_object_type(prop_obj) != PROP_TYPE_DICTIONARY) { 155 if (prop_object_type(prop_obj) != PROP_TYPE_DICTIONARY) {
156 warnx("invalid allowedip"); 156 warnx("invalid allowedip");
157 continue; 157 continue;
158 } 158 }
159 allowedip = prop_obj; 159 allowedip = prop_obj;
160 160
161 if (!prop_dictionary_get_uint8(allowedip, "family", &family)) { 161 if (!prop_dictionary_get_uint8(allowedip, "family", &family)) {
162 warnx("allowed-ip without family"); 162 warnx("allowed-ip without family");
163 continue; 163 continue;
164 } 164 }
165 165
166 if (!prop_dictionary_get_uint8(allowedip, "cidr", &cidr)) { 166 if (!prop_dictionary_get_uint8(allowedip, "cidr", &cidr)) {
167 warnx("allowed-ip without cidr"); 167 warnx("allowed-ip without cidr");
168 continue; 168 continue;
169 } 169 }
170 170
171 if (!prop_dictionary_get_data(allowedip, "ip", 171 if (!prop_dictionary_get_data(allowedip, "ip",
172 &addr, &addrlen)) { 172 &addr, &addrlen)) {
173 warnx("allowed-ip without ip"); 173 warnx("allowed-ip without ip");
174 continue; 174 continue;
175 } 175 }
176 176
177 switch (family) { 177 switch (family) {
178 case AF_INET: 178 case AF_INET:
179 famaddrlen = sizeof(struct in_addr); 179 famaddrlen = sizeof(struct in_addr);
180 break; 180 break;
181 case AF_INET6: 181 case AF_INET6:
182 famaddrlen = sizeof(struct in6_addr); 182 famaddrlen = sizeof(struct in6_addr);
183 break; 183 break;
184 default: 184 default:
185 warnx("unknown family %d", family); 185 warnx("unknown family %d", family);
186 continue; 186 continue;
187 } 187 }
188 if (addrlen != famaddrlen) { 188 if (addrlen != famaddrlen) {
189 warnx("allowed-ip bad ip length"); 189 warnx("allowed-ip bad ip length");
190 continue; 190 continue;
191 } 191 }
192 192
193 ntopret = inet_ntop(family, addr, ntopbuf, sizeof(ntopbuf)); 193 ntopret = inet_ntop(family, addr, ntopbuf, sizeof(ntopbuf));
194 if (ntopret == NULL) 194 if (ntopret == NULL)
195 errx(EXIT_FAILURE, "inet_ntop failed"); 195 errx(EXIT_FAILURE, "inet_ntop failed");
196 printf("%s%s/%u", first ? "" : ",", ntopbuf, cidr); 196 printf("%s%s/%u", first ? "" : ",", ntopbuf, cidr);
197 first = false; 197 first = false;
198 } 198 }
199 if (first) 199 if (first)
200 printf("(none)\n"); 200 printf("(none)\n");
201 else 201 else
202 printf("\n"); 202 printf("\n");
203} 203}
204 204
205static prop_dictionary_t 205static prop_dictionary_t
206ioctl_get(const char *interface) 206ioctl_get(const char *interface)
207{ 207{
208 int error = 0; 208 int error = 0;
209 struct ifdrv ifd; 209 struct ifdrv ifd;
210 int sock; 210 int sock;
211 char *buf; 211 char *buf;
212 prop_dictionary_t prop_dict; 212 prop_dictionary_t prop_dict;
213 213
214 sock = socket(AF_INET, SOCK_DGRAM, 0); 214 sock = socket(AF_INET, SOCK_DGRAM, 0);
215 if (error == -1) 215 if (error == -1)
216 err(EXIT_FAILURE, "socket"); 216 err(EXIT_FAILURE, "socket");
217 buf = malloc(PROP_BUFFER_LEN); 217 buf = malloc(PROP_BUFFER_LEN);
218 if (buf == NULL) 218 if (buf == NULL)
219 errx(EXIT_FAILURE, "malloc failed"); 219 errx(EXIT_FAILURE, "malloc failed");
220 220
221 strlcpy(ifd.ifd_name, interface, sizeof(ifd.ifd_name)); 221 strlcpy(ifd.ifd_name, interface, sizeof(ifd.ifd_name));
222 ifd.ifd_cmd = 0; 222 ifd.ifd_cmd = 0;
223 ifd.ifd_data = buf; 223 ifd.ifd_data = buf;
224 ifd.ifd_len = PROP_BUFFER_LEN; 224 ifd.ifd_len = PROP_BUFFER_LEN;
225 225
226 error = ioctl(sock, SIOCGDRVSPEC, &ifd); 226 error = ioctl(sock, SIOCGDRVSPEC, &ifd);
227 if (error == -1) 227 if (error == -1)
228 err(EXIT_FAILURE, "ioctl(SIOCGDRVSPEC)"); 228 err(EXIT_FAILURE, "ioctl(SIOCGDRVSPEC)");
229 229
230 prop_dict = prop_dictionary_internalize(buf); 230 prop_dict = prop_dictionary_internalize(buf);
231 if (prop_dict == NULL) 231 if (prop_dict == NULL)
232 errx(EXIT_FAILURE, "prop_dictionary_internalize failed"); 232 errx(EXIT_FAILURE, "prop_dictionary_internalize failed");
233 233
234 free(buf); 234 free(buf);
235 close(sock); 235 close(sock);
236 236
237 return prop_dict; 237 return prop_dict;
238} 238}
239 239
240static void 240static void
241show_peer(prop_dictionary_t peer, const char *prefix, bool show_psk) 241show_peer(prop_dictionary_t peer, const char *prefix, bool show_psk)
242{ 242{
243 prop_object_t prop_obj; 243 prop_object_t prop_obj;
244 uint64_t sec; 244 time_t sec;
245 245
246 prop_obj = prop_dictionary_get(peer, "public_key"); 246 prop_obj = prop_dictionary_get(peer, "public_key");
247 if (prop_obj == NULL) { 247 if (prop_obj == NULL) {
248 warnx("peer without public-key"); 248 warnx("peer without public-key");
249 return; 249 return;
250 } 250 }
251 printf("%spublic-key: %s\n", prefix, format_key(prop_obj)); 251 printf("%spublic-key: %s\n", prefix, format_key(prop_obj));
252 252
253 prop_obj = prop_dictionary_get(peer, "endpoint"); 253 prop_obj = prop_dictionary_get(peer, "endpoint");
254 if (prop_obj == NULL) 254 if (prop_obj == NULL)
255 printf("%sendpoint: (none)\n", prefix); 255 printf("%sendpoint: (none)\n", prefix);
256 else 256 else
257 printf("%sendpoint: %s\n", prefix, format_endpoint(prop_obj)); 257 printf("%sendpoint: %s\n", prefix, format_endpoint(prop_obj));
258 258
259 if (show_psk) { 259 if (show_psk) {
260 prop_obj = prop_dictionary_get(peer, "preshared_key"); 260 prop_obj = prop_dictionary_get(peer, "preshared_key");
261 printf("%spreshared-key: %s\n", prefix, format_key(prop_obj)); 261 printf("%spreshared-key: %s\n", prefix, format_key(prop_obj));
262 } else { 262 } else {
263 printf("%spreshared-key: (hidden)\n", prefix); 263 printf("%spreshared-key: (hidden)\n", prefix);
264 } 264 }
265 265
266 handle_allowed_ips(peer, prefix); 266 handle_allowed_ips(peer, prefix);
267 267
268 if (prop_dictionary_get_uint64(peer, "last_handshake_time_sec", &sec)) 268 if (prop_dictionary_get_int64(peer, "last_handshake_time_sec", &sec)) {
269 printf("%slatest-handshake: %"PRIu64"\n", prefix, sec); 269 if (sec > 0)
270 else 270 printf("%slatest-handshake: %s", prefix, ctime(&sec));
 271 else
 272 printf("%slatest-handshake: (never)\n", prefix);
 273 } else {
271 printf("%slatest-handshake: (none)\n", prefix); 274 printf("%slatest-handshake: (none)\n", prefix);
 275 }
272} 276}
273 277
274static int 278static int
275cmd_show_all(const char *interface, int argc, char *argv[]) 279cmd_show_all(const char *interface, int argc, char *argv[])
276{ 280{
277 prop_dictionary_t prop_dict; 281 prop_dictionary_t prop_dict;
278 prop_object_t prop_obj; 282 prop_object_t prop_obj;
279 uint16_t port; 283 uint16_t port;
280 prop_array_t peers; 284 prop_array_t peers;
281 285
282 prop_dict = ioctl_get(interface); 286 prop_dict = ioctl_get(interface);
283 287
284 printf("interface: %s\n", interface); 288 printf("interface: %s\n", interface);
285 289
286#if 0 290#if 0
287 prop_obj = prop_dictionary_get(prop_dict, "private_key"); 291 prop_obj = prop_dictionary_get(prop_dict, "private_key");
288 printf("\tprivate-key: %s\n", format_key(prop_obj)); 292 printf("\tprivate-key: %s\n", format_key(prop_obj));
289#else 293#else
290 printf("\tprivate-key: (hidden)\n"); 294 printf("\tprivate-key: (hidden)\n");
291#endif 295#endif
292 296
293 if (prop_dictionary_get_uint16(prop_dict, "listen_port", &port)) { 297 if (prop_dictionary_get_uint16(prop_dict, "listen_port", &port)) {
294 printf("\tlisten-port: %u\n", port); 298 printf("\tlisten-port: %u\n", port);
295 } else { 299 } else {
296 printf("\tlisten-port: (none)\n"); 300 printf("\tlisten-port: (none)\n");
297 } 301 }
298 302
299 prop_obj = prop_dictionary_get(prop_dict, "peers"); 303 prop_obj = prop_dictionary_get(prop_dict, "peers");
300 if (prop_obj == NULL) 304 if (prop_obj == NULL)
301 return EXIT_SUCCESS; 305 return EXIT_SUCCESS;
302 if (prop_object_type(prop_obj) != PROP_TYPE_ARRAY) 306 if (prop_object_type(prop_obj) != PROP_TYPE_ARRAY)
303 errx(EXIT_FAILURE, "invalid peers"); 307 errx(EXIT_FAILURE, "invalid peers");
304 peers = prop_obj; 308 peers = prop_obj;
305 309
306 prop_object_iterator_t it = prop_array_iterator(peers); 310 prop_object_iterator_t it = prop_array_iterator(peers);
307 while ((prop_obj = prop_object_iterator_next(it)) != NULL) { 311 while ((prop_obj = prop_object_iterator_next(it)) != NULL) {
308 const char *name; 312 const char *name;
309 313
310 if (prop_object_type(prop_obj) != PROP_TYPE_DICTIONARY) 314 if (prop_object_type(prop_obj) != PROP_TYPE_DICTIONARY)
311 errx(EXIT_FAILURE, "invalid peer"); 315 errx(EXIT_FAILURE, "invalid peer");
312 prop_dictionary_t peer = prop_obj; 316 prop_dictionary_t peer = prop_obj;
313 317
314 if (prop_dictionary_get_string(peer, "name", &name)) { 318 if (prop_dictionary_get_string(peer, "name", &name)) {
315 printf("\tpeer: %s\n", name); 319 printf("\tpeer: %s\n", name);
316 } else 320 } else
317 printf("\tpeer: (none)\n"); 321 printf("\tpeer: (none)\n");
318 322
319 show_peer(peer, "\t\t", false); 323 show_peer(peer, "\t\t", false);
320 } 324 }
321 325
322 return EXIT_SUCCESS; 326 return EXIT_SUCCESS;
323} 327}
324 328
325static int 329static int
326cmd_show_peer(const char *interface, int argc, char *argv[]) 330cmd_show_peer(const char *interface, int argc, char *argv[])
327{ 331{
328 prop_dictionary_t prop_dict; 332 prop_dictionary_t prop_dict;
329 prop_object_t prop_obj; 333 prop_object_t prop_obj;
330 const char *target; 334 const char *target;
331 const char *opt = "--show-preshared-key"; 335 const char *opt = "--show-preshared-key";
332 bool show_psk = false; 336 bool show_psk = false;
333 337
334 if (argc != 1 && argc != 2) 338 if (argc != 1 && argc != 2)
335 usage(); 339 usage();
336 target = argv[0]; 340 target = argv[0];
337 if (argc == 2) { 341 if (argc == 2) {
338 if (strncmp(argv[1], opt, strlen(opt)) != 0) 342 if (strncmp(argv[1], opt, strlen(opt)) != 0)
339 usage(); 343 usage();
340 show_psk = true; 344 show_psk = true;
341 } 345 }
342 346
343 prop_dict = ioctl_get(interface); 347 prop_dict = ioctl_get(interface);
344 348
345 prop_obj = prop_dictionary_get(prop_dict, "peers"); 349 prop_obj = prop_dictionary_get(prop_dict, "peers");
346 if (prop_obj == NULL) 350 if (prop_obj == NULL)
347 return EXIT_SUCCESS; 351 return EXIT_SUCCESS;
348 if (prop_object_type(prop_obj) != PROP_TYPE_ARRAY) 352 if (prop_object_type(prop_obj) != PROP_TYPE_ARRAY)
349 errx(EXIT_FAILURE, "invalid peers"); 353 errx(EXIT_FAILURE, "invalid peers");
350 354
351 prop_array_t peers = prop_obj; 355 prop_array_t peers = prop_obj;
352 prop_object_iterator_t it = prop_array_iterator(peers); 356 prop_object_iterator_t it = prop_array_iterator(peers);
353 while ((prop_obj = prop_object_iterator_next(it)) != NULL) { 357 while ((prop_obj = prop_object_iterator_next(it)) != NULL) {
354 const char *name; 358 const char *name;
355 359
356 if (prop_object_type(prop_obj) != PROP_TYPE_DICTIONARY) 360 if (prop_object_type(prop_obj) != PROP_TYPE_DICTIONARY)
357 errx(EXIT_FAILURE, "invalid peer"); 361 errx(EXIT_FAILURE, "invalid peer");
358 prop_dictionary_t peer = prop_obj; 362 prop_dictionary_t peer = prop_obj;
359 363
360 if (!prop_dictionary_get_string(peer, "name", &name)) 364 if (!prop_dictionary_get_string(peer, "name", &name))
361 continue; 365 continue;
362 if (strcmp(name, target) == 0) { 366 if (strcmp(name, target) == 0) {
363 printf("peer: %s\n", name); 367 printf("peer: %s\n", name);
364 show_peer(peer, "\t", show_psk); 368 show_peer(peer, "\t", show_psk);
365 return EXIT_SUCCESS; 369 return EXIT_SUCCESS;
366 } 370 }
367 } 371 }
368 372
369 return EXIT_FAILURE; 373 return EXIT_FAILURE;
370} 374}
371 375
372static int 376static int
373cmd_show_private_key(const char *interface, int argc, char *argv[]) 377cmd_show_private_key(const char *interface, int argc, char *argv[])
374{ 378{
375 prop_dictionary_t prop_dict; 379 prop_dictionary_t prop_dict;
376 prop_object_t prop_obj; 380 prop_object_t prop_obj;
377 381
378 prop_dict = ioctl_get(interface); 382 prop_dict = ioctl_get(interface);
379 383
380 prop_obj = prop_dictionary_get(prop_dict, "private_key"); 384 prop_obj = prop_dictionary_get(prop_dict, "private_key");
381 printf("private-key: %s\n", format_key(prop_obj)); 385 printf("private-key: %s\n", format_key(prop_obj));
382 386
383 return EXIT_SUCCESS; 387 return EXIT_SUCCESS;
384} 388}
385 389
386static void 390static void
387ioctl_set(const char *interface, int cmd, char *propstr) 391ioctl_set(const char *interface, int cmd, char *propstr)
388{ 392{
389 int error; 393 int error;
390 struct ifdrv ifd; 394 struct ifdrv ifd;
391 int sock; 395 int sock;
392 396
393 strlcpy(ifd.ifd_name, interface, sizeof(ifd.ifd_name)); 397 strlcpy(ifd.ifd_name, interface, sizeof(ifd.ifd_name));
394 ifd.ifd_cmd = cmd; 398 ifd.ifd_cmd = cmd;
395 ifd.ifd_data = propstr; 399 ifd.ifd_data = propstr;
396 ifd.ifd_len = strlen(propstr); 400 ifd.ifd_len = strlen(propstr);
397 sock = socket(AF_INET, SOCK_DGRAM, 0); 401 sock = socket(AF_INET, SOCK_DGRAM, 0);
398 error = ioctl(sock, SIOCSDRVSPEC, &ifd); 402 error = ioctl(sock, SIOCSDRVSPEC, &ifd);
399 if (error == -1) 403 if (error == -1)
400 err(EXIT_FAILURE, "ioctl(SIOCSDRVSPEC): cmd=%d", cmd); 404 err(EXIT_FAILURE, "ioctl(SIOCSDRVSPEC): cmd=%d", cmd);
401 close(sock); 405 close(sock);
402} 406}
403 407
404static void 408static void
405base64_decode(const char keyb64buf[KEY_BASE64_LEN + 1], 409base64_decode(const char keyb64buf[KEY_BASE64_LEN + 1],
406 unsigned char keybuf[KEY_LEN]) 410 unsigned char keybuf[KEY_LEN])
407{ 411{
408 int ret; 412 int ret;
409 413
410 ret = b64_pton(keyb64buf, keybuf, KEY_LEN); 414 ret = b64_pton(keyb64buf, keybuf, KEY_LEN);
411 if (ret == -1) 415 if (ret == -1)
412 errx(EXIT_FAILURE, "b64_pton failed"); 416 errx(EXIT_FAILURE, "b64_pton failed");
413} 417}
414 418
415static void 419static void
416read_key(const char *path, unsigned char keybuf[KEY_LEN]) 420read_key(const char *path, unsigned char keybuf[KEY_LEN])
417{ 421{
418 FILE *fp; 422 FILE *fp;
419 char keyb64buf[KEY_BASE64_LEN + 1]; 423 char keyb64buf[KEY_BASE64_LEN + 1];
420 size_t n; 424 size_t n;
421 425
422 fp = fopen(path, "r"); 426 fp = fopen(path, "r");
423 if (fp == NULL) 427 if (fp == NULL)
424 err(EXIT_FAILURE, "fopen"); 428 err(EXIT_FAILURE, "fopen");
425 429
426 n = fread(keyb64buf, 1, KEY_BASE64_LEN, fp); 430 n = fread(keyb64buf, 1, KEY_BASE64_LEN, fp);
427 if (n != KEY_BASE64_LEN) 431 if (n != KEY_BASE64_LEN)
428 errx(EXIT_FAILURE, "base64 key len is short: %zu", n); 432 errx(EXIT_FAILURE, "base64 key len is short: %zu", n);
429 keyb64buf[KEY_BASE64_LEN] = '\0'; 433 keyb64buf[KEY_BASE64_LEN] = '\0';
430 434
431 base64_decode(keyb64buf, keybuf); 435 base64_decode(keyb64buf, keybuf);
432} 436}
433 437
434static int 438static int
435cmd_set_private_key(const char *interface, int argc, char *argv[]) 439cmd_set_private_key(const char *interface, int argc, char *argv[])
436{ 440{
437 unsigned char keybuf[KEY_LEN]; 441 unsigned char keybuf[KEY_LEN];
438 442
439 if (argc != 1) 443 if (argc != 1)
440 usage(); 444 usage();
441 445
442 read_key(argv[0], keybuf); 446 read_key(argv[0], keybuf);
443 447
444 prop_dictionary_t prop_dict; 448 prop_dictionary_t prop_dict;
445 prop_dict = prop_dictionary_create(); 449 prop_dict = prop_dictionary_create();
446 if (prop_dict == NULL) 450 if (prop_dict == NULL)
447 errx(EXIT_FAILURE, "prop_dictionary_create"); 451 errx(EXIT_FAILURE, "prop_dictionary_create");
448 452
449 if (!prop_dictionary_set_data(prop_dict, "private_key", 453 if (!prop_dictionary_set_data(prop_dict, "private_key",
450 keybuf, sizeof(keybuf))) 454 keybuf, sizeof(keybuf)))
451 errx(EXIT_FAILURE, "prop_dictionary_set_data"); 455 errx(EXIT_FAILURE, "prop_dictionary_set_data");
452 456
453 char *buf = prop_dictionary_externalize(prop_dict); 457 char *buf = prop_dictionary_externalize(prop_dict);
454 if (buf == NULL) 458 if (buf == NULL)
455 err(EXIT_FAILURE, "prop_dictionary_externalize failed"); 459 err(EXIT_FAILURE, "prop_dictionary_externalize failed");
456 ioctl_set(interface, WG_IOCTL_SET_PRIVATE_KEY, buf); 460 ioctl_set(interface, WG_IOCTL_SET_PRIVATE_KEY, buf);
457 461
458 return EXIT_SUCCESS; 462 return EXIT_SUCCESS;
459} 463}
460 464
461static uint16_t 465static uint16_t
462strtouint16(const char *str) 466strtouint16(const char *str)
463{ 467{
464 char *ep; 468 char *ep;
465 long val; 469 long val;
466 470
467 errno = 0; 471 errno = 0;
468 val = strtol(str, &ep, 10); 472 val = strtol(str, &ep, 10);
469 if (ep == str) 473 if (ep == str)
470 errx(EXIT_FAILURE, "strtol: not a number"); 474 errx(EXIT_FAILURE, "strtol: not a number");
471 if (*ep != '\0') 475 if (*ep != '\0')
472 errx(EXIT_FAILURE, "strtol: trailing garbage"); 476 errx(EXIT_FAILURE, "strtol: trailing garbage");
473 if (errno != 0) 477 if (errno != 0)
474 err(EXIT_FAILURE, "strtol"); 478 err(EXIT_FAILURE, "strtol");
475 if (val < 0 || val > USHRT_MAX) 479 if (val < 0 || val > USHRT_MAX)
476 errx(EXIT_FAILURE, "out of range"); 480 errx(EXIT_FAILURE, "out of range");
477 481
478 return (uint16_t)val; 482 return (uint16_t)val;
479} 483}
480 484
481static int 485static int
482cmd_set_listen_port(const char *interface, int argc, char *argv[]) 486cmd_set_listen_port(const char *interface, int argc, char *argv[])
483{ 487{
484 uint16_t port; 488 uint16_t port;
485 489
486 if (argc != 1) 490 if (argc != 1)
487 usage(); 491 usage();
488 492
489 port = strtouint16(argv[0]); 493 port = strtouint16(argv[0]);
490 if (port == 0) 494 if (port == 0)
491 errx(EXIT_FAILURE, "port 0 is not allowed"); 495 errx(EXIT_FAILURE, "port 0 is not allowed");
492 496
493 prop_dictionary_t prop_dict; 497 prop_dictionary_t prop_dict;
494 prop_dict = prop_dictionary_create(); 498 prop_dict = prop_dictionary_create();
495 if (prop_dict == NULL) 499 if (prop_dict == NULL)
496 errx(EXIT_FAILURE, "prop_dictionary_create"); 500 errx(EXIT_FAILURE, "prop_dictionary_create");
497 501
498 if (!prop_dictionary_set_uint16(prop_dict, "listen_port", port)) 502 if (!prop_dictionary_set_uint16(prop_dict, "listen_port", port))
499 errx(EXIT_FAILURE, "prop_dictionary_set_uint16"); 503 errx(EXIT_FAILURE, "prop_dictionary_set_uint16");
500 504
501 char *buf = prop_dictionary_externalize(prop_dict); 505 char *buf = prop_dictionary_externalize(prop_dict);
502 if (buf == NULL) 506 if (buf == NULL)
503 err(EXIT_FAILURE, "prop_dictionary_externalize failed"); 507 err(EXIT_FAILURE, "prop_dictionary_externalize failed");
504 ioctl_set(interface, WG_IOCTL_SET_LISTEN_PORT, buf); 508 ioctl_set(interface, WG_IOCTL_SET_LISTEN_PORT, buf);
505 509
506 return EXIT_SUCCESS; 510 return EXIT_SUCCESS;
507} 511}
508 512
509static void 513static void
510handle_option_endpoint(const char *_addr_port, prop_dictionary_t prop_dict) 514handle_option_endpoint(const char *_addr_port, prop_dictionary_t prop_dict)
511{ 515{
512 int error; 516 int error;
513 char *port; 517 char *port;
514 struct addrinfo hints, *res; 518 struct addrinfo hints, *res;
515 char *addr_port, *addr; 519 char *addr_port, *addr;
516 520
517 addr = addr_port = strdup(_addr_port); 521 addr = addr_port = strdup(_addr_port);
518 522
519 if (addr_port[0] == '[') { 523 if (addr_port[0] == '[') {
520 /* [<ipv6>]:<port> */ 524 /* [<ipv6>]:<port> */
521 /* Accept [<ipv4>]:<port> too, but it's not a big deal. */ 525 /* Accept [<ipv4>]:<port> too, but it's not a big deal. */
522 char *bracket, *colon; 526 char *bracket, *colon;
523 if (strlen(addr_port) < strlen("[::]:0")) 527 if (strlen(addr_port) < strlen("[::]:0"))
524 errx(EXIT_FAILURE, "invalid endpoint format"); 528 errx(EXIT_FAILURE, "invalid endpoint format");
525 addr = addr_port + 1; 529 addr = addr_port + 1;
526 bracket = strchr(addr, ']'); 530 bracket = strchr(addr, ']');
527 if (bracket == NULL) 531 if (bracket == NULL)
528 errx(EXIT_FAILURE, "invalid endpoint format"); 532 errx(EXIT_FAILURE, "invalid endpoint format");
529 *bracket = '\0'; 533 *bracket = '\0';
530 colon = bracket + 1; 534 colon = bracket + 1;
531 if (*colon != ':') 535 if (*colon != ':')
532 errx(EXIT_FAILURE, "invalid endpoint format"); 536 errx(EXIT_FAILURE, "invalid endpoint format");
533 *colon = '\0'; 537 *colon = '\0';
534 port = colon + 1; 538 port = colon + 1;
535 } else { 539 } else {
536 char *colon, *tmp; 540 char *colon, *tmp;
537 colon = strchr(addr_port, ':'); 541 colon = strchr(addr_port, ':');
538 if (colon == NULL) 542 if (colon == NULL)
539 errx(EXIT_FAILURE, "no ':' found in endpoint"); 543 errx(EXIT_FAILURE, "no ':' found in endpoint");
540 tmp = strchr(colon + 1, ':'); 544 tmp = strchr(colon + 1, ':');
541 if (tmp != NULL) { 545 if (tmp != NULL) {
542 /* <ipv6>:<port> */ 546 /* <ipv6>:<port> */
543 /* Assume the last colon is a separator */ 547 /* Assume the last colon is a separator */
544 char *last_colon = tmp; 548 char *last_colon = tmp;
545 while ((tmp = strchr(tmp + 1, ':')) != NULL) 549 while ((tmp = strchr(tmp + 1, ':')) != NULL)
546 last_colon = tmp; 550 last_colon = tmp;
547 colon = last_colon; 551 colon = last_colon;
548 *colon = '\0'; 552 *colon = '\0';
549 port = colon + 1; 553 port = colon + 1;
550 } else { 554 } else {
551 /* <ipv4>:<port> */ 555 /* <ipv4>:<port> */
552 *colon = '\0'; 556 *colon = '\0';
553 port = colon + 1; 557 port = colon + 1;
554 } 558 }
555 } 559 }
556 560
557 memset(&hints, 0, sizeof(hints)); 561 memset(&hints, 0, sizeof(hints));
558 hints.ai_family = AF_UNSPEC; 562 hints.ai_family = AF_UNSPEC;
559 hints.ai_flags = AI_NUMERICHOST; 563 hints.ai_flags = AI_NUMERICHOST;
560 error = getaddrinfo(addr, port, &hints, &res); 564 error = getaddrinfo(addr, port, &hints, &res);
561 if (error) 565 if (error)
562 errx(EXIT_FAILURE, "getaddrinfo: %s", gai_strerror(error)); 566 errx(EXIT_FAILURE, "getaddrinfo: %s", gai_strerror(error));
563 567
564 if (!prop_dictionary_set_data(prop_dict, "endpoint", 568 if (!prop_dictionary_set_data(prop_dict, "endpoint",
565 res->ai_addr, res->ai_addrlen)) 569 res->ai_addr, res->ai_addrlen))
566 errx(EXIT_FAILURE, "prop_dictionary_set_data"); 570 errx(EXIT_FAILURE, "prop_dictionary_set_data");
567 571
568 freeaddrinfo(res); 572 freeaddrinfo(res);
569 free(addr_port); 573 free(addr_port);
570} 574}
571 575
572static void 576static void
573handle_option_allowed_ips(const char *_allowed_ips, prop_dictionary_t prop_dict) 577handle_option_allowed_ips(const char *_allowed_ips, prop_dictionary_t prop_dict)
574{ 578{
575 prop_array_t allowedips; 579 prop_array_t allowedips;
576 int i; 580 int i;
577 char *allowed_ips, *ip; 581 char *allowed_ips, *ip;
578 582
579 allowed_ips = strdup(_allowed_ips); 583 allowed_ips = strdup(_allowed_ips);
580 if (allowed_ips == NULL) 584 if (allowed_ips == NULL)
581 errx(EXIT_FAILURE, "strdup"); 585 errx(EXIT_FAILURE, "strdup");
582 586
583 allowedips = prop_array_create(); 587 allowedips = prop_array_create();
584 if (allowedips == NULL) 588 if (allowedips == NULL)
585 errx(EXIT_FAILURE, "prop_array_create"); 589 errx(EXIT_FAILURE, "prop_array_create");
586 590
587 for (i = 0; (ip = strsep(&allowed_ips, ",")) != NULL; i++) { 591 for (i = 0; (ip = strsep(&allowed_ips, ",")) != NULL; i++) {
588 prop_dictionary_t prop_allowedip; 592 prop_dictionary_t prop_allowedip;
589 uint16_t cidr; 593 uint16_t cidr;
590 char *cidrp; 594 char *cidrp;
591 struct addrinfo hints, *res; 595 struct addrinfo hints, *res;
592 int error; 596 int error;
593 597
594 prop_allowedip = prop_dictionary_create(); 598 prop_allowedip = prop_dictionary_create();
595 if (prop_allowedip == NULL) 599 if (prop_allowedip == NULL)
596 errx(EXIT_FAILURE, "prop_dictionary_create"); 600 errx(EXIT_FAILURE, "prop_dictionary_create");
597 601
598 cidrp = strchr(ip, '/'); 602 cidrp = strchr(ip, '/');
599 if (cidrp == NULL) 603 if (cidrp == NULL)
600 errx(EXIT_FAILURE, "no '/' found in allowed-ip"); 604 errx(EXIT_FAILURE, "no '/' found in allowed-ip");
601 *cidrp = '\0'; 605 *cidrp = '\0';
602 cidrp++; 606 cidrp++;
603 607
604 cidr = strtouint16(cidrp); 608 cidr = strtouint16(cidrp);
605 609
606 memset(&hints, 0, sizeof(hints)); 610 memset(&hints, 0, sizeof(hints));
607 hints.ai_family = AF_UNSPEC; 611 hints.ai_family = AF_UNSPEC;
608 hints.ai_flags = AI_NUMERICHOST; 612 hints.ai_flags = AI_NUMERICHOST;
609 error = getaddrinfo(ip, 0, &hints, &res); 613 error = getaddrinfo(ip, 0, &hints, &res);
610 if (error) 614 if (error)
611 errx(EXIT_FAILURE, "getaddrinfo: %s", 615 errx(EXIT_FAILURE, "getaddrinfo: %s",
612 gai_strerror(errno)); 616 gai_strerror(errno));
613 617
614 sa_family_t family = res->ai_addr->sa_family; 618 sa_family_t family = res->ai_addr->sa_family;
615 if (!prop_dictionary_set_uint8(prop_allowedip, "family", 619 if (!prop_dictionary_set_uint8(prop_allowedip, "family",
616 family)) 620 family))
617 errx(EXIT_FAILURE, "prop_dictionary_set_uint8"); 621 errx(EXIT_FAILURE, "prop_dictionary_set_uint8");
618 622
619 const void *addr; 623 const void *addr;
620 size_t addrlen; 624 size_t addrlen;
621 switch (family) { 625 switch (family) {
622 case AF_INET: { 626 case AF_INET: {
623 const struct sockaddr_in *sin = 627 const struct sockaddr_in *sin =
624 (const struct sockaddr_in *)res->ai_addr; 628 (const struct sockaddr_in *)res->ai_addr;
625 addr = &sin->sin_addr; 629 addr = &sin->sin_addr;
626 addrlen = sizeof(sin->sin_addr); 630 addrlen = sizeof(sin->sin_addr);
627 break; 631 break;
628 } 632 }
629 case AF_INET6: { 633 case AF_INET6: {
630 const struct sockaddr_in6 *sin6 = 634 const struct sockaddr_in6 *sin6 =
631 (const struct sockaddr_in6 *)res->ai_addr; 635 (const struct sockaddr_in6 *)res->ai_addr;
632 addr = &sin6->sin6_addr; 636 addr = &sin6->sin6_addr;
633 addrlen = sizeof(sin6->sin6_addr); 637 addrlen = sizeof(sin6->sin6_addr);
634 break; 638 break;
635 } 639 }
636 default: 640 default:
637 errx(EXIT_FAILURE, "invalid family: %d", family); 641 errx(EXIT_FAILURE, "invalid family: %d", family);
638 } 642 }
639 if (!prop_dictionary_set_data(prop_allowedip, "ip", 643 if (!prop_dictionary_set_data(prop_allowedip, "ip",
640 addr, addrlen)) 644 addr, addrlen))
641 errx(EXIT_FAILURE, "prop_dictionary_set_data"); 645 errx(EXIT_FAILURE, "prop_dictionary_set_data");
642 if (!prop_dictionary_set_uint16(prop_allowedip, "cidr", cidr)) 646 if (!prop_dictionary_set_uint16(prop_allowedip, "cidr", cidr))
643 errx(EXIT_FAILURE, "prop_dictionary_set_uint16"); 647 errx(EXIT_FAILURE, "prop_dictionary_set_uint16");
644 648
645 freeaddrinfo(res); 649 freeaddrinfo(res);
646 prop_array_set(allowedips, i, prop_allowedip); 650 prop_array_set(allowedips, i, prop_allowedip);
647 } 651 }
648 prop_dictionary_set(prop_dict, "allowedips", allowedips); 652 prop_dictionary_set(prop_dict, "allowedips", allowedips);
649 prop_object_release(allowedips); 653 prop_object_release(allowedips);
650 654
651 free(allowed_ips); 655 free(allowed_ips);
652} 656}
653 657
654static void 658static void
655handle_option_preshared_key(const char *path, prop_dictionary_t prop_dict) 659handle_option_preshared_key(const char *path, prop_dictionary_t prop_dict)
656{ 660{
657 unsigned char keybuf[KEY_LEN]; 661 unsigned char keybuf[KEY_LEN];
658 662
659 read_key(path, keybuf); 663 read_key(path, keybuf);
660 if (!prop_dictionary_set_data(prop_dict, "preshared_key", 664 if (!prop_dictionary_set_data(prop_dict, "preshared_key",
661 keybuf, sizeof(keybuf))) 665 keybuf, sizeof(keybuf)))
662 errx(EXIT_FAILURE, "prop_dictionary_set_data"); 666 errx(EXIT_FAILURE, "prop_dictionary_set_data");
663} 667}
664 668
665static const struct option { 669static const struct option {
666 const char *option; 670 const char *option;
667 void (*func)(const char *, prop_dictionary_t); 671 void (*func)(const char *, prop_dictionary_t);
668} options[] = { 672} options[] = {
669 {"--endpoint=", handle_option_endpoint}, 673 {"--endpoint=", handle_option_endpoint},
670 {"--allowed-ips=", handle_option_allowed_ips}, 674 {"--allowed-ips=", handle_option_allowed_ips},
671 {"--preshared-key=", handle_option_preshared_key}, 675 {"--preshared-key=", handle_option_preshared_key},
672}; 676};
673 677
674static void 678static void
675handle_options(int argc, char *argv[], prop_dictionary_t prop_dict) 679handle_options(int argc, char *argv[], prop_dictionary_t prop_dict)
676{ 680{
677 681
678 while (argc > 0) { 682 while (argc > 0) {
679 for (size_t i = 0; i < __arraycount(options); i++) { 683 for (size_t i = 0; i < __arraycount(options); i++) {
680 const struct option *opt = &options[i]; 684 const struct option *opt = &options[i];
681 size_t optlen = strlen(opt->option); 685 size_t optlen = strlen(opt->option);
682 if (strncmp(argv[0], opt->option, optlen) == 0) { 686 if (strncmp(argv[0], opt->option, optlen) == 0) {
683 opt->func(argv[0] + optlen, prop_dict); 687 opt->func(argv[0] + optlen, prop_dict);
684 break; 688 break;
685 } 689 }
686 } 690 }
687 argc -= 1; 691 argc -= 1;
688 argv += 1; 692 argv += 1;
689 } 693 }
690 694
691 if (argc != 0) 695 if (argc != 0)
692 usage(); 696 usage();
693} 697}
694 698
695static int 699static int
696cmd_add_peer(const char *interface, int argc, char *argv[]) 700cmd_add_peer(const char *interface, int argc, char *argv[])
697{ 701{
698 const char *name; 702 const char *name;
699 unsigned char keybuf[KEY_LEN]; 703 unsigned char keybuf[KEY_LEN];
700 704
701 if (argc < 2) 705 if (argc < 2)
702 usage(); 706 usage();
703 707
704 prop_dictionary_t prop_dict; 708 prop_dictionary_t prop_dict;
705 prop_dict = prop_dictionary_create(); 709 prop_dict = prop_dictionary_create();
706 if (prop_dict == NULL) 710 if (prop_dict == NULL)
707 errx(EXIT_FAILURE, "prop_dictionary_create"); 711 errx(EXIT_FAILURE, "prop_dictionary_create");
708 712
709 name = argv[0]; 713 name = argv[0];
710 if (strlen(name) > WG_PEER_NAME_MAXLEN) 714 if (strlen(name) > WG_PEER_NAME_MAXLEN)
711 errx(EXIT_FAILURE, "peer name too long"); 715 errx(EXIT_FAILURE, "peer name too long");
712 if (strnlen(argv[1], KEY_BASE64_LEN + 1) != KEY_BASE64_LEN) 716 if (strnlen(argv[1], KEY_BASE64_LEN + 1) != KEY_BASE64_LEN)
713 errx(EXIT_FAILURE, "invalid public-key length: %zu", 717 errx(EXIT_FAILURE, "invalid public-key length: %zu",
714 strlen(argv[1])); 718 strlen(argv[1]));
715 base64_decode(argv[1], keybuf); 719 base64_decode(argv[1], keybuf);
716 720
717 if (!prop_dictionary_set_string(prop_dict, "name", name)) 721 if (!prop_dictionary_set_string(prop_dict, "name", name))
718 errx(EXIT_FAILURE, "prop_dictionary_set_string"); 722 errx(EXIT_FAILURE, "prop_dictionary_set_string");
719 if (!prop_dictionary_set_data(prop_dict, "public_key", 723 if (!prop_dictionary_set_data(prop_dict, "public_key",
720 keybuf, sizeof(keybuf))) 724 keybuf, sizeof(keybuf)))
721 errx(EXIT_FAILURE, "prop_dictionary_set_data"); 725 errx(EXIT_FAILURE, "prop_dictionary_set_data");
722 726
723 argc -= 2; 727 argc -= 2;
724 argv += 2; 728 argv += 2;
725 729
726 handle_options(argc, argv, prop_dict); 730 handle_options(argc, argv, prop_dict);
727 731
728 char *buf = prop_dictionary_externalize(prop_dict); 732 char *buf = prop_dictionary_externalize(prop_dict);
729 if (buf == NULL) 733 if (buf == NULL)
730 err(EXIT_FAILURE, "prop_dictionary_externalize failed"); 734 err(EXIT_FAILURE, "prop_dictionary_externalize failed");
731 ioctl_set(interface, WG_IOCTL_ADD_PEER, buf); 735 ioctl_set(interface, WG_IOCTL_ADD_PEER, buf);
732 736
733 return EXIT_SUCCESS; 737 return EXIT_SUCCESS;
734} 738}
735 739
736static int 740static int
737cmd_delete_peer(const char *interface, int argc, char *argv[]) 741cmd_delete_peer(const char *interface, int argc, char *argv[])
738{ 742{
739 const char *name; 743 const char *name;
740 744
741 if (argc != 1) 745 if (argc != 1)
742 usage(); 746 usage();
743 747
744 prop_dictionary_t prop_dict; 748 prop_dictionary_t prop_dict;
745 prop_dict = prop_dictionary_create(); 749 prop_dict = prop_dictionary_create();
746 if (prop_dict == NULL) 750 if (prop_dict == NULL)
747 errx(EXIT_FAILURE, "prop_dictionary_create"); 751 errx(EXIT_FAILURE, "prop_dictionary_create");
748 752
749 name = argv[0]; 753 name = argv[0];
750 if (strlen(name) > WG_PEER_NAME_MAXLEN) 754 if (strlen(name) > WG_PEER_NAME_MAXLEN)
751 errx(EXIT_FAILURE, "peer name too long"); 755 errx(EXIT_FAILURE, "peer name too long");
752 756
753 if (!prop_dictionary_set_string(prop_dict, "name", name)) 757 if (!prop_dictionary_set_string(prop_dict, "name", name))
754 errx(EXIT_FAILURE, "prop_dictionary_set_string"); 758 errx(EXIT_FAILURE, "prop_dictionary_set_string");
755 759
756 char *buf = prop_dictionary_externalize(prop_dict); 760 char *buf = prop_dictionary_externalize(prop_dict);
757 if (buf == NULL) 761 if (buf == NULL)
758 err(EXIT_FAILURE, "prop_dictionary_externalize failed"); 762 err(EXIT_FAILURE, "prop_dictionary_externalize failed");
759 ioctl_set(interface, WG_IOCTL_DELETE_PEER, buf); 763 ioctl_set(interface, WG_IOCTL_DELETE_PEER, buf);
760 764
761 return EXIT_SUCCESS; 765 return EXIT_SUCCESS;
762} 766}
763 767
764static const struct command { 768static const struct command {
765 const char *command; 769 const char *command;
766 const char *target; 770 const char *target;
767 int (*func)(const char *, int, char **); 771 int (*func)(const char *, int, char **);
768} commands[] = { 772} commands[] = {
769 {"show", "all", cmd_show_all}, 773 {"show", "all", cmd_show_all},
770 {"show", "peer", cmd_show_peer}, 774 {"show", "peer", cmd_show_peer},
771 {"show", "private-key", cmd_show_private_key}, 775 {"show", "private-key", cmd_show_private_key},
772 {"set", "private-key", cmd_set_private_key}, 776 {"set", "private-key", cmd_set_private_key},
773 {"set", "listen-port", cmd_set_listen_port}, 777 {"set", "listen-port", cmd_set_listen_port},
774 {"add", "peer", cmd_add_peer}, 778 {"add", "peer", cmd_add_peer},
775 {"delete", "peer", cmd_delete_peer}, 779 {"delete", "peer", cmd_delete_peer},
776}; 780};
777 781
778int 782int
779main(int argc, char *argv[]) 783main(int argc, char *argv[])
780{ 784{
781 const char *interface; 785 const char *interface;
782 const char *command; 786 const char *command;
783 const char *target; 787 const char *target;
784 788
785 if (argc < 2 || 789 if (argc < 2 ||
786 strcmp(argv[1], "-h") == 0 || 790 strcmp(argv[1], "-h") == 0 ||
787 strcmp(argv[1], "-?") == 0 || 791 strcmp(argv[1], "-?") == 0 ||
788 strcmp(argv[1], "--help") == 0) { 792 strcmp(argv[1], "--help") == 0) {
789 usage(); 793 usage();
790 } 794 }
791 795
792 interface = argv[1]; 796 interface = argv[1];
793 if (strlen(interface) > IFNAMSIZ) 797 if (strlen(interface) > IFNAMSIZ)
794 errx(EXIT_FAILURE, "interface name too long"); 798 errx(EXIT_FAILURE, "interface name too long");
795 if (argc == 2) { 799 if (argc == 2) {
796 return cmd_show_all(interface, 0, NULL); 800 return cmd_show_all(interface, 0, NULL);
797 } 801 }
798 if (argc < 4) { 802 if (argc < 4) {
799 usage(); 803 usage();
800 } 804 }
801 command = argv[2]; 805 command = argv[2];
802 target = argv[3]; 806 target = argv[3];
803 807
804 argc -= 4; 808 argc -= 4;
805 argv += 4; 809 argv += 4;
806 810
807 for (size_t i = 0; i < __arraycount(commands); i++) { 811 for (size_t i = 0; i < __arraycount(commands); i++) {
808 const struct command *cmd = &commands[i]; 812 const struct command *cmd = &commands[i];
809 if (strncmp(command, cmd->command, strlen(cmd->command)) == 0 && 813 if (strncmp(command, cmd->command, strlen(cmd->command)) == 0 &&
810 strncmp(target, cmd->target, strlen(cmd->target)) == 0) { 814 strncmp(target, cmd->target, strlen(cmd->target)) == 0) {
811 return cmd->func(interface, argc, argv); 815 return cmd->func(interface, argc, argv);
812 } 816 }
813 } 817 }
814 818
815 usage(); 819 usage();
816} 820}