summaryrefslogtreecommitdiff
path: root/systemd/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'systemd/util.c')
-rw-r--r--systemd/util.c187
1 files changed, 187 insertions, 0 deletions
diff --git a/systemd/util.c b/systemd/util.c
new file mode 100644
index 0000000..e02c825
--- /dev/null
+++ b/systemd/util.c
@@ -0,0 +1,187 @@
+/***
+ 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 <http://www.gnu.org/licenses/>.
+***/
+
+/* stuff imported from systemd without any changes */
+
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <net/if.h>
+
+#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;
+}