mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 08:25:20 +09:00
util-lib: move things that parse ifnames to shared/
In subsequent commits, calls to if_nametoindex() will be replaced by a wrapper that falls back to alternative name resolution over netlink. netlink support requires libsystemd (for sd-netlink), and we don't want to add any functions that require netlink in basic/. So stuff that calls if_nametoindex() for user supplied interface names, and everything that depends on that, needs to be moved.
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
#include "pretty-print.h"
|
||||
#include "process-util.h"
|
||||
#include "signal-util.h"
|
||||
#include "socket-netlink.h"
|
||||
#include "socket-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
|
||||
@@ -439,42 +439,6 @@ int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret_addr, int *ret_ifindex) {
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
const char *suffix;
|
||||
int r, ifindex = 0;
|
||||
|
||||
assert(s);
|
||||
assert(family);
|
||||
assert(ret_addr);
|
||||
|
||||
/* Similar to in_addr_from_string_auto() but also parses an optionally appended IPv6 zone suffix ("scope id")
|
||||
* if one is found. */
|
||||
|
||||
suffix = strchr(s, '%');
|
||||
if (suffix) {
|
||||
if (ret_ifindex) {
|
||||
/* If we shall return the interface index, try to parse it */
|
||||
ifindex = parse_ifindex_or_ifname(suffix + 1);
|
||||
if (ifindex < 0)
|
||||
return ifindex;
|
||||
}
|
||||
|
||||
s = buf = strndup(s, suffix - s);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
r = in_addr_from_string_auto(s, family, ret_addr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret_ifindex)
|
||||
*ret_ifindex = ifindex;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr) {
|
||||
assert(addr);
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned
|
||||
int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret);
|
||||
int in_addr_from_string(int family, const char *s, union in_addr_union *ret);
|
||||
int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret);
|
||||
int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex);
|
||||
|
||||
unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr);
|
||||
struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen);
|
||||
int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen);
|
||||
|
||||
@@ -134,31 +134,3 @@ int socket_address_listen(
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int make_socket_fd(int log_level, const char* address, int type, int flags) {
|
||||
SocketAddress a;
|
||||
int fd, r;
|
||||
|
||||
r = socket_address_parse(&a, address);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse socket address \"%s\": %m", address);
|
||||
|
||||
a.type = type;
|
||||
|
||||
fd = socket_address_listen(&a, type | flags, SOMAXCONN, SOCKET_ADDRESS_DEFAULT,
|
||||
NULL, false, false, false, 0755, 0644, NULL);
|
||||
if (fd < 0 || log_get_max_level() >= log_level) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
|
||||
r = socket_address_print(&a, &p);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "socket_address_print(): %m");
|
||||
|
||||
if (fd < 0)
|
||||
log_error_errno(fd, "Failed to listen on %s: %m", p);
|
||||
else
|
||||
log_full(log_level, "Listening on %s", p);
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
@@ -52,227 +52,6 @@ static const char* const socket_address_type_table[] = {
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(socket_address_type, int);
|
||||
|
||||
int socket_address_parse(SocketAddress *a, const char *s) {
|
||||
_cleanup_free_ char *n = NULL;
|
||||
char *e;
|
||||
int r;
|
||||
|
||||
assert(a);
|
||||
assert(s);
|
||||
|
||||
*a = (SocketAddress) {
|
||||
.type = SOCK_STREAM,
|
||||
};
|
||||
|
||||
if (*s == '[') {
|
||||
uint16_t port;
|
||||
|
||||
/* IPv6 in [x:.....:z]:p notation */
|
||||
|
||||
e = strchr(s+1, ']');
|
||||
if (!e)
|
||||
return -EINVAL;
|
||||
|
||||
n = strndup(s+1, e-s-1);
|
||||
if (!n)
|
||||
return -ENOMEM;
|
||||
|
||||
errno = 0;
|
||||
if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0)
|
||||
return errno_or_else(EINVAL);
|
||||
|
||||
e++;
|
||||
if (*e != ':')
|
||||
return -EINVAL;
|
||||
|
||||
e++;
|
||||
r = parse_ip_port(e, &port);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
a->sockaddr.in6.sin6_family = AF_INET6;
|
||||
a->sockaddr.in6.sin6_port = htobe16(port);
|
||||
a->size = sizeof(struct sockaddr_in6);
|
||||
|
||||
} else if (*s == '/') {
|
||||
/* AF_UNIX socket */
|
||||
|
||||
size_t l;
|
||||
|
||||
l = strlen(s);
|
||||
if (l >= sizeof(a->sockaddr.un.sun_path)) /* Note that we refuse non-NUL-terminated sockets when
|
||||
* parsing (the kernel itself is less strict here in what it
|
||||
* accepts) */
|
||||
return -EINVAL;
|
||||
|
||||
a->sockaddr.un.sun_family = AF_UNIX;
|
||||
memcpy(a->sockaddr.un.sun_path, s, l);
|
||||
a->size = offsetof(struct sockaddr_un, sun_path) + l + 1;
|
||||
|
||||
} else if (*s == '@') {
|
||||
/* Abstract AF_UNIX socket */
|
||||
size_t l;
|
||||
|
||||
l = strlen(s+1);
|
||||
if (l >= sizeof(a->sockaddr.un.sun_path) - 1) /* Note that we refuse non-NUL-terminated sockets here
|
||||
* when parsing, even though abstract namespace sockets
|
||||
* explicitly allow embedded NUL bytes and don't consider
|
||||
* them special. But it's simply annoying to debug such
|
||||
* sockets. */
|
||||
return -EINVAL;
|
||||
|
||||
a->sockaddr.un.sun_family = AF_UNIX;
|
||||
memcpy(a->sockaddr.un.sun_path+1, s+1, l);
|
||||
a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
|
||||
|
||||
} else if (startswith(s, "vsock:")) {
|
||||
/* AF_VSOCK socket in vsock:cid:port notation */
|
||||
const char *cid_start = s + STRLEN("vsock:");
|
||||
unsigned port;
|
||||
|
||||
e = strchr(cid_start, ':');
|
||||
if (!e)
|
||||
return -EINVAL;
|
||||
|
||||
r = safe_atou(e+1, &port);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
n = strndup(cid_start, e - cid_start);
|
||||
if (!n)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!isempty(n)) {
|
||||
r = safe_atou(n, &a->sockaddr.vm.svm_cid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else
|
||||
a->sockaddr.vm.svm_cid = VMADDR_CID_ANY;
|
||||
|
||||
a->sockaddr.vm.svm_family = AF_VSOCK;
|
||||
a->sockaddr.vm.svm_port = port;
|
||||
a->size = sizeof(struct sockaddr_vm);
|
||||
|
||||
} else {
|
||||
uint16_t port;
|
||||
|
||||
e = strchr(s, ':');
|
||||
if (e) {
|
||||
r = parse_ip_port(e + 1, &port);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
n = strndup(s, e-s);
|
||||
if (!n)
|
||||
return -ENOMEM;
|
||||
|
||||
/* IPv4 in w.x.y.z:p notation? */
|
||||
r = inet_pton(AF_INET, n, &a->sockaddr.in.sin_addr);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
if (r > 0) {
|
||||
/* Gotcha, it's a traditional IPv4 address */
|
||||
a->sockaddr.in.sin_family = AF_INET;
|
||||
a->sockaddr.in.sin_port = htobe16(port);
|
||||
a->size = sizeof(struct sockaddr_in);
|
||||
} else {
|
||||
unsigned idx;
|
||||
|
||||
if (strlen(n) > IF_NAMESIZE-1)
|
||||
return -EINVAL;
|
||||
|
||||
/* Uh, our last resort, an interface name */
|
||||
idx = if_nametoindex(n);
|
||||
if (idx == 0)
|
||||
return -EINVAL;
|
||||
|
||||
a->sockaddr.in6.sin6_family = AF_INET6;
|
||||
a->sockaddr.in6.sin6_port = htobe16(port);
|
||||
a->sockaddr.in6.sin6_scope_id = idx;
|
||||
a->sockaddr.in6.sin6_addr = in6addr_any;
|
||||
a->size = sizeof(struct sockaddr_in6);
|
||||
}
|
||||
} else {
|
||||
|
||||
/* Just a port */
|
||||
r = parse_ip_port(s, &port);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (socket_ipv6_is_supported()) {
|
||||
a->sockaddr.in6.sin6_family = AF_INET6;
|
||||
a->sockaddr.in6.sin6_port = htobe16(port);
|
||||
a->sockaddr.in6.sin6_addr = in6addr_any;
|
||||
a->size = sizeof(struct sockaddr_in6);
|
||||
} else {
|
||||
a->sockaddr.in.sin_family = AF_INET;
|
||||
a->sockaddr.in.sin_port = htobe16(port);
|
||||
a->sockaddr.in.sin_addr.s_addr = INADDR_ANY;
|
||||
a->size = sizeof(struct sockaddr_in);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int socket_address_parse_and_warn(SocketAddress *a, const char *s) {
|
||||
SocketAddress b;
|
||||
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;
|
||||
|
||||
if (!socket_ipv6_is_supported() && b.sockaddr.sa.sa_family == AF_INET6) {
|
||||
log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
*a = b;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int socket_address_parse_netlink(SocketAddress *a, const char *s) {
|
||||
_cleanup_free_ char *word = NULL;
|
||||
unsigned group = 0;
|
||||
int family, r;
|
||||
|
||||
assert(a);
|
||||
assert(s);
|
||||
|
||||
zero(*a);
|
||||
a->type = SOCK_RAW;
|
||||
|
||||
r = extract_first_word(&s, &word, NULL, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EINVAL;
|
||||
|
||||
family = netlink_family_from_string(word);
|
||||
if (family < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!isempty(s)) {
|
||||
r = safe_atou(s, &group);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
a->sockaddr.nl.nl_family = AF_NETLINK;
|
||||
a->sockaddr.nl.nl_groups = group;
|
||||
|
||||
a->type = SOCK_RAW;
|
||||
a->size = sizeof(struct sockaddr_nl);
|
||||
a->protocol = family;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int socket_address_verify(const SocketAddress *a, bool strict) {
|
||||
assert(a);
|
||||
|
||||
@@ -482,32 +261,6 @@ bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool socket_address_is(const SocketAddress *a, const char *s, int type) {
|
||||
struct SocketAddress b;
|
||||
|
||||
assert(a);
|
||||
assert(s);
|
||||
|
||||
if (socket_address_parse(&b, s) < 0)
|
||||
return false;
|
||||
|
||||
b.type = type;
|
||||
|
||||
return socket_address_equal(a, &b);
|
||||
}
|
||||
|
||||
bool socket_address_is_netlink(const SocketAddress *a, const char *s) {
|
||||
struct SocketAddress b;
|
||||
|
||||
assert(a);
|
||||
assert(s);
|
||||
|
||||
if (socket_address_parse_netlink(&b, s) < 0)
|
||||
return false;
|
||||
|
||||
return socket_address_equal(a, &b);
|
||||
}
|
||||
|
||||
const char* socket_address_get_path(const SocketAddress *a) {
|
||||
assert(a);
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@ union sockaddr_union {
|
||||
uint8_t un_buffer[sizeof(struct sockaddr_un) + 1];
|
||||
};
|
||||
|
||||
#define SUN_PATH_LEN (sizeof(((struct sockaddr_un){}).sun_path))
|
||||
|
||||
typedef struct SocketAddress {
|
||||
union sockaddr_union sockaddr;
|
||||
|
||||
@@ -68,12 +70,6 @@ typedef enum SocketAddressBindIPv6Only {
|
||||
const char* socket_address_type_to_string(int t) _const_;
|
||||
int socket_address_type_from_string(const char *s) _pure_;
|
||||
|
||||
int socket_address_parse(SocketAddress *a, const char *s);
|
||||
int socket_address_parse_and_warn(SocketAddress *a, const char *s);
|
||||
int socket_address_parse_netlink(SocketAddress *a, const char *s);
|
||||
int socket_address_print(const SocketAddress *a, char **p);
|
||||
int socket_address_verify(const SocketAddress *a, bool strict) _pure_;
|
||||
|
||||
int sockaddr_un_unlink(const struct sockaddr_un *sa);
|
||||
|
||||
static inline int socket_address_unlink(const SocketAddress *a) {
|
||||
@@ -94,11 +90,9 @@ int socket_address_listen(
|
||||
mode_t directory_mode,
|
||||
mode_t socket_mode,
|
||||
const char *label);
|
||||
int make_socket_fd(int log_level, const char* address, int type, int flags);
|
||||
|
||||
bool socket_address_is(const SocketAddress *a, const char *s, int type);
|
||||
bool socket_address_is_netlink(const SocketAddress *a, const char *s);
|
||||
|
||||
int socket_address_verify(const SocketAddress *a, bool strict) _pure_;
|
||||
int socket_address_print(const SocketAddress *a, char **p);
|
||||
bool socket_address_matches_fd(const SocketAddress *a, int fd);
|
||||
|
||||
bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) _pure_;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "socket.h"
|
||||
#include "socket-netlink.h"
|
||||
#include "socket-util.h"
|
||||
#include "string-util.h"
|
||||
#include "unit.h"
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
#endif
|
||||
#include "securebits-util.h"
|
||||
#include "signal-util.h"
|
||||
#include "socket-netlink.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "signal-util.h"
|
||||
#include "smack-util.h"
|
||||
#include "socket.h"
|
||||
#include "socket-netlink.h"
|
||||
#include "special.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "process-util.h"
|
||||
#include "rlimit-util.h"
|
||||
#include "signal-util.h"
|
||||
#include "socket-netlink.h"
|
||||
#include "socket-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-table.h"
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include "escape.h"
|
||||
#include "format-util.h"
|
||||
#include "gcrypt-util.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "main-func.h"
|
||||
#include "missing_network.h"
|
||||
#include "netlink-util.h"
|
||||
@@ -27,6 +26,7 @@
|
||||
#include "resolvectl.h"
|
||||
#include "resolved-def.h"
|
||||
#include "resolved-dns-packet.h"
|
||||
#include "socket-netlink.h"
|
||||
#include "string-table.h"
|
||||
#include "strv.h"
|
||||
#include "terminal-util.h"
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "resolved-dnssd-bus.h"
|
||||
#include "resolved-dnssd.h"
|
||||
#include "resolved-link-bus.h"
|
||||
#include "socket-netlink.h"
|
||||
#include "stdio-util.h"
|
||||
#include "strv.h"
|
||||
#include "user-util.h"
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "hostname-util.h"
|
||||
#include "resolved-dns-synthesize.h"
|
||||
#include "resolved-etc-hosts.h"
|
||||
#include "socket-netlink.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "time-util.h"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "macro.h"
|
||||
#include "resolved-util.h"
|
||||
#include "socket-netlink.h"
|
||||
|
||||
int in_addr_ifindex_name_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex, char **server_name) {
|
||||
_cleanup_free_ char *buf = NULL, *name = NULL;
|
||||
|
||||
@@ -165,6 +165,8 @@ shared_sources = files('''
|
||||
serialize.h
|
||||
sleep-config.c
|
||||
sleep-config.h
|
||||
socket-netlink.c
|
||||
socket-netlink.h
|
||||
spawn-ask-password-agent.c
|
||||
spawn-ask-password-agent.h
|
||||
spawn-polkit-agent.c
|
||||
|
||||
327
src/shared/socket-netlink.c
Normal file
327
src/shared/socket-netlink.c
Normal file
@@ -0,0 +1,327 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <net/if.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "extract-word.h"
|
||||
#include "log.h"
|
||||
#include "memory-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "socket-netlink.h"
|
||||
#include "socket-util.h"
|
||||
#include "string-util.h"
|
||||
|
||||
int socket_address_parse(SocketAddress *a, const char *s) {
|
||||
_cleanup_free_ char *n = NULL;
|
||||
char *e;
|
||||
int r;
|
||||
|
||||
assert(a);
|
||||
assert(s);
|
||||
|
||||
*a = (SocketAddress) {
|
||||
.type = SOCK_STREAM,
|
||||
};
|
||||
|
||||
if (*s == '[') {
|
||||
uint16_t port;
|
||||
|
||||
/* IPv6 in [x:.....:z]:p notation */
|
||||
|
||||
e = strchr(s+1, ']');
|
||||
if (!e)
|
||||
return -EINVAL;
|
||||
|
||||
n = strndup(s+1, e-s-1);
|
||||
if (!n)
|
||||
return -ENOMEM;
|
||||
|
||||
errno = 0;
|
||||
if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0)
|
||||
return errno_or_else(EINVAL);
|
||||
|
||||
e++;
|
||||
if (*e != ':')
|
||||
return -EINVAL;
|
||||
|
||||
e++;
|
||||
r = parse_ip_port(e, &port);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
a->sockaddr.in6.sin6_family = AF_INET6;
|
||||
a->sockaddr.in6.sin6_port = htobe16(port);
|
||||
a->size = sizeof(struct sockaddr_in6);
|
||||
|
||||
} else if (*s == '/') {
|
||||
/* AF_UNIX socket */
|
||||
|
||||
size_t l;
|
||||
|
||||
l = strlen(s);
|
||||
if (l >= sizeof(a->sockaddr.un.sun_path)) /* Note that we refuse non-NUL-terminated sockets when
|
||||
* parsing (the kernel itself is less strict here in what it
|
||||
* accepts) */
|
||||
return -EINVAL;
|
||||
|
||||
a->sockaddr.un.sun_family = AF_UNIX;
|
||||
memcpy(a->sockaddr.un.sun_path, s, l);
|
||||
a->size = offsetof(struct sockaddr_un, sun_path) + l + 1;
|
||||
|
||||
} else if (*s == '@') {
|
||||
/* Abstract AF_UNIX socket */
|
||||
size_t l;
|
||||
|
||||
l = strlen(s+1);
|
||||
if (l >= sizeof(a->sockaddr.un.sun_path) - 1) /* Note that we refuse non-NUL-terminated sockets here
|
||||
* when parsing, even though abstract namespace sockets
|
||||
* explicitly allow embedded NUL bytes and don't consider
|
||||
* them special. But it's simply annoying to debug such
|
||||
* sockets. */
|
||||
return -EINVAL;
|
||||
|
||||
a->sockaddr.un.sun_family = AF_UNIX;
|
||||
memcpy(a->sockaddr.un.sun_path+1, s+1, l);
|
||||
a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
|
||||
|
||||
} else if (startswith(s, "vsock:")) {
|
||||
/* AF_VSOCK socket in vsock:cid:port notation */
|
||||
const char *cid_start = s + STRLEN("vsock:");
|
||||
unsigned port;
|
||||
|
||||
e = strchr(cid_start, ':');
|
||||
if (!e)
|
||||
return -EINVAL;
|
||||
|
||||
r = safe_atou(e+1, &port);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
n = strndup(cid_start, e - cid_start);
|
||||
if (!n)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!isempty(n)) {
|
||||
r = safe_atou(n, &a->sockaddr.vm.svm_cid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else
|
||||
a->sockaddr.vm.svm_cid = VMADDR_CID_ANY;
|
||||
|
||||
a->sockaddr.vm.svm_family = AF_VSOCK;
|
||||
a->sockaddr.vm.svm_port = port;
|
||||
a->size = sizeof(struct sockaddr_vm);
|
||||
|
||||
} else {
|
||||
uint16_t port;
|
||||
|
||||
e = strchr(s, ':');
|
||||
if (e) {
|
||||
r = parse_ip_port(e + 1, &port);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
n = strndup(s, e-s);
|
||||
if (!n)
|
||||
return -ENOMEM;
|
||||
|
||||
/* IPv4 in w.x.y.z:p notation? */
|
||||
r = inet_pton(AF_INET, n, &a->sockaddr.in.sin_addr);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
if (r > 0) {
|
||||
/* Gotcha, it's a traditional IPv4 address */
|
||||
a->sockaddr.in.sin_family = AF_INET;
|
||||
a->sockaddr.in.sin_port = htobe16(port);
|
||||
a->size = sizeof(struct sockaddr_in);
|
||||
} else {
|
||||
unsigned idx;
|
||||
|
||||
if (strlen(n) > IF_NAMESIZE-1)
|
||||
return -EINVAL;
|
||||
|
||||
/* Uh, our last resort, an interface name */
|
||||
idx = if_nametoindex(n);
|
||||
if (idx == 0)
|
||||
return -EINVAL;
|
||||
|
||||
a->sockaddr.in6.sin6_family = AF_INET6;
|
||||
a->sockaddr.in6.sin6_port = htobe16(port);
|
||||
a->sockaddr.in6.sin6_scope_id = idx;
|
||||
a->sockaddr.in6.sin6_addr = in6addr_any;
|
||||
a->size = sizeof(struct sockaddr_in6);
|
||||
}
|
||||
} else {
|
||||
|
||||
/* Just a port */
|
||||
r = parse_ip_port(s, &port);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (socket_ipv6_is_supported()) {
|
||||
a->sockaddr.in6.sin6_family = AF_INET6;
|
||||
a->sockaddr.in6.sin6_port = htobe16(port);
|
||||
a->sockaddr.in6.sin6_addr = in6addr_any;
|
||||
a->size = sizeof(struct sockaddr_in6);
|
||||
} else {
|
||||
a->sockaddr.in.sin_family = AF_INET;
|
||||
a->sockaddr.in.sin_port = htobe16(port);
|
||||
a->sockaddr.in.sin_addr.s_addr = INADDR_ANY;
|
||||
a->size = sizeof(struct sockaddr_in);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int socket_address_parse_and_warn(SocketAddress *a, const char *s) {
|
||||
SocketAddress b;
|
||||
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;
|
||||
|
||||
if (!socket_ipv6_is_supported() && b.sockaddr.sa.sa_family == AF_INET6) {
|
||||
log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
*a = b;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int socket_address_parse_netlink(SocketAddress *a, const char *s) {
|
||||
_cleanup_free_ char *word = NULL;
|
||||
unsigned group = 0;
|
||||
int family, r;
|
||||
|
||||
assert(a);
|
||||
assert(s);
|
||||
|
||||
zero(*a);
|
||||
a->type = SOCK_RAW;
|
||||
|
||||
r = extract_first_word(&s, &word, NULL, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EINVAL;
|
||||
|
||||
family = netlink_family_from_string(word);
|
||||
if (family < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!isempty(s)) {
|
||||
r = safe_atou(s, &group);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
a->sockaddr.nl.nl_family = AF_NETLINK;
|
||||
a->sockaddr.nl.nl_groups = group;
|
||||
|
||||
a->type = SOCK_RAW;
|
||||
a->size = sizeof(struct sockaddr_nl);
|
||||
a->protocol = family;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool socket_address_is(const SocketAddress *a, const char *s, int type) {
|
||||
struct SocketAddress b;
|
||||
|
||||
assert(a);
|
||||
assert(s);
|
||||
|
||||
if (socket_address_parse(&b, s) < 0)
|
||||
return false;
|
||||
|
||||
b.type = type;
|
||||
|
||||
return socket_address_equal(a, &b);
|
||||
}
|
||||
|
||||
bool socket_address_is_netlink(const SocketAddress *a, const char *s) {
|
||||
struct SocketAddress b;
|
||||
|
||||
assert(a);
|
||||
assert(s);
|
||||
|
||||
if (socket_address_parse_netlink(&b, s) < 0)
|
||||
return false;
|
||||
|
||||
return socket_address_equal(a, &b);
|
||||
}
|
||||
|
||||
int make_socket_fd(int log_level, const char* address, int type, int flags) {
|
||||
SocketAddress a;
|
||||
int fd, r;
|
||||
|
||||
r = socket_address_parse(&a, address);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse socket address \"%s\": %m", address);
|
||||
|
||||
a.type = type;
|
||||
|
||||
fd = socket_address_listen(&a, type | flags, SOMAXCONN, SOCKET_ADDRESS_DEFAULT,
|
||||
NULL, false, false, false, 0755, 0644, NULL);
|
||||
if (fd < 0 || log_get_max_level() >= log_level) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
|
||||
r = socket_address_print(&a, &p);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "socket_address_print(): %m");
|
||||
|
||||
if (fd < 0)
|
||||
log_error_errno(fd, "Failed to listen on %s: %m", p);
|
||||
else
|
||||
log_full(log_level, "Listening on %s", p);
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret_addr, int *ret_ifindex) {
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
const char *suffix;
|
||||
int r, ifindex = 0;
|
||||
|
||||
assert(s);
|
||||
assert(family);
|
||||
assert(ret_addr);
|
||||
|
||||
/* Similar to in_addr_from_string_auto() but also parses an optionally appended IPv6 zone suffix ("scope id")
|
||||
* if one is found. */
|
||||
|
||||
suffix = strchr(s, '%');
|
||||
if (suffix) {
|
||||
if (ret_ifindex) {
|
||||
/* If we shall return the interface index, try to parse it */
|
||||
ifindex = parse_ifindex_or_ifname(suffix + 1);
|
||||
if (ifindex < 0)
|
||||
return ifindex;
|
||||
}
|
||||
|
||||
s = buf = strndup(s, suffix - s);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
r = in_addr_from_string_auto(s, family, ret_addr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret_ifindex)
|
||||
*ret_ifindex = ifindex;
|
||||
|
||||
return r;
|
||||
}
|
||||
17
src/shared/socket-netlink.h
Normal file
17
src/shared/socket-netlink.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include "in-addr-util.h"
|
||||
#include "macro.h"
|
||||
#include "socket-util.h"
|
||||
|
||||
int make_socket_fd(int log_level, const char* address, int type, int flags);
|
||||
|
||||
int socket_address_parse(SocketAddress *a, const char *s);
|
||||
int socket_address_parse_and_warn(SocketAddress *a, const char *s);
|
||||
int socket_address_parse_netlink(SocketAddress *a, const char *s);
|
||||
|
||||
bool socket_address_is(const SocketAddress *a, const char *s, int type);
|
||||
bool socket_address_is_netlink(const SocketAddress *a, const char *s);
|
||||
|
||||
int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex);
|
||||
@@ -362,6 +362,10 @@ tests += [
|
||||
[],
|
||||
[]],
|
||||
|
||||
[['src/test/test-socket-netlink.c'],
|
||||
[],
|
||||
[]],
|
||||
|
||||
[['src/test/test-in-addr-util.c'],
|
||||
[],
|
||||
[]],
|
||||
|
||||
227
src/test/test-socket-netlink.c
Normal file
227
src/test/test-socket-netlink.c
Normal file
@@ -0,0 +1,227 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "tests.h"
|
||||
#include "socket-netlink.h"
|
||||
#include "string-util.h"
|
||||
|
||||
static void test_socket_address_parse_one(const char *in, int ret, int family, const char *expected) {
|
||||
SocketAddress a;
|
||||
_cleanup_free_ char *out = NULL;
|
||||
int r;
|
||||
|
||||
r = socket_address_parse(&a, in);
|
||||
if (r >= 0)
|
||||
assert_se(socket_address_print(&a, &out) >= 0);
|
||||
|
||||
log_info("\"%s\" → %s → \"%s\" (expect \"%s\")", in,
|
||||
r >= 0 ? "✓" : "✗", empty_to_dash(out), r >= 0 ? expected ?: in : "-");
|
||||
assert_se(r == ret);
|
||||
if (r >= 0) {
|
||||
assert_se(a.sockaddr.sa.sa_family == family);
|
||||
assert_se(streq(out, expected ?: in));
|
||||
}
|
||||
}
|
||||
|
||||
static void test_socket_address_parse(void) {
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
test_socket_address_parse_one("junk", -EINVAL, 0, NULL);
|
||||
test_socket_address_parse_one("192.168.1.1", -EINVAL, 0, NULL);
|
||||
test_socket_address_parse_one(".168.1.1", -EINVAL, 0, NULL);
|
||||
test_socket_address_parse_one("989.168.1.1", -EINVAL, 0, NULL);
|
||||
test_socket_address_parse_one("192.168.1.1:65536", -ERANGE, 0, NULL);
|
||||
test_socket_address_parse_one("192.168.1.1:0", -EINVAL, 0, NULL);
|
||||
test_socket_address_parse_one("0", -EINVAL, 0, NULL);
|
||||
test_socket_address_parse_one("65536", -ERANGE, 0, NULL);
|
||||
|
||||
const int default_family = socket_ipv6_is_supported() ? AF_INET6 : AF_INET;
|
||||
|
||||
test_socket_address_parse_one("65535", 0, default_family, "[::]:65535");
|
||||
|
||||
/* The checks below will pass even if ipv6 is disabled in
|
||||
* kernel. The underlying glibc's inet_pton() is just a string
|
||||
* parser and doesn't make any syscalls. */
|
||||
|
||||
test_socket_address_parse_one("[::1]", -EINVAL, 0, NULL);
|
||||
test_socket_address_parse_one("[::1]8888", -EINVAL, 0, NULL);
|
||||
test_socket_address_parse_one("::1", -EINVAL, 0, NULL);
|
||||
test_socket_address_parse_one("[::1]:0", -EINVAL, 0, NULL);
|
||||
test_socket_address_parse_one("[::1]:65536", -ERANGE, 0, NULL);
|
||||
test_socket_address_parse_one("[a:b:1]:8888", -EINVAL, 0, NULL);
|
||||
|
||||
test_socket_address_parse_one("8888", 0, default_family, "[::]:8888");
|
||||
test_socket_address_parse_one("[2001:0db8:0000:85a3:0000:0000:ac1f:8001]:8888", 0, AF_INET6,
|
||||
"[2001:db8:0:85a3::ac1f:8001]:8888");
|
||||
test_socket_address_parse_one("[::1]:8888", 0, AF_INET6, NULL);
|
||||
test_socket_address_parse_one("192.168.1.254:8888", 0, AF_INET, NULL);
|
||||
test_socket_address_parse_one("/foo/bar", 0, AF_UNIX, NULL);
|
||||
test_socket_address_parse_one("/", 0, AF_UNIX, NULL);
|
||||
test_socket_address_parse_one("@abstract", 0, AF_UNIX, NULL);
|
||||
|
||||
{
|
||||
char aaa[SUN_PATH_LEN + 1] = "@";
|
||||
|
||||
memset(aaa + 1, 'a', SUN_PATH_LEN - 1);
|
||||
char_array_0(aaa);
|
||||
|
||||
test_socket_address_parse_one(aaa, -EINVAL, 0, NULL);
|
||||
|
||||
aaa[SUN_PATH_LEN - 1] = '\0';
|
||||
test_socket_address_parse_one(aaa, 0, AF_UNIX, NULL);
|
||||
}
|
||||
|
||||
test_socket_address_parse_one("vsock:2:1234", 0, AF_VSOCK, NULL);
|
||||
test_socket_address_parse_one("vsock::1234", 0, AF_VSOCK, NULL);
|
||||
test_socket_address_parse_one("vsock:2:1234x", -EINVAL, 0, NULL);
|
||||
test_socket_address_parse_one("vsock:2x:1234", -EINVAL, 0, NULL);
|
||||
test_socket_address_parse_one("vsock:2", -EINVAL, 0, NULL);
|
||||
}
|
||||
|
||||
static void test_socket_address_parse_netlink(void) {
|
||||
SocketAddress a;
|
||||
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
assert_se(socket_address_parse_netlink(&a, "junk") < 0);
|
||||
assert_se(socket_address_parse_netlink(&a, "") < 0);
|
||||
|
||||
assert_se(socket_address_parse_netlink(&a, "route") >= 0);
|
||||
assert_se(a.sockaddr.nl.nl_family == AF_NETLINK);
|
||||
assert_se(a.sockaddr.nl.nl_groups == 0);
|
||||
assert_se(a.protocol == NETLINK_ROUTE);
|
||||
assert_se(socket_address_parse_netlink(&a, "route") >= 0);
|
||||
assert_se(socket_address_parse_netlink(&a, "route 10") >= 0);
|
||||
assert_se(a.sockaddr.nl.nl_family == AF_NETLINK);
|
||||
assert_se(a.sockaddr.nl.nl_groups == 10);
|
||||
assert_se(a.protocol == NETLINK_ROUTE);
|
||||
|
||||
/* With spaces and tabs */
|
||||
assert_se(socket_address_parse_netlink(&a, " kobject-uevent ") >= 0);
|
||||
assert_se(a.sockaddr.nl.nl_family == AF_NETLINK);
|
||||
assert_se(a.sockaddr.nl.nl_groups == 0);
|
||||
assert_se(a.protocol == NETLINK_KOBJECT_UEVENT);
|
||||
assert_se(socket_address_parse_netlink(&a, " \t kobject-uevent \t 10") >= 0);
|
||||
assert_se(a.sockaddr.nl.nl_family == AF_NETLINK);
|
||||
assert_se(a.sockaddr.nl.nl_groups == 10);
|
||||
assert_se(a.protocol == NETLINK_KOBJECT_UEVENT);
|
||||
assert_se(socket_address_parse_netlink(&a, "kobject-uevent\t10") >= 0);
|
||||
assert_se(a.sockaddr.nl.nl_family == AF_NETLINK);
|
||||
assert_se(a.sockaddr.nl.nl_groups == 10);
|
||||
assert_se(a.protocol == NETLINK_KOBJECT_UEVENT);
|
||||
|
||||
/* trailing space is not supported */
|
||||
assert_se(socket_address_parse_netlink(&a, "kobject-uevent\t10 ") < 0);
|
||||
|
||||
/* Group must be unsigned */
|
||||
assert_se(socket_address_parse_netlink(&a, "kobject-uevent -1") < 0);
|
||||
|
||||
/* oss-fuzz #6884 */
|
||||
assert_se(socket_address_parse_netlink(&a, "\xff") < 0);
|
||||
}
|
||||
|
||||
static void test_socket_address_equal(void) {
|
||||
SocketAddress a, b;
|
||||
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
|
||||
assert_se(socket_address_parse(&b, "192.168.1.1:888") >= 0);
|
||||
assert_se(!socket_address_equal(&a, &b));
|
||||
|
||||
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
|
||||
assert_se(socket_address_parse(&b, "192.16.1.1:8888") >= 0);
|
||||
assert_se(!socket_address_equal(&a, &b));
|
||||
|
||||
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
|
||||
assert_se(socket_address_parse(&b, "8888") >= 0);
|
||||
assert_se(!socket_address_equal(&a, &b));
|
||||
|
||||
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
|
||||
assert_se(socket_address_parse(&b, "/foo/bar/") >= 0);
|
||||
assert_se(!socket_address_equal(&a, &b));
|
||||
|
||||
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
|
||||
assert_se(socket_address_parse(&b, "192.168.1.1:8888") >= 0);
|
||||
assert_se(socket_address_equal(&a, &b));
|
||||
|
||||
assert_se(socket_address_parse(&a, "/foo/bar") >= 0);
|
||||
assert_se(socket_address_parse(&b, "/foo/bar") >= 0);
|
||||
assert_se(socket_address_equal(&a, &b));
|
||||
|
||||
assert_se(socket_address_parse(&a, "[::1]:8888") >= 0);
|
||||
assert_se(socket_address_parse(&b, "[::1]:8888") >= 0);
|
||||
assert_se(socket_address_equal(&a, &b));
|
||||
|
||||
assert_se(socket_address_parse(&a, "@abstract") >= 0);
|
||||
assert_se(socket_address_parse(&b, "@abstract") >= 0);
|
||||
assert_se(socket_address_equal(&a, &b));
|
||||
|
||||
assert_se(socket_address_parse_netlink(&a, "firewall") >= 0);
|
||||
assert_se(socket_address_parse_netlink(&b, "firewall") >= 0);
|
||||
assert_se(socket_address_equal(&a, &b));
|
||||
|
||||
assert_se(socket_address_parse(&a, "vsock:2:1234") >= 0);
|
||||
assert_se(socket_address_parse(&b, "vsock:2:1234") >= 0);
|
||||
assert_se(socket_address_equal(&a, &b));
|
||||
assert_se(socket_address_parse(&b, "vsock:2:1235") >= 0);
|
||||
assert_se(!socket_address_equal(&a, &b));
|
||||
assert_se(socket_address_parse(&b, "vsock:3:1234") >= 0);
|
||||
assert_se(!socket_address_equal(&a, &b));
|
||||
}
|
||||
|
||||
static void test_socket_address_get_path(void) {
|
||||
SocketAddress a;
|
||||
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
|
||||
assert_se(!socket_address_get_path(&a));
|
||||
|
||||
assert_se(socket_address_parse(&a, "@abstract") >= 0);
|
||||
assert_se(!socket_address_get_path(&a));
|
||||
|
||||
assert_se(socket_address_parse(&a, "[::1]:8888") >= 0);
|
||||
assert_se(!socket_address_get_path(&a));
|
||||
|
||||
assert_se(socket_address_parse(&a, "/foo/bar") >= 0);
|
||||
assert_se(streq(socket_address_get_path(&a), "/foo/bar"));
|
||||
|
||||
assert_se(socket_address_parse(&a, "vsock:2:1234") >= 0);
|
||||
assert_se(!socket_address_get_path(&a));
|
||||
}
|
||||
|
||||
static void test_socket_address_is(void) {
|
||||
SocketAddress a;
|
||||
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
|
||||
assert_se(socket_address_is(&a, "192.168.1.1:8888", SOCK_STREAM));
|
||||
assert_se(!socket_address_is(&a, "route", SOCK_STREAM));
|
||||
assert_se(!socket_address_is(&a, "192.168.1.1:8888", SOCK_RAW));
|
||||
}
|
||||
|
||||
static void test_socket_address_is_netlink(void) {
|
||||
SocketAddress a;
|
||||
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
assert_se(socket_address_parse_netlink(&a, "route 10") >= 0);
|
||||
assert_se(socket_address_is_netlink(&a, "route 10"));
|
||||
assert_se(!socket_address_is_netlink(&a, "192.168.1.1:8888"));
|
||||
assert_se(!socket_address_is_netlink(&a, "route 1"));
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
test_setup_logging(LOG_DEBUG);
|
||||
|
||||
test_socket_address_parse();
|
||||
test_socket_address_parse_netlink();
|
||||
test_socket_address_equal();
|
||||
test_socket_address_get_path();
|
||||
test_socket_address_is();
|
||||
test_socket_address_is_netlink();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -17,11 +17,14 @@
|
||||
#include "macro.h"
|
||||
#include "missing_network.h"
|
||||
#include "process-util.h"
|
||||
#include "socket-netlink.h"
|
||||
#include "socket-util.h"
|
||||
#include "string-util.h"
|
||||
#include "tests.h"
|
||||
#include "tmpfile-util.h"
|
||||
|
||||
assert_cc(SUN_PATH_LEN == 108);
|
||||
|
||||
static void test_ifname_valid(void) {
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
@@ -48,82 +51,6 @@ static void test_ifname_valid(void) {
|
||||
assert(ifname_valid_full("xxxxxxxxxxxxxxxx", true));
|
||||
}
|
||||
|
||||
static void test_socket_address_parse_one(const char *in, int ret, int family, const char *expected) {
|
||||
SocketAddress a;
|
||||
_cleanup_free_ char *out = NULL;
|
||||
int r;
|
||||
|
||||
r = socket_address_parse(&a, in);
|
||||
if (r >= 0)
|
||||
assert_se(socket_address_print(&a, &out) >= 0);
|
||||
|
||||
log_info("\"%s\" → %s → \"%s\" (expect \"%s\")", in,
|
||||
r >= 0 ? "✓" : "✗", empty_to_dash(out), r >= 0 ? expected ?: in : "-");
|
||||
assert_se(r == ret);
|
||||
if (r >= 0) {
|
||||
assert_se(a.sockaddr.sa.sa_family == family);
|
||||
assert_se(streq(out, expected ?: in));
|
||||
}
|
||||
}
|
||||
|
||||
#define SUN_PATH_LEN (sizeof(((struct sockaddr_un){}).sun_path))
|
||||
assert_cc(SUN_PATH_LEN == 108);
|
||||
|
||||
static void test_socket_address_parse(void) {
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
test_socket_address_parse_one("junk", -EINVAL, 0, NULL);
|
||||
test_socket_address_parse_one("192.168.1.1", -EINVAL, 0, NULL);
|
||||
test_socket_address_parse_one(".168.1.1", -EINVAL, 0, NULL);
|
||||
test_socket_address_parse_one("989.168.1.1", -EINVAL, 0, NULL);
|
||||
test_socket_address_parse_one("192.168.1.1:65536", -ERANGE, 0, NULL);
|
||||
test_socket_address_parse_one("192.168.1.1:0", -EINVAL, 0, NULL);
|
||||
test_socket_address_parse_one("0", -EINVAL, 0, NULL);
|
||||
test_socket_address_parse_one("65536", -ERANGE, 0, NULL);
|
||||
|
||||
const int default_family = socket_ipv6_is_supported() ? AF_INET6 : AF_INET;
|
||||
|
||||
test_socket_address_parse_one("65535", 0, default_family, "[::]:65535");
|
||||
|
||||
/* The checks below will pass even if ipv6 is disabled in
|
||||
* kernel. The underlying glibc's inet_pton() is just a string
|
||||
* parser and doesn't make any syscalls. */
|
||||
|
||||
test_socket_address_parse_one("[::1]", -EINVAL, 0, NULL);
|
||||
test_socket_address_parse_one("[::1]8888", -EINVAL, 0, NULL);
|
||||
test_socket_address_parse_one("::1", -EINVAL, 0, NULL);
|
||||
test_socket_address_parse_one("[::1]:0", -EINVAL, 0, NULL);
|
||||
test_socket_address_parse_one("[::1]:65536", -ERANGE, 0, NULL);
|
||||
test_socket_address_parse_one("[a:b:1]:8888", -EINVAL, 0, NULL);
|
||||
|
||||
test_socket_address_parse_one("8888", 0, default_family, "[::]:8888");
|
||||
test_socket_address_parse_one("[2001:0db8:0000:85a3:0000:0000:ac1f:8001]:8888", 0, AF_INET6,
|
||||
"[2001:db8:0:85a3::ac1f:8001]:8888");
|
||||
test_socket_address_parse_one("[::1]:8888", 0, AF_INET6, NULL);
|
||||
test_socket_address_parse_one("192.168.1.254:8888", 0, AF_INET, NULL);
|
||||
test_socket_address_parse_one("/foo/bar", 0, AF_UNIX, NULL);
|
||||
test_socket_address_parse_one("/", 0, AF_UNIX, NULL);
|
||||
test_socket_address_parse_one("@abstract", 0, AF_UNIX, NULL);
|
||||
|
||||
{
|
||||
char aaa[SUN_PATH_LEN + 1] = "@";
|
||||
|
||||
memset(aaa + 1, 'a', SUN_PATH_LEN - 1);
|
||||
char_array_0(aaa);
|
||||
|
||||
test_socket_address_parse_one(aaa, -EINVAL, 0, NULL);
|
||||
|
||||
aaa[SUN_PATH_LEN - 1] = '\0';
|
||||
test_socket_address_parse_one(aaa, 0, AF_UNIX, NULL);
|
||||
}
|
||||
|
||||
test_socket_address_parse_one("vsock:2:1234", 0, AF_VSOCK, NULL);
|
||||
test_socket_address_parse_one("vsock::1234", 0, AF_VSOCK, NULL);
|
||||
test_socket_address_parse_one("vsock:2:1234x", -EINVAL, 0, NULL);
|
||||
test_socket_address_parse_one("vsock:2x:1234", -EINVAL, 0, NULL);
|
||||
test_socket_address_parse_one("vsock:2", -EINVAL, 0, NULL);
|
||||
}
|
||||
|
||||
static void test_socket_print_unix_one(const char *in, size_t len_in, const char *expected) {
|
||||
_cleanup_free_ char *out = NULL, *c = NULL;
|
||||
|
||||
@@ -157,141 +84,6 @@ static void test_socket_print_unix(void) {
|
||||
test_socket_print_unix_one("\0\a\b\n\255", 6, "@\\a\\b\\n\\255\\000");
|
||||
}
|
||||
|
||||
static void test_socket_address_parse_netlink(void) {
|
||||
SocketAddress a;
|
||||
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
assert_se(socket_address_parse_netlink(&a, "junk") < 0);
|
||||
assert_se(socket_address_parse_netlink(&a, "") < 0);
|
||||
|
||||
assert_se(socket_address_parse_netlink(&a, "route") >= 0);
|
||||
assert_se(a.sockaddr.nl.nl_family == AF_NETLINK);
|
||||
assert_se(a.sockaddr.nl.nl_groups == 0);
|
||||
assert_se(a.protocol == NETLINK_ROUTE);
|
||||
assert_se(socket_address_parse_netlink(&a, "route") >= 0);
|
||||
assert_se(socket_address_parse_netlink(&a, "route 10") >= 0);
|
||||
assert_se(a.sockaddr.nl.nl_family == AF_NETLINK);
|
||||
assert_se(a.sockaddr.nl.nl_groups == 10);
|
||||
assert_se(a.protocol == NETLINK_ROUTE);
|
||||
|
||||
/* With spaces and tabs */
|
||||
assert_se(socket_address_parse_netlink(&a, " kobject-uevent ") >= 0);
|
||||
assert_se(a.sockaddr.nl.nl_family == AF_NETLINK);
|
||||
assert_se(a.sockaddr.nl.nl_groups == 0);
|
||||
assert_se(a.protocol == NETLINK_KOBJECT_UEVENT);
|
||||
assert_se(socket_address_parse_netlink(&a, " \t kobject-uevent \t 10") >= 0);
|
||||
assert_se(a.sockaddr.nl.nl_family == AF_NETLINK);
|
||||
assert_se(a.sockaddr.nl.nl_groups == 10);
|
||||
assert_se(a.protocol == NETLINK_KOBJECT_UEVENT);
|
||||
assert_se(socket_address_parse_netlink(&a, "kobject-uevent\t10") >= 0);
|
||||
assert_se(a.sockaddr.nl.nl_family == AF_NETLINK);
|
||||
assert_se(a.sockaddr.nl.nl_groups == 10);
|
||||
assert_se(a.protocol == NETLINK_KOBJECT_UEVENT);
|
||||
|
||||
/* trailing space is not supported */
|
||||
assert_se(socket_address_parse_netlink(&a, "kobject-uevent\t10 ") < 0);
|
||||
|
||||
/* Group must be unsigned */
|
||||
assert_se(socket_address_parse_netlink(&a, "kobject-uevent -1") < 0);
|
||||
|
||||
/* oss-fuzz #6884 */
|
||||
assert_se(socket_address_parse_netlink(&a, "\xff") < 0);
|
||||
}
|
||||
|
||||
static void test_socket_address_equal(void) {
|
||||
SocketAddress a, b;
|
||||
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
|
||||
assert_se(socket_address_parse(&b, "192.168.1.1:888") >= 0);
|
||||
assert_se(!socket_address_equal(&a, &b));
|
||||
|
||||
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
|
||||
assert_se(socket_address_parse(&b, "192.16.1.1:8888") >= 0);
|
||||
assert_se(!socket_address_equal(&a, &b));
|
||||
|
||||
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
|
||||
assert_se(socket_address_parse(&b, "8888") >= 0);
|
||||
assert_se(!socket_address_equal(&a, &b));
|
||||
|
||||
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
|
||||
assert_se(socket_address_parse(&b, "/foo/bar/") >= 0);
|
||||
assert_se(!socket_address_equal(&a, &b));
|
||||
|
||||
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
|
||||
assert_se(socket_address_parse(&b, "192.168.1.1:8888") >= 0);
|
||||
assert_se(socket_address_equal(&a, &b));
|
||||
|
||||
assert_se(socket_address_parse(&a, "/foo/bar") >= 0);
|
||||
assert_se(socket_address_parse(&b, "/foo/bar") >= 0);
|
||||
assert_se(socket_address_equal(&a, &b));
|
||||
|
||||
assert_se(socket_address_parse(&a, "[::1]:8888") >= 0);
|
||||
assert_se(socket_address_parse(&b, "[::1]:8888") >= 0);
|
||||
assert_se(socket_address_equal(&a, &b));
|
||||
|
||||
assert_se(socket_address_parse(&a, "@abstract") >= 0);
|
||||
assert_se(socket_address_parse(&b, "@abstract") >= 0);
|
||||
assert_se(socket_address_equal(&a, &b));
|
||||
|
||||
assert_se(socket_address_parse_netlink(&a, "firewall") >= 0);
|
||||
assert_se(socket_address_parse_netlink(&b, "firewall") >= 0);
|
||||
assert_se(socket_address_equal(&a, &b));
|
||||
|
||||
assert_se(socket_address_parse(&a, "vsock:2:1234") >= 0);
|
||||
assert_se(socket_address_parse(&b, "vsock:2:1234") >= 0);
|
||||
assert_se(socket_address_equal(&a, &b));
|
||||
assert_se(socket_address_parse(&b, "vsock:2:1235") >= 0);
|
||||
assert_se(!socket_address_equal(&a, &b));
|
||||
assert_se(socket_address_parse(&b, "vsock:3:1234") >= 0);
|
||||
assert_se(!socket_address_equal(&a, &b));
|
||||
}
|
||||
|
||||
static void test_socket_address_get_path(void) {
|
||||
SocketAddress a;
|
||||
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
|
||||
assert_se(!socket_address_get_path(&a));
|
||||
|
||||
assert_se(socket_address_parse(&a, "@abstract") >= 0);
|
||||
assert_se(!socket_address_get_path(&a));
|
||||
|
||||
assert_se(socket_address_parse(&a, "[::1]:8888") >= 0);
|
||||
assert_se(!socket_address_get_path(&a));
|
||||
|
||||
assert_se(socket_address_parse(&a, "/foo/bar") >= 0);
|
||||
assert_se(streq(socket_address_get_path(&a), "/foo/bar"));
|
||||
|
||||
assert_se(socket_address_parse(&a, "vsock:2:1234") >= 0);
|
||||
assert_se(!socket_address_get_path(&a));
|
||||
}
|
||||
|
||||
static void test_socket_address_is(void) {
|
||||
SocketAddress a;
|
||||
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
|
||||
assert_se(socket_address_is(&a, "192.168.1.1:8888", SOCK_STREAM));
|
||||
assert_se(!socket_address_is(&a, "route", SOCK_STREAM));
|
||||
assert_se(!socket_address_is(&a, "192.168.1.1:8888", SOCK_RAW));
|
||||
}
|
||||
|
||||
static void test_socket_address_is_netlink(void) {
|
||||
SocketAddress a;
|
||||
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
assert_se(socket_address_parse_netlink(&a, "route 10") >= 0);
|
||||
assert_se(socket_address_is_netlink(&a, "route 10"));
|
||||
assert_se(!socket_address_is_netlink(&a, "192.168.1.1:8888"));
|
||||
assert_se(!socket_address_is_netlink(&a, "route 1"));
|
||||
}
|
||||
|
||||
static void test_in_addr_is_null(void) {
|
||||
union in_addr_union i = {};
|
||||
|
||||
@@ -876,13 +668,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
test_ifname_valid();
|
||||
|
||||
test_socket_address_parse();
|
||||
test_socket_print_unix();
|
||||
test_socket_address_parse_netlink();
|
||||
test_socket_address_equal();
|
||||
test_socket_address_get_path();
|
||||
test_socket_address_is();
|
||||
test_socket_address_is_netlink();
|
||||
|
||||
test_in_addr_is_null();
|
||||
test_in_addr_prefix_intersect();
|
||||
|
||||
Reference in New Issue
Block a user