#define nspace __gnu_cxx
+
+/** If you make a change which breaks the protocol, increment this.
+ * If you completely change the protocol, completely change the number.
+ */
+const long ProtocolVersion = 1100;
+
/*
* The server list in InspIRCd is maintained as two structures
* which hold the data in different ways. Most of the time, we
class Link : public classbase
{
public:
- irc::string Name;
- std::string IPAddr;
- int Port;
- std::string SendPass;
- std::string RecvPass;
- unsigned long AutoConnect;
- time_t NextConnectTime;
- std::string EncryptionKey;
- bool HiddenFromStats;
+ irc::string Name;
+ std::string IPAddr;
+ int Port;
+ std::string SendPass;
+ std::string RecvPass;
+ unsigned long AutoConnect;
+ time_t NextConnectTime;
+ std::string EncryptionKey;
+ bool HiddenFromStats;
+ irc::string FailOver;
};
+void DoFailOver(Link* x);
+Link* FindLink(const std::string& name);
+
/* The usual stuff for inspircd modules,
* plus the vector of Link classes which we
* use to store the <link> tags from the config
syntax = "<remote-server-mask> <servermask>";
}
- void Handle (const char** parameters, int pcnt, userrec *user)
+ CmdResult Handle (const char** parameters, int pcnt, userrec *user)
{
user->WriteServ("NOTICE %s :*** RCONNECT: Sending remote connect to \002%s\002 to connect server \002%s\002.",user->nick,parameters[0],parameters[1]);
/* Is this aimed at our server? */
const char* para[1];
para[0] = parameters[1];
Creator->OnPreCommand("CONNECT", para, 1, user, true);
+
+ return CMD_SUCCESS;
}
+
+ return CMD_FAILURE;
}
};
*/
if (e == I_ERR_CONNECT)
{
- this->Instance->SNO->WriteToSnoMask('l',"Connection failed: Connection refused");
+ this->Instance->SNO->WriteToSnoMask('l',"Connection failed: Connection to \002"+myhost+"\002 refused");
+ Link* MyLink = FindLink(myhost);
+ if (MyLink)
+ DoFailOver(MyLink);
}
}
#ifdef SUPPORT_IP6LINKS
ip6support = 1;
#endif
- this->WriteLine("CAPAB CAPABILITIES :NICKMAX="+ConvToStr(NICKMAX)+" HALFOP="+ConvToStr(this->Instance->Config->AllowHalfop)+" CHANMAX="+ConvToStr(CHANMAX)+" MAXMODES="+ConvToStr(MAXMODES)+" IDENTMAX="+ConvToStr(IDENTMAX)+" MAXQUIT="+ConvToStr(MAXQUIT)+" MAXTOPIC="+ConvToStr(MAXTOPIC)+" MAXKICK="+ConvToStr(MAXKICK)+" MAXGECOS="+ConvToStr(MAXGECOS)+" MAXAWAY="+ConvToStr(MAXAWAY)+" IP6NATIVE="+ConvToStr(ip6)+" IP6SUPPORT="+ConvToStr(ip6support));
+ this->WriteLine("CAPAB CAPABILITIES :NICKMAX="+ConvToStr(NICKMAX)+" HALFOP="+ConvToStr(this->Instance->Config->AllowHalfop)+" CHANMAX="+ConvToStr(CHANMAX)+" MAXMODES="+ConvToStr(MAXMODES)+" IDENTMAX="+ConvToStr(IDENTMAX)+" MAXQUIT="+ConvToStr(MAXQUIT)+" MAXTOPIC="+ConvToStr(MAXTOPIC)+" MAXKICK="+ConvToStr(MAXKICK)+" MAXGECOS="+ConvToStr(MAXGECOS)+" MAXAWAY="+ConvToStr(MAXAWAY)+" IP6NATIVE="+ConvToStr(ip6)+" IP6SUPPORT="+ConvToStr(ip6support)+" PROTOCOL="+ConvToStr(ProtocolVersion));
this->WriteLine("CAPAB END");
}
if (((this->CapKeys.find("NICKMAX") == this->CapKeys.end()) || ((this->CapKeys.find("NICKMAX") != this->CapKeys.end()) && (this->CapKeys.find("NICKMAX")->second != ConvToStr(NICKMAX)))))
reason = "Maximum nickname lengths differ or remote nickname length not specified";
+ if (((this->CapKeys.find("PROTOCOL") == this->CapKeys.end()) || ((this->CapKeys.find("PROTOCOL") != this->CapKeys.end()) && (this->CapKeys.find("PROTOCOL")->second != ConvToStr(ProtocolVersion)))))
+ {
+ if (this->CapKeys.find("PROTOCOL") != this->CapKeys.end())
+ {
+ reason = "Mismatched protocol versions "+this->CapKeys.find("PROTOCOL")->second+" and "+ConvToStr(ProtocolVersion);
+ }
+ else
+ {
+ reason = "Protocol version not specified";
+ }
+ }
+
if (((this->CapKeys.find("HALFOP") == this->CapKeys.end()) && (Instance->Config->AllowHalfop)) || ((this->CapKeys.find("HALFOP") != this->CapKeys.end()) && (this->CapKeys.find("HALFOP")->second != ConvToStr(Instance->Config->AllowHalfop))))
reason = "We don't both have halfop support enabled/disabled identically";
}
else
{
- for (unsigned int f = 2; f < modectr; f++)
- free(mode_users[f]);
-
- this->WriteLine("ERROR :Invalid user '"+std::string(usr)+"' in FJOIN to '"+channel+"'");
- return false;
+ ServerInstance->Log(SPARSE,"Warning! Invalid user %s in FJOIN to channel %s IGNORED", who->nick, channel.c_str());
+ continue;
}
}
}
return true;
}
- bool SyncChannelTS(std::string source, std::deque<std::string> ¶ms)
- {
- if (params.size() >= 2)
- {
- chanrec* c = this->Instance->FindChan(params[0]);
- if (c)
- {
- time_t theirTS = atoi(params[1].c_str());
- time_t ourTS = c->age;
- if (ourTS >= theirTS)
- {
- ServerInstance->Log(DEBUG,"Updating timestamp for %s, our timestamp was %lu and theirs is %lu",c->name,ourTS,theirTS);
- c->age = theirTS;
- }
- }
- }
- DoOneToAllButSender(this->Instance->Config->ServerName,"SYNCTS",params,source);
- return true;
- }
-
/* NICK command */
bool IntroduceClient(std::string source, std::deque<std::string> ¶ms)
{
{
strparams[q] = params[q].c_str();
}
- if (!this->Instance->CallCommandHandler(command.c_str(), strparams, params.size(), who))
+ switch (this->Instance->CallCommandHandler(command.c_str(), strparams, params.size(), who))
{
- this->WriteLine("ERROR :Unrecognised command '"+std::string(command.c_str())+"' -- possibly loaded mismatched modules");
- return false;
+ case CMD_INVALID:
+ this->WriteLine("ERROR :Unrecognised command '"+std::string(command.c_str())+"' -- possibly loaded mismatched modules");
+ return false;
+ break;
+ case CMD_FAILURE:
+ return true;
+ break;
+ default:
+ break;
}
}
else
if (this->LinkState == CONNECTING)
{
this->Instance->SNO->WriteToSnoMask('l',"CONNECT: Connection to \002"+myhost+"\002 timed out.");
+ Link* MyLink = FindLink(myhost);
+ if (MyLink)
+ DoFailOver(MyLink);
}
}
{
Squit(s,"Remote host closed the connection");
}
- this->Instance->WriteOpers("Server '\2%s\2' closed the connection.",quitserver.c_str());
+ this->Instance->SNO->WriteToSnoMask('l',"Connection to '\2%s\2' failed.",quitserver.c_str());
}
virtual int OnIncomingConnection(int newsock, char* ip)
if (!found)
{
- this->Instance->WriteOpers("Server connection from %s denied (no link blocks with that IP address)", ip);
+ this->Instance->SNO->WriteToSnoMask('l',"Server connection from %s denied (no link blocks with that IP address)", ip);
close(newsock);
return false;
}
/* Something barfed, show the opers */
ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",MyLink.Name.c_str(),strerror(errno));
delete newsocket;
+ DoFailOver(&MyLink);
}
}
}
{
/* Ooops! */
ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: Unable to resolve hostname - %s",MyLink.Name.c_str(),errormessage.c_str());
+ DoFailOver(&MyLink);
}
};
std::string Allow = Conf->ReadValue("link","allowmask",j);
L.Name = (Conf->ReadValue("link","name",j)).c_str();
L.IPAddr = Conf->ReadValue("link","ipaddr",j);
+ L.FailOver = Conf->ReadValue("link","failover",j).c_str();
L.Port = Conf->ReadInteger("link","port",j,true);
L.SendPass = Conf->ReadValue("link","sendpass",j);
L.RecvPass = Conf->ReadValue("link","recvpass",j);
}
}
+ void ConnectServer(Link* x)
+ {
+ insp_inaddr binip;
+
+ /* Do we already have an IP? If so, no need to resolve it. */
+ if (insp_aton(x->IPAddr.c_str(), &binip) > 0)
+ {
+ TreeSocket* newsocket = new TreeSocket(ServerInstance, x->IPAddr,x->Port,false,10,x->Name.c_str());
+ if (newsocket->GetFd() > -1)
+ {
+ /* Handled automatically on success */
+ }
+ else
+ {
+ ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(),strerror(errno));
+ delete newsocket;
+ this->DoFailOver(x);
+ }
+ }
+ else
+ {
+ try
+ {
+ ServernameResolver* snr = new ServernameResolver(ServerInstance,x->IPAddr, *x);
+ ServerInstance->AddResolver(snr);
+ }
+ catch (ModuleException& e)
+ {
+ ServerInstance->Log(DEBUG,"Error in resolver: %s",e.GetReason());
+ this->DoFailOver(x);
+ }
+ }
+ }
+
+ void DoFailOver(Link* x)
+ {
+ if (x->FailOver.length())
+ {
+ if (x->FailOver == x->Name)
+ {
+ ServerInstance->SNO->WriteToSnoMask('l',"FAILOVER: Some muppet configured the failover for server \002%s\002 to point at itself. Not following it!", x->Name.c_str());
+ return;
+ }
+ Link* TryThisOne = this->FindLink(x->FailOver.c_str());
+ if (TryThisOne)
+ {
+ ServerInstance->SNO->WriteToSnoMask('l',"FAILOVER: Trying failover link for \002%s\002: \002%s\002...", x->Name.c_str(), TryThisOne->Name.c_str());
+ ConnectServer(TryThisOne);
+ }
+ else
+ {
+ ServerInstance->SNO->WriteToSnoMask('l',"FAILOVER: Invalid failover server specified for server \002%s\002, will not follow!", x->Name.c_str());
+ }
+ }
+ }
+
+ Link* FindLink(const std::string& name)
+ {
+ for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
+ {
+ if (ServerInstance->MatchText(x->Name.c_str(), name.c_str()))
+ {
+ return &(*x);
+ }
+ }
+ return NULL;
+ }
+
void AutoConnectServers(time_t curtime)
{
for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
ServerInstance->Log(DEBUG,"Auto-Connecting %s",x->Name.c_str());
x->NextConnectTime = curtime + x->AutoConnect;
TreeServer* CheckDupe = FindServer(x->Name.c_str());
+ if (x->FailOver.length())
+ {
+ TreeServer* CheckFailOver = FindServer(x->FailOver.c_str());
+ if (CheckFailOver)
+ {
+ /* The failover for this server is currently a member of the network.
+ * The failover probably succeeded, where the main link did not.
+ * Don't try the main link until the failover is gone again.
+ */
+ continue;
+ }
+ }
if (!CheckDupe)
{
// an autoconnected server is not connected. Check if its time to connect it
ServerInstance->SNO->WriteToSnoMask('l',"AUTOCONNECT: Auto-connecting server \002%s\002 (%lu seconds until next attempt)",x->Name.c_str(),x->AutoConnect);
-
- insp_inaddr binip;
-
- /* Do we already have an IP? If so, no need to resolve it. */
- if (insp_aton(x->IPAddr.c_str(), &binip) > 0)
- {
- TreeSocket* newsocket = new TreeSocket(ServerInstance, x->IPAddr,x->Port,false,10,x->Name.c_str());
- if (newsocket->GetFd() > -1)
- {
- }
- else
- {
- ServerInstance->SNO->WriteToSnoMask('l',"AUTOCONNECT: Error autoconnecting \002%s\002: %s.",x->Name.c_str(),strerror(errno));
- delete newsocket;
- }
- }
- else
- {
- try
- {
- ServernameResolver* snr = new ServernameResolver(ServerInstance,x->IPAddr, *x);
- ServerInstance->AddResolver(snr);
- }
- catch (ModuleException& e)
- {
- ServerInstance->Log(DEBUG,"Error in resolver: %s",e.GetReason());
- }
- }
-
+ this->ConnectServer(&(*x));
}
}
}
if (!CheckDupe)
{
user->WriteServ("NOTICE %s :*** CONNECT: Connecting to server: \002%s\002 (%s:%d)",user->nick,x->Name.c_str(),(x->HiddenFromStats ? "<hidden>" : x->IPAddr.c_str()),x->Port);
- insp_inaddr binip;
-
- /* Do we already have an IP? If so, no need to resolve it. */
- if (insp_aton(x->IPAddr.c_str(), &binip) > 0)
- {
- TreeSocket* newsocket = new TreeSocket(ServerInstance,x->IPAddr,x->Port,false,10,x->Name.c_str());
- if (newsocket->GetFd() > -1)
- {
- }
- else
- {
- ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(),strerror(errno));
- delete newsocket;
- }
- }
- else
- {
- try
- {
- ServernameResolver* snr = new ServernameResolver(ServerInstance, x->IPAddr, *x);
- ServerInstance->AddResolver(snr);
- }
- catch (ModuleException& e)
- {
- ServerInstance->Log(DEBUG,"Error in resolver: %s",e.GetReason());
- }
- }
+ ConnectServer(&(*x));
return 1;
}
else
this->HandleVersion(parameters,pcnt,user);
return 1;
}
- else if (ServerInstance->IsValidModuleCommand(command, pcnt, user))
+
+ return 0;
+ }
+
+ virtual void OnPostCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, CmdResult result)
+ {
+ if ((result == CMD_SUCCESS) && (ServerInstance->IsValidModuleCommand(command, pcnt, user)))
{
// this bit of code cleverly routes all module commands
// to all remote severs *automatically* so that modules
ServerInstance->Log(DEBUG,"Globally route '%s'",command.c_str());
DoOneToMany(user->nick,command,params);
}
- return 0;
}
virtual void OnGetServerDescription(const std::string &servername,std::string &description)
// Only do this for local users
if (IS_LOCAL(user))
{
+ char ts[24];
+ snprintf(ts,24,"%lu",(unsigned long)channel->age);
+
std::deque<std::string> params;
params.clear();
params.push_back(channel->name);
+ /** XXX: The client protocol will IGNORE this parameter.
+ * We could make use of it if we wanted to keep the TS
+ * in step if somehow we lose it.
+ */
+ params.push_back(ts);
+
if (channel->GetUserCounter() > 1)
{
// not the first in the channel
{
// first in the channel, set up their permissions
// and the channel TS with FJOIN.
- char ts[24];
- snprintf(ts,24,"%lu",(unsigned long)channel->age);
params.clear();
params.push_back(channel->name);
params.push_back(ts);
List[I_OnUserQuit] = List[I_OnUserPostNick] = List[I_OnUserKick] = List[I_OnRemoteKill] = List[I_OnRehash] = 1;
List[I_OnOper] = List[I_OnAddGLine] = List[I_OnAddZLine] = List[I_OnAddQLine] = List[I_OnAddELine] = 1;
List[I_OnDelGLine] = List[I_OnDelZLine] = List[I_OnDelQLine] = List[I_OnDelELine] = List[I_ProtoSendMode] = List[I_OnMode] = 1;
- List[I_OnStats] = List[I_ProtoSendMetaData] = List[I_OnEvent] = List[I_OnSetAway] = List[I_OnCancelAway] = 1;
+ List[I_OnStats] = List[I_ProtoSendMetaData] = List[I_OnEvent] = List[I_OnSetAway] = List[I_OnCancelAway] = List[I_OnPostCommand] = 1;
}
/* It is IMPORTANT that m_spanningtree is the last module in the chain
}
};
+void DoFailOver(Link* x)
+{
+ TreeProtocolModule->DoFailOver(x);
+}
+
+Link* FindLink(const std::string& name)
+{
+ return TreeProtocolModule->FindLink(name);
+}
class ModuleSpanningTreeFactory : public ModuleFactory
{