X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fbasic%2Fsocket-util.c;h=16646ff9d83987bc90b0d4a303fe65a9b6bbaf10;hp=1acab1ef95bab65534dad3bf0717a1b5e083235e;hb=c1d93f2a0a009a95440778c53d89aa693bc6787e;hpb=b96ed50e3493103d075ff2ce4c3fbad8f26b2e22 diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index 1acab1ef9..16646ff9d 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -1,5 +1,3 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - /*** This file is part of systemd. @@ -21,19 +19,22 @@ #include #include +#include #include #include #include #include +#include #include +#include #include -#include #include #include "alloc-util.h" #include "fd-util.h" #include "fileio.h" #include "formats-util.h" +#include "log.h" #include "macro.h" #include "missing.h" #include "parse-util.h" @@ -41,9 +42,11 @@ #include "socket-util.h" #include "string-table.h" #include "string-util.h" +#include "strv.h" #include "user-util.h" #include "util.h" +#if 0 /// UNNEEDED by elogind int socket_address_parse(SocketAddress *a, const char *s) { char *e, *n; unsigned u; @@ -81,7 +84,7 @@ int socket_address_parse(SocketAddress *a, const char *s) { return -EINVAL; a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htons((uint16_t) u); + a->sockaddr.in6.sin6_port = htobe16((uint16_t)u); a->size = sizeof(struct sockaddr_in6); } else if (*s == '/') { @@ -129,7 +132,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 = htons((uint16_t) u); + a->sockaddr.in.sin_port = htobe16((uint16_t)u); a->size = sizeof(struct sockaddr_in); } else { unsigned idx; @@ -143,7 +146,7 @@ int socket_address_parse(SocketAddress *a, const char *s) { return -EINVAL; a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htons((uint16_t) u); + a->sockaddr.in6.sin6_port = htobe16((uint16_t)u); a->sockaddr.in6.sin6_scope_id = idx; a->sockaddr.in6.sin6_addr = in6addr_any; a->size = sizeof(struct sockaddr_in6); @@ -160,12 +163,12 @@ int socket_address_parse(SocketAddress *a, const char *s) { if (socket_ipv6_is_supported()) { a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htons((uint16_t) u); + a->sockaddr.in6.sin6_port = htobe16((uint16_t)u); 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 = htons((uint16_t) u); + a->sockaddr.in.sin_port = htobe16((uint16_t)u); a->sockaddr.in.sin_addr.s_addr = INADDR_ANY; a->size = sizeof(struct sockaddr_in); } @@ -435,21 +438,16 @@ const char* socket_address_get_path(const SocketAddress *a) { return a->sockaddr.un.sun_path; } +#endif // 0 bool socket_ipv6_is_supported(void) { - _cleanup_free_ char *l = NULL; - - if (access("/sys/module/ipv6", F_OK) != 0) + if (access("/proc/net/sockstat6", F_OK) != 0) return false; - /* If we can't check "disable" parameter, assume enabled */ - if (read_one_line_file("/sys/module/ipv6/parameters/disable", &l) < 0) - return true; - - /* If module was loaded with disable=1 no IPv6 available */ - return l[0] == '0'; + return true; } +#if 0 /// UNNEEDED by elogind bool socket_address_matches_fd(const SocketAddress *a, int fd) { SocketAddress b; socklen_t solen; @@ -491,9 +489,7 @@ int sockaddr_port(const struct sockaddr *_sa) { if (!IN_SET(sa->sa.sa_family, AF_INET, AF_INET6)) return -EAFNOSUPPORT; - return ntohs(sa->sa.sa_family == AF_INET6 ? - sa->in6.sin6_port : - sa->in.sin_port); + return be16toh(sa->sa.sa_family == AF_INET6 ? sa->in6.sin6_port : sa->in.sin_port); } int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret) { @@ -509,13 +505,13 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ case AF_INET: { uint32_t a; - a = ntohl(sa->in.sin_addr.s_addr); + a = be32toh(sa->in.sin_addr.s_addr); if (include_port) r = asprintf(&p, "%u.%u.%u.%u:%u", a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF, - ntohs(sa->in.sin_port)); + be16toh(sa->in.sin_port)); else r = asprintf(&p, "%u.%u.%u.%u", @@ -537,7 +533,7 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ r = asprintf(&p, "%u.%u.%u.%u:%u", a[0], a[1], a[2], a[3], - ntohs(sa->in6.sin6_port)); + be16toh(sa->in6.sin6_port)); else r = asprintf(&p, "%u.%u.%u.%u", @@ -553,7 +549,7 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ r = asprintf(&p, "[%s]:%u", a, - ntohs(sa->in6.sin6_port)); + be16toh(sa->in6.sin6_port)); if (r < 0) return -ENOMEM; } else { @@ -605,7 +601,7 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ return 0; } -int getpeername_pretty(int fd, char **ret) { +int getpeername_pretty(int fd, bool include_port, char **ret) { union sockaddr_union sa; socklen_t salen = sizeof(sa); int r; @@ -635,7 +631,7 @@ int getpeername_pretty(int fd, char **ret) { /* For remote sockets we translate IPv6 addresses back to IPv4 * if applicable, since that's nicer. */ - return sockaddr_pretty(&sa.sa, salen, true, true, ret); + return sockaddr_pretty(&sa.sa, salen, true, include_port, ret); } int getsockname_pretty(int fd, char **ret) { @@ -755,6 +751,7 @@ bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b return false; } +#endif // 0 int fd_inc_sndbuf(int fd, size_t n) { int r, value; @@ -791,6 +788,7 @@ int fd_inc_rcvbuf(int fd, size_t n) { return 1; } +#if 0 /// UNNEEDED by elogind static const char* const ip_tos_table[] = { [IPTOS_LOWDELAY] = "low-delay", [IPTOS_THROUGHPUT] = "throughput", @@ -799,6 +797,43 @@ static const char* const ip_tos_table[] = { }; DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff); +#endif // 0 + +bool ifname_valid(const char *p) { + bool numeric = true; + + /* Checks whether a network interface name is valid. This is inspired by dev_valid_name() in the kernel sources + * but slightly stricter, as we only allow non-control, non-space ASCII characters in the interface name. We + * also don't permit names that only container numbers, to avoid confusion with numeric interface indexes. */ + + if (isempty(p)) + return false; + + if (strlen(p) >= IFNAMSIZ) + return false; + + if (STR_IN_SET(p, ".", "..")) + return false; + + while (*p) { + if ((unsigned char) *p >= 127U) + return false; + + if ((unsigned char) *p <= 32U) + return false; + + if (*p == ':' || *p == '/') + return false; + + numeric = numeric && (*p >= '0' && *p <= '9'); + p++; + } + + if (numeric) + return false; + + return true; +} int getpeercred(int fd, struct ucred *ucred) { socklen_t n = sizeof(struct ucred); @@ -867,12 +902,19 @@ int getpeersec(int fd, char **ret) { return 0; } -int send_one_fd(int transport_fd, int fd, int flags) { +int send_one_fd_sa( + int transport_fd, + int fd, + const struct sockaddr *sa, socklen_t len, + int flags) { + union { struct cmsghdr cmsghdr; uint8_t buf[CMSG_SPACE(sizeof(int))]; } control = {}; struct msghdr mh = { + .msg_name = (struct sockaddr*) sa, + .msg_namelen = len, .msg_control = &control, .msg_controllen = sizeof(control), }; @@ -894,6 +936,7 @@ int send_one_fd(int transport_fd, int fd, int flags) { return 0; } +#if 0 /// UNNEEDED by elogind int receive_one_fd(int transport_fd, int flags) { union { struct cmsghdr cmsghdr; @@ -935,3 +978,91 @@ int receive_one_fd(int transport_fd, int flags) { return *(int*) CMSG_DATA(found); } + +ssize_t next_datagram_size_fd(int fd) { + ssize_t l; + int k; + + /* This is a bit like FIONREAD/SIOCINQ, however a bit more powerful. The difference being: recv(MSG_PEEK) will + * actually cause the next datagram in the queue to be validated regarding checksums, which FIONREAD doesn't + * do. This difference is actually of major importance as we need to be sure that the size returned here + * actually matches what we will read with recvmsg() next, as otherwise we might end up allocating a buffer of + * the wrong size. */ + + l = recv(fd, NULL, 0, MSG_PEEK|MSG_TRUNC); + if (l < 0) { + if (errno == EOPNOTSUPP || errno == EFAULT) + goto fallback; + + return -errno; + } + if (l == 0) + goto fallback; + + return l; + +fallback: + k = 0; + + /* Some sockets (AF_PACKET) do not support null-sized recv() with MSG_TRUNC set, let's fall back to FIONREAD + * for them. Checksums don't matter for raw sockets anyway, hence this should be fine. */ + + if (ioctl(fd, FIONREAD, &k) < 0) + return -errno; + + return (ssize_t) k; +} + +int flush_accept(int fd) { + + struct pollfd pollfd = { + .fd = fd, + .events = POLLIN, + }; + int r; + + + /* Similar to flush_fd() but flushes all incoming connection by accepting them and immediately closing them. */ + + for (;;) { + int cfd; + + r = poll(&pollfd, 1, 0); + if (r < 0) { + if (errno == EINTR) + continue; + + return -errno; + + } else if (r == 0) + return 0; + + cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); + if (cfd < 0) { + if (errno == EINTR) + continue; + + if (errno == EAGAIN) + return 0; + + return -errno; + } + + close(cfd); + } +} +#endif // 0 + +struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length) { + struct cmsghdr *cmsg; + + assert(mh); + + CMSG_FOREACH(cmsg, mh) + if (cmsg->cmsg_level == level && + cmsg->cmsg_type == type && + (length == (socklen_t) -1 || length == cmsg->cmsg_len)) + return cmsg; + + return NULL; +}