protocol: read CASEMAPPING from CAPAB and verify it matches echos ascii identity folding, warning on mismatch instead of silently desyncing
All checks were successful
CI / check (push) Successful in 4m31s
All checks were successful
CI / check (push) Successful in 4m31s
This commit is contained in:
parent
e206f4edbb
commit
0ca06247f8
3 changed files with 29 additions and 0 deletions
|
|
@ -69,6 +69,9 @@ pub enum NetEvent {
|
||||||
// The ircd's live channel-mode set from its `CAPAB CHANMODES` burst — mode
|
// The ircd's live channel-mode set from its `CAPAB CHANMODES` burst — mode
|
||||||
// letters, param arity, and prefix (status) modes incl custom ones like `Y`.
|
// letters, param arity, and prefix (status) modes incl custom ones like `Y`.
|
||||||
ChanModeRegistry { modes: Vec<ChanModeCap> },
|
ChanModeRegistry { modes: Vec<ChanModeCap> },
|
||||||
|
// The ircd's `CASEMAPPING` from its `CAPAB CAPABILITIES` burst. echo folds
|
||||||
|
// identifiers as ascii, so it verifies this matches and warns otherwise.
|
||||||
|
Casemapping { name: String },
|
||||||
// A server split away (SQUIT). `server` is its SID; every user behind it — and
|
// A server split away (SQUIT). `server` is its SID; every user behind it — and
|
||||||
// behind any server in its subtree — is gone, since a split is signalled once
|
// behind any server in its subtree — is gone, since a split is signalled once
|
||||||
// rather than as a QUIT per user.
|
// rather than as a QUIT per user.
|
||||||
|
|
|
||||||
|
|
@ -330,6 +330,13 @@ impl Protocol for InspIrcd {
|
||||||
vec![NetEvent::ChanModeRegistry { modes }]
|
vec![NetEvent::ChanModeRegistry { modes }]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// CAPAB CAPABILITIES :K=V … — a KV list; we care about CASEMAPPING.
|
||||||
|
Some(s) if s.eq_ignore_ascii_case("CAPABILITIES") => {
|
||||||
|
match rest.split_whitespace().find_map(|t| t.trim_start_matches(':').strip_prefix("CASEMAPPING=")) {
|
||||||
|
Some(name) => vec![NetEvent::Casemapping { name: name.to_string() }],
|
||||||
|
None => vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => vec![],
|
_ => vec![],
|
||||||
},
|
},
|
||||||
_ => vec![NetEvent::Unknown { line: line.to_string() }],
|
_ => vec![NetEvent::Unknown { line: line.to_string() }],
|
||||||
|
|
@ -656,6 +663,15 @@ mod tests {
|
||||||
assert!(p.parse("CAPAB CHANMODES :ban=b").is_empty(), "other CAPAB subcommands ignored");
|
assert!(p.parse("CAPAB CHANMODES :ban=b").is_empty(), "other CAPAB subcommands ignored");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CAPAB CAPABILITIES surfaces CASEMAPPING (echo verifies it's ascii).
|
||||||
|
#[test]
|
||||||
|
fn parses_capab_casemapping() {
|
||||||
|
let mut p = proto();
|
||||||
|
assert!(matches!(p.parse("CAPAB CAPABILITIES :NICKMAX=31 CASEMAPPING=ascii CHANMAX=65").as_slice(),
|
||||||
|
[NetEvent::Casemapping { name }] if name == "ascii"));
|
||||||
|
assert!(p.parse("CAPAB CAPABILITIES :NICKMAX=31 CHANMAX=65").is_empty(), "no CASEMAPPING token -> nothing");
|
||||||
|
}
|
||||||
|
|
||||||
// CAPAB CHANMODES surfaces mode arity + prefix modes, including a custom prefix
|
// CAPAB CHANMODES surfaces mode arity + prefix modes, including a custom prefix
|
||||||
// like ojoin's Y (symbol !, rank 9000000) that the hardcoded set doesn't know.
|
// like ojoin's Y (symbol !, rank 9000000) that the hardcoded set doesn't know.
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -1116,6 +1116,16 @@ impl Engine {
|
||||||
self.db.set_live_chanmodes(modes);
|
self.db.set_live_chanmodes(modes);
|
||||||
Vec::new()
|
Vec::new()
|
||||||
}
|
}
|
||||||
|
NetEvent::Casemapping { name } => {
|
||||||
|
// echo folds identifiers as ascii. Verify the ircd agrees, rather than
|
||||||
|
// assuming it silently — a mismatch would desync account/channel identity.
|
||||||
|
if name.eq_ignore_ascii_case("ascii") {
|
||||||
|
tracing::info!(casemapping = %name, "ircd casemapping matches echo (ascii)");
|
||||||
|
} else {
|
||||||
|
tracing::warn!(casemapping = %name, "ircd CASEMAPPING is not ascii — echo compares identifiers as ascii, so nicks/channels differing only by rfc1459-equivalent characters may be mismatched; keep the ircd on CASEMAPPING=ascii");
|
||||||
|
}
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
NetEvent::UserConnect { uid, nick, host, ip } => {
|
NetEvent::UserConnect { uid, nick, host, ip } => {
|
||||||
let arriving_nick = nick.clone();
|
let arriving_nick = nick.clone();
|
||||||
self.network.user_connect(uid.clone(), nick, host, ip.clone());
|
self.network.user_connect(uid.clone(), nick, host, ip.clone());
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue