Skip to content

Commit

Permalink
rules: Split Rule struct into matches and action
Browse files Browse the repository at this point in the history
In 242c78d, we improved the README file
by more clearly distinguishing between matches and an action of a rule.

However, the code did not yet reflect this and all rule arguments were
thrown together into one single struct without this important
distinction.

It makes sense to actually model our data structures in the same terms
we use in our documentation, so this is only a start of that.

The reason why I'm using the plural "matches" is because we can have
several of them, while onle one action is possible.

Right now, the Rule.action field is still a struct, but ideally this
should be an std::variant so that we can enforce this constraint at the
type level.

Since std::variant and particularly std::visit is very gruesome compared
to eg. the "match" keyword in Rust, "case" in Haskell or any other
language with proper algebraic data types, moving to std::variant will
be more involved. For now however, splitting Rule into these two
sub-structs is at least a move in the right direction.

Signed-off-by: aszlig <[email protected]>
  • Loading branch information
aszlig committed Aug 14, 2023
1 parent 221680a commit f4354ae
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 163 deletions.
66 changes: 40 additions & 26 deletions src/preload.cc
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ static void init_rules(void)

#ifdef SYSTEMD_SUPPORT
for (const Rule &rule : *rules) {
if (!rule.socket_activation)
if (!rule.action.socket_activation)
continue;

Systemd::init(*rules);
Expand Down Expand Up @@ -178,35 +178,38 @@ static RuleMatch match_rule(const SockAddr &addr, const Socket::Ptr sock,
) {
const Rule &rule = *it;

if (rule.direction && rule.direction != dir)
if (rule.matches.direction && rule.matches.direction != dir)
continue;

if (rule.type && sock->type != rule.type)
if (rule.matches.type && sock->type != rule.matches.type)
continue;

if (rule.address && addr.get_host() != rule.address)
if (rule.matches.address && addr.get_host() != rule.matches.address)
continue;

if (rule.port) {
if (rule.matches.port) {
std::optional<uint16_t> addrport = addr.get_port();
if (addrport && rule.port_end) {
if (rule.port.value() > addrport.value())
if (addrport && rule.matches.port_end) {
if (rule.matches.port.value() > addrport.value())
continue;
if (rule.port_end < addrport.value())
if (rule.matches.port_end < addrport.value())
continue;
} else if (addrport != rule.port)
} else if (addrport != rule.matches.port)
continue;
}

if (rule.ignore)
if (rule.action.ignore)
return std::nullopt;

#ifdef SYSTEMD_SUPPORT
if (rule.socket_activation)
if (rule.action.socket_activation)
return std::make_pair(rulepos, rule);
#endif
if (!rule.socket_path && !rule.reject && !rule.blackhole)
continue;
if (
!rule.action.socket_path &&
!rule.action.reject &&
!rule.action.blackhole
) continue;

return std::make_pair(rulepos, rule);
}
Expand Down Expand Up @@ -251,18 +254,18 @@ static inline int bind_connect(SockFun &&sockfun, RealFun &&realfun,
return std::invoke(realfun, fd, addr, addrlen);
}

if (rule->second.reject) {
errno = rule->second.reject_errno.value_or(EACCES);
if (rule->second.action.reject) {
errno = rule->second.action.reject_errno.value_or(EACCES);
return -1;
}

if (rule->second.blackhole) {
if (rule->second.action.blackhole) {
sock->blackhole();
return std::invoke(sockfun, sock, inaddr, SocketPath());
}

#ifdef SYSTEMD_SUPPORT
if (rule->second.socket_activation) {
if (rule->second.action.socket_activation) {
std::optional<Systemd::FdInfo> fdinfo =
Systemd::acquire_fdinfo_for_rulepos(rule->first);

Expand All @@ -276,7 +279,12 @@ static inline int bind_connect(SockFun &&sockfun, RealFun &&realfun,
}
#endif

return std::invoke(sockfun, sock, inaddr, *rule->second.socket_path);
return std::invoke(
sockfun,
sock,
inaddr,
*rule->second.action.socket_path
);
}, [&]() {
return std::invoke(realfun, fd, addr, addrlen);
});
Expand Down Expand Up @@ -448,15 +456,18 @@ extern "C" ssize_t WRAP_SYM(sendto)(int fd, const void *buf, size_t len,

RuleMatch rule = match_rule(addrcopy, sock, RuleDir::OUTGOING);

if (!rule || !rule->second.socket_path)
if (!rule || !rule->second.action.socket_path)
return real::sendto(fd, buf, len, flags, addr, addrlen);

if (rule->second.reject) {
errno = rule->second.reject_errno.value_or(EACCES);
if (rule->second.action.reject) {
errno = rule->second.action.reject_errno.value_or(EACCES);
return ssize_t{-1};
}

newdest = sock->rewrite_dest(addrcopy, *rule->second.socket_path);
newdest = sock->rewrite_dest(
addrcopy,
*rule->second.action.socket_path
);
}

if (newdest) {
Expand Down Expand Up @@ -493,15 +504,18 @@ extern "C" ssize_t WRAP_SYM(sendmsg)(int fd, const struct msghdr *msg,

RuleMatch rule = match_rule(addrcopy, sock, RuleDir::OUTGOING);

if (!rule || !rule->second.socket_path)
if (!rule || !rule->second.action.socket_path)
return real::sendmsg(fd, msg, flags);

if (rule->second.reject) {
errno = rule->second.reject_errno.value_or(EACCES);
if (rule->second.action.reject) {
errno = rule->second.action.reject_errno.value_or(EACCES);
return ssize_t{-1};
}

newdest = sock->rewrite_dest(addrcopy, *rule->second.socket_path);
newdest = sock->rewrite_dest(
addrcopy,
*rule->second.action.socket_path
);
}

msghdr newmsg;
Expand Down
36 changes: 23 additions & 13 deletions src/rules.hh
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,34 @@
enum class RuleDir { INCOMING, OUTGOING };

struct Rule {
std::optional<RuleDir> direction = std::nullopt;
std::optional<SocketType> type = std::nullopt;
std::optional<std::string> address = std::nullopt;
std::optional<uint16_t> port = std::nullopt;
std::optional<uint16_t> port_end = std::nullopt;

struct Matches {
std::optional<RuleDir> direction = std::nullopt;
std::optional<SocketType> type = std::nullopt;
std::optional<std::string> address = std::nullopt;
std::optional<uint16_t> port = std::nullopt;
std::optional<uint16_t> port_end = std::nullopt;
};

// XXX: This should ideally be std::variant once we get to a C++ version
// where handling/matching variants is not as gruesome as eg. using
// std::visit.
struct Action {
#ifdef SYSTEMD_SUPPORT
bool socket_activation = false;
std::optional<std::string> fd_name = std::nullopt;
bool socket_activation = false;
std::optional<std::string> fd_name = std::nullopt;
#endif

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

bool reject = false;
std::optional<int> reject_errno = std::nullopt;

bool reject = false;
std::optional<int> reject_errno = std::nullopt;
bool blackhole = false;
bool ignore = false;
};

bool blackhole = false;
bool ignore = false;
Matches matches = {};
Action action = {};
};

bool is_yaml_rule_file(const std::string&);
Expand Down
Loading

0 comments on commit f4354ae

Please sign in to comment.