diff options
Diffstat (limited to 'src/systemd/src/basic/socket-util.c')
| -rw-r--r-- | src/systemd/src/basic/socket-util.c | 163 |
1 files changed, 97 insertions, 66 deletions
diff --git a/src/systemd/src/basic/socket-util.c b/src/systemd/src/basic/socket-util.c index 798ab16e..8ae68b6f 100644 --- a/src/systemd/src/basic/socket-util.c +++ b/src/systemd/src/basic/socket-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -42,6 +43,7 @@ #include "missing.h" #include "parse-util.h" #include "path-util.h" +#include "process-util.h" #include "socket-util.h" #include "string-table.h" #include "string-util.h" @@ -52,14 +54,24 @@ #if 0 /* NM_IGNORED */ #if ENABLE_IDN -# define IDN_FLAGS (NI_IDN|NI_IDN_USE_STD3_ASCII_RULES) +# define IDN_FLAGS NI_IDN #else # define IDN_FLAGS 0 #endif +static const char* const socket_address_type_table[] = { + [SOCK_STREAM] = "Stream", + [SOCK_DGRAM] = "Datagram", + [SOCK_RAW] = "Raw", + [SOCK_RDM] = "ReliableDatagram", + [SOCK_SEQPACKET] = "SequentialPacket", + [SOCK_DCCP] = "DatagramCongestionControl", +}; + +DEFINE_STRING_TABLE_LOOKUP(socket_address_type, int); + int socket_address_parse(SocketAddress *a, const char *s) { char *e, *n; - unsigned u; int r; assert(a); @@ -69,6 +81,8 @@ int socket_address_parse(SocketAddress *a, const char *s) { a->type = SOCK_STREAM; if (*s == '[') { + uint16_t port; + /* IPv6 in [x:.....:z]:p notation */ e = strchr(s+1, ']'); @@ -86,15 +100,12 @@ int socket_address_parse(SocketAddress *a, const char *s) { return -EINVAL; e++; - r = safe_atou(e, &u); + r = parse_ip_port(e, &port); if (r < 0) return r; - if (u <= 0 || u > 0xFFFF) - return -EINVAL; - a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htobe16((uint16_t)u); + a->sockaddr.in6.sin6_port = htobe16(port); a->size = sizeof(struct sockaddr_in6); } else if (*s == '/') { @@ -124,13 +135,14 @@ int socket_address_parse(SocketAddress *a, const char *s) { } else if (startswith(s, "vsock:")) { /* AF_VSOCK socket in vsock:cid:port notation */ - const char *cid_start = s + strlen("vsock:"); + const char *cid_start = s + STRLEN("vsock:"); + unsigned port; e = strchr(cid_start, ':'); if (!e) return -EINVAL; - r = safe_atou(e+1, &u); + r = safe_atou(e+1, &port); if (r < 0) return r; @@ -143,19 +155,18 @@ int socket_address_parse(SocketAddress *a, const char *s) { a->sockaddr.vm.svm_cid = VMADDR_CID_ANY; a->sockaddr.vm.svm_family = AF_VSOCK; - a->sockaddr.vm.svm_port = u; + a->sockaddr.vm.svm_port = port; a->size = sizeof(struct sockaddr_vm); } else { + uint16_t port; + e = strchr(s, ':'); if (e) { - r = safe_atou(e+1, &u); + r = parse_ip_port(e + 1, &port); if (r < 0) return r; - if (u <= 0 || u > 0xFFFF) - return -EINVAL; - n = strndupa(s, e-s); /* IPv4 in w.x.y.z:p notation? */ @@ -166,7 +177,7 @@ int socket_address_parse(SocketAddress *a, const char *s) { if (r > 0) { /* Gotcha, it's a traditional IPv4 address */ a->sockaddr.in.sin_family = AF_INET; - a->sockaddr.in.sin_port = htobe16((uint16_t)u); + a->sockaddr.in.sin_port = htobe16(port); a->size = sizeof(struct sockaddr_in); } else { unsigned idx; @@ -180,7 +191,7 @@ int socket_address_parse(SocketAddress *a, const char *s) { return -EINVAL; a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htobe16((uint16_t)u); + a->sockaddr.in6.sin6_port = htobe16(port); a->sockaddr.in6.sin6_scope_id = idx; a->sockaddr.in6.sin6_addr = in6addr_any; a->size = sizeof(struct sockaddr_in6); @@ -188,21 +199,18 @@ int socket_address_parse(SocketAddress *a, const char *s) { } else { /* Just a port */ - r = safe_atou(s, &u); + r = parse_ip_port(s, &port); if (r < 0) return r; - if (u <= 0 || u > 0xFFFF) - return -EINVAL; - if (socket_ipv6_is_supported()) { a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htobe16((uint16_t)u); + a->sockaddr.in6.sin6_port = htobe16(port); a->sockaddr.in6.sin6_addr = in6addr_any; a->size = sizeof(struct sockaddr_in6); } else { a->sockaddr.in.sin_family = AF_INET; - a->sockaddr.in.sin_port = htobe16((uint16_t)u); + a->sockaddr.in.sin_port = htobe16(port); a->sockaddr.in.sin_addr.s_addr = INADDR_ANY; a->size = sizeof(struct sockaddr_in); } @@ -530,22 +538,25 @@ bool socket_address_matches_fd(const SocketAddress *a, int fd) { return socket_address_equal(a, &b); } -int sockaddr_port(const struct sockaddr *_sa, unsigned *port) { +int sockaddr_port(const struct sockaddr *_sa, unsigned *ret_port) { union sockaddr_union *sa = (union sockaddr_union*) _sa; + /* Note, this returns the port as 'unsigned' rather than 'uint16_t', as AF_VSOCK knows larger ports */ + assert(sa); switch (sa->sa.sa_family) { + case AF_INET: - *port = be16toh(sa->in.sin_port); + *ret_port = be16toh(sa->in.sin_port); return 0; case AF_INET6: - *port = be16toh(sa->in6.sin6_port); + *ret_port = be16toh(sa->in6.sin6_port); return 0; case AF_VSOCK: - *port = sa->vm.svm_port; + *ret_port = sa->vm.svm_port; return 0; default: @@ -750,19 +761,6 @@ int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) return 0; } -int getnameinfo_pretty(int fd, char **ret) { - union sockaddr_union sa; - socklen_t salen = sizeof(sa); - - assert(fd >= 0); - assert(ret); - - if (getsockname(fd, &sa.sa, &salen) < 0) - return -errno; - - return socknameinfo_pretty(&sa, salen, ret); -} - int socket_address_unlink(SocketAddress *a) { assert(a); @@ -809,6 +807,18 @@ static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIN DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only); +SocketAddressBindIPv6Only parse_socket_address_bind_ipv6_only_or_bool(const char *n) { + int r; + + r = parse_boolean(n); + if (r > 0) + return SOCKET_ADDRESS_IPV6_ONLY; + if (r == 0) + return SOCKET_ADDRESS_BOTH; + + return socket_address_bind_ipv6_only_from_string(n); +} + bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b) { assert(a); assert(b); @@ -943,56 +953,77 @@ int getpeercred(int fd, struct ucred *ucred) { if (n != sizeof(struct ucred)) return -EIO; - /* Check if the data is actually useful and not suppressed due - * to namespacing issues */ - if (u.pid <= 0) - return -ENODATA; - if (u.uid == UID_INVALID) - return -ENODATA; - if (u.gid == GID_INVALID) + /* Check if the data is actually useful and not suppressed due to namespacing issues */ + if (!pid_is_valid(u.pid)) return -ENODATA; + /* Note that we don't check UID/GID here, as namespace translation works differently there: instead of + * receiving in "invalid" user/group we get the overflow UID/GID. */ + *ucred = u; return 0; } int getpeersec(int fd, char **ret) { + _cleanup_free_ char *s = NULL; socklen_t n = 64; - char *s; - int r; assert(fd >= 0); assert(ret); - s = new0(char, n); - if (!s) - return -ENOMEM; + for (;;) { + s = new0(char, n+1); + if (!s) + return -ENOMEM; - r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n); - if (r < 0) { - free(s); + if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0) + break; if (errno != ERANGE) return -errno; - s = new0(char, n); - if (!s) + s = mfree(s); + } + + if (isempty(s)) + return -EOPNOTSUPP; + + *ret = TAKE_PTR(s); + + return 0; +} + +int getpeergroups(int fd, gid_t **ret) { + socklen_t n = sizeof(gid_t) * 64; + _cleanup_free_ gid_t *d = NULL; + + assert(fd >= 0); + assert(ret); + + for (;;) { + d = malloc(n); + if (!d) return -ENOMEM; - r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n); - if (r < 0) { - free(s); + if (getsockopt(fd, SOL_SOCKET, SO_PEERGROUPS, d, &n) >= 0) + break; + + if (errno != ERANGE) return -errno; - } - } - if (isempty(s)) { - free(s); - return -EOPNOTSUPP; + d = mfree(d); } - *ret = s; - return 0; + assert_se(n % sizeof(gid_t) == 0); + n /= sizeof(gid_t); + + if ((socklen_t) (int) n != n) + return -E2BIG; + + *ret = d; + d = NULL; + + return (int) n; } int send_one_fd_sa( |