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
20static 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;
53out:
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
64static 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
91static 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	}
116out:
117	closedir(dir);
118	return ret;
119}
120