diff options
Diffstat (limited to 'lib/util/msghdr.c')
-rw-r--r-- | lib/util/msghdr.c | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/lib/util/msghdr.c b/lib/util/msghdr.c new file mode 100644 index 0000000..3a1d6f5 --- /dev/null +++ b/lib/util/msghdr.c @@ -0,0 +1,273 @@ +/* + * Unix SMB/CIFS implementation. + * Copyright (C) Volker Lendecke 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "replace.h" +#include "lib/util/msghdr.h" +#include "lib/util/iov_buf.h" +#include <sys/socket.h> + +#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) + +ssize_t msghdr_prep_fds(struct msghdr *msg, uint8_t *buf, size_t bufsize, + const int *fds, size_t num_fds) +{ + size_t fds_size = sizeof(int) * MIN(num_fds, INT8_MAX); + size_t cmsg_len = CMSG_LEN(fds_size); + size_t cmsg_space = CMSG_SPACE(fds_size); + struct cmsghdr *cmsg; + void *fdptr; + + if (num_fds == 0) { + if (msg != NULL) { + msg->msg_control = NULL; + msg->msg_controllen = 0; + } + /* + * C99 doesn't allow 0-length arrays + */ + return 1; + } + if (num_fds > INT8_MAX) { + return -1; + } + if ((msg == NULL) || (cmsg_space > bufsize)) { + /* + * C99 doesn't allow 0-length arrays + */ + return MAX(cmsg_space, 1); + } + + msg->msg_control = 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; + + return cmsg_space; +} + +size_t msghdr_prep_recv_fds(struct msghdr *msg, uint8_t *buf, size_t bufsize, + size_t num_fds) +{ + size_t ret = CMSG_SPACE(sizeof(int) * num_fds); + + if (bufsize < ret) { + return ret; + } + if (msg != NULL) { + if (num_fds != 0) { + msg->msg_control = buf; + msg->msg_controllen = ret; + } else { + msg->msg_control = NULL; + msg->msg_controllen = 0; + } + } + return ret; +} + +size_t msghdr_extract_fds(struct msghdr *msg, int *fds, size_t fds_size) +{ + struct cmsghdr *cmsg; + size_t num_fds; + + for(cmsg = CMSG_FIRSTHDR(msg); + cmsg != NULL; + cmsg = CMSG_NXTHDR(msg, cmsg)) + { + if ((cmsg->cmsg_type == SCM_RIGHTS) && + (cmsg->cmsg_level == SOL_SOCKET)) { + break; + } + } + + if (cmsg == NULL) { + return 0; + } + + num_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); + + if ((num_fds != 0) && (fds != NULL) && (fds_size >= num_fds)) { + memcpy(fds, CMSG_DATA(cmsg), num_fds * sizeof(int)); + } + + return num_fds; +} + +#elif defined(HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS) + +ssize_t msghdr_prep_fds(struct msghdr *msg, uint8_t *buf, size_t bufsize, + const int *fds, size_t num_fds) +{ + size_t needed; + + if (num_fds > INT8_MAX) { + return -1; + } + + needed = sizeof(int) * num_fds; + + if ((msg == NULL) || (needed > bufsize)) { + return needed; + } + + memcpy(buf, fds, needed); + + msg->msg_accrights = (caddr_t) buf; + msg->msg_accrightslen = needed; + + return needed; +} + +size_t msghdr_prep_recv_fds(struct msghdr *msg, uint8_t *buf, size_t bufsize, + size_t num_fds) +{ + size_t ret = num_fds * sizeof(int); + + if (bufsize < ret) { + return ret; + } + + if (msg != NULL) { + if (num_fds != 0) { + msg->msg_accrights = (caddr_t) buf; + msg->msg_accrightslen = ret; + } else { + msg->msg_accrights = NULL; + msg->msg_accrightslen = 0; + } + } + return ret; +} + +size_t msghdr_extract_fds(struct msghdr *msg, int *fds, size_t fds_size) +{ + size_t num_fds = msg->msg_accrightslen / sizeof(int); + + if ((fds != 0) && (num_fds <= fds_size)) { + memcpy(fds, msg->msg_accrights, msg->msg_accrightslen); + } + + return num_fds; +} + +#else + +ssize_t msghdr_prep_fds(struct msghdr *msg, uint8_t *buf, size_t bufsize, + const int *fds, size_t num_fds) +{ + return -1; +} + +size_t msghdr_prep_recv_fds(struct msghdr *msg, uint8_t *buf, size_t bufsize, + size_t num_fds) +{ + return 0; +} + +size_t msghdr_extract_fds(struct msghdr *msg, int *fds, size_t fds_size) +{ + return 0; +} + +#endif + +struct msghdr_buf { + struct msghdr msg; + struct sockaddr_storage addr; + struct iovec iov; + uint8_t buf[]; +}; + +ssize_t msghdr_copy(struct msghdr_buf *msg, size_t msgsize, + const void *addr, socklen_t addrlen, + const struct iovec *iov, int iovcnt, + const int *fds, size_t num_fds) +{ + ssize_t fd_len; + size_t iov_len, needed, bufsize; + + bufsize = (msgsize > offsetof(struct msghdr_buf, buf)) ? + msgsize - offsetof(struct msghdr_buf, buf) : 0; + + if (msg != NULL) { + msg->msg = (struct msghdr) { 0 }; + + fd_len = msghdr_prep_fds(&msg->msg, msg->buf, bufsize, + fds, num_fds); + } else { + fd_len = msghdr_prep_fds(NULL, NULL, bufsize, fds, num_fds); + } + + if (fd_len == -1) { + return -1; + } + + if (bufsize >= (size_t)fd_len) { + bufsize -= fd_len; + } else { + bufsize = 0; + } + + if (msg != NULL) { + + if (addr != NULL) { + if (addrlen > sizeof(struct sockaddr_storage)) { + errno = EMSGSIZE; + return -1; + } + memcpy(&msg->addr, addr, addrlen); + msg->msg.msg_name = &msg->addr; + msg->msg.msg_namelen = addrlen; + } else { + msg->msg.msg_name = NULL; + msg->msg.msg_namelen = 0; + } + + msg->iov.iov_base = msg->buf + fd_len; + msg->iov.iov_len = iov_buf( + iov, iovcnt, msg->iov.iov_base, bufsize); + iov_len = msg->iov.iov_len; + + msg->msg.msg_iov = &msg->iov; + msg->msg.msg_iovlen = 1; + } else { + iov_len = iov_buflen(iov, iovcnt); + } + + needed = offsetof(struct msghdr_buf, buf) + fd_len; + if (needed < (size_t)fd_len) { + return -1; + } + needed += iov_len; + if (needed < iov_len) { + return -1; + } + + return needed; +} + +struct msghdr *msghdr_buf_msghdr(struct msghdr_buf *msg) +{ + return &msg->msg; +} |