Answer WHOIS IDLE for our pseudo-clients
All checks were successful
CI / check (push) Successful in 3m54s
All checks were successful
CI / check (push) Successful in 3m54s
A two-parameter WHOIS (/whois x x — what mIRC sends when you double-click a nick, to fetch idle time) routes the query to the target's server. For a service that server is echo, which was dropping the resulting IDLE request as an unknown command, so the WHOIS produced no output at all: InspIRCd only emits the whois numerics once the IDLE reply arrives (m_spanningtree/idle.cpp), so no reply meant total silence. Handle the inbound IDLE request and answer it for our own services and bots: ":<target> IDLE <requester> <signon> <idle>", signon = burst time, idle 0 (a pseudo-client is always active). A routed WHOIS of NickServ/ChanServ/etc. now completes.
This commit is contained in:
parent
4e12531815
commit
3f11f77a81
4 changed files with 59 additions and 0 deletions
|
|
@ -107,6 +107,7 @@ pub struct Engine {
|
|||
enforce_seq: u32, // counter appended to guest_nick
|
||||
pending_enforce: Vec<PendingEnforce>, // registered nicks awaiting identify-or-rename
|
||||
bot_uids: HashMap<String, String>, // casefolded bot nick -> live uid (per connection)
|
||||
services_signon: u64, // burst time our pseudo-clients signed on, for WHOIS IDLE replies
|
||||
bot_idents: HashMap<String, u64>, // casefolded bot nick -> hash of user/host/gecos (to spot BOT CHANGE)
|
||||
next_bot_index: u32,
|
||||
bot_channels: std::collections::HashSet<(String, String)>, // (bot nick lc, channel) the bot has joined
|
||||
|
|
@ -240,6 +241,7 @@ impl Engine {
|
|||
enforce_seq: 0,
|
||||
pending_enforce: Vec::new(),
|
||||
bot_uids: HashMap::new(),
|
||||
services_signon: 0,
|
||||
bot_idents: HashMap::new(),
|
||||
next_bot_index: 0,
|
||||
bot_channels: std::collections::HashSet::new(),
|
||||
|
|
@ -902,7 +904,15 @@ impl Engine {
|
|||
}
|
||||
|
||||
// Sent right after the SERVER line: burst, introduce every service, endburst.
|
||||
// Whether `uid` is one of our own pseudo-clients — a service or a bot — so we
|
||||
// answer a WHOIS IDLE request for it (and only it).
|
||||
fn is_own_client(&self, uid: &str) -> bool {
|
||||
self.services.iter().any(|s| s.uid() == uid) || self.bot_uids.values().any(|v| v == uid)
|
||||
}
|
||||
|
||||
pub fn startup_actions(&mut self) -> Vec<NetAction> {
|
||||
// Fresh link: our pseudo-clients (re)sign on now — remember it for IDLE.
|
||||
self.services_signon = self.now_secs();
|
||||
// Fresh link: forget bot uids minted on any previous connection.
|
||||
self.bot_uids.clear();
|
||||
self.bot_idents.clear();
|
||||
|
|
@ -955,6 +965,16 @@ impl Engine {
|
|||
let mut out = self.sweep_unbans();
|
||||
let evout = match event {
|
||||
NetEvent::Ping { token, from } => vec![NetAction::Pong { token, from }],
|
||||
NetEvent::Idle { requester, target } => {
|
||||
// A routed WHOIS (2-param / mIRC double-click) of one of our
|
||||
// pseudo-clients asks us for its idle/signon; answer or that WHOIS
|
||||
// shows nothing at all. Services are always active (idle 0).
|
||||
if self.is_own_client(&target) {
|
||||
vec![NetAction::IdleReply { target, requester, signon: self.services_signon, idle: 0 }]
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
NetEvent::UserConnect { uid, nick, host, ip } => {
|
||||
let arriving_nick = nick.clone();
|
||||
self.network.user_connect(uid.clone(), nick, host, ip.clone());
|
||||
|
|
|
|||
|
|
@ -43,6 +43,22 @@
|
|||
assert!(is_success(&out), "{out:?}");
|
||||
}
|
||||
|
||||
// A routed WHOIS (mIRC double-click) asks the service's server for its idle
|
||||
// time; we must answer for our own pseudo-clients, and only them.
|
||||
#[test]
|
||||
fn idle_request_is_answered_for_our_clients_only() {
|
||||
let mut e = engine_with("idle", "foo", "sesame");
|
||||
e.set_sid("42S".into());
|
||||
let ours = e.handle(NetEvent::Idle { requester: "000AAAAAB".into(), target: "42SAAAAAA".into() });
|
||||
assert!(
|
||||
ours.iter().any(|a| matches!(a, NetAction::IdleReply { target, requester, idle, .. }
|
||||
if target == "42SAAAAAA" && requester == "000AAAAAB" && *idle == 0)),
|
||||
"expected an IDLE reply for NickServ, got {ours:?}"
|
||||
);
|
||||
let foreign = e.handle(NetEvent::Idle { requester: "000AAAAAB".into(), target: "000AAAAAZ".into() });
|
||||
assert!(!foreign.iter().any(|a| matches!(a, NetAction::IdleReply { .. })), "{foreign:?}");
|
||||
}
|
||||
|
||||
// DebugServ streams auth outcomes into the log channel, and stays silent when
|
||||
// it isn't running.
|
||||
#[test]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue