operserv: NOTIFY lines carry real IP, rdns, login and origin server for the oper feed
All checks were successful
CI / check (push) Successful in 4m4s
All checks were successful
CI / check (push) Successful in 4m4s
This commit is contained in:
parent
2fb1970030
commit
4d327c3f47
2 changed files with 24 additions and 3 deletions
|
|
@ -390,8 +390,28 @@ impl Engine {
|
||||||
if !self.db.notify_flags(Some(&target), chan).contains(flag) {
|
if !self.db.notify_flags(Some(&target), chan).contains(flag) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
// The feed lands in the oper-only staff channel, so a watch line carries the
|
||||||
|
// forensic detail a cloak hides: the real IP (with rdns if it differs), the
|
||||||
|
// login, and the origin server. The connection port is not available — the
|
||||||
|
// ircd's s2s UID burst never sends it. `nick!ident@host` keeps the cloak so
|
||||||
|
// it still correlates with what ordinary users see.
|
||||||
|
let mut parts: Vec<String> = Vec::new();
|
||||||
|
if !target.ip.is_empty() {
|
||||||
|
let mut s = format!("\x02{}\x02", target.ip);
|
||||||
|
if !target.realhost.is_empty() && target.realhost != target.ip {
|
||||||
|
s.push_str(&format!(" ({})", target.realhost));
|
||||||
|
}
|
||||||
|
parts.push(s);
|
||||||
|
}
|
||||||
|
if let Some(account) = target.account {
|
||||||
|
parts.push(format!("acct \x02{account}\x02"));
|
||||||
|
}
|
||||||
|
if !target.server.is_empty() {
|
||||||
|
parts.push(format!("via {}", target.server));
|
||||||
|
}
|
||||||
|
let detail = if parts.is_empty() { String::new() } else { format!(" · {}", parts.join(" · ")) };
|
||||||
let who = format!("\x02{}\x02!{}@{}", target.nick, target.ident, target.host);
|
let who = format!("\x02{}\x02!{}@{}", target.nick, target.ident, target.host);
|
||||||
self.feed("NOTIFY", format!("{who} {what}"))
|
self.feed("NOTIFY", format!("{who} {what}{detail}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inactivity-expiry thresholds (seconds); None leaves that kind never
|
// Inactivity-expiry thresholds (seconds); None leaves that kind never
|
||||||
|
|
|
||||||
|
|
@ -5408,8 +5408,9 @@ fn notify_hook_emits_feed_for_watched_user() {
|
||||||
let out = e.handle(NetEvent::UserAttrs { uid: "000AAAAAC".into(), ident: "x".into(), realhost: "h".into(), gecos: "g".into() });
|
let out = e.handle(NetEvent::UserAttrs { uid: "000AAAAAC".into(), ident: "x".into(), realhost: "h".into(), gecos: "g".into() });
|
||||||
assert!(
|
assert!(
|
||||||
out.iter().any(|a| matches!(a, NetAction::Privmsg { from, to, text }
|
out.iter().any(|a| matches!(a, NetAction::Privmsg { from, to, text }
|
||||||
if from == "42SAAAAAO" && to == "#services" && text.contains("[NOTIFY]") && text.contains("connected"))),
|
if from == "42SAAAAAO" && to == "#services" && text.contains("[NOTIFY]") && text.contains("connected")
|
||||||
"expected a NOTIFY connect line, got {out:?}"
|
&& text.contains("1.2.3.4"))), // the real IP is carried, not just the cloak
|
||||||
|
"expected a NOTIFY connect line with the real IP, got {out:?}"
|
||||||
);
|
);
|
||||||
// A clean user draws nothing.
|
// A clean user draws nothing.
|
||||||
e.handle(NetEvent::UserConnect { uid: "000AAAAAD".into(), nick: "nice".into(), host: "h".into(), ip: "9.9.9.9".into() });
|
e.handle(NetEvent::UserConnect { uid: "000AAAAAD".into(), nick: "nice".into(), host: "h".into(), ip: "9.9.9.9".into() });
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue