diff options
author | Tom Gundersen <teg@jklm.no> | 2014-05-10 19:55:00 +0200 |
---|---|---|
committer | Tom Gundersen <teg@jklm.no> | 2014-05-10 20:56:37 +0200 |
commit | 26349add97c8e9bfd26f2f16d61dd80a324f504e (patch) | |
tree | 673567fb1ae1adeae15813bd0b828c022175d2f2 /src | |
parent | 127dc4ea9487397b1ab70447e2f44d091e44ab5f (diff) |
rtnl: message - verify that we read the pending message size from the kernel
Reuse the auth-checking for both the peek and the real read.
Diffstat (limited to 'src')
-rw-r--r-- | src/libsystemd/sd-rtnl/rtnl-message.c | 89 |
1 files changed, 50 insertions, 39 deletions
diff --git a/src/libsystemd/sd-rtnl/rtnl-message.c b/src/libsystemd/sd-rtnl/rtnl-message.c index fb429864b9..9558e112af 100644 --- a/src/libsystemd/sd-rtnl/rtnl-message.c +++ b/src/libsystemd/sd-rtnl/rtnl-message.c @@ -1098,61 +1098,28 @@ int socket_write_message(sd_rtnl *nl, sd_rtnl_message *m) { return k; } -/* On success, the number of bytes received is returned and *ret points to the received message - * which has a valid header and the correct size. - * If nothing useful was received 0 is returned. - * On failure, a negative error code is returned. - */ -int socket_read_message(sd_rtnl *rtnl) { - _cleanup_rtnl_message_unref_ sd_rtnl_message *first = NULL; +static int socket_recv_message(int fd, struct iovec *iov, bool peek) { uint8_t cred_buffer[CMSG_SPACE(sizeof(struct ucred))]; - struct iovec iov = {}; struct msghdr msg = { - .msg_iov = &iov, + .msg_iov = iov, .msg_iovlen = 1, .msg_control = cred_buffer, .msg_controllen = sizeof(cred_buffer), }; struct cmsghdr *cmsg; - bool auth = false, multi_part = false, done = false; - struct nlmsghdr *new_msg; - size_t len; + bool auth = false; int r; - unsigned i = 0; - - assert(rtnl); - assert(rtnl->rbuffer); - assert(rtnl->rbuffer_allocated >= sizeof(struct nlmsghdr)); - - /* read nothing, just get the pending message size */ - r = recvmsg(rtnl->fd, &msg, MSG_PEEK | MSG_TRUNC); - if (r < 0) - /* no data */ - return (errno == EAGAIN) ? 0 : -errno; - else if (r == 0) - /* connection was closed by the kernel */ - return -ECONNRESET; - else - len = (size_t)r; - - /* make room for the pending message */ - if (!greedy_realloc((void **)&rtnl->rbuffer, - &rtnl->rbuffer_allocated, - len, sizeof(uint8_t))) - return -ENOMEM; - iov.iov_base = rtnl->rbuffer; - iov.iov_len = rtnl->rbuffer_allocated; + assert(fd >= 0); + assert(iov); - r = recvmsg(rtnl->fd, &msg, MSG_TRUNC); + r = recvmsg(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0)); if (r < 0) /* no data */ return (errno == EAGAIN) ? 0 : -errno; else if (r == 0) /* connection was closed by the kernel */ return -ECONNRESET; - else - len = (size_t)r; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && @@ -1172,6 +1139,50 @@ int socket_read_message(sd_rtnl *rtnl) { /* not from the kernel, ignore */ return 0; + return r; +} + +/* On success, the number of bytes received is returned and *ret points to the received message + * which has a valid header and the correct size. + * If nothing useful was received 0 is returned. + * On failure, a negative error code is returned. + */ +int socket_read_message(sd_rtnl *rtnl) { + _cleanup_rtnl_message_unref_ sd_rtnl_message *first = NULL; + struct iovec iov = {}; + bool multi_part = false, done = false; + struct nlmsghdr *new_msg; + size_t len; + int r; + unsigned i = 0; + + assert(rtnl); + assert(rtnl->rbuffer); + assert(rtnl->rbuffer_allocated >= sizeof(struct nlmsghdr)); + + /* read nothing, just get the pending message size */ + r = socket_recv_message(rtnl->fd, &iov, true); + if (r <= 0) + return r; + else + len = (size_t)r; + + /* make room for the pending message */ + if (!greedy_realloc((void **)&rtnl->rbuffer, + &rtnl->rbuffer_allocated, + len, sizeof(uint8_t))) + return -ENOMEM; + + iov.iov_base = rtnl->rbuffer; + iov.iov_len = rtnl->rbuffer_allocated; + + /* read the pending message */ + r = socket_recv_message(rtnl->fd, &iov, false); + if (r <= 0) + return r; + else + len = (size_t)r; + if (len > rtnl->rbuffer_allocated) /* message did not fit in read buffer */ return -EIO; |