relaymsg: gate RELAYMSG on oper (it spoofs an arbitrary source nick), matching InspIRCd

This commit is contained in:
Jean Chevronnet 2026-08-17 19:06:17 +00:00
parent d6f27ac56c
commit 99eb9c74f3
2 changed files with 17 additions and 5 deletions

View file

@ -261,8 +261,8 @@ amu_target = both
# --- extbanbanlist: no config — adds the matching extban
# `b:<#channel>`, so `+b b:#staff` catches everyone banned in #staff (shares a
# ban list between channels).
# --- relaymsg (draft/relaymsg): a member whose client negotiated the
# capability can /RELAYMSG <#chan> <nick> <text> to speak under a spoofed relay
# --- relaymsg (draft/relaymsg): an OPERATOR whose client negotiated the capability
# and who is in the channel can /RELAYMSG <#chan> <nick> <text> to speak under a spoofed relay
# nick (for bridges). The nick must contain a separator and not collide.
# relaymsg_separators = /
# relaymsg_ident = relay

View file

@ -1,6 +1,8 @@
//! relaymsg — `RELAYMSG <channel> <nick> <text>` (IRCv3 `draft/relaymsg`): a member
//! whose client negotiated the capability sends a channel message under a spoofed
//! "relay" nick (e.g. `discord/alice`), for stateless bridges. The message is
//! relaymsg — `RELAYMSG <channel> <nick> <text>` (IRCv3 `draft/relaymsg`): an
//! operator whose client negotiated the capability, and who is in the channel,
//! sends a channel message under a spoofed "relay" nick (e.g. `discord/alice`), for
//! stateless bridges (operator-only, as in InspIRCd — the source nick is spoofed).
//! The message is
//! tagged `@draft/relaymsg=<sender>` so clients can attribute it. The spoofed nick
//! must contain a configured separator and must not collide with a real nick.
//!
@ -35,6 +37,16 @@ impl Command for RelayMsg {
CmdResult::Fail
};
// RELAYMSG spoofs an arbitrary source nick, so it's operator-only (as in
// InspIRCd, CmdAccess::OPERATOR) — bridges run their relay as an oper.
if !s.is_oper(uid) {
s.numeric(
uid,
ERR_NOPRIVILEGES,
":Permission Denied- RELAYMSG is for IRC operators",
);
return CmdResult::Fail;
}
if !s.users.get(&uid).map(|u| u.caps.relaymsg).unwrap_or(false) {
s.numeric(
uid,