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