summary refs log tree commit diff
path: root/src/libnm-systemd-shared/src/basic/socket-util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libnm-systemd-shared/src/basic/socket-util.c')
-rw-r--r--src/libnm-systemd-shared/src/basic/socket-util.c99
1 files changed, 75 insertions, 24 deletions
diff --git a/src/libnm-systemd-shared/src/basic/socket-util.c b/src/libnm-systemd-shared/src/basic/socket-util.c
index df3e2c17..9cb0d7f4 100644
--- a/src/libnm-systemd-shared/src/basic/socket-util.c
+++ b/src/libnm-systemd-shared/src/basic/socket-util.c
@@ -2,10 +2,11 @@
 
 #include "nm-sd-adapt-shared.h"
 
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
 #include <arpa/inet.h>
 #include <errno.h>
 #include <limits.h>
-#include <net/if.h>
 #include <netdb.h>
 #include <netinet/ip.h>
 #include <poll.h>
@@ -24,7 +25,7 @@
 #include "escape.h"
 #include "fd-util.h"
 #include "fileio.h"
-#include "format-util.h"
+#include "format-ifname.h"
 #include "io-util.h"
 #include "log.h"
 #include "memory-util.h"
@@ -458,6 +459,7 @@ int sockaddr_pretty(
 
         assert(sa);
         assert(salen >= sizeof(sa->sa.sa_family));
+        assert(ret);
 
         switch (sa->sa.sa_family) {
 
@@ -638,7 +640,8 @@ int socknameinfo_pretty(const struct sockaddr *sa, socklen_t salen, char **ret)
         int r;
 
         assert(sa);
-        assert(salen > sizeof(sa_family_t));
+        assert(salen >= sizeof(sa_family_t));
+        assert(ret);
 
         r = getnameinfo(sa, salen, host, sizeof(host), /* service= */ NULL, /* service_len= */ 0, IDN_FLAGS);
         if (r != 0) {
@@ -652,15 +655,7 @@ int socknameinfo_pretty(const struct sockaddr *sa, socklen_t salen, char **ret)
                 return sockaddr_pretty(sa, salen, /* translate_ipv6= */ true, /* include_port= */ true, ret);
         }
 
-        if (ret) {
-                char *copy = strdup(host);
-                if (!copy)
-                        return -ENOMEM;
-
-                *ret = copy;
-        }
-
-        return 0;
+        return strdup_to(ret, host);
 }
 
 static const char* const netlink_family_table[] = {
@@ -987,6 +982,28 @@ int getpeerpidfd(int fd) {
         return pidfd;
 }
 
+int getpeerpidref(int fd, PidRef *ret) {
+        int r;
+
+        assert(fd >= 0);
+        assert(ret);
+
+        int pidfd = getpeerpidfd(fd);
+        if (pidfd < 0) {
+                if (!ERRNO_IS_NEG_NOT_SUPPORTED(pidfd))
+                        return pidfd;
+
+                struct ucred ucred;
+                r = getpeercred(fd, &ucred);
+                if (r < 0)
+                        return r;
+
+                return pidref_set_pid(ret, ucred.pid);
+        }
+
+        return pidref_set_pidfd_consume(ret, pidfd);
+}
+
 ssize_t send_many_fds_iov_sa(
                 int transport_fd,
                 int *fds_array, size_t n_fds_array,
@@ -1124,14 +1141,10 @@ ssize_t receive_many_fds_iov(
                 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
                         size_t n = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
 
-                        fds_array = GREEDY_REALLOC(fds_array, n_fds_array + n);
-                        if (!fds_array) {
+                        if (!GREEDY_REALLOC_APPEND(fds_array, n_fds_array, CMSG_TYPED_DATA(cmsg, int), n)) {
                                 cmsg_close_all(&mh);
                                 return -ENOMEM;
                         }
-
-                        memcpy(fds_array + n_fds_array, CMSG_TYPED_DATA(cmsg, int), sizeof(int) * n);
-                        n_fds_array += n;
                 }
 
         if (n_fds_array == 0) {
@@ -1468,18 +1481,22 @@ int socket_bind_to_ifindex(int fd, int ifindex) {
 ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags) {
         ssize_t n;
 
-        /* A wrapper around recvmsg() that checks for MSG_CTRUNC, and turns it into an error, in a reasonably
-         * safe way, closing any SCM_RIGHTS fds in the error path.
+        /* A wrapper around recvmsg() that checks for MSG_CTRUNC and MSG_TRUNC, and turns them into an error,
+         * in a reasonably safe way, closing any received fds in the error path.
          *
          * Note that unlike our usual coding style this might modify *msg on failure. */
 
+        assert(sockfd >= 0);
+        assert(msg);
+
         n = recvmsg(sockfd, msg, flags);
         if (n < 0)
                 return -errno;
 
-        if (FLAGS_SET(msg->msg_flags, MSG_CTRUNC)) {
+        if (FLAGS_SET(msg->msg_flags, MSG_CTRUNC) ||
+            (!FLAGS_SET(flags, MSG_PEEK) && FLAGS_SET(msg->msg_flags, MSG_TRUNC))) {
                 cmsg_close_all(msg);
-                return -EXFULL; /* a recognizable error code */
+                return FLAGS_SET(msg->msg_flags, MSG_CTRUNC) ? -ECHRNG : -EXFULL;
         }
 
         return n;
@@ -1781,15 +1798,49 @@ int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) {
 int vsock_get_local_cid(unsigned *ret) {
         _cleanup_close_ int vsock_fd = -EBADF;
 
-        assert(ret);
-
         vsock_fd = open("/dev/vsock", O_RDONLY|O_CLOEXEC);
         if (vsock_fd < 0)
                 return log_debug_errno(errno, "Failed to open /dev/vsock: %m");
 
-        if (ioctl(vsock_fd, IOCTL_VM_SOCKETS_GET_LOCAL_CID, ret) < 0)
+        unsigned tmp;
+        if (ioctl(vsock_fd, IOCTL_VM_SOCKETS_GET_LOCAL_CID, ret ?: &tmp) < 0)
                 return log_debug_errno(errno, "Failed to query local AF_VSOCK CID: %m");
 
         return 0;
 }
+
+int netlink_socket_get_multicast_groups(int fd, size_t *ret_len, uint32_t **ret_groups) {
+        _cleanup_free_ uint32_t *groups = NULL;
+        socklen_t len = 0, old_len;
+
+        assert(fd >= 0);
+
+        /* This returns ENOPROTOOPT if the kernel is older than 4.2. */
+
+        if (getsockopt(fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, NULL, &len) < 0)
+                return -errno;
+
+        if (len == 0)
+                goto finalize;
+
+        groups = new0(uint32_t, len);
+        if (!groups)
+                return -ENOMEM;
+
+        old_len = len;
+
+        if (getsockopt(fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, groups, &len) < 0)
+                return -errno;
+
+        if (old_len != len)
+                return -EIO;
+
+finalize:
+        if (ret_len)
+                *ret_len = len;
+        if (ret_groups)
+                *ret_groups = TAKE_PTR(groups);
+
+        return 0;
+}
 #endif /* NM_IGNORED */