}
}
+int sockaddr_set_in_addr(
+ union sockaddr_union *u,
+ int family,
+ const union in_addr_union *a,
+ uint16_t port) {
+
+ assert(u);
+ assert(a);
+
+ switch (family) {
+
+ case AF_INET:
+ u->in = (struct sockaddr_in) {
+ .sin_family = AF_INET,
+ .sin_addr = a->in,
+ .sin_port = htobe16(port),
+ };
+
+ return 0;
+
+ case AF_INET6:
+ u->in6 = (struct sockaddr_in6) {
+ .sin6_family = AF_INET6,
+ .sin6_addr = a->in6,
+ .sin6_port = htobe16(port),
+ };
+
+ return 0;
+
+ default:
+ return -EAFNOSUPPORT;
+
+ }
+}
+
int sockaddr_pretty(
const struct sockaddr *_sa,
socklen_t salen,
if (r < 0)
return -ENOMEM;
} else {
- char a[INET6_ADDRSTRLEN], ifname[IF_NAMESIZE + 1];
-
- inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a));
- if (sa->in6.sin6_scope_id != 0)
- format_ifname_full(sa->in6.sin6_scope_id, ifname, FORMAT_IFNAME_IFINDEX);
+ const char *a = IN6_ADDR_TO_STRING(&sa->in6.sin6_addr);
if (include_port) {
- r = asprintf(&p,
+ if (asprintf(&p,
"[%s]:%u%s%s",
a,
be16toh(sa->in6.sin6_port),
sa->in6.sin6_scope_id != 0 ? "%" : "",
- sa->in6.sin6_scope_id != 0 ? ifname : "");
- if (r < 0)
+ FORMAT_IFNAME_FULL(sa->in6.sin6_scope_id, FORMAT_IFNAME_IFINDEX)) < 0)
return -ENOMEM;
} else {
- p = sa->in6.sin6_scope_id != 0 ? strjoin(a, "%", ifname) : strdup(a);
+ if (sa->in6.sin6_scope_id != 0)
+ p = strjoin(a, "%", FORMAT_IFNAME_FULL(sa->in6.sin6_scope_id, FORMAT_IFNAME_IFINDEX));
+ else
+ p = strdup(a);
if (!p)
return -ENOMEM;
}
return -errno;
if (sa.sa.sa_family == AF_UNIX) {
- struct ucred ucred = {};
+ struct ucred ucred = UCRED_INVALID;
/* UNIX connection sockets are anonymous, so let's use
* PID/UID as pretty credentials instead */
}
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_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",
- [NETLINK_RDMA] = "rdma",
+ [NETLINK_GENERIC] = "generic",
+ [NETLINK_SCSITRANSPORT] = "scsitransport",
+ [NETLINK_ECRYPTFS] = "ecryptfs",
+ [NETLINK_RDMA] = "rdma",
};
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(netlink_family, int, INT_MAX);
}
static const char* const ip_tos_table[] = {
- [IPTOS_LOWDELAY] = "low-delay",
- [IPTOS_THROUGHPUT] = "throughput",
+ [IPTOS_LOWDELAY] = "low-delay",
+ [IPTOS_THROUGHPUT] = "throughput",
[IPTOS_RELIABILITY] = "reliability",
- [IPTOS_LOWCOST] = "low-cost",
+ [IPTOS_LOWCOST] = "low-cost",
};
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff);
/* Let's refuse "all" and "default" as interface name, to avoid collisions with the special sysctl
* directories /proc/sys/net/{ipv4,ipv6}/conf/{all,default} */
- if (STR_IN_SET(p, "all", "default"))
+ if (!FLAGS_SET(flags, IFNAME_VALID_SPECIAL) && STR_IN_SET(p, "all", "default"))
return false;
for (const char *t = p; *t; t++) {
if (!ifname_valid_char(*t))
return false;
- numeric = numeric && (*t >= '0' && *t <= '9');
+ numeric = numeric && ascii_isdigit(*t);
}
/* It's fully numeric but didn't parse as valid ifindex above? if so, it must be too large or zero or
ssize_t send_one_fd_iov_sa(
int transport_fd,
int fd,
- struct iovec *iov, size_t iovlen,
+ const struct iovec *iov, size_t iovlen,
const struct sockaddr *sa, socklen_t len,
int flags) {
struct msghdr mh = {
.msg_name = (struct sockaddr*) sa,
.msg_namelen = len,
- .msg_iov = iov,
+ .msg_iov = (struct iovec *)iov,
.msg_iovlen = iovlen,
};
ssize_t k;
if (found)
*ret_fd = *(int*) CMSG_DATA(found);
else
- *ret_fd = -1;
+ *ret_fd = -EBADF;
return k;
}
* addresses!), which the kernel doesn't. We do this to reduce chance of incompatibility with other apps that
* do not expect non-NUL terminated file system path. */
if (l+1 > sizeof(ret->sun_path))
- return -EINVAL;
+ return path[0] == '@' ? -EINVAL : -ENAMETOOLONG; /* return a recognizable error if this is
+ * too long to fit into a sockaddr_un, but
+ * is a file system path, and thus might be
+ * connectible via O_PATH indirection. */
*ret = (struct sockaddr_un) {
.sun_family = AF_UNIX,
/* Call with NULL to drop binding */
- if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen_ptr(ifname)) < 0)
- return -errno;
-
- return 0;
+ return RET_NERRNO(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen_ptr(ifname)));
}
int socket_bind_to_ifindex(int fd, int ifindex) {
- char ifname[IF_NAMESIZE + 1];
+ char ifname[IF_NAMESIZE];
int r;
assert(fd >= 0);
- if (ifindex <= 0) {
+ if (ifindex <= 0)
/* Drop binding */
- if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, NULL, 0) < 0)
- return -errno;
-
- return 0;
- }
+ return RET_NERRNO(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, NULL, 0));
r = setsockopt_int(fd, SOL_SOCKET, SO_BINDTOIFINDEX, ifindex);
if (r != -ENOPROTOOPT)
return r;
/* Fall back to SO_BINDTODEVICE on kernels < 5.0 which didn't have SO_BINDTOIFINDEX */
- if (!format_ifname(ifindex, ifname))
- return -errno;
+ r = format_ifname(ifindex, ifname);
+ if (r < 0)
+ return r;
return socket_bind_to_ifname(fd, ifname);
}
switch (af) {
case AF_INET:
- if (setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)) < 0)
- return -errno;
-
- return 0;
+ return RET_NERRNO(setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)));
case AF_INET6:
- if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)) < 0)
- return -errno;
-
- return 0;
+ return RET_NERRNO(setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)));
default:
return -EAFNOSUPPORT;
*ret = (size_t) mtu;
return 0;
}
+
+int connect_unix_path(int fd, int dir_fd, const char *path) {
+ _cleanup_close_ int inode_fd = -EBADF;
+ union sockaddr_union sa = {
+ .un.sun_family = AF_UNIX,
+ };
+ size_t path_len;
+ socklen_t salen;
+
+ assert(fd >= 0);
+ assert(dir_fd == AT_FDCWD || dir_fd >= 0);
+ assert(path);
+
+ /* Connects to the specified AF_UNIX socket in the file system. Works around the 108 byte size limit
+ * in sockaddr_un, by going via O_PATH if needed. This hence works for any kind of path. */
+
+ path_len = strlen(path);
+
+ /* Refuse zero length path early, to make sure AF_UNIX stack won't mistake this for an abstract
+ * namespace path, since first char is NUL */
+ if (path_len <= 0)
+ return -EINVAL;
+
+ if (dir_fd == AT_FDCWD && path_len < sizeof(sa.un.sun_path)) {
+ memcpy(sa.un.sun_path, path, path_len + 1);
+ salen = offsetof(struct sockaddr_un, sun_path) + path_len + 1;
+ } else {
+ const char *proc;
+ size_t proc_len;
+
+ /* If dir_fd is specified, then we need to go the indirect O_PATH route, because connectat()
+ * does not exist. If the path is too long, we also need to take the indirect route, since we
+ * can't fit this into a sockaddr_un directly. */
+
+ inode_fd = openat(dir_fd, path, O_PATH|O_CLOEXEC);
+ if (inode_fd < 0)
+ return -errno;
+
+ proc = FORMAT_PROC_FD_PATH(inode_fd);
+ proc_len = strlen(proc);
+
+ assert(proc_len < sizeof(sa.un.sun_path));
+ memcpy(sa.un.sun_path, proc, proc_len + 1);
+ salen = offsetof(struct sockaddr_un, sun_path) + proc_len + 1;
+ }
+
+ return RET_NERRNO(connect(fd, &sa.sa, salen));
+}