/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
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/>.
***/
#include <errno.h>
#include <limits.h>
#include <netdb.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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"
#include "path-util.h"
#include "socket-util.h"
#include "string-table.h"
#include "string-util.h"
#include "user-util.h"
#include "util.h"
char *e, *n;
unsigned u;
int r;
assert(a);
assert(s);
zero(*a);
a->type = SOCK_STREAM;
if (*s == '[') {
/* IPv6 in [x:.....:z]:p notation */
if (!e)
return -EINVAL;
errno = 0;
e++;
if (*e != ':')
return -EINVAL;
e++;
r = safe_atou(e, &u);
if (r < 0)
return r;
if (u <= 0 || u > 0xFFFF)
return -EINVAL;
a->size = sizeof(struct sockaddr_in6);
} else if (*s == '/') {
/* AF_UNIX socket */
size_t l;
l = strlen(s);
return -EINVAL;
} else if (*s == '@') {
/* Abstract AF_UNIX socket */
size_t l;
l = strlen(s+1);
return -EINVAL;
} 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);
/* IPv4 in w.x.y.z:p notation? */
if (r < 0)
return -errno;
if (r > 0) {
/* Gotcha, it's a traditional IPv4 address */
a->size = sizeof(struct sockaddr_in);
} else {
unsigned idx;
return -EINVAL;
/* Uh, our last resort, an interface name */
idx = if_nametoindex(n);
if (idx == 0)
return -EINVAL;
a->size = sizeof(struct sockaddr_in6);
}
} else {
/* Just a port */
r = safe_atou(s, &u);
if (r < 0)
return r;
if (u <= 0 || u > 0xFFFF)
return -EINVAL;
if (socket_ipv6_is_supported()) {
a->size = sizeof(struct sockaddr_in6);
} else {
a->size = sizeof(struct sockaddr_in);
}
}
}
return 0;
}
int r;
/* Similar to socket_address_parse() but warns for IPv6 sockets when we don't support them. */
r = socket_address_parse(&b, s);
if (r < 0)
return r;
log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
return -EAFNOSUPPORT;
}
*a = b;
return 0;
}
int family;
unsigned group = 0;
assert(a);
assert(s);
zero(*a);
errno = 0;
if (family < 0)
return -EINVAL;
a->size = sizeof(struct sockaddr_nl);
return 0;
}
assert(a);
switch (socket_address_family(a)) {
case AF_INET:
if (a->size != sizeof(struct sockaddr_in))
return -EINVAL;
return -EINVAL;
return -EINVAL;
return 0;
case AF_INET6:
if (a->size != sizeof(struct sockaddr_in6))
return -EINVAL;
return -EINVAL;
return -EINVAL;
return 0;
case AF_UNIX:
return -EINVAL;
char *e;
/* path */
if (!e)
return -EINVAL;
return -EINVAL;
}
}
return -EINVAL;
return 0;
case AF_NETLINK:
if (a->size != sizeof(struct sockaddr_nl))
return -EINVAL;
return -EINVAL;
return 0;
default:
return -EAFNOSUPPORT;
}
}
int r;
assert(a);
r = socket_address_verify(a);
if (r < 0)
return r;
if (socket_address_family(a) == AF_NETLINK) {
if (r < 0)
return r;
if (r < 0)
return -ENOMEM;
return 0;
}
}
assert(a);
return
a->type == SOCK_STREAM ||
a->type == SOCK_SEQPACKET;
}
assert(a);
assert(b);
/* Invalid addresses are unequal to all */
if (socket_address_verify(a) < 0 ||
socket_address_verify(b) < 0)
return false;
return false;
if (socket_address_family(a) != socket_address_family(b))
return false;
switch (socket_address_family(a)) {
case AF_INET:
return false;
return false;
break;
case AF_INET6:
if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0)
return false;
return false;
break;
case AF_UNIX:
return false;
return false;
return false;
} else {
return false;
return false;
}
break;
case AF_NETLINK:
return false;
return false;
break;
default:
/* Cannot compare, so we assume the addresses are different */
return false;
}
return true;
}
struct SocketAddress b;
assert(a);
assert(s);
if (socket_address_parse(&b, s) < 0)
return false;
return socket_address_equal(a, &b);
}
struct SocketAddress b;
assert(a);
assert(s);
if (socket_address_parse_netlink(&b, s) < 0)
return false;
return socket_address_equal(a, &b);
}
assert(a);
if (socket_address_family(a) != AF_UNIX)
return NULL;
return NULL;
}
bool socket_ipv6_is_supported(void) {
return false;
return true;
}
assert(a);
return false;
return false;
return false;
return false;
if (a->protocol != 0) {
return false;
return false;
}
return socket_address_equal(a, &b);
}
return -EAFNOSUPPORT;
}
int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret) {
char *p;
int r;
case AF_INET: {
uint32_t a;
if (include_port)
r = asprintf(&p,
"%u.%u.%u.%u:%u",
a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
else
r = asprintf(&p,
"%u.%u.%u.%u",
a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF);
if (r < 0)
return -ENOMEM;
break;
}
case AF_INET6: {
static const unsigned char ipv4_prefix[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF
};
if (translate_ipv6 &&
if (include_port)
r = asprintf(&p,
"%u.%u.%u.%u:%u",
a[0], a[1], a[2], a[3],
else
r = asprintf(&p,
"%u.%u.%u.%u",
a[0], a[1], a[2], a[3]);
if (r < 0)
return -ENOMEM;
} else {
char a[INET6_ADDRSTRLEN];
if (include_port) {
r = asprintf(&p,
"[%s]:%u",
a,
if (r < 0)
return -ENOMEM;
} else {
p = strdup(a);
if (!p)
return -ENOMEM;
}
}
break;
}
case AF_UNIX:
p = strdup("<unnamed>");
if (!p)
return -ENOMEM;
/* abstract */
/* FIXME: We assume we can print the
* socket path here and that it hasn't
* more than one NUL byte. That is
* actually an invalid assumption */
if (!p)
return -ENOMEM;
p[0] = '@';
} else {
if (!p)
return -ENOMEM;
}
break;
default:
return -EOPNOTSUPP;
}
*ret = p;
return 0;
}
int r;
return -errno;
/* UNIX connection sockets are anonymous, so let's use
if (r < 0)
return r;
return -ENOMEM;
return 0;
}
/* For remote sockets we translate IPv6 addresses back to IPv4
* if applicable, since that's nicer. */
}
return -errno;
/* For local sockets we do not translate IPv6 addresses back
* to IPv6 if applicable, since this is usually used for
* listening sockets where the difference between IPv4 and
* IPv6 matters. */
}
int r;
if (r != 0) {
if (r < 0)
return r;
} else {
if (!ret)
return -ENOMEM;
}
return 0;
}
return -errno;
}
assert(a);
if (socket_address_family(a) != AF_UNIX)
return 0;
return 0;
return -errno;
return 1;
}
static const char* const netlink_family_table[] = {
[NETLINK_ROUTE] = "route",
[NETLINK_FIREWALL] = "firewall",
[NETLINK_INET_DIAG] = "inet-diag",
[NETLINK_NFLOG] = "nflog",
[NETLINK_XFRM] = "xfrm",
[NETLINK_SELINUX] = "selinux",
[NETLINK_ISCSI] = "iscsi",
[NETLINK_AUDIT] = "audit",
[NETLINK_FIB_LOOKUP] = "fib-lookup",
[NETLINK_CONNECTOR] = "connector",
[NETLINK_NETFILTER] = "netfilter",
[NETLINK_IP6_FW] = "ip6-fw",
[NETLINK_DNRTMSG] = "dnrtmsg",
[NETLINK_KOBJECT_UEVENT] = "kobject-uevent",
[NETLINK_GENERIC] = "generic",
[NETLINK_SCSITRANSPORT] = "scsitransport",
[NETLINK_ECRYPTFS] = "ecryptfs"
};
static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = {
[SOCKET_ADDRESS_DEFAULT] = "default",
[SOCKET_ADDRESS_BOTH] = "both",
[SOCKET_ADDRESS_IPV6_ONLY] = "ipv6-only"
};
assert(a);
assert(b);
return false;
return false;
}
int r, value;
return 0;
/* If we have the privileges we will ignore the kernel limit. */
value = (int) n;
return -errno;
return 1;
}
int r, value;
return 0;
/* If we have the privileges we will ignore the kernel limit. */
value = (int) n;
return -errno;
return 1;
}
static const char* const ip_tos_table[] = {
[IPTOS_LOWDELAY] = "low-delay",
[IPTOS_THROUGHPUT] = "throughput",
[IPTOS_RELIABILITY] = "reliability",
[IPTOS_LOWCOST] = "low-cost",
};
struct ucred u;
int r;
if (r < 0)
return -errno;
if (n != sizeof(struct ucred))
return -EIO;
/* Check if the data is actually useful and not suppressed due
* to namespacing issues */
if (u.pid <= 0)
return -ENODATA;
if (u.uid == UID_INVALID)
return -ENODATA;
if (u.gid == GID_INVALID)
return -ENODATA;
*ucred = u;
return 0;
}
socklen_t n = 64;
char *s;
int r;
s = new0(char, n);
if (!s)
return -ENOMEM;
if (r < 0) {
free(s);
return -errno;
s = new0(char, n);
if (!s)
return -ENOMEM;
if (r < 0) {
free(s);
return -errno;
}
}
if (isempty(s)) {
free(s);
return -EOPNOTSUPP;
}
*ret = s;
return 0;
}
int send_one_fd_sa(
int transport_fd,
int fd,
int flags) {
union {
} control = {};
.msg_namelen = len,
.msg_control = &control,
.msg_controllen = sizeof(control),
};
assert(transport_fd >= 0);
return -errno;
return 0;
}
union {
} control = {};
.msg_control = &control,
.msg_controllen = sizeof(control),
};
assert(transport_fd >= 0);
/*
* Receive a single FD via @transport_fd. We don't care for
* the transport-type. We retrieve a single FD at most, so for
* packet-based transports, the caller must ensure to send
* only a single FD per packet. This is best used in
* combination with send_one_fd().
*/
return -errno;
break;
}
}
if (!found) {
cmsg_close_all(&mh);
return -EIO;
}
}