OpenVPN 3 Core Library
Loading...
Searching...
No Matches
xmitfd.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12// Transmit/Receive file descriptors over a unix domain socket
13
14#ifndef OPENVPN_COMMON_XMITFD_H
15#define OPENVPN_COMMON_XMITFD_H
16
17#include <string>
18#include <memory>
19#include <cstring>
20
21#include <sys/types.h>
22#include <sys/socket.h>
23#include <poll.h>
24#include <errno.h>
25
28
29namespace openvpn {
30
31class XmitFD
32{
33 public:
34 OPENVPN_EXCEPTION(xmit_fd_error);
35
36 static void xmit_fd(const int sock_fd,
37 const int payload_fd, // optional (set to -1 to disable)
38 const std::string &message,
39 const int timeout_ms)
40 {
41 unsigned char buf[CMSG_SPACE(sizeof(payload_fd))];
42 std::memset(buf, 0, sizeof(buf));
43
44 struct iovec io;
45 io.iov_base = const_cast<char *>(message.c_str());
46 io.iov_len = message.length();
47
48 struct msghdr msg = {0};
49 msg.msg_iov = &io;
50 msg.msg_iovlen = 1;
51
52 if (payload_fd >= 0)
53 {
54 msg.msg_control = buf;
55 msg.msg_controllen = sizeof(buf);
56
57 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
58 cmsg->cmsg_level = SOL_SOCKET;
59 cmsg->cmsg_type = SCM_RIGHTS;
60 cmsg->cmsg_len = CMSG_LEN(sizeof(payload_fd));
61
62 std::memcpy(CMSG_DATA(cmsg), &payload_fd, sizeof(payload_fd));
63
64 msg.msg_controllen = cmsg->cmsg_len;
65 }
66
67 poll_wait(sock_fd, true, timeout_ms);
68 const ssize_t status = ::sendmsg(sock_fd, &msg, 0);
69 if (status < 0)
70 {
71 const int eno = errno;
72 OPENVPN_THROW(xmit_fd_error, "xmit_fd: " << strerror_str(eno));
73 }
74 else if (status != static_cast<ssize_t>(message.length()))
75 OPENVPN_THROW(xmit_fd_error, "xmit_fd: unexpected send size");
76 }
77
78 static int recv_fd(const int sock_fd,
79 std::string &message,
80 const size_t buf_size,
81 const int timeout_ms)
82 {
83 struct msghdr msg = {0};
84
85 std::unique_ptr<char[]> buf(new char[buf_size]);
86 struct iovec io = {.iov_base = buf.get(), .iov_len = buf_size};
87 msg.msg_iov = &io;
88 msg.msg_iovlen = 1;
89
90 unsigned char c_buffer[256];
91 msg.msg_control = c_buffer;
92 msg.msg_controllen = sizeof(c_buffer);
93
94 poll_wait(sock_fd, false, timeout_ms);
95 const ssize_t status = ::recvmsg(sock_fd, &msg, 0);
96 if (status < 0)
97 {
98 const int eno = errno;
99 OPENVPN_THROW(xmit_fd_error, "recv_fd: " << strerror_str(eno));
100 }
101 else if (status == 0)
102 OPENVPN_THROW(xmit_fd_error, "recv_fd: eof");
103 else if (status > static_cast<ssize_t>(buf_size))
104 OPENVPN_THROW(xmit_fd_error, "recv_fd: unexpectedly large message");
105
106 int fd;
107 for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg))
108 {
109 if (cmsg->cmsg_len == CMSG_LEN(sizeof(fd))
110 && cmsg->cmsg_level == SOL_SOCKET
111 && cmsg->cmsg_type == SCM_RIGHTS)
112 {
113 std::memcpy(&fd, CMSG_DATA(cmsg), sizeof(fd));
114 if (fd >= 0)
115 {
116 message = std::string(buf.get(), status);
117 return fd;
118 }
119 }
120 }
121 OPENVPN_THROW(xmit_fd_error, "recv_fd: no fd in message");
122 }
123
124 private:
125 static void poll_wait(const int fd,
126 const bool write,
127 const int timeout)
128 {
129 struct pollfd fds[1];
130 fds[0].fd = fd;
131 fds[0].events = write ? POLLOUT : (POLLIN | POLLPRI);
132 fds[0].revents = 0;
133 const int status = ::poll(fds, 1, timeout);
134 if (status < 0)
135 {
136 const int eno = errno;
137 OPENVPN_THROW(xmit_fd_error, "poll_wait: poll failed: " << strerror_str(eno));
138 }
139 else if (status == 0)
140 OPENVPN_THROW(xmit_fd_error, "poll_wait: poll timeout");
141 else if (status != 1)
142 OPENVPN_THROW(xmit_fd_error, "poll_wait: poll failed with unexpected return value=" << status);
143 }
144};
145
146} // namespace openvpn
147
148#endif
static void poll_wait(const int fd, const bool write, const int timeout)
Definition xmitfd.hpp:125
OPENVPN_EXCEPTION(xmit_fd_error)
static int recv_fd(const int sock_fd, std::string &message, const size_t buf_size, const int timeout_ms)
Definition xmitfd.hpp:78
static void xmit_fd(const int sock_fd, const int payload_fd, const std::string &message, const int timeout_ms)
Definition xmitfd.hpp:36
#define OPENVPN_THROW(exc, stuff)
std::string strerror_str(const int errnum)
Definition strerror.hpp:21
const char message[]
#define msg(flags,...)