diff --git a/CHANGELOG.md b/CHANGELOG.md index b715c92..4fadce1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ The format is based on [Keep a Changelog], and this project adheres to ### Added - Deprecation warnings if rules are specified in YAML format. - Unlink socket file before `bind` if `SO_REUSEADDR` is used. +- Support for Linux abstract sockets. ### Changed - Rule files (`-f`) are now just a list of newline-separated rule (`-r`) diff --git a/README.adoc b/README.adoc index 8303334..58ba557 100644 --- a/README.adoc +++ b/README.adoc @@ -252,6 +252,16 @@ Placeholders are allowed here and are substituted accordingly: datagram socket) *%%*;; verbatim `%` +ifndef::without-abstract[] +*abstract*='NAME':: + +An abstract namespace Unix domain socket not being created in the file system +but solely distinguished by its name. ++ +The placeholders supported in <> are also supported +here. +endif::[] + ifndef::without-systemd[] *systemd*[='FD_NAME']:: Use the socket passed along via file descriptor by systemd instead of diff --git a/meson.build b/meson.build index 845feb4..bf28827 100644 --- a/meson.build +++ b/meson.build @@ -104,6 +104,10 @@ if not systemd_enabled adoc_attrs += ['-a', 'without-systemd'] endif +if host_machine.system() != 'linux' + adoc_attrs += ['-a', 'without-abstract'] +endif + ################################## MANPAGE ################################## mandest = join_paths(get_option('prefix'), get_option('mandir'), 'man1') diff --git a/src/blackhole.hh b/src/blackhole.hh index 26d92ee..654490d 100644 --- a/src/blackhole.hh +++ b/src/blackhole.hh @@ -2,6 +2,8 @@ #ifndef IP2UNIX_BLACKHOLE_HH #define IP2UNIX_BLACKHOLE_HH +#include "socketpath.hh" + #include #include @@ -10,8 +12,9 @@ struct BlackHole BlackHole(); ~BlackHole(); - inline std::optional get_path() const { - return this->filepath; + inline std::optional get_path() const { + if (!this->filepath) return std::nullopt; + return SocketPath(SocketPath::Type::FILESYSTEM, *this->filepath); } private: diff --git a/src/preload.cc b/src/preload.cc index 75d4a25..17c5b93 100644 --- a/src/preload.cc +++ b/src/preload.cc @@ -258,7 +258,7 @@ static inline int bind_connect(SockFun &&sockfun, RealFun &&realfun, if (rule->second.blackhole) { sock->blackhole(); - return std::invoke(sockfun, sock, inaddr, ""); + return std::invoke(sockfun, sock, inaddr, SocketPath()); } #ifdef SYSTEMD_SUPPORT @@ -272,7 +272,7 @@ static inline int bind_connect(SockFun &&sockfun, RealFun &&realfun, LOG(WARNING) << "Systemd file descriptor queue empty, " << "blackholing socket with fd " << fd << '.'; sock->blackhole(); - return std::invoke(sockfun, sock, inaddr, ""); + return std::invoke(sockfun, sock, inaddr, SocketPath()); } #endif diff --git a/src/rules.hh b/src/rules.hh index 6d17442..c1459fa 100644 --- a/src/rules.hh +++ b/src/rules.hh @@ -4,6 +4,8 @@ #include "types.hh" +#include "socketpath.hh" + #include #include #include @@ -24,7 +26,7 @@ struct Rule { std::optional fd_name = std::nullopt; #endif - std::optional socket_path = std::nullopt; + std::optional socket_path = std::nullopt; bool reject = false; std::optional reject_errno = std::nullopt; diff --git a/src/rules/parse.cc b/src/rules/parse.cc index e05d27e..a5f5786 100644 --- a/src/rules/parse.cc +++ b/src/rules/parse.cc @@ -57,27 +57,54 @@ static std::optional validate_rule(Rule &rule) } if (rule.socket_path) { - if (rule.socket_path.value().empty()) - return "Socket path has to be non-empty."; - if (rule.socket_path.value()[0] != '/') - return "Socket path has to be absolute."; + if (rule.socket_path->type == SocketPath::Type::FILESYSTEM) { + const std::string path = rule.socket_path->value; + + if (path.empty()) + return "Socket path has to be non-empty."; + if (path[0] != '/') + return "Socket path has to be absolute."; + +#ifdef SYSTEMD_SUPPORT + if (rule.socket_activation) + return "Can't enable socket activation in conjunction with a" + " socket path."; +#endif + if (rule.reject) + return "Using a reject action in conjuction with a socket" + " path is not allowed."; + + if (rule.ignore) + return "Using an ignore action in conjuction with a socket" + " path is not allowed."; + + if (rule.blackhole) + return "Using a blackhole action in conjuction with a socket" + " path is not allowed."; +#if defined(__linux__) + } else if (rule.socket_path->type == SocketPath::Type::ABSTRACT) { + if (rule.socket_path->value.empty()) + return "Abstract socket name has to be non-empty."; #ifdef SYSTEMD_SUPPORT - if (rule.socket_activation) - return "Can't enable socket activation in conjunction with a" - " socket path."; + if (rule.socket_activation) + return "Can't enable socket activation in conjunction with an" + " abstract socket name."; #endif - if (rule.reject) - return "Using a reject action in conjuction with a socket" - " path is not allowed."; - if (rule.ignore) - return "Using an ignore action in conjuction with a socket" - " path is not allowed."; + if (rule.reject) + return "Using a reject action in conjuction with an abstract" + " socket name is not allowed."; + + if (rule.ignore) + return "Using an ignore action in conjuction with an abstract" + " socket name is not allowed."; - if (rule.blackhole) - return "Using a blackhole action in conjuction with a socket" - " path is not allowed."; + if (rule.blackhole) + return "Using a blackhole action in conjuction with an" + " abstract socket name is not allowed."; +#endif + } } else if (rule.reject && rule.blackhole) { return "Reject and blackhole actions are mutually exclusive."; } else if (rule.ignore && (rule.blackhole || rule.reject)) { @@ -97,10 +124,12 @@ static std::optional validate_rule(Rule &rule) #ifdef SYSTEMD_SUPPORT } else if (!rule.socket_activation) { return "Socket activation is disabled and no socket" - " path, reject, ignore or blackhole action was specified."; + " path, abstract name, reject, ignore or blackhole action was" + " specified."; #else } else { - return "No socket path, reject, ignore or blackhole action specified."; + return "No socket path, abstract name, reject, ignore or blackhole" + " action specified."; #endif } @@ -236,8 +265,23 @@ static std::optional parse_rule(const std::string &file, int pos, } else if (key == "ignore") { RULE_CONVERT(rule.ignore, "ignore", bool, "bool"); } else if (key == "socketPath") { - RULE_CONVERT(rule.socket_path, "socketPath", std::string, - "string"); + if (rule.socket_path) { + RULE_ERROR("Socket path or abstract name already specified."); + return std::nullopt; + } + std::string path; + RULE_CONVERT(path, "socketPath", std::string, "string"); + rule.socket_path = SocketPath(SocketPath::Type::FILESYSTEM, path); +#if defined(__linux__) + } else if (key == "abstract") { + if (rule.socket_path) { + RULE_ERROR("Socket path or abstract name already specified."); + return std::nullopt; + } + std::string name; + RULE_CONVERT(name, "abstract", std::string, "string"); + rule.socket_path = SocketPath(SocketPath::Type::ABSTRACT, name); +#endif } else { RULE_ERROR("Invalid key \"" << key << "\"."); return std::nullopt; @@ -339,7 +383,31 @@ std::optional parse_rule_arg(size_t rulepos, const std::string &arg) if (i == arglen || arg[i] == ',') { /* Handle key=value options. */ if (key.value() == "path") { - rule.socket_path = std::filesystem::absolute(buf); + if (rule.socket_path) { + print_arg_error( + rulepos, arg, errpos, errlen, + "socket path or abstract name specified earlier" + ); + return std::nullopt; + } + rule.socket_path = SocketPath( + SocketPath::Type::FILESYSTEM, + std::filesystem::absolute(buf) + ); +#if defined(__linux__) + } else if (key.value() == "abstract") { + if (rule.socket_path) { + print_arg_error( + rulepos, arg, errpos, errlen, + "socket path or abstract name specified earlier" + ); + return std::nullopt; + } + rule.socket_path = SocketPath( + SocketPath::Type::ABSTRACT, + buf + ); +#endif #ifdef SYSTEMD_SUPPORT } else if (key.value() == "systemd") { rule.socket_activation = true; @@ -512,9 +580,20 @@ void print_rules(std::vector &rules, std::ostream &out) out << " Blackhole the socket." << std::endl; } else if (rule.ignore) { out << " Don't handle this socket." << std::endl; - } else { - out << " Socket path: " << rule.socket_path.value() - << std::endl; + } else if (rule.socket_path) { +#if defined(__linux__) + if (rule.socket_path->type == SocketPath::Type::ABSTRACT) { + out << " Abstract name: " + << rule.socket_path->value + << std::endl; + } else { +#endif + out << " Socket path: " + << rule.socket_path->value + << std::endl; +#if defined(__linux__) + } +#endif } #ifdef SYSTEMD_SUPPORT } diff --git a/src/serial.cc b/src/serial.cc index 462fbf2..6771b0c 100644 --- a/src/serial.cc +++ b/src/serial.cc @@ -2,6 +2,7 @@ #include "serial.hh" #include "rules.hh" +#include "socketpath.hh" #include "systemd.hh" #include "types.hh" @@ -157,6 +158,46 @@ MaybeError deserialise(std::istream &in, SocketType *out) return std::nullopt; } +void serialise(const SocketPath &path, std::ostream &out) +{ + switch (path.type) { + case SocketPath::Type::FILESYSTEM: + out.put('f'); + break; + case SocketPath::Type::ABSTRACT: + out.put('a'); + break; + } + + serialise(path.value, out); +} + +MaybeError deserialise(std::istream &in, SocketPath *out) +{ + char c; + + in.get(c); + + if (in.eof()) + return "End of stream while reading socket path type."; + + switch (c) { + case 'f': + out->type = SocketPath::Type::FILESYSTEM; + break; +#if defined(__linux__) + case 'a': + out->type = SocketPath::Type::ABSTRACT; + break; +#endif + default: + return std::string("Invalid character '") + + c + "' used as socket path type."; + } + + return deserialise(in, &out->value); +} + void serialise(const Rule &rule, std::ostream &out) { serialise(rule.direction, out); diff --git a/src/sockaddr.cc b/src/sockaddr.cc index 76e7fba..9d94d5b 100644 --- a/src/sockaddr.cc +++ b/src/sockaddr.cc @@ -40,16 +40,31 @@ void SockAddr::set_family(sa_family_t family) this->inner_size = family2size(family); } -std::optional SockAddr::unix(const std::string &path) +std::optional SockAddr::unix(const SocketPath &path) { struct sockaddr_un ua; memset(&ua, 0, sizeof ua); ua.sun_family = AF_UNIX; - if (path.size() >= sizeof(ua.sun_path)) + + if (path.value.size() >= sizeof(ua.sun_path)) return std::nullopt; - strncpy(ua.sun_path, path.c_str(), sizeof(ua.sun_path) - 1); - return SockAddr(reinterpret_cast(&ua)); + switch (path.type) { + case SocketPath::Type::FILESYSTEM: + strncpy(ua.sun_path, path.value.c_str(), sizeof(ua.sun_path) - 1); + return SockAddr(reinterpret_cast(&ua)); +#if defined(__linux__) + case SocketPath::Type::ABSTRACT: + ua.sun_path[0] = '\0'; + memcpy(ua.sun_path + 1, path.value.c_str(), path.value.size()); + + SockAddr sa(reinterpret_cast(&ua)); + sa.inner_size = sizeof(sa_family_t) + path.value.size() + 1; + return sa; +#endif + } + + return std::nullopt; } SockAddr SockAddr::copy() const @@ -163,10 +178,13 @@ bool SockAddr::set_random_host(void) return false; } -std::optional SockAddr::get_sockpath(void) const +std::optional SockAddr::get_sockpath(void) const { if (this->is_unix()) { - return std::string(this->cast_un()->sun_path); + return SocketPath( + SocketPath::Type::FILESYSTEM, + std::string(this->cast_un()->sun_path) + ); } return std::nullopt; diff --git a/src/sockaddr.hh b/src/sockaddr.hh index 753fb90..9717ccc 100644 --- a/src/sockaddr.hh +++ b/src/sockaddr.hh @@ -2,6 +2,8 @@ #ifndef IP2UNIX_SOCKETADDR_HH #define IP2UNIX_SOCKETADDR_HH +#include "socketpath.hh" + #include #include #include @@ -21,7 +23,7 @@ struct SockAddr SockAddr(); SockAddr(const sockaddr*); - static std::optional unix(const std::string&); + static std::optional unix(const SocketPath&); SockAddr copy(void) const; @@ -32,7 +34,7 @@ struct SockAddr bool set_random_host(void); - std::optional get_sockpath(void) const; + std::optional get_sockpath(void) const; std::optional get_port(void) const; bool set_port(uint16_t); diff --git a/src/socket.cc b/src/socket.cc index 7858553..e95ed80 100644 --- a/src/socket.cc +++ b/src/socket.cc @@ -24,7 +24,7 @@ std::optional Socket::find(int fd) return found->second; } -bool Socket::has_sockpath(const std::string &path) +bool Socket::has_sockpath(const SocketPath &path) { using itype = decltype(Socket::sockpath_registry)::const_iterator; itype found = Socket::sockpath_registry.find(path); @@ -56,7 +56,7 @@ static inline SocketType get_sotype(const int type) std::mutex Socket::registry_mutex; std::unordered_map Socket::registry; -std::unordered_set Socket::sockpath_registry; +std::unordered_set Socket::sockpath_registry; Socket::Socket(int sfd, int sdomain, int stype, int sproto) : type(get_sotype(stype)) @@ -174,15 +174,15 @@ int Socket::listen(int backlog) const /* * Replace placeholders such as %p or %a accordingly in the socket path. */ -std::string Socket::format_sockpath(const std::string &path, - const SockAddr &addr) const +SocketPath Socket::format_sockpath(const SocketPath &path, + const SockAddr &addr) const { std::string out; - size_t path_len = path.size(); + size_t path_len = path.value.size(); for (size_t i = 0; i < path_len; ++i) { - if (path[i] == '%' && i + 1 < path_len) { - switch (path[i + 1]) { + if (path.value[i] == '%' && i + 1 < path_len) { + switch (path.value[i + 1]) { case '%': out += '%'; i++; continue; case 'a': out += addr.get_host().value_or("unknown"); i++; continue; @@ -198,10 +198,10 @@ std::string Socket::format_sockpath(const std::string &path, continue; } } - out += path[i]; + out += path.value[i]; } - return out; + return SocketPath(path.type, out); } /* @@ -322,7 +322,7 @@ int Socket::activate(const SockAddr &addr, int filedes, bool is_inet) #define USOCK_OR_EFAULT(path) \ DO_USOCK_OR_FAIL(path, { errno = EFAULT; return -1; }) -int Socket::bind(const SockAddr &addr, const std::string &path) +int Socket::bind(const SockAddr &addr, const SocketPath &path) { if (!this->make_unix()) return -1; @@ -339,7 +339,7 @@ int Socket::bind(const SockAddr &addr, const std::string &path) port = anyport; } - std::string newpath = this->format_sockpath(path, newaddr); + SocketPath newpath = this->format_sockpath(path, newaddr); int ret; @@ -347,7 +347,7 @@ int Socket::bind(const SockAddr &addr, const std::string &path) // exact same path, let's blackhole the current socket. if (this->is_blackhole || Socket::has_sockpath(newpath)) { BlackHole bh; - std::optional bh_path = bh.get_path(); + std::optional bh_path = bh.get_path(); if (!bh_path) return -1; USOCK_OR_EFAULT(bh_path.value()); @@ -355,13 +355,14 @@ int Socket::bind(const SockAddr &addr, const std::string &path) if (ret == 0) this->blackhole(); } else { - if (this->reuse_addr) - unlink(newpath.c_str()); + if (this->reuse_addr && newpath.is_real_file()) + unlink(newpath.value.c_str()); USOCK_OR_EFAULT(newpath); ret = real::bind(this->fd, dest.cast(), dest.size()); if (ret == 0) { Socket::sockpath_registry.insert(newpath); - this->unlink_sockpath = newpath; + if (newpath.is_real_file()) + this->unlink_sockpath = newpath.value; } } @@ -388,7 +389,7 @@ std::optional Socket::connect_peermap(const SockAddr &addr) return std::nullopt; } -int Socket::connect(const SockAddr &addr, const std::string &path) +int Socket::connect(const SockAddr &addr, const SocketPath &path) { if (this->type == SocketType::UDP && !this->binding) { /* If we connect without prior binding on a datagram socket, we need to @@ -407,7 +408,7 @@ int Socket::connect(const SockAddr &addr, const std::string &path) return ret; } - std::string new_sockpath = this->format_sockpath(path, addr); + SocketPath new_sockpath = this->format_sockpath(path, addr); USOCK_OR_EFAULT(new_sockpath); if (!this->make_unix()) @@ -523,7 +524,7 @@ bool Socket::rewrite_src(const SockAddr &real_addr, struct sockaddr *addr, if (!this->binding) return true; - std::optional path = real_addr.get_sockpath(); + std::optional path = real_addr.get_sockpath(); if (!path) return true; @@ -566,7 +567,7 @@ Socket::rewrite_dest_peermap(const SockAddr &addr) const /* Rewrite address provided by sendto/sendmsg. */ std::optional Socket::rewrite_dest(const SockAddr &addr, - const std::string &path) + const SocketPath &path) { if (this->type != SocketType::UDP) return std::nullopt; @@ -588,7 +589,7 @@ std::optional Socket::rewrite_dest(const SockAddr &addr, */ if (!this->binding) { std::unique_ptr bh = std::make_unique(); - std::optional bh_path = bh->get_path(); + std::optional bh_path = bh->get_path(); if (!bh_path) return std::nullopt; USOCK_OR_FAIL(bh_path.value(), std::nullopt); @@ -654,7 +655,9 @@ int Socket::close(void) << "'."; unlink(this->unlink_sockpath.value().c_str()); errno = old_errno; - Socket::sockpath_registry.erase(this->unlink_sockpath.value()); + Socket::sockpath_registry.erase(SocketPath( + SocketPath::Type::FILESYSTEM, this->unlink_sockpath.value() + )); this->unlink_sockpath = std::nullopt; } } diff --git a/src/socket.hh b/src/socket.hh index f456803..f6795c2 100644 --- a/src/socket.hh +++ b/src/socket.hh @@ -5,6 +5,7 @@ #include "blackhole.hh" #include "dynports.hh" #include "sockaddr.hh" +#include "socketpath.hh" #include "sockopts.hh" #include "types.hh" @@ -67,9 +68,9 @@ struct Socket : std::enable_shared_from_this int activate(const SockAddr&, int, bool); #endif - int bind(const SockAddr&, const std::string&); + int bind(const SockAddr&, const SocketPath&); std::optional connect_peermap(const SockAddr&); - int connect(const SockAddr&, const std::string&); + int connect(const SockAddr&, const SocketPath&); int accept(int, sockaddr*, socklen_t*); int getsockname(sockaddr*, socklen_t*); @@ -77,7 +78,7 @@ struct Socket : std::enable_shared_from_this bool rewrite_src(const SockAddr&, sockaddr*, socklen_t*); std::optional rewrite_dest_peermap(const SockAddr&) const; - std::optional rewrite_dest(const SockAddr&, const std::string&); + std::optional rewrite_dest(const SockAddr&, const SocketPath&); int dup(void); int dup(int, int); @@ -102,8 +103,8 @@ struct Socket : std::enable_shared_from_this * and look them up either in the next recvfrom/recvmsg or in a * connect(). */ - std::unordered_map peermap; - std::unordered_map revpeermap; + std::unordered_map peermap; + std::unordered_map revpeermap; /* Constructor and reference getter. */ Socket(int, int, int, int); @@ -116,13 +117,13 @@ struct Socket : std::enable_shared_from_this static std::optional find(int); /* Check if a socket path is registered. */ - static bool has_sockpath(const std::string&); + static bool has_sockpath(const SocketPath&); /* All INET/INET6 sockets are registered here. */ static std::unordered_map registry; /* Mapping from bound socket paths to sockets. */ - static std::unordered_set sockpath_registry; + static std::unordered_set sockpath_registry; /* Whether the socket has been converted to an AF_UNIX socket. */ bool is_unix = false; @@ -141,7 +142,7 @@ struct Socket : std::enable_shared_from_this bool make_unix(int = -1); bool create_binding(const SockAddr&); - std::string format_sockpath(const std::string&, const SockAddr&) const; + SocketPath format_sockpath(const SocketPath&, const SockAddr&) const; }; #endif diff --git a/src/socketpath.hh b/src/socketpath.hh new file mode 100644 index 0000000..8760f49 --- /dev/null +++ b/src/socketpath.hh @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: LGPL-3.0-only +#ifndef IP2UNIX_SOCKPATH_HH +#define IP2UNIX_SOCKPATH_HH + +#include + +struct SocketPath { + enum class Type { ABSTRACT, FILESYSTEM }; + + inline SocketPath() : type(Type::FILESYSTEM), value() {} + inline SocketPath(Type t, const std::string &v) : type(t), value(v) {} + + inline bool operator==(const SocketPath &other) const { + return this->type == other.type && this->value == other.value; + } + + inline bool operator!=(const SocketPath &other) const { + return this->type != other.type || this->value != other.value; + } + + inline bool is_real_file() const { + return this->type == Type::FILESYSTEM; + } + + Type type; + std::string value; +}; + +namespace std { + template<> struct hash { + std::size_t operator()(const SocketPath &addr) const { + return std::hash()(addr.value); // TODO + } + }; +} + +#endif diff --git a/tests/helper.py b/tests/helper.py index b4d3c3c..301c3fe 100644 --- a/tests/helper.py +++ b/tests/helper.py @@ -1,4 +1,5 @@ import json +import sys import subprocess from contextlib import contextmanager @@ -8,7 +9,7 @@ __all__ = ['IP2UNIX', 'LIBIP2UNIX', 'SYSTEMD_SUPPORT', 'SYSTEMD_SA_PATH', 'ip2unix', 'systemd_only', 'non_systemd_only', - 'systemd_sa_helper_only'] + 'systemd_sa_helper_only', 'abstract_sockets_only'] @contextmanager @@ -30,3 +31,7 @@ def ip2unix(rules, childargs, *args, **kwargs): systemd_sa_helper_only = pytest.mark.skipif( SYSTEMD_SA_PATH is None, reason="no 'systemd-socket-activate' helper" ) +abstract_sockets_only = pytest.mark.skipif( + sys.platform != 'linux', + reason='abstract sockets are only supported on Linux' +) diff --git a/tests/test_abstract_socket.py b/tests/test_abstract_socket.py new file mode 100644 index 0000000..d5c3da0 --- /dev/null +++ b/tests/test_abstract_socket.py @@ -0,0 +1,30 @@ +import socket +import subprocess +import sys + +from helper import abstract_sockets_only, IP2UNIX + +TESTPROG = r''' +import socket + +with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + sock.connect(('4.3.2.1', 4321)) + sock.sendall(b'hello') + assert sock.recv(5) == b'world' +''' + + +@abstract_sockets_only +def test_abstcact_socket(): + with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock: + sock.bind('\0abstest') + sock.listen(10) + + cmd = [IP2UNIX] + cmd += ['-r', 'addr=4.3.2.1,abstract=abstest'] + cmd += [sys.executable, '-c', TESTPROG] + + with subprocess.Popen(cmd, stdout=subprocess.PIPE), \ + sock.accept()[0] as conn: + assert conn.recv(5) == b'hello' + conn.sendall(b'world') diff --git a/tests/test_connections.py b/tests/test_connections.py index f7778f6..f975ae2 100644 --- a/tests/test_connections.py +++ b/tests/test_connections.py @@ -135,6 +135,13 @@ def test_socket_activation_with_fdname(self): finally: os.unlink(extrasock) + @helper.abstract_sockets_only + def test_abstract_socket(self): + srule = {'direction': 'incoming', 'abstract': 'a%p'} + crule = {'direction': 'outgoing', 'abstract': 'a%p'} + with self.run_server([srule], '1.2.3.4', 929): + self.assert_client([crule], '1.2.3.0', 929) + class UdpConnectionTest(TcpConnectionTest): SOTYPE = 'udp' diff --git a/tests/test_rule_file.py b/tests/test_rule_file.py index 32bb9cb..c41a7a8 100644 --- a/tests/test_rule_file.py +++ b/tests/test_rule_file.py @@ -5,7 +5,8 @@ from tempfile import NamedTemporaryFile -from helper import IP2UNIX, systemd_only, non_systemd_only +from helper import IP2UNIX, systemd_only, non_systemd_only, \ + abstract_sockets_only class RuleFileTest(unittest.TestCase): @@ -70,6 +71,18 @@ def test_relative_socket_path(self): def test_absolute_socket_path(self): self.assert_good_rules([{'socketPath': '/xxx'}]) + @abstract_sockets_only + def test_valid_abstract_name(self): + self.assert_good_rules([{'abstract': 'foobar'}]) + + @abstract_sockets_only + def test_invalid_abstract_name(self): + self.assert_bad_rules([{'abstract': ''}]) + + @abstract_sockets_only + def test_abstract_and_path(self): + self.assert_bad_rules([{'abstract': 'xxx', 'socketPath': '/xxx'}]) + def test_invalid_enums(self): self.assert_bad_rules([{'socketPath': '/bbb', 'direction': 111}]) self.assert_bad_rules([{'socketPath': '/bbb', 'direction': False}]) diff --git a/tests/unit/serial.cc b/tests/unit/serial.cc index debc0f6..0946508 100644 --- a/tests/unit/serial.cc +++ b/tests/unit/serial.cc @@ -22,6 +22,13 @@ static std::vector> sotypes = { SocketType::INVALID }; +static std::vector socketpathtypes = { +#if defined(__linux__) + SocketPath::Type::ABSTRACT, +#endif + SocketPath::Type::FILESYSTEM +}; + static std::vector> strings = { std::nullopt, "", @@ -80,6 +87,19 @@ std::string pprint(const SocketType &type) { throw std::runtime_error("Invalid SocketType value"); } +std::string pprint(const SocketPath &socket_path) { + switch (socket_path.type) { + case SocketPath::Type::FILESYSTEM: + return socket_path.value; +#if defined(__linux__) + case SocketPath::Type::ABSTRACT: + return std::string("@") + socket_path.value; +#endif + } + + throw std::runtime_error("Invalid SocketPath value"); +} + template std::string pprint(const std::optional &x) { @@ -136,7 +156,11 @@ static unsigned long test_rule(unsigned long seed) rule.socket_activation = CHOOSE(bools); rule.fd_name = CHOOSE(strings); #endif - rule.socket_path = CHOOSE(strings); + std::optional value = CHOOSE(strings); + if (value) { + SocketPath::Type socketpathtype = CHOOSE(socketpathtypes); + rule.socket_path = SocketPath(socketpathtype, *value); + } rule.reject = CHOOSE(bools); rule.reject_errno = CHOOSE(ints); rule.blackhole = CHOOSE(bools);