CBAN (forbid channel-name globs) + SSLINFO command
This commit is contained in:
parent
cab53016e8
commit
4a53c80d4d
5 changed files with 91 additions and 0 deletions
|
|
@ -405,6 +405,17 @@ impl Server {
|
|||
{
|
||||
return; // unknown user, or already joined
|
||||
}
|
||||
// CBAN — a forbidden channel name (opers bypass)
|
||||
if !self.users.get(&uid).map(|u| u.flags.oper).unwrap_or(false) {
|
||||
if let Some(reason) = self.matched_cban(&key) {
|
||||
self.numeric(
|
||||
uid,
|
||||
ERR_BADCHANNEL,
|
||||
&format!("{name} :Channel is CBAN'd: {reason}"),
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// an existing channel can refuse the join (+k / +b / +i / +l)
|
||||
if let Some(ch) = self.channels.get(&key) {
|
||||
if let Some(k) = &ch.modes.key {
|
||||
|
|
|
|||
|
|
@ -14,9 +14,56 @@ pub fn commands() -> Vec<Box<dyn Command>> {
|
|||
Box::new(Motd),
|
||||
Box::new(VersionCmd),
|
||||
Box::new(Links),
|
||||
Box::new(SslInfo),
|
||||
]
|
||||
}
|
||||
|
||||
/// SSLINFO — report a user's TLS status and client-cert fingerprint (InspIRCd
|
||||
/// `m_sslinfo`). You may query yourself; querying another user requires oper.
|
||||
struct SslInfo;
|
||||
impl Command for SslInfo {
|
||||
fn name(&self) -> &'static str {
|
||||
"SSLINFO"
|
||||
}
|
||||
fn min_params(&self) -> usize {
|
||||
1
|
||||
}
|
||||
fn handle(&self, s: &mut Server, uid: Uid, params: &[String]) -> CmdResult {
|
||||
let target = params[0].clone();
|
||||
let Some(tuid) = s.find_nick(&target) else {
|
||||
s.numeric(
|
||||
uid,
|
||||
ERR_NOSUCHNICK,
|
||||
&format!("{target} :No such nick/channel"),
|
||||
);
|
||||
return CmdResult::Fail;
|
||||
};
|
||||
if tuid != uid && !s.is_oper(uid) {
|
||||
s.numeric(uid, ERR_NOPRIVILEGES, ":You may only SSLINFO yourself");
|
||||
return CmdResult::Fail;
|
||||
}
|
||||
let asker = s
|
||||
.users
|
||||
.get(&uid)
|
||||
.map(|u| u.nick.clone())
|
||||
.unwrap_or_else(|| "*".to_string());
|
||||
let (nick, secure, certfp) = {
|
||||
let u = &s.users[&tuid];
|
||||
(u.nick.clone(), u.secure, u.certfp.clone())
|
||||
};
|
||||
let tls = if secure { "yes" } else { "no" };
|
||||
let fp = certfp.unwrap_or_else(|| "none".to_string());
|
||||
s.send(
|
||||
uid,
|
||||
format!(
|
||||
":{} NOTICE {asker} :SSLINFO {nick}: TLS={tls} certfp={fp}",
|
||||
s.name
|
||||
),
|
||||
);
|
||||
CmdResult::Ok
|
||||
}
|
||||
}
|
||||
|
||||
/// LINKS — the servers this one knows about (itself + every linked peer).
|
||||
struct Links;
|
||||
impl Command for Links {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ pub fn commands() -> Vec<Box<dyn Command>> {
|
|||
Box::new(Eline),
|
||||
Box::new(Shun),
|
||||
Box::new(Qline),
|
||||
Box::new(Cban),
|
||||
Box::new(Connect),
|
||||
Box::new(ChgHost),
|
||||
Box::new(ChgIdent),
|
||||
|
|
@ -683,6 +684,21 @@ impl Command for Qline {
|
|||
}
|
||||
}
|
||||
|
||||
/// CBAN — forbid a channel-name glob (opers bypass it). Mask alone removes; a
|
||||
/// mask + duration adds. InspIRCd `m_cban`.
|
||||
struct Cban;
|
||||
impl Command for Cban {
|
||||
fn name(&self) -> &'static str {
|
||||
"CBAN"
|
||||
}
|
||||
fn min_params(&self) -> usize {
|
||||
1
|
||||
}
|
||||
fn handle(&self, s: &mut Server, uid: Uid, params: &[String]) -> CmdResult {
|
||||
do_xline(s, uid, params, XKind::Cban)
|
||||
}
|
||||
}
|
||||
|
||||
/// CONNECT — dial a configured server link on demand. `CONNECT <servername>`.
|
||||
struct Connect;
|
||||
impl Command for Connect {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ pub const ERR_UNAVAILRESOURCE: u16 = 437; // channel temporarily unavailable (+j
|
|||
pub const ERR_LINKCHANNEL: u16 = 470; // +L — you were redirected to another channel
|
||||
pub const ERR_DELAYREJOIN: u16 = 495; // +J — must wait before rejoining after a kick
|
||||
pub const ERR_CANTSENDTOUSER: u16 = 531; // +c — no shared channel with the target
|
||||
pub const ERR_BADCHANNEL: u16 = 926; // CBAN — this channel name is forbidden
|
||||
pub const RPL_ENDOFSPAMFILTER: u16 = 940; // end of the +g word-filter list
|
||||
pub const RPL_SPAMFILTER: u16 = 941; // one +g word-filter entry
|
||||
pub const RPL_KNOCK: u16 = 710; // channel gets the knock
|
||||
|
|
|
|||
16
src/xline.rs
16
src/xline.rs
|
|
@ -15,6 +15,7 @@ pub enum XKind {
|
|||
Eline, // user@host / ip EXEMPT from K/G/Z-lines
|
||||
Shun, // user@host allowed to connect but whose commands are dropped
|
||||
Qline, // a reserved/forbidden nick glob
|
||||
Cban, // a forbidden channel-name glob
|
||||
}
|
||||
|
||||
impl XKind {
|
||||
|
|
@ -26,6 +27,7 @@ impl XKind {
|
|||
XKind::Eline => "E",
|
||||
XKind::Shun => "SHUN",
|
||||
XKind::Qline => "Q",
|
||||
XKind::Cban => "CBAN",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -109,6 +111,20 @@ impl Server {
|
|||
.map(|x| x.reason.clone())
|
||||
}
|
||||
|
||||
/// The reason channel `chan` is CBAN'd (forbidden), if any. Case-insensitive.
|
||||
pub fn matched_cban(&self, chan: &str) -> Option<String> {
|
||||
let n = now();
|
||||
let c = chan.to_ascii_lowercase();
|
||||
self.xlines
|
||||
.iter()
|
||||
.find(|x| {
|
||||
x.kind == XKind::Cban
|
||||
&& (x.expires == 0 || x.expires > n)
|
||||
&& glob_match(&x.mask.to_ascii_lowercase(), &c)
|
||||
})
|
||||
.map(|x| x.reason.clone())
|
||||
}
|
||||
|
||||
/// The reason a `user@host` / `ip` is banned by an active x-line, if any.
|
||||
/// An E-line (exemption) overrides every K/G/Z-line.
|
||||
pub fn matched_xline(&self, ident: &str, host: &str, ip: &str) -> Option<String> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue