Implement g_sck_send_fd_set() and g_sck_recv_fd_set()

This commit is contained in:
matt335672
2023-01-03 12:15:15 +00:00
parent 57bd1be5b1
commit a5034e3572
2 changed files with 175 additions and 0 deletions
+139
View File
@@ -1428,6 +1428,145 @@ g_sck_send(int sck, const void *ptr, unsigned int len, int flags)
#endif
}
/*****************************************************************************/
int
g_sck_recv_fd_set(int sck, void *ptr, unsigned int len,
int fds[], unsigned int maxfd,
unsigned int *fdcount)
{
int rv = -1;
#if !defined(_WIN32)
// The POSIX API gives us no way to see how much ancillary data is
// present for recvmsg() - just use a big buffer.
//
// Use a union, so control_un.control is properly aligned.
union
{
struct cmsghdr cm;
unsigned char control[8192];
} control_un;
struct msghdr msg = {0};
*fdcount = 0;
/* Set up descriptor for vanilla data */
struct iovec iov[1] = { {ptr, len} };
msg.msg_iov = &iov[0];
msg.msg_iovlen = 1;
/* Add in the ancillary data buffer */
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
if ((rv = recvmsg(sck, &msg, 0)) > 0)
{
struct cmsghdr *cmsg;
if ((msg.msg_flags & MSG_CTRUNC) != 0)
{
LOG(LOG_LEVEL_WARNING, "Ancillary data on recvmsg() was truncated");
}
// Iterate over the cmsghdr structures in the ancillary data
for (cmsg = CMSG_FIRSTHDR(&msg);
cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg))
{
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_RIGHTS)
{
const unsigned char *data = CMSG_DATA(cmsg);
unsigned int data_len = cmsg->cmsg_len - CMSG_LEN(0);
// Check the data length doesn't point past the end of
// control_un.control (see below). This shouldn't happen,
// but is conceivable if the ancillary data is truncated
// and the OS doesn't handle that properly.
//
// <-- (sizeof(control_un.control) -->
// +------------------------------------+
// | |
// +------------------------------------+
// ^ ^
// | | <- data_len ->
// | |
// control_un.control data
unsigned int max_data_len =
sizeof(control_un.control) - (data - control_un.control);
if (len > max_data_len)
{
len = max_data_len;
}
// Process all the file descriptors in the structure
while (data_len >= sizeof(int))
{
int fd;
memcpy(&fd, data, sizeof(int));
data += sizeof(int);
data_len -= sizeof(int);
if (*fdcount < maxfd)
{
fds[(*fdcount)++] = fd;
}
else
{
// No room in the user's buffer for this fd
close(fd);
}
}
}
}
}
#endif /* !WIN32 */
return rv;
}
/*****************************************************************************/
int
g_sck_send_fd_set(int sck, const void *ptr, unsigned int len,
int fds[], unsigned int fdcount)
{
int rv = -1;
#if !defined(_WIN32)
struct msghdr msg = {0};
/* Set up descriptor for vanilla data */
struct iovec iov[1] = { {(void *)ptr, len} };
msg.msg_iov = &iov[0];
msg.msg_iovlen = 1;
if (fdcount > 0)
{
unsigned int fdsize = sizeof(fds[0]) * fdcount; /* Payload size */
/* Allocate ancillary data structure */
msg.msg_controllen = CMSG_SPACE(fdsize);
msg.msg_control = (struct cmsghdr *)g_malloc(msg.msg_controllen, 1);
if (msg.msg_control == NULL)
{
/* Memory allocation failure */
LOG(LOG_LEVEL_ERROR, "Error allocating buffer for %u fds",
fdcount);
return -1;
}
/* Fill in the ancillary data structure */
struct cmsghdr *cmptr = CMSG_FIRSTHDR(&msg);
cmptr->cmsg_len = CMSG_LEN(fdsize);
cmptr->cmsg_level = SOL_SOCKET;
cmptr->cmsg_type = SCM_RIGHTS;
memcpy(CMSG_DATA(cmptr), fds, fdsize);
}
rv = sendmsg(sck, &msg, 0);
g_free(msg.msg_control);
#endif /* !WIN32 */
return rv;
}
/*****************************************************************************/
/* returns boolean */
int
+36
View File
@@ -88,6 +88,42 @@ int g_sck_listen(int sck);
int g_sck_accept(int sck);
int g_sck_recv(int sck, void *ptr, unsigned int len, int flags);
int g_sck_send(int sck, const void *ptr, unsigned int len, int flags);
/**
* Receives data and file descriptors on a unix domain socket
*
* @param sck - Socket to receive data + file descriptors from
* @param ptr - Pointer to buffer for incoming data
* @param len - Length of data. Must be > 0
* @param[out] fds - Array of file descriptors
* @param [in] maxfd - Max number of elements in fds
* @param[out] fdcount - Actual number of file descriptors received
* @return Bytes received, or < 0 for error.
*
* If the result is > 0 but less than len, the file descriptors have
* been received. Get the rest of the data with normal g_sck_recv() calls.
*
* fdcount may be more that maxfd. This indicates that more file descriptors
* were received than there was space for. The excess file descriptors
* are closed and discarded.
*/
int g_sck_recv_fd_set(int sck, void *ptr, unsigned int len,
int fds[], unsigned int maxfd,
unsigned int *fdcount);
/**
* Sends data and file descriptors on a unix domain socket
*
* @param sck - Socket to send data + file descriptors on
* @param ptr - Data to send
* @param len - Length of data. Must be > 0
* @param fds - Array of file descriptors
* @param fdcount - Number of file descriptors
* @return Bytes sent, or < 0 for error.
*
* If the result is > 0 but less than len, the file descriptors have
* been sent. Send the rest of the data with normal g_sck_send() calls.
*/
int g_sck_send_fd_set(int sck, const void *ptr, unsigned int len,
int fds[], unsigned int fdcount);
int g_sck_last_error_would_block(int sck);
int g_sck_socket_ok(int sck);
int g_sck_can_send(int sck, int millis);