mirror of
https://github.com/anope/anope.git
synced 2026-07-05 08:13:13 +02:00
ca8fcbe119
This is useful because for some IRCv3 specifications we need to apply tags to all messages and its annoying to have to do this inline when sending each message.
220 lines
5.7 KiB
C++
220 lines
5.7 KiB
C++
// Anope IRC Services <https://www.anope.org/>
|
|
//
|
|
// Copyright (C) 2003-2026 Anope Contributors
|
|
//
|
|
// Anope is free software. You can use, modify, and/or distribute it under the
|
|
// terms of version 2 of the GNU General Public License. See docs/LICENSE.txt
|
|
// for the complete terms of this license and docs/AUTHORS.txt for a list of
|
|
// contributors.
|
|
//
|
|
// Based on the original code of Epona by Lara
|
|
// Based on the original code of Services by Andy Church
|
|
//
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
#include "uplink.h"
|
|
#include "logger.h"
|
|
#include "config.h"
|
|
#include "protocol.h"
|
|
#include "servers.h"
|
|
|
|
UplinkSocket *UplinkSock = NULL;
|
|
|
|
class ReconnectTimer final
|
|
: public Timer
|
|
{
|
|
public:
|
|
ReconnectTimer(time_t wait)
|
|
: Timer(wait)
|
|
{
|
|
}
|
|
|
|
bool Tick() override
|
|
{
|
|
try
|
|
{
|
|
Uplink::Connect();
|
|
}
|
|
catch (const SocketException &ex)
|
|
{
|
|
Log(LOG_TERMINAL) << "Unable to connect to uplink #" << (Anope::CurrentUplink + 1) << " (" << Config->Uplinks[Anope::CurrentUplink].str() << "): " << ex.GetReason();
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
void Uplink::Connect()
|
|
{
|
|
if (Config->Uplinks.empty())
|
|
{
|
|
Log() << "Warning: There are no configured uplinks.";
|
|
return;
|
|
}
|
|
|
|
if (++Anope::CurrentUplink >= Config->Uplinks.size())
|
|
Anope::CurrentUplink = 0;
|
|
|
|
Configuration::Uplink &u = Config->Uplinks[Anope::CurrentUplink];
|
|
|
|
new UplinkSocket();
|
|
if (!Config->GetBlock("serverinfo").Get<const Anope::string>("localhost").empty())
|
|
UplinkSock->Bind(Config->GetBlock("serverinfo").Get<const Anope::string>("localhost"));
|
|
FOREACH_MOD(OnPreServerConnect, ());
|
|
Anope::string ip = Anope::Resolve(u.host, u.protocol);
|
|
Log(LOG_TERMINAL) << "Attempting to connect to uplink #" << (Anope::CurrentUplink + 1) << " " << ip << " (" << u.str() << ")";
|
|
UplinkSock->Connect(ip, u.port);
|
|
}
|
|
|
|
void Uplink::SendInternal(const Anope::map<Anope::string> &tags, const MessageSource &source, const Anope::string &command, const std::vector<Anope::string> ¶ms)
|
|
{
|
|
if (!UplinkSock)
|
|
{
|
|
Log(LOG_DEBUG) << "Attempted to send \"" << command << "\" from " << source.GetName() << " with a null uplink socket";
|
|
return;
|
|
}
|
|
|
|
Anope::map<Anope::string> fulltags(tags);
|
|
IRCD->PopulateTags(fulltags, source, command, params);
|
|
|
|
Anope::string message;
|
|
if (!IRCD->Format(message, fulltags, source, command, params))
|
|
return;
|
|
|
|
UplinkSock->sent_msgs++;
|
|
UplinkSock->Write(message);
|
|
|
|
Log(LOG_RAWIO) << "Sent " << message;
|
|
if (Anope::ProtocolDebug)
|
|
{
|
|
auto sent_tag = false;
|
|
for (const auto &[tname, tvalue] : fulltags)
|
|
{
|
|
if (IRCD->IsTagValid(tname, tvalue))
|
|
{
|
|
Log() << "\tTag " << tname << ": " << tvalue;
|
|
sent_tag = true;
|
|
}
|
|
}
|
|
if (!sent_tag)
|
|
Log() << "\tNo tags";
|
|
|
|
if (source.GetSource().empty())
|
|
Log() << "\tNo source";
|
|
else
|
|
Log() << "\tSource: " << source.GetSource();
|
|
|
|
Log() << "\tCommand: " << command;
|
|
|
|
if (params.empty())
|
|
Log() << "\tNo params";
|
|
else
|
|
{
|
|
for (size_t i = 0; i < params.size(); ++i)
|
|
Log() << "\tParam " << i << ": " << params[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
UplinkSocket::UplinkSocket() : Socket(-1, Config->Uplinks[Anope::CurrentUplink].protocol), ConnectionSocket(), BufferedSocket()
|
|
{
|
|
UplinkSock = this;
|
|
}
|
|
|
|
UplinkSocket::~UplinkSocket()
|
|
{
|
|
if (!error && !Anope::Quitting)
|
|
{
|
|
this->OnError("");
|
|
|
|
std::vector<Anope::string> advice = {
|
|
"You are connecting Anope to an SSL-only port without configuring SSL (or vice versa).",
|
|
};
|
|
if (IRCD)
|
|
{
|
|
advice.push_back(Anope::Format("You are using the wrong protocol module (currently %s for %s).",
|
|
IRCD->owner->name.c_str(), IRCD->GetProtocolName().c_str()));
|
|
IRCD->GetLinkAdvice(advice);
|
|
}
|
|
|
|
Log(LOG_TERMINAL) << "Potential causes include:";
|
|
for (const auto &line : advice)
|
|
Log(LOG_TERMINAL) << "* " << line;
|
|
}
|
|
|
|
if (IRCD && Servers::GetUplink() && Servers::GetUplink()->IsSynced())
|
|
{
|
|
FOREACH_MOD(OnServerDisconnect, ());
|
|
|
|
for (const auto &[_, u] : UserListByNick)
|
|
{
|
|
if (u->server == Me)
|
|
{
|
|
/* Don't use quitmsg here, it may contain information you don't want people to see */
|
|
const auto *reason = Anope::Restarting ? "Restarting" : "Shutting down";
|
|
IRCD->SendQuit(u, reason, Anope::QuitReason);
|
|
BotInfo *bi = BotInfo::Find(u->GetUID());
|
|
if (bi != NULL)
|
|
bi->introduced = false;
|
|
}
|
|
}
|
|
|
|
IRCD->SendSquit(Me, Anope::QuitReason);
|
|
}
|
|
|
|
for (unsigned i = Me->GetLinks().size(); i > 0; --i)
|
|
if (!Me->GetLinks()[i - 1]->IsJuped())
|
|
Me->GetLinks()[i - 1]->Delete(Me->GetName() + " " + Me->GetLinks()[i - 1]->GetName());
|
|
|
|
this->ProcessWrite(); // Write out the last bit
|
|
UplinkSock = NULL;
|
|
|
|
Me->Unsync();
|
|
|
|
if (Anope::AtTerm())
|
|
{
|
|
if (static_cast<unsigned>(Anope::CurrentUplink + 1) == Config->Uplinks.size())
|
|
{
|
|
Anope::QuitReason = "Unable to connect to any uplink";
|
|
Anope::Quitting = true;
|
|
Anope::ReturnValue = -1;
|
|
}
|
|
else
|
|
{
|
|
new ReconnectTimer(1);
|
|
}
|
|
}
|
|
else if (!Anope::Quitting)
|
|
{
|
|
time_t retry = Config->GetBlock("options").Get<time_t>("retrywait");
|
|
|
|
Log() << "Disconnected, retrying in " << retry << " seconds";
|
|
new ReconnectTimer(retry);
|
|
}
|
|
}
|
|
|
|
bool UplinkSocket::ProcessRead()
|
|
{
|
|
bool b = BufferedSocket::ProcessRead();
|
|
for (Anope::string buf; !(buf = this->GetLine()).empty();)
|
|
{
|
|
Anope::Process(buf);
|
|
User::QuitUsers();
|
|
Channel::DeleteChannels();
|
|
}
|
|
return b;
|
|
}
|
|
|
|
void UplinkSocket::OnConnect()
|
|
{
|
|
Log(LOG_TERMINAL) << "Successfully connected to uplink #" << (Anope::CurrentUplink + 1) << " " << Config->Uplinks[Anope::CurrentUplink].str();
|
|
IRCD->SendConnect();
|
|
FOREACH_MOD(OnServerConnect, ());
|
|
}
|
|
|
|
void UplinkSocket::OnError(const Anope::string &err)
|
|
{
|
|
Anope::string what = !this->flags[SF_CONNECTED] ? "Unable to connect to" : "Lost connection from";
|
|
Log(LOG_TERMINAL) << what << " uplink #" << (Anope::CurrentUplink + 1) << " (" << Config->Uplinks[Anope::CurrentUplink].str() << ")" << (!err.empty() ? (": " + err) : "");
|
|
error |= !err.empty();
|
|
}
|