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 
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 
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 
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