int sock;
ssize_t sent;
int sys_errno;
+ size_t num_fds;
+ int *fds;
size_t buflen;
uint8_t buf[];
};
struct unix_dgram_msg *msg;
msg = q->msgs;
DLIST_REMOVE(q->msgs, msg);
+ close_fd_array(msg->fds, msg->num_fds);
free(msg);
}
close(q->sock);
}
static int queue_msg(struct unix_dgram_send_queue *q,
- const struct iovec *iov, int iovlen)
+ const struct iovec *iov, int iovlen,
+ const int *fds, size_t num_fds)
{
struct unix_dgram_msg *msg;
ssize_t buflen;
size_t msglen;
+ size_t fds_size = sizeof(int) * num_fds;
+ int fds_copy[MIN(num_fds, INT8_MAX)];
+ size_t fds_padding = 0;
int i;
+ size_t tmp;
+ int ret = -1;
+
+ if (num_fds > INT8_MAX) {
+ return EINVAL;
+ }
+
+ for (i = 0; i < num_fds; i++) {
+ fds_copy[i] = -1;
+ }
+
+ for (i = 0; i < num_fds; i++) {
+ fds_copy[i] = dup(fds[i]);
+ if (fds_copy[i] == -1) {
+ ret = errno;
+ goto fail;
+ }
+ }
buflen = iov_buflen(iov, iovlen);
if (buflen == -1) {
- return EINVAL;
+ goto invalid;
}
- msglen = offsetof(struct unix_dgram_msg, buf) + buflen;
- if ((msglen < buflen) ||
- (msglen < offsetof(struct unix_dgram_msg, buf))) {
+ msglen = offsetof(struct unix_dgram_msg, buf);
+ tmp = msglen + buflen;
+ if ((tmp < msglen) || (tmp < buflen)) {
/* overflow */
- return EINVAL;
+ goto invalid;
+ }
+ msglen = tmp;
+
+ if (num_fds > 0) {
+ const size_t fds_align = sizeof(int) - 1;
+
+ tmp = msglen + fds_align;
+ if ((tmp < msglen) || (tmp < fds_align)) {
+ /* overflow */
+ goto invalid;
+ }
+ tmp &= ~fds_align;
+
+ fds_padding = tmp - msglen;
+ msglen = tmp;
+
+ tmp = msglen + fds_size;
+ if ((tmp < msglen) || (tmp < fds_size)) {
+ /* overflow */
+ goto invalid;
+ }
+ msglen = tmp;
}
msg = malloc(msglen);
if (msg == NULL) {
- return ENOMEM;
+ ret = ENOMEM;
+ goto fail;
}
msg->buflen = buflen;
msg->sock = q->sock;
buflen += iov[i].iov_len;
}
+ msg->num_fds = num_fds;
+ if (msg->num_fds > 0) {
+ void *fds_ptr = (void *)&msg->buf[buflen+fds_padding];
+ memcpy(fds_ptr, fds_copy, fds_size);
+ msg->fds = (int *)fds_ptr;
+ } else {
+ msg->fds = NULL;
+ }
+
DLIST_ADD_END(q->msgs, msg, struct unix_dgram_msg);
return 0;
+
+invalid:
+ ret = EINVAL;
+fail:
+ close_fd_array(fds_copy, num_fds);
+ return ret;
}
static void unix_dgram_send_job(void *private_data)
.msg_iov = &iov,
.msg_iovlen = 1,
};
+#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
+ struct cmsghdr *cmsg;
+ size_t fds_size = sizeof(int) * dmsg->num_fds;
+ size_t cmsg_len = CMSG_LEN(fds_size);
+ size_t cmsg_space = CMSG_SPACE(fds_size);
+ char cmsg_buf[cmsg_space];
+
+ if (dmsg->num_fds > 0) {
+ void *fdptr;
+
+ memset(cmsg_buf, 0, cmsg_space);
+
+ msg.msg_control = cmsg_buf;
+ msg.msg_controllen = cmsg_space;
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = cmsg_len;
+ fdptr = CMSG_DATA(cmsg);
+ memcpy(fdptr, dmsg->fds, fds_size);
+ msg.msg_controllen = cmsg->cmsg_len;
+ }
+#endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */
do {
dmsg->sent = sendmsg(dmsg->sock, &msg, 0);
} while ((dmsg->sent == -1) && (errno == EINTR));
+
+ close_fd_array(dmsg->fds, dmsg->num_fds);
}
static void unix_dgram_job_finished(struct poll_watch *w, int fd, short events,
msg = q->msgs;
DLIST_REMOVE(q->msgs, msg);
+ close_fd_array(msg->fds, msg->num_fds);
free(msg);
if (q->msgs != NULL) {
static int unix_dgram_send(struct unix_dgram_ctx *ctx,
const struct sockaddr_un *dst,
- const struct iovec *iov, int iovlen)
+ const struct iovec *iov, int iovlen,
+ const int *fds, size_t num_fds)
{
struct unix_dgram_send_queue *q;
struct msghdr msg;
+#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
+ struct cmsghdr *cmsg;
+ size_t fds_size = sizeof(int) * num_fds;
+ size_t cmsg_len = CMSG_LEN(fds_size);
+ size_t cmsg_space = CMSG_SPACE(fds_size);
+ char cmsg_buf[cmsg_space];
+#endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */
int ret;
+ int i;
+
+ if (num_fds > INT8_MAX) {
+ return EINVAL;
+ }
+
+#ifndef HAVE_STRUCT_MSGHDR_MSG_CONTROL
+ if (num_fds > 0) {
+ return ENOSYS;
+ }
+#endif /* ! HAVE_STRUCT_MSGHDR_MSG_CONTROL */
+
+ for (i = 0; i < num_fds; i++) {
+ /*
+ * Make sure we only allow fd passing
+ * for communication channels,
+ * e.g. sockets, pipes, fifos, ...
+ */
+ ret = lseek(fds[i], 0, SEEK_CUR);
+ if (ret == -1 && errno == ESPIPE) {
+ /* ok */
+ continue;
+ }
+
+ /*
+ * Reject the message as we may need to call dup(),
+ * if we queue the message.
+ *
+ * That might result in unexpected behavior for the caller
+ * for files and broken posix locking.
+ */
+ return EINVAL;
+ }
/*
* To preserve message ordering, we have to queue a message when
*/
q = find_send_queue(ctx, dst->sun_path);
if (q != NULL) {
- return queue_msg(q, iov, iovlen);
+ return queue_msg(q, iov, iovlen, fds, num_fds);
}
/*
.msg_iov = discard_const_p(struct iovec, iov),
.msg_iovlen = iovlen
};
+#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
+ if (num_fds > 0) {
+ void *fdptr;
+
+ memset(cmsg_buf, 0, cmsg_space);
+
+ msg.msg_control = cmsg_buf;
+ msg.msg_controllen = cmsg_space;
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = cmsg_len;
+ fdptr = CMSG_DATA(cmsg);
+ memcpy(fdptr, fds, fds_size);
+ msg.msg_controllen = cmsg->cmsg_len;
+ }
+#endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */
ret = sendmsg(ctx->sock, &msg, 0);
if (ret >= 0) {
if (ret != 0) {
return ret;
}
- ret = queue_msg(q, iov, iovlen);
+ ret = queue_msg(q, iov, iovlen, fds, num_fds);
if (ret != 0) {
unix_dgram_send_queue_free(q);
return ret;
sizeof(struct iovec) * iovlen);
}
- return unix_dgram_send(ctx->dgram, dst, iov_copy, iovlen+1);
+ return unix_dgram_send(ctx->dgram, dst, iov_copy, iovlen+1,
+ NULL, 0);
}
hdr = (struct unix_msg_hdr) {
}
sent += (fragment_len - sizeof(ctx->cookie) - sizeof(hdr));
- ret = unix_dgram_send(ctx->dgram, dst, iov_copy, iov_index);
+ ret = unix_dgram_send(ctx->dgram, dst, iov_copy, iov_index,
+ NULL, 0);
if (ret != 0) {
break;
}