1*adf37648SKyle Evans // SPDX-License-Identifier: MIT
2*adf37648SKyle Evans /*
3*adf37648SKyle Evans * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
4*adf37648SKyle Evans */
5*adf37648SKyle Evans
6*adf37648SKyle Evans #include <stdbool.h>
7*adf37648SKyle Evans #include <stddef.h>
8*adf37648SKyle Evans #include <stdio.h>
9*adf37648SKyle Evans #include <string.h>
10*adf37648SKyle Evans #include <unistd.h>
11*adf37648SKyle Evans #include <dirent.h>
12*adf37648SKyle Evans #include <errno.h>
13*adf37648SKyle Evans #include <sys/socket.h>
14*adf37648SKyle Evans #include <sys/stat.h>
15*adf37648SKyle Evans #include <sys/un.h>
16*adf37648SKyle Evans
17*adf37648SKyle Evans #define SOCK_PATH RUNSTATEDIR "/wireguard/"
18*adf37648SKyle Evans #define SOCK_SUFFIX ".sock"
19*adf37648SKyle Evans
userspace_interface_file(const char * iface)20*adf37648SKyle Evans static FILE *userspace_interface_file(const char *iface)
21*adf37648SKyle Evans {
22*adf37648SKyle Evans struct stat sbuf;
23*adf37648SKyle Evans struct sockaddr_un addr = { .sun_family = AF_UNIX };
24*adf37648SKyle Evans int fd = -1, ret;
25*adf37648SKyle Evans FILE *f = NULL;
26*adf37648SKyle Evans
27*adf37648SKyle Evans errno = EINVAL;
28*adf37648SKyle Evans if (strchr(iface, '/'))
29*adf37648SKyle Evans goto out;
30*adf37648SKyle Evans ret = snprintf(addr.sun_path, sizeof(addr.sun_path), SOCK_PATH "%s" SOCK_SUFFIX, iface);
31*adf37648SKyle Evans if (ret < 0)
32*adf37648SKyle Evans goto out;
33*adf37648SKyle Evans ret = stat(addr.sun_path, &sbuf);
34*adf37648SKyle Evans if (ret < 0)
35*adf37648SKyle Evans goto out;
36*adf37648SKyle Evans errno = EBADF;
37*adf37648SKyle Evans if (!S_ISSOCK(sbuf.st_mode))
38*adf37648SKyle Evans goto out;
39*adf37648SKyle Evans
40*adf37648SKyle Evans ret = fd = socket(AF_UNIX, SOCK_STREAM, 0);
41*adf37648SKyle Evans if (ret < 0)
42*adf37648SKyle Evans goto out;
43*adf37648SKyle Evans
44*adf37648SKyle Evans ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
45*adf37648SKyle Evans if (ret < 0) {
46*adf37648SKyle Evans if (errno == ECONNREFUSED) /* If the process is gone, we try to clean up the socket. */
47*adf37648SKyle Evans unlink(addr.sun_path);
48*adf37648SKyle Evans goto out;
49*adf37648SKyle Evans }
50*adf37648SKyle Evans f = fdopen(fd, "r+");
51*adf37648SKyle Evans if (f)
52*adf37648SKyle Evans errno = 0;
53*adf37648SKyle Evans out:
54*adf37648SKyle Evans ret = -errno;
55*adf37648SKyle Evans if (ret) {
56*adf37648SKyle Evans if (fd >= 0)
57*adf37648SKyle Evans close(fd);
58*adf37648SKyle Evans errno = -ret;
59*adf37648SKyle Evans return NULL;
60*adf37648SKyle Evans }
61*adf37648SKyle Evans return f;
62*adf37648SKyle Evans }
63*adf37648SKyle Evans
userspace_has_wireguard_interface(const char * iface)64*adf37648SKyle Evans static bool userspace_has_wireguard_interface(const char *iface)
65*adf37648SKyle Evans {
66*adf37648SKyle Evans struct stat sbuf;
67*adf37648SKyle Evans struct sockaddr_un addr = { .sun_family = AF_UNIX };
68*adf37648SKyle Evans int fd, ret;
69*adf37648SKyle Evans
70*adf37648SKyle Evans if (strchr(iface, '/'))
71*adf37648SKyle Evans return false;
72*adf37648SKyle Evans if (snprintf(addr.sun_path, sizeof(addr.sun_path), SOCK_PATH "%s" SOCK_SUFFIX, iface) < 0)
73*adf37648SKyle Evans return false;
74*adf37648SKyle Evans if (stat(addr.sun_path, &sbuf) < 0)
75*adf37648SKyle Evans return false;
76*adf37648SKyle Evans if (!S_ISSOCK(sbuf.st_mode))
77*adf37648SKyle Evans return false;
78*adf37648SKyle Evans ret = fd = socket(AF_UNIX, SOCK_STREAM, 0);
79*adf37648SKyle Evans if (ret < 0)
80*adf37648SKyle Evans return false;
81*adf37648SKyle Evans ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
82*adf37648SKyle Evans if (ret < 0 && errno == ECONNREFUSED) { /* If the process is gone, we try to clean up the socket. */
83*adf37648SKyle Evans close(fd);
84*adf37648SKyle Evans unlink(addr.sun_path);
85*adf37648SKyle Evans return false;
86*adf37648SKyle Evans }
87*adf37648SKyle Evans close(fd);
88*adf37648SKyle Evans return true;
89*adf37648SKyle Evans }
90*adf37648SKyle Evans
userspace_get_wireguard_interfaces(struct string_list * list)91*adf37648SKyle Evans static int userspace_get_wireguard_interfaces(struct string_list *list)
92*adf37648SKyle Evans {
93*adf37648SKyle Evans DIR *dir;
94*adf37648SKyle Evans struct dirent *ent;
95*adf37648SKyle Evans size_t len;
96*adf37648SKyle Evans char *end;
97*adf37648SKyle Evans int ret = 0;
98*adf37648SKyle Evans
99*adf37648SKyle Evans dir = opendir(SOCK_PATH);
100*adf37648SKyle Evans if (!dir)
101*adf37648SKyle Evans return errno == ENOENT ? 0 : -errno;
102*adf37648SKyle Evans while ((ent = readdir(dir))) {
103*adf37648SKyle Evans len = strlen(ent->d_name);
104*adf37648SKyle Evans if (len <= strlen(SOCK_SUFFIX))
105*adf37648SKyle Evans continue;
106*adf37648SKyle Evans end = &ent->d_name[len - strlen(SOCK_SUFFIX)];
107*adf37648SKyle Evans if (strncmp(end, SOCK_SUFFIX, strlen(SOCK_SUFFIX)))
108*adf37648SKyle Evans continue;
109*adf37648SKyle Evans *end = '\0';
110*adf37648SKyle Evans if (!userspace_has_wireguard_interface(ent->d_name))
111*adf37648SKyle Evans continue;
112*adf37648SKyle Evans ret = string_list_add(list, ent->d_name);
113*adf37648SKyle Evans if (ret < 0)
114*adf37648SKyle Evans goto out;
115*adf37648SKyle Evans }
116*adf37648SKyle Evans out:
117*adf37648SKyle Evans closedir(dir);
118*adf37648SKyle Evans return ret;
119*adf37648SKyle Evans }
120