diff options
Diffstat (limited to 'src/libnm-systemd-shared/src/basic/socket-util.c')
-rw-r--r-- | src/libnm-systemd-shared/src/basic/socket-util.c | 130 |
1 files changed, 80 insertions, 50 deletions
diff --git a/src/libnm-systemd-shared/src/basic/socket-util.c b/src/libnm-systemd-shared/src/basic/socket-util.c index e7ea030008..9b411e07a2 100644 --- a/src/libnm-systemd-shared/src/basic/socket-util.c +++ b/src/libnm-systemd-shared/src/basic/socket-util.c @@ -228,7 +228,7 @@ bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) { return false; if (a->sockaddr.un.sun_path[0]) { - if (!path_equal_or_files_same(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, 0)) + if (!path_equal_or_inode_same(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, 0)) return false; } else { if (a->size != b->size) @@ -1054,7 +1054,7 @@ ssize_t receive_one_fd_iov( } if (found) - *ret_fd = *(int*) CMSG_DATA(found); + *ret_fd = *CMSG_TYPED_DATA(found, int); else *ret_fd = -EBADF; @@ -1181,6 +1181,24 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng return NULL; } +void* cmsg_find_and_copy_data(struct msghdr *mh, int level, int type, void *buf, size_t buf_len) { + struct cmsghdr *cmsg; + + assert(mh); + assert(buf); + assert(buf_len > 0); + + /* This is similar to cmsg_find_data(), but copy the found data to buf. This should be typically used + * when reading possibly unaligned data such as timestamp, as time_t is 64bit and size_t is 32bit on + * RISCV32. See issue #27241. */ + + cmsg = cmsg_find(mh, level, type, CMSG_LEN(buf_len)); + if (!cmsg) + return NULL; + + return memcpy_safe(buf, CMSG_DATA(cmsg), buf_len); +} + #if 0 /* NM_IGNORED */ int socket_ioctl_fd(void) { int fd; @@ -1320,7 +1338,7 @@ ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags) { } #if 0 /* NM_IGNORED */ -int socket_get_family(int fd, int *ret) { +int socket_get_family(int fd) { int af; socklen_t sl = sizeof(af); @@ -1334,12 +1352,11 @@ int socket_get_family(int fd, int *ret) { } int socket_set_recvpktinfo(int fd, int af, bool b) { - int r; if (af == AF_UNSPEC) { - r = socket_get_family(fd, &af); - if (r < 0) - return r; + af = socket_get_family(fd); + if (af < 0) + return af; } switch (af) { @@ -1363,12 +1380,11 @@ int socket_set_recvpktinfo(int fd, int af, bool b) { int socket_set_unicast_if(int fd, int af, int ifi) { be32_t ifindex_be = htobe32(ifi); - int r; if (af == AF_UNSPEC) { - r = socket_get_family(fd, &af); - if (r < 0) - return r; + af = socket_get_family(fd); + if (af < 0) + return af; } switch (af) { @@ -1385,12 +1401,10 @@ int socket_set_unicast_if(int fd, int af, int ifi) { } int socket_set_option(int fd, int af, int opt_ipv4, int opt_ipv6, int val) { - int r; - if (af == AF_UNSPEC) { - r = socket_get_family(fd, &af); - if (r < 0) - return r; + af = socket_get_family(fd); + if (af < 0) + return af; } switch (af) { @@ -1410,9 +1424,9 @@ int socket_get_mtu(int fd, int af, size_t *ret) { int mtu, r; if (af == AF_UNSPEC) { - r = socket_get_family(fd, &af); - if (r < 0) - return r; + af = socket_get_family(fd); + if (af < 0) + return af; } switch (af) { @@ -1439,52 +1453,60 @@ int socket_get_mtu(int fd, int af, size_t *ret) { } #endif /* NM_IGNORED */ -int connect_unix_path(int fd, int dir_fd, const char *path) { - _cleanup_close_ int inode_fd = -EBADF; +static int connect_unix_path_simple(int fd, const char *path) { union sockaddr_union sa = { .un.sun_family = AF_UNIX, }; - size_t path_len; - socklen_t salen; + size_t l; assert(fd >= 0); - assert(dir_fd == AT_FDCWD || dir_fd >= 0); assert(path); + l = strlen(path); + assert(l > 0); + assert(l < sizeof(sa.un.sun_path)); + + memcpy(sa.un.sun_path, path, l + 1); + return RET_NERRNO(connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + l + 1)); +} + +static int connect_unix_inode(int fd, int inode_fd) { + assert(fd >= 0); + assert(inode_fd >= 0); + + return connect_unix_path_simple(fd, FORMAT_PROC_FD_PATH(inode_fd)); +} + +int connect_unix_path(int fd, int dir_fd, const char *path) { + _cleanup_close_ int inode_fd = -EBADF; + + assert(fd >= 0); + assert(dir_fd == AT_FDCWD || dir_fd >= 0); + /* Connects to the specified AF_UNIX socket in the file system. Works around the 108 byte size limit * in sockaddr_un, by going via O_PATH if needed. This hence works for any kind of path. */ - path_len = strlen(path); + if (!path) + return connect_unix_inode(fd, dir_fd); /* If no path is specified, then dir_fd refers to the socket inode to connect to. */ /* Refuse zero length path early, to make sure AF_UNIX stack won't mistake this for an abstract * namespace path, since first char is NUL */ - if (path_len <= 0) + if (isempty(path)) return -EINVAL; - if (dir_fd == AT_FDCWD && path_len < sizeof(sa.un.sun_path)) { - memcpy(sa.un.sun_path, path, path_len + 1); - salen = offsetof(struct sockaddr_un, sun_path) + path_len + 1; - } else { - const char *proc; - size_t proc_len; - - /* If dir_fd is specified, then we need to go the indirect O_PATH route, because connectat() - * does not exist. If the path is too long, we also need to take the indirect route, since we - * can't fit this into a sockaddr_un directly. */ + /* Shortcut for the simple case */ + if (dir_fd == AT_FDCWD && strlen(path) < sizeof_field(struct sockaddr_un, sun_path)) + return connect_unix_path_simple(fd, path); - inode_fd = openat(dir_fd, path, O_PATH|O_CLOEXEC); - if (inode_fd < 0) - return -errno; + /* If dir_fd is specified, then we need to go the indirect O_PATH route, because connectat() does not + * exist. If the path is too long, we also need to take the indirect route, since we can't fit this + * into a sockaddr_un directly. */ - proc = FORMAT_PROC_FD_PATH(inode_fd); - proc_len = strlen(proc); - - assert(proc_len < sizeof(sa.un.sun_path)); - memcpy(sa.un.sun_path, proc, proc_len + 1); - salen = offsetof(struct sockaddr_un, sun_path) + proc_len + 1; - } + inode_fd = openat(dir_fd, path, O_PATH|O_CLOEXEC); + if (inode_fd < 0) + return -errno; - return RET_NERRNO(connect(fd, &sa.sa, salen)); + return connect_unix_inode(fd, inode_fd); } int socket_address_parse_unix(SocketAddress *ret_address, const char *s) { @@ -1515,13 +1537,20 @@ int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) { _cleanup_free_ char *n = NULL; char *e, *cid_start; unsigned port, cid; - int r; + int type, r; assert(ret_address); assert(s); - cid_start = startswith(s, "vsock:"); - if (!cid_start) + if ((cid_start = startswith(s, "vsock:"))) + type = 0; + else if ((cid_start = startswith(s, "vsock-dgram:"))) + type = SOCK_DGRAM; + else if ((cid_start = startswith(s, "vsock-seqpacket:"))) + type = SOCK_SEQPACKET; + else if ((cid_start = startswith(s, "vsock-stream:"))) + type = SOCK_STREAM; + else return -EPROTO; e = strchr(cid_start, ':'); @@ -1550,6 +1579,7 @@ int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) { .svm_family = AF_VSOCK, .svm_port = port, }, + .type = type, .size = sizeof(struct sockaddr_vm), }; |