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=73e19310e065eb2f112d207b13f23221085facf9;hb=906f94da76802b2aae4dd052018766f5378ae4b8;hpb=66ecc207e203db5434610395cd04c40ae8727b58 diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index 73e19310e..16646ff9d 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -42,6 +42,7 @@ #include "socket-util.h" #include "string-table.h" #include "string-util.h" +#include "strv.h" #include "user-util.h" #include "util.h" @@ -83,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 == '/') { @@ -131,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; @@ -145,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); @@ -162,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); } @@ -488,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) { @@ -506,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", @@ -534,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", @@ -550,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 { @@ -800,6 +799,42 @@ 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); struct ucred u; @@ -943,4 +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; +}