1
0
mirror of https://github.com/anope/anope.git synced 2026-07-05 08:13:13 +02:00
Files
anope/src/uplink.cpp
T
Sadie Powell ca8fcbe119 Allow protocol modules to apply tags to sent messages.
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.
2026-06-10 09:14:09 +01:00

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> &params)
{
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();
}