/*** This file is part of systemd. Copyright 2010 Lennart Poettering systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. systemd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with systemd; If not, see . ***/ /* stuff imported from systemd without any changes */ #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include "util.h" int safe_atou(const char *s, unsigned *ret_u) { char *x = NULL; unsigned long l; assert(s); assert(ret_u); /* strtoul() is happy to parse negative values, and silently * converts them to unsigned values without generating an * error. We want a clean error, hence let's look for the "-" * prefix on our own, and generate an error. But let's do so * only after strtoul() validated that the string is clean * otherwise, so that we return EINVAL preferably over * ERANGE. */ errno = 0; l = strtoul(s, &x, 0); if (errno > 0) return -errno; if (!x || x == s || *x) return -EINVAL; if (s[0] == '-') return -ERANGE; if ((unsigned long) (unsigned) l != l) return -ERANGE; *ret_u = (unsigned) l; return 0; } static bool socket_ipv6_is_supported(void) { if (access("/proc/net/if_inet6", F_OK) != 0) return false; return true; } static int assign_address(const char *s, uint16_t port, union sockaddr_union *addr, unsigned *addr_len) { int r; /* IPv4 in w.x.y.z:p notation? */ r = inet_pton(AF_INET, s, &addr->in.sin_addr); if (r < 0) return -errno; if (r > 0) { /* Gotcha, it's a traditional IPv4 address */ addr->in.sin_family = AF_INET; addr->in.sin_port = htobe16(port); *addr_len = sizeof(struct sockaddr_in); } else { unsigned idx; if (strlen(s) > IF_NAMESIZE-1) return -EINVAL; /* Uh, our last resort, an interface name */ idx = if_nametoindex(s); if (idx == 0) return -EINVAL; addr->in6.sin6_family = AF_INET6; addr->in6.sin6_port = htobe16(port); addr->in6.sin6_scope_id = idx; addr->in6.sin6_addr = in6addr_any; *addr_len = sizeof(struct sockaddr_in6); } return 0; } int parse_sockaddr(const char *s, union sockaddr_union *addr, unsigned *addr_len) { char *e, *n; unsigned u; int r; if (*s == '[') { /* IPv6 in [x:.....:z]:p notation */ e = strchr(s+1, ']'); if (!e) return -EINVAL; n = strndupa(s+1, e-s-1); errno = 0; if (inet_pton(AF_INET6, n, &addr->in6.sin6_addr) <= 0) return errno > 0 ? -errno : -EINVAL; e++; if (*e) { if (*e != ':') return -EINVAL; e++; r = safe_atou(e, &u); if (r < 0) return r; if (u <= 0 || u > 0xFFFF) return -EINVAL; addr->in6.sin6_port = htobe16((uint16_t)u); } addr->in6.sin6_family = AF_INET6; *addr_len = sizeof(struct sockaddr_in6); } else { e = strchr(s, ':'); if (e) { r = safe_atou(e+1, &u); if (r < 0) return r; if (u <= 0 || u > 0xFFFF) return -EINVAL; n = strndupa(s, e-s); return assign_address(n, u, addr, addr_len); } else { r = safe_atou(s, &u); if (r < 0) return assign_address(s, 0, addr, addr_len); /* Just a port */ if (u <= 0 || u > 0xFFFF) return -EINVAL; if (socket_ipv6_is_supported()) { addr->in6.sin6_family = AF_INET6; addr->in6.sin6_port = htobe16((uint16_t)u); addr->in6.sin6_addr = in6addr_any; *addr_len = sizeof(struct sockaddr_in6); } else { addr->in.sin_family = AF_INET; addr->in.sin_port = htobe16((uint16_t)u); addr->in.sin_addr.s_addr = INADDR_ANY; *addr_len = sizeof(struct sockaddr_in); } } } return 0; }