#include "UMNSystem.h" #include #include #include #include #include #ifdef __APPLE__ #include #include #include #include #endif int umn_utun_open(char *name, size_t name_capacity) { #ifdef __APPLE__ int fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); if (fd < 0) return -1; struct ctl_info info; memset(&info, 0, sizeof(info)); strlcpy(info.ctl_name, UTUN_CONTROL_NAME, sizeof(info.ctl_name)); if (ioctl(fd, CTLIOCGINFO, &info) < 0) { close(fd); return -1; } struct sockaddr_ctl address; memset(&address, 0, sizeof(address)); address.sc_len = sizeof(address); address.sc_family = AF_SYSTEM; address.ss_sysaddr = AF_SYS_CONTROL; address.sc_id = info.ctl_id; address.sc_unit = 0; if (connect(fd, (struct sockaddr *)&address, sizeof(address)) < 0) { close(fd); return -1; } socklen_t length = (socklen_t)name_capacity; if (getsockopt(fd, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, name, &length) < 0) { close(fd); return -1; } return fd; #else (void)name; (void)name_capacity; errno = ENOTSUP; return -1; #endif } int umn_get_peer_eid(int socket_fd, uid_t *uid, gid_t *gid) { #ifdef __APPLE__ return getpeereid(socket_fd, uid, gid); #else struct ucred credential; socklen_t size = sizeof(credential); if (getsockopt(socket_fd, SOL_SOCKET, SO_PEERCRED, &credential, &size) < 0) return -1; *uid = credential.uid; *gid = credential.gid; return 0; #endif } int umn_send_fd(int socket_fd, int passed_fd, const void *data, size_t length) { struct iovec io = { .iov_base = (void *)data, .iov_len = length }; char control[CMSG_SPACE(sizeof(int))]; memset(control, 0, sizeof(control)); struct msghdr message; memset(&message, 0, sizeof(message)); message.msg_iov = &io; message.msg_iovlen = 1; message.msg_control = control; message.msg_controllen = sizeof(control); struct cmsghdr *header = CMSG_FIRSTHDR(&message); header->cmsg_level = SOL_SOCKET; header->cmsg_type = SCM_RIGHTS; header->cmsg_len = CMSG_LEN(sizeof(int)); memcpy(CMSG_DATA(header), &passed_fd, sizeof(int)); ssize_t result = sendmsg(socket_fd, &message, 0); if (result < 0) return -1; if ((size_t)result != length) { errno = EIO; return -1; } return 0; } ssize_t umn_recv_fd(int socket_fd, int *passed_fd, void *data, size_t capacity) { struct iovec io = { .iov_base = data, .iov_len = capacity }; char control[CMSG_SPACE(sizeof(int))]; memset(control, 0, sizeof(control)); struct msghdr message; memset(&message, 0, sizeof(message)); message.msg_iov = &io; message.msg_iovlen = 1; message.msg_control = control; message.msg_controllen = sizeof(control); ssize_t result = recvmsg(socket_fd, &message, 0); if (result <= 0) return result; *passed_fd = -1; for (struct cmsghdr *header = CMSG_FIRSTHDR(&message); header; header = CMSG_NXTHDR(&message, header)) { if (header->cmsg_level == SOL_SOCKET && header->cmsg_type == SCM_RIGHTS && header->cmsg_len >= CMSG_LEN(sizeof(int))) { memcpy(passed_fd, CMSG_DATA(header), sizeof(int)); break; } } if (*passed_fd < 0) { errno = EBADMSG; return -1; } return result; }