libipm: Add support for receiving file descriptors

This commit is contained in:
matt335672 2023-01-09 14:11:43 +00:00
parent 8a71322fe4
commit c9adb3a2a6
4 changed files with 123 additions and 1 deletions

View File

@ -32,6 +32,23 @@
const char *libipm_valid_type_chars = "ybnqiuxtsdhogB"; const char *libipm_valid_type_chars = "ybnqiuxtsdhogB";
/*****************************************************************************/
void
libipm_msg_in_close_file_descriptors(struct trans *self)
{
struct libipm_priv *priv = (struct libipm_priv *)self->extra_data;
if (priv != NULL)
{
unsigned int i;
for (i = priv->in_fd_index ; i < priv->in_fd_count; ++i)
{
g_file_close(priv->in_fds[i]);
}
priv->in_fd_count = 0;
priv->in_fd_index = 0;
}
}
/**************************************************************************//** /**************************************************************************//**
* Send function for a struct trans initialised with libipm_init_trans() * Send function for a struct trans initialised with libipm_init_trans()
* *
@ -60,6 +77,52 @@ libipm_trans_send_proc(struct trans *self, const char *data, int len)
return rv; return rv;
} }
/**************************************************************************//**
* Receive function for a struct trans initialised with libipm_init_trans()
*
* @param trans Transport to receive on
* @param data pointer to receive data buffer
* @param len Length of data to read
* @return As for read(2)
*/
static int
libipm_trans_recv_proc(struct trans *self, char *data, int len)
{
int rv;
struct libipm_priv *priv = (struct libipm_priv *)self->extra_data;
if (priv != NULL && data == self->in_s->data)
{
/* We're receiving the message header */
unsigned int fdcount;
/* Check there are no live file descriptors in the input
* buffer from a previous message. This shouldn't happen, but
* if by some chance it does, we could leak file descriptors */
if (priv->in_fd_count > 0)
{
LOG(LOG_LEVEL_WARNING, "Unconsumed file descriptors detected");
libipm_msg_in_close_file_descriptors(self);
}
rv = g_sck_recv_fd_set(self->sck, data, len,
priv->in_fds, MAX_FD_PER_MSG,
&fdcount);
if (fdcount > MAX_FD_PER_MSG)
{
LOG(LOG_LEVEL_WARNING,
"%d file descriptors were discarded on recvmsg()",
fdcount - MAX_FD_PER_MSG);
fdcount = MAX_FD_PER_MSG;
}
priv->in_fd_count = fdcount;
}
else
{
rv = g_sck_recv(self->sck, data, len, 0);
}
return rv;
}
/**************************************************************************//** /**************************************************************************//**
* Destructor for a struct trans initialised with libipm_init_trans() * Destructor for a struct trans initialised with libipm_init_trans()
@ -71,6 +134,7 @@ libipm_trans_destructor(struct trans *trans)
{ {
struct libipm_priv *priv = (struct libipm_priv *)trans->extra_data; struct libipm_priv *priv = (struct libipm_priv *)trans->extra_data;
libipm_msg_in_close_file_descriptors(trans);
if (priv != NULL) if (priv != NULL)
{ {
if ((priv->flags & LIBIPM_E_MSG_IN_ERASE_AFTER_USE) != 0 && if ((priv->flags & LIBIPM_E_MSG_IN_ERASE_AFTER_USE) != 0 &&
@ -113,6 +177,7 @@ libipm_init_trans(struct trans *trans,
priv->msgno_to_str = msgno_to_str; priv->msgno_to_str = msgno_to_str;
trans->trans_send = libipm_trans_send_proc; trans->trans_send = libipm_trans_send_proc;
trans->trans_recv = libipm_trans_recv_proc;
trans->extra_data = priv; trans->extra_data = priv;
trans->extra_destructor = libipm_trans_destructor; trans->extra_destructor = libipm_trans_destructor;

View File

@ -300,8 +300,8 @@ libipm_msg_in_peek_type(struct trans *trans);
* x | int64_t * | Signed (two's complement) 64-bit integer * x | int64_t * | Signed (two's complement) 64-bit integer
* t | uint64_t * | Unsigned 64-bit integer * t | uint64_t * | Unsigned 64-bit integer
* s | char ** | NULL-terminated string * s | char ** | NULL-terminated string
* h | int * | File descriptor
* d | - | (reserved) * d | - | (reserved)
* h | - | (reserved)
* o | - | (reserved) * o | - | (reserved)
* g | - | (reserved) * g | - | (reserved)
* *
@ -315,6 +315,9 @@ libipm_msg_in_peek_type(struct trans *trans);
* returned. This pointer will only be valid until the next call to * returned. This pointer will only be valid until the next call to
* libipm_msg_in_reset() * libipm_msg_in_reset()
* *
* The 'h' type can only be used where the underlying transport is a
* UNIX domain socket.
*
* For the 'B' type, pass in the address of an initialised descriptor * For the 'B' type, pass in the address of an initialised descriptor
* containing the address and size of the object to copy the data * containing the address and size of the object to copy the data
* to. The size in the descriptor must match the size of the object * to. The size in the descriptor must match the size of the object
@ -330,6 +333,9 @@ libipm_msg_in_parse(struct trans *trans, const char *format, ...);
* If the LIBIPM_E_MSG_IN_ERASE_AFTER_USE flag is set for the transport, * If the LIBIPM_E_MSG_IN_ERASE_AFTER_USE flag is set for the transport,
* the entire buffer is erased, and the flag is cleared * the entire buffer is erased, and the flag is cleared
* *
* Any file descriptors received from the other end but not parsed
* by the application are closed.
*
* @param trans libipm transport * @param trans libipm transport
*/ */
void void

View File

@ -61,6 +61,11 @@ struct libipm_priv
int out_fds[MAX_FD_PER_MSG]; int out_fds[MAX_FD_PER_MSG];
unsigned short in_msgno; unsigned short in_msgno;
unsigned short in_param_count; unsigned short in_param_count;
/** Pointer to next fd to be consumed by the app */
unsigned short in_fd_index;
/** Number of fds in the incoming message */
unsigned short in_fd_count;
int in_fds[MAX_FD_PER_MSG];
}; };
/** /**
@ -71,4 +76,15 @@ struct libipm_priv
*/ */
extern const char *libipm_valid_type_chars; extern const char *libipm_valid_type_chars;
/**
* Close input file descriptors for an input message
*
* If file descriptors are read from the other end, but not passed to the
* application, they must be closed to prevent file descriptor leaks
*
* @param trans Transport to close file descriptors for
*/
void
libipm_msg_in_close_file_descriptors(struct trans *self);
#endif /* LIBIPM__PRIVATE_H */ #endif /* LIBIPM__PRIVATE_H */

View File

@ -460,6 +460,36 @@ extract_char_ptr_type(char c, struct trans *trans, va_list *argptr)
return rv; return rv;
} }
/**************************************************************************//**
* Extract a file descriptor from the input stream
*
* @param c Type letter which triggered the call
* @param trans libipm transport
* @param argptr argptr to pointer to receive the value
* @return != 0 for error
*/
static enum libipm_status
extract_fd_type(char c, struct trans *trans, va_list *argptr)
{
enum libipm_status rv = E_LI_SUCCESS;
struct libipm_priv *priv = (struct libipm_priv *)trans->extra_data;
/* File descriptor available? */
if (priv->in_fd_index >= priv->in_fd_count)
{
log_parse_error(trans, "No file descriptors available");
rv = E_LI_TOO_MANY_FDS;
}
else
{
int *tmp = va_arg(*argptr, int *);
*tmp = priv->in_fds[priv->in_fd_index++];
}
return rv;
}
/**************************************************************************//** /**************************************************************************//**
* Extract a fixed size block from the input stream * Extract a fixed size block from the input stream
* *
@ -588,6 +618,10 @@ libipm_msg_in_parsev(struct trans *trans, const char *format, va_list *argptr)
rv = extract_char_ptr_type(c, trans, argptr); rv = extract_char_ptr_type(c, trans, argptr);
break; break;
case 'h':
rv = extract_fd_type(c, trans, argptr);
break;
case 'B': case 'B':
rv = extract_fsb_type(c, trans, argptr); rv = extract_fsb_type(c, trans, argptr);
break; break;
@ -650,6 +684,7 @@ libipm_msg_in_reset(struct trans *trans)
} }
priv->in_msgno = 0; priv->in_msgno = 0;
priv->in_param_count = 0; priv->in_param_count = 0;
libipm_msg_in_close_file_descriptors(trans);
} }
trans->extra_flags = 0; trans->extra_flags = 0;