Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for abstract Unix sockets #14

Merged
merged 1 commit into from
Aug 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`)
Expand Down
10 changes: 10 additions & 0 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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 <<rule-socket-path,*path*>> are also supported
here.
endif::[]

ifndef::without-systemd[]
*systemd*[='FD_NAME']::
Use the socket passed along via file descriptor by systemd instead of
Expand Down
4 changes: 4 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
7 changes: 5 additions & 2 deletions src/blackhole.hh
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#ifndef IP2UNIX_BLACKHOLE_HH
#define IP2UNIX_BLACKHOLE_HH

#include "socketpath.hh"

#include <optional>
#include <string>

Expand All @@ -10,8 +12,9 @@ struct BlackHole
BlackHole();
~BlackHole();

inline std::optional<std::string> get_path() const {
return this->filepath;
inline std::optional<SocketPath> get_path() const {
if (!this->filepath) return std::nullopt;
return SocketPath(SocketPath::Type::FILESYSTEM, *this->filepath);
}

private:
Expand Down
4 changes: 2 additions & 2 deletions src/preload.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down
4 changes: 3 additions & 1 deletion src/rules.hh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

#include "types.hh"

#include "socketpath.hh"

#include <iostream>
#include <optional>
#include <vector>
Expand All @@ -24,7 +26,7 @@ struct Rule {
std::optional<std::string> fd_name = std::nullopt;
#endif

std::optional<std::string> socket_path = std::nullopt;
std::optional<SocketPath> socket_path = std::nullopt;

bool reject = false;
std::optional<int> reject_errno = std::nullopt;
Expand Down
127 changes: 103 additions & 24 deletions src/rules/parse.cc
Original file line number Diff line number Diff line change
Expand Up @@ -57,27 +57,54 @@ static std::optional<std::string> 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)) {
Expand All @@ -97,10 +124,12 @@ static std::optional<std::string> 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
}

Expand Down Expand Up @@ -236,8 +265,23 @@ static std::optional<Rule> 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;
Expand Down Expand Up @@ -339,7 +383,31 @@ std::optional<Rule> 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;
Expand Down Expand Up @@ -512,9 +580,20 @@ void print_rules(std::vector<Rule> &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
}
Expand Down
41 changes: 41 additions & 0 deletions src/serial.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "serial.hh"

#include "rules.hh"
#include "socketpath.hh"
#include "systemd.hh"
#include "types.hh"

Expand Down Expand Up @@ -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);
Expand Down
30 changes: 24 additions & 6 deletions src/sockaddr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,31 @@ void SockAddr::set_family(sa_family_t family)
this->inner_size = family2size(family);
}

std::optional<SockAddr> SockAddr::unix(const std::string &path)
std::optional<SockAddr> 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<const sockaddr*>(&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<const sockaddr*>(&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<const sockaddr*>(&ua));
sa.inner_size = sizeof(sa_family_t) + path.value.size() + 1;
return sa;
#endif
}

return std::nullopt;
}

SockAddr SockAddr::copy() const
Expand Down Expand Up @@ -163,10 +178,13 @@ bool SockAddr::set_random_host(void)
return false;
}

std::optional<std::string> SockAddr::get_sockpath(void) const
std::optional<SocketPath> 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;
Expand Down
6 changes: 4 additions & 2 deletions src/sockaddr.hh
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#ifndef IP2UNIX_SOCKETADDR_HH
#define IP2UNIX_SOCKETADDR_HH

#include "socketpath.hh"

#include <cstddef>
#include <optional>
#include <string>
Expand All @@ -21,7 +23,7 @@ struct SockAddr
SockAddr();
SockAddr(const sockaddr*);

static std::optional<SockAddr> unix(const std::string&);
static std::optional<SockAddr> unix(const SocketPath&);

SockAddr copy(void) const;

Expand All @@ -32,7 +34,7 @@ struct SockAddr

bool set_random_host(void);

std::optional<std::string> get_sockpath(void) const;
std::optional<SocketPath> get_sockpath(void) const;

std::optional<uint16_t> get_port(void) const;
bool set_port(uint16_t);
Expand Down
Loading
Loading