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