]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/modules/m_spanningtree.cpp
Made SANICK not collide the user (theres no need to in the new 1.1 now we have return...
[user/henk/code/inspircd.git] / src / modules / m_spanningtree.cpp
index 9610aa07ec0b08ab1d80edc3b0ee0cdd03d832d8..c52a167871515a76a59a778706a08de89469c322 100644 (file)
@@ -33,6 +33,12 @@ using namespace std;
 
 #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
@@ -491,17 +497,21 @@ class TreeServer : public classbase
 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
@@ -584,7 +594,7 @@ class cmd_rconnect : public command_t
                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? */
@@ -595,7 +605,11 @@ class cmd_rconnect : public command_t
                        const char* para[1];
                        para[0] = parameters[1];
                        Creator->OnPreCommand("CONNECT", para, 1, user, true);
+
+                       return CMD_SUCCESS;
                }
+
+               return CMD_FAILURE;
        }
 };
  
@@ -754,7 +768,10 @@ class TreeSocket : public InspSocket
                 */
                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);
                }
        }
 
@@ -799,7 +816,7 @@ class TreeSocket : public InspSocket
 
                for (int i = 0; i <= this->Instance->GetModuleCount(); i++)
                {
-                       if ((this->Instance->modules[i]->GetVersion().Flags & VF_STATIC) || (this->Instance->modules[i]->GetVersion().Flags & VF_COMMON))
+                       if (this->Instance->modules[i]->GetVersion().Flags & VF_COMMON)
                                modlist.push_back(this->Instance->Config->module_names[i]);
                }
                sort(modlist.begin(),modlist.end());
@@ -845,7 +862,7 @@ class TreeSocket : public InspSocket
 #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");
        }
@@ -931,6 +948,18 @@ class TreeSocket : public InspSocket
                        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";
 
@@ -1611,11 +1640,8 @@ class TreeSocket : public InspSocket
                                }
                                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;
                                }
                        }
                }
@@ -1657,26 +1683,6 @@ class TreeSocket : public InspSocket
                return true;
        }
 
-       bool SyncChannelTS(std::string source, std::deque<std::string> &params)
-       {
-               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> &params)
        {
@@ -1694,7 +1700,9 @@ class TreeSocket : public InspSocket
                /* This used to have a pretty craq'y loop doing the same thing,
                 * now we just let the STL do the hard work (more efficiently)
                 */
-               params[5] = params[5].substr(params[5].find_first_not_of('+'));
+               std::string::size_type pos_after_plus = params[5].find_first_not_of('+');
+               if (pos_after_plus != std::string::npos)
+                       params[5] = params[5].substr(pos_after_plus);
                
                const char* tempnick = params[1].c_str();
                ServerInstance->Log(DEBUG,"Introduce client %s!%s@%s",tempnick,params[4].c_str(),params[2].c_str());
@@ -3177,10 +3185,17 @@ class TreeSocket : public InspSocket
                                                {
                                                        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
@@ -3220,6 +3235,9 @@ class TreeSocket : public InspSocket
                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);
                }
        }
 
@@ -3238,7 +3256,7 @@ class TreeSocket : public InspSocket
                {
                        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)
@@ -3258,7 +3276,7 @@ class TreeSocket : public InspSocket
 
                        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;
                        }
@@ -3308,6 +3326,7 @@ class ServernameResolver : public Resolver
                                /* 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);
                        }
                }
        }
@@ -3316,6 +3335,7 @@ class ServernameResolver : public Resolver
        {
                /* 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);
        }
 };
 
@@ -3569,6 +3589,7 @@ void ReadConfiguration(bool rebind)
                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);
@@ -4031,6 +4052,74 @@ class ModuleSpanningTree : public Module
                }
        }
 
+       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++)
@@ -4040,39 +4129,23 @@ class ModuleSpanningTree : public Module
                                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));
                                }
                        }
                }
@@ -4125,33 +4198,7 @@ class ModuleSpanningTree : public Module
                                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
@@ -4239,7 +4286,13 @@ class ModuleSpanningTree : public Module
                        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
@@ -4262,7 +4315,6 @@ class ModuleSpanningTree : public Module
                        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)
@@ -4414,10 +4466,19 @@ class ModuleSpanningTree : public Module
                // 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
@@ -4427,8 +4488,6 @@ class ModuleSpanningTree : public Module
                        {
                                // 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);
@@ -4783,7 +4842,7 @@ class ModuleSpanningTree : public Module
                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
@@ -4799,6 +4858,15 @@ class ModuleSpanningTree : public Module
        }
 };
 
+void DoFailOver(Link* x)
+{
+       TreeProtocolModule->DoFailOver(x);
+}
+
+Link* FindLink(const std::string& name)
+{
+       return TreeProtocolModule->FindLink(name);
+}
 
 class ModuleSpanningTreeFactory : public ModuleFactory
 {