120 lines
2.6 KiB
C
120 lines
2.6 KiB
C
|
// SPDX-License-Identifier: MIT
|
||
|
/*
|
||
|
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||
|
*/
|
||
|
|
||
|
#include <stdbool.h>
|
||
|
#include <stddef.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <unistd.h>
|
||
|
#include <dirent.h>
|
||
|
#include <errno.h>
|
||
|
#include <sys/socket.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <sys/un.h>
|
||
|
|
||
|
#define SOCK_PATH RUNSTATEDIR "/wireguard/"
|
||
|
#define SOCK_SUFFIX ".sock"
|
||
|
|
||
|
static FILE *userspace_interface_file(const char *iface)
|
||
|
{
|
||
|
struct stat sbuf;
|
||
|
struct sockaddr_un addr = { .sun_family = AF_UNIX };
|
||
|
int fd = -1, ret;
|
||
|
FILE *f = NULL;
|
||
|
|
||
|
errno = EINVAL;
|
||
|
if (strchr(iface, '/'))
|
||
|
goto out;
|
||
|
ret = snprintf(addr.sun_path, sizeof(addr.sun_path), SOCK_PATH "%s" SOCK_SUFFIX, iface);
|
||
|
if (ret < 0)
|
||
|
goto out;
|
||
|
ret = stat(addr.sun_path, &sbuf);
|
||
|
if (ret < 0)
|
||
|
goto out;
|
||
|
errno = EBADF;
|
||
|
if (!S_ISSOCK(sbuf.st_mode))
|
||
|
goto out;
|
||
|
|
||
|
ret = fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||
|
if (ret < 0)
|
||
|
goto out;
|
||
|
|
||
|
ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
|
||
|
if (ret < 0) {
|
||
|
if (errno == ECONNREFUSED) /* If the process is gone, we try to clean up the socket. */
|
||
|
unlink(addr.sun_path);
|
||
|
goto out;
|
||
|
}
|
||
|
f = fdopen(fd, "r+");
|
||
|
if (f)
|
||
|
errno = 0;
|
||
|
out:
|
||
|
ret = -errno;
|
||
|
if (ret) {
|
||
|
if (fd >= 0)
|
||
|
close(fd);
|
||
|
errno = -ret;
|
||
|
return NULL;
|
||
|
}
|
||
|
return f;
|
||
|
}
|
||
|
|
||
|
static bool userspace_has_wireguard_interface(const char *iface)
|
||
|
{
|
||
|
struct stat sbuf;
|
||
|
struct sockaddr_un addr = { .sun_family = AF_UNIX };
|
||
|
int fd, ret;
|
||
|
|
||
|
if (strchr(iface, '/'))
|
||
|
return false;
|
||
|
if (snprintf(addr.sun_path, sizeof(addr.sun_path), SOCK_PATH "%s" SOCK_SUFFIX, iface) < 0)
|
||
|
return false;
|
||
|
if (stat(addr.sun_path, &sbuf) < 0)
|
||
|
return false;
|
||
|
if (!S_ISSOCK(sbuf.st_mode))
|
||
|
return false;
|
||
|
ret = fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||
|
if (ret < 0)
|
||
|
return false;
|
||
|
ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
|
||
|
if (ret < 0 && errno == ECONNREFUSED) { /* If the process is gone, we try to clean up the socket. */
|
||
|
close(fd);
|
||
|
unlink(addr.sun_path);
|
||
|
return false;
|
||
|
}
|
||
|
close(fd);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static int userspace_get_wireguard_interfaces(struct string_list *list)
|
||
|
{
|
||
|
DIR *dir;
|
||
|
struct dirent *ent;
|
||
|
size_t len;
|
||
|
char *end;
|
||
|
int ret = 0;
|
||
|
|
||
|
dir = opendir(SOCK_PATH);
|
||
|
if (!dir)
|
||
|
return errno == ENOENT ? 0 : -errno;
|
||
|
while ((ent = readdir(dir))) {
|
||
|
len = strlen(ent->d_name);
|
||
|
if (len <= strlen(SOCK_SUFFIX))
|
||
|
continue;
|
||
|
end = &ent->d_name[len - strlen(SOCK_SUFFIX)];
|
||
|
if (strncmp(end, SOCK_SUFFIX, strlen(SOCK_SUFFIX)))
|
||
|
continue;
|
||
|
*end = '\0';
|
||
|
if (!userspace_has_wireguard_interface(ent->d_name))
|
||
|
continue;
|
||
|
ret = string_list_add(list, ent->d_name);
|
||
|
if (ret < 0)
|
||
|
goto out;
|
||
|
}
|
||
|
out:
|
||
|
closedir(dir);
|
||
|
return ret;
|
||
|
}
|