1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
27 #include <arpa/inet.h>
30 #include <sys/types.h>
33 #include <sys/ioctl.h>
39 #include "path-util.h"
40 #include "socket-util.h"
44 int socket_address_parse(SocketAddress *a, const char *s) {
53 a->type = SOCK_STREAM;
56 /* IPv6 in [x:.....:z]:p notation */
58 if (!socket_ipv6_is_supported()) {
59 log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
67 n = strndupa(s+1, e-s-1);
70 if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0)
71 return errno > 0 ? -errno : -EINVAL;
82 if (u <= 0 || u > 0xFFFF)
85 a->sockaddr.in6.sin6_family = AF_INET6;
86 a->sockaddr.in6.sin6_port = htons((uint16_t) u);
87 a->size = sizeof(struct sockaddr_in6);
89 } else if (*s == '/') {
95 if (l >= sizeof(a->sockaddr.un.sun_path))
98 a->sockaddr.un.sun_family = AF_UNIX;
99 memcpy(a->sockaddr.un.sun_path, s, l);
100 a->size = offsetof(struct sockaddr_un, sun_path) + l + 1;
102 } else if (*s == '@') {
103 /* Abstract AF_UNIX socket */
107 if (l >= sizeof(a->sockaddr.un.sun_path) - 1)
110 a->sockaddr.un.sun_family = AF_UNIX;
111 memcpy(a->sockaddr.un.sun_path+1, s+1, l);
112 a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
117 r = safe_atou(e+1, &u);
121 if (u <= 0 || u > 0xFFFF)
124 n = strndupa(s, e-s);
126 /* IPv4 in w.x.y.z:p notation? */
127 r = inet_pton(AF_INET, n, &a->sockaddr.in.sin_addr);
132 /* Gotcha, it's a traditional IPv4 address */
133 a->sockaddr.in.sin_family = AF_INET;
134 a->sockaddr.in.sin_port = htons((uint16_t) u);
135 a->size = sizeof(struct sockaddr_in);
139 if (strlen(n) > IF_NAMESIZE-1)
142 /* Uh, our last resort, an interface name */
143 idx = if_nametoindex(n);
147 if (!socket_ipv6_is_supported()) {
148 log_warning("Binding to interface is not available since kernel does not support IPv6.");
149 return -EAFNOSUPPORT;
152 a->sockaddr.in6.sin6_family = AF_INET6;
153 a->sockaddr.in6.sin6_port = htons((uint16_t) u);
154 a->sockaddr.in6.sin6_scope_id = idx;
155 a->sockaddr.in6.sin6_addr = in6addr_any;
156 a->size = sizeof(struct sockaddr_in6);
161 r = safe_atou(s, &u);
165 if (u <= 0 || u > 0xFFFF)
168 if (socket_ipv6_is_supported()) {
169 a->sockaddr.in6.sin6_family = AF_INET6;
170 a->sockaddr.in6.sin6_port = htons((uint16_t) u);
171 a->sockaddr.in6.sin6_addr = in6addr_any;
172 a->size = sizeof(struct sockaddr_in6);
174 a->sockaddr.in.sin_family = AF_INET;
175 a->sockaddr.in.sin_port = htons((uint16_t) u);
176 a->sockaddr.in.sin_addr.s_addr = INADDR_ANY;
177 a->size = sizeof(struct sockaddr_in);
185 int socket_address_parse_netlink(SocketAddress *a, const char *s) {
188 _cleanup_free_ char *sfamily = NULL;
196 if (sscanf(s, "%ms %u", &sfamily, &group) < 1)
197 return errno > 0 ? -errno : -EINVAL;
199 family = netlink_family_from_string(sfamily);
203 a->sockaddr.nl.nl_family = AF_NETLINK;
204 a->sockaddr.nl.nl_groups = group;
207 a->size = sizeof(struct sockaddr_nl);
208 a->protocol = family;
213 int socket_address_verify(const SocketAddress *a) {
216 switch (socket_address_family(a)) {
219 if (a->size != sizeof(struct sockaddr_in))
222 if (a->sockaddr.in.sin_port == 0)
225 if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
231 if (a->size != sizeof(struct sockaddr_in6))
234 if (a->sockaddr.in6.sin6_port == 0)
237 if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
243 if (a->size < offsetof(struct sockaddr_un, sun_path))
246 if (a->size > offsetof(struct sockaddr_un, sun_path)) {
248 if (a->sockaddr.un.sun_path[0] != 0) {
252 e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path));
256 if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1)
261 if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM && a->type != SOCK_SEQPACKET)
268 if (a->size != sizeof(struct sockaddr_nl))
271 if (a->type != SOCK_RAW && a->type != SOCK_DGRAM)
277 return -EAFNOSUPPORT;
281 int socket_address_print(const SocketAddress *a, char **ret) {
287 r = socket_address_verify(a);
291 if (socket_address_family(a) == AF_NETLINK) {
292 _cleanup_free_ char *sfamily = NULL;
294 r = netlink_family_to_string_alloc(a->protocol, &sfamily);
298 r = asprintf(ret, "%s %u", sfamily, a->sockaddr.nl.nl_groups);
305 return sockaddr_pretty(&a->sockaddr.sa, a->size, false, ret);
308 bool socket_address_can_accept(const SocketAddress *a) {
312 a->type == SOCK_STREAM ||
313 a->type == SOCK_SEQPACKET;
316 bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
320 /* Invalid addresses are unequal to all */
321 if (socket_address_verify(a) < 0 ||
322 socket_address_verify(b) < 0)
325 if (a->type != b->type)
328 if (a->size != b->size)
331 if (socket_address_family(a) != socket_address_family(b))
334 switch (socket_address_family(a)) {
337 if (a->sockaddr.in.sin_addr.s_addr != b->sockaddr.in.sin_addr.s_addr)
340 if (a->sockaddr.in.sin_port != b->sockaddr.in.sin_port)
346 if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0)
349 if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port)
356 if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0))
359 if (a->sockaddr.un.sun_path[0]) {
360 if (!strneq(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, sizeof(a->sockaddr.un.sun_path)))
363 if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0)
371 if (a->protocol != b->protocol)
374 if (a->sockaddr.nl.nl_groups != b->sockaddr.nl.nl_groups)
380 /* Cannot compare, so we assume the addresses are different */
387 bool socket_address_is(const SocketAddress *a, const char *s, int type) {
388 struct SocketAddress b;
393 if (socket_address_parse(&b, s) < 0)
398 return socket_address_equal(a, &b);
401 bool socket_address_is_netlink(const SocketAddress *a, const char *s) {
402 struct SocketAddress b;
407 if (socket_address_parse_netlink(&b, s) < 0)
410 return socket_address_equal(a, &b);
413 const char* socket_address_get_path(const SocketAddress *a) {
416 if (socket_address_family(a) != AF_UNIX)
419 if (a->sockaddr.un.sun_path[0] == 0)
422 return a->sockaddr.un.sun_path;
425 bool socket_ipv6_is_supported(void) {
426 _cleanup_free_ char *l = NULL;
428 if (access("/sys/module/ipv6", F_OK) != 0)
431 /* If we can't check "disable" parameter, assume enabled */
432 if (read_one_line_file("/sys/module/ipv6/parameters/disable", &l) < 0)
435 /* If module was loaded with disable=1 no IPv6 available */
439 bool socket_address_matches_fd(const SocketAddress *a, int fd) {
440 union sockaddr_union sa;
441 socklen_t salen = sizeof(sa), solen;
447 if (getsockname(fd, &sa.sa, &salen) < 0)
450 if (sa.sa.sa_family != a->sockaddr.sa.sa_family)
453 solen = sizeof(type);
454 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &solen) < 0)
460 if (a->protocol != 0) {
461 solen = sizeof(protocol);
462 if (getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &protocol, &solen) < 0)
465 if (protocol != a->protocol)
469 switch (sa.sa.sa_family) {
472 return sa.in.sin_port == a->sockaddr.in.sin_port &&
473 sa.in.sin_addr.s_addr == a->sockaddr.in.sin_addr.s_addr;
476 return sa.in6.sin6_port == a->sockaddr.in6.sin6_port &&
477 memcmp(&sa.in6.sin6_addr, &a->sockaddr.in6.sin6_addr, sizeof(struct in6_addr)) == 0;
480 return salen == a->size &&
481 memcmp(sa.un.sun_path, a->sockaddr.un.sun_path, salen - offsetof(struct sockaddr_un, sun_path)) == 0;
488 int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, char **ret) {
489 union sockaddr_union *sa = (union sockaddr_union*) _sa;
493 assert(salen >= sizeof(sa->sa.sa_family));
495 switch (sa->sa.sa_family) {
500 a = ntohl(sa->in.sin_addr.s_addr);
504 a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
505 ntohs(sa->in.sin_port)) < 0)
512 static const unsigned char ipv4_prefix[] = {
513 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF
516 if (translate_ipv6 && memcmp(&sa->in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) {
517 const uint8_t *a = sa->in6.sin6_addr.s6_addr+12;
521 a[0], a[1], a[2], a[3],
522 ntohs(sa->in6.sin6_port)) < 0)
525 char a[INET6_ADDRSTRLEN];
529 inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a)),
530 ntohs(sa->in6.sin6_port)) < 0)
538 if (salen <= offsetof(struct sockaddr_un, sun_path)) {
539 p = strdup("<unnamed>");
543 } else if (sa->un.sun_path[0] == 0) {
546 /* FIXME: We assume we can print the
547 * socket path here and that it hasn't
548 * more than one NUL byte. That is
549 * actually an invalid assumption */
551 p = new(char, sizeof(sa->un.sun_path)+1);
556 memcpy(p+1, sa->un.sun_path+1, sizeof(sa->un.sun_path)-1);
557 p[sizeof(sa->un.sun_path)] = 0;
560 p = strndup(sa->un.sun_path, sizeof(sa->un.sun_path));
576 int getpeername_pretty(int fd, char **ret) {
577 union sockaddr_union sa;
578 socklen_t salen = sizeof(sa);
584 if (getpeername(fd, &sa.sa, &salen) < 0)
587 if (sa.sa.sa_family == AF_UNIX) {
588 struct ucred ucred = {};
590 /* UNIX connection sockets are anonymous, so let's use
591 * PID/UID as pretty credentials instead */
593 r = getpeercred(fd, &ucred);
597 if (asprintf(ret, "PID "PID_FMT"/UID "UID_FMT, ucred.pid, ucred.uid) < 0)
603 /* For remote sockets we translate IPv6 addresses back to IPv4
604 * if applicable, since that's nicer. */
606 return sockaddr_pretty(&sa.sa, salen, true, ret);
609 int getsockname_pretty(int fd, char **ret) {
610 union sockaddr_union sa;
611 socklen_t salen = sizeof(sa);
616 if (getsockname(fd, &sa.sa, &salen) < 0)
619 /* For local sockets we do not translate IPv6 addresses back
620 * to IPv6 if applicable, since this is usually used for
621 * listening sockets where the difference between IPv4 and
624 return sockaddr_pretty(&sa.sa, salen, false, ret);
627 int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) {
629 char host[NI_MAXHOST], *ret;
633 r = getnameinfo(&sa->sa, salen, host, sizeof(host), NULL, 0,
634 NI_IDN|NI_IDN_USE_STD3_ASCII_RULES);
636 _cleanup_free_ char *sockname = NULL;
637 int saved_errno = errno;
639 r = sockaddr_pretty(&sa->sa, salen, true, &sockname);
641 log_error("sockadd_pretty() failed: %s", strerror(-r));
643 log_error("getnameinfo(%s) failed: %s", sockname, strerror(-r));
655 int getnameinfo_pretty(int fd, char **ret) {
656 union sockaddr_union sa;
657 socklen_t salen = sizeof(sa);
662 if (getsockname(fd, &sa.sa, &salen) < 0) {
663 log_error("getsockname(%d) failed: %m", fd);
667 return socknameinfo_pretty(&sa, salen, ret);
670 int socket_address_unlink(SocketAddress *a) {
673 if (socket_address_family(a) != AF_UNIX)
676 if (a->sockaddr.un.sun_path[0] == 0)
679 if (unlink(a->sockaddr.un.sun_path) < 0)
685 static const char* const netlink_family_table[] = {
686 [NETLINK_ROUTE] = "route",
687 [NETLINK_FIREWALL] = "firewall",
688 [NETLINK_INET_DIAG] = "inet-diag",
689 [NETLINK_NFLOG] = "nflog",
690 [NETLINK_XFRM] = "xfrm",
691 [NETLINK_SELINUX] = "selinux",
692 [NETLINK_ISCSI] = "iscsi",
693 [NETLINK_AUDIT] = "audit",
694 [NETLINK_FIB_LOOKUP] = "fib-lookup",
695 [NETLINK_CONNECTOR] = "connector",
696 [NETLINK_NETFILTER] = "netfilter",
697 [NETLINK_IP6_FW] = "ip6-fw",
698 [NETLINK_DNRTMSG] = "dnrtmsg",
699 [NETLINK_KOBJECT_UEVENT] = "kobject-uevent",
700 [NETLINK_GENERIC] = "generic",
701 [NETLINK_SCSITRANSPORT] = "scsitransport",
702 [NETLINK_ECRYPTFS] = "ecryptfs"
705 DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(netlink_family, int, INT_MAX);
707 static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = {
708 [SOCKET_ADDRESS_DEFAULT] = "default",
709 [SOCKET_ADDRESS_BOTH] = "both",
710 [SOCKET_ADDRESS_IPV6_ONLY] = "ipv6-only"
713 DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);