2 * InspIRCd -- Internet Relay Chat Daemon
4 * Copyright (C) 2019 Sadie Powell <sadie@witchery.services>
5 * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
7 * This file is part of InspIRCd. InspIRCd is free software: you can
8 * redistribute it and/or modify it under the terms of the GNU General Public
9 * License as published by the Free Software Foundation, version 2.
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "pingtimer.h"
24 #include "treeserver.h"
25 #include "commandbuilder.h"
27 PingTimer::PingTimer(TreeServer* ts)
28 : Timer(Utils->PingFreq)
34 PingTimer::State PingTimer::TickInternal()
36 // Timer expired, take next action based on what happened last time
37 if (state == PS_SENDPING)
39 // Last ping was answered, send next ping
40 server->GetSocket()->WriteLine(CmdBuilder("PING").push(server->GetId()));
41 LastPingMsec = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
42 // Warn next unless warnings are disabled. If they are, jump straight to timeout.
43 if (Utils->PingWarnTime)
48 else if (state == PS_WARN)
50 // No pong arrived in PingWarnTime seconds, send a warning to opers
51 ServerInstance->SNO->WriteToSnoMask('l', "Server \002%s\002 has not responded to PING for %d seconds, high latency.", server->GetName().c_str(), GetInterval());
56 // They didn't answer the last ping, if they are locally connected, get rid of them
57 if (server->IsLocal())
59 TreeSocket* sock = server->GetSocket();
60 sock->SendError("Ping timeout");
64 // If the server is non-locally connected, don't do anything until we get a PONG.
65 // This is to avoid pinging the server and warning opers more than once.
66 // If they do answer eventually, we will move to the PS_SENDPING state and ping them again.
71 void PingTimer::SetState(State newstate)
75 // Set when should the next Tick() happen based on the state
76 if (state == PS_SENDPING)
77 SetInterval(Utils->PingFreq);
78 else if (state == PS_WARN)
79 SetInterval(Utils->PingWarnTime);
80 else if (state == PS_TIMEOUT)
81 SetInterval(Utils->PingFreq - Utils->PingWarnTime);
83 // If state == PS_IDLE, do not set the timer, see above why
86 bool PingTimer::Tick(time_t currtime)
91 SetState(TickInternal());
95 void PingTimer::OnPong()
98 long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
99 server->rtt = ts - LastPingMsec;
101 // Change state to send ping next, also reschedules the timer appropriately
102 SetState(PS_SENDPING);