]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/modules/m_spanningtree.cpp
userrec::UpdateNickHash(), userrec::ForceNickChange(), userrec::FullConnect()
[user/henk/code/inspircd.git] / src / modules / m_spanningtree.cpp
index 77929b38254d270f0d0ce2c4795393d8bfd9909c..1e908f07b139ce063d5dfccfa4b50358e811c58b 100644 (file)
@@ -30,6 +30,7 @@ using namespace std;
 #include "modules.h"
 #include "commands.h"
 #include "commands/cmd_whois.h"
+#include "commands/cmd_stats.h"
 #include "socket.h"
 #include "helperfuncs.h"
 #include "inspircd.h"
@@ -111,6 +112,9 @@ static Server* Srv;
 typedef nspace::hash_map<std::string, TreeServer*, nspace::hash<string>, irc::StrHashComp> server_hash;
 server_hash serverlist;
 
+typedef nspace::hash_map<std::string, userrec*> uid_hash;
+typedef nspace::hash_map<std::string, char*> sid_hash;
+
 /* More forward declarations */
 bool DoOneToOne(std::string prefix, std::string command, std::deque<std::string> &params, std::string target);
 bool DoOneToAllButSender(std::string prefix, std::string command, std::deque<std::string> &params, std::string omit);
@@ -135,6 +139,56 @@ extern std::vector<ZLine> pzlines;
 extern std::vector<QLine> pqlines;
 extern std::vector<ELine> pelines;
 
+std::vector<std::string> ValidIPs;
+
+class UserManager : public classbase
+{
+       uid_hash uids;
+       sid_hash sids;
+ public:
+       UserManager()
+       {
+               uids.clear();
+               sids.clear();
+       }
+
+       std::string UserToUID(userrec* user)
+       {
+               return "";
+       }
+
+       std::string UIDToUser(const std::string &UID)
+       {
+               return "";
+       }
+
+       std::string CreateAndAdd(userrec* user)
+       {
+               return "";
+       }
+
+       std::string CreateAndAdd(const std::string &servername)
+       {
+               return "";
+       }
+
+       std::string ServerToSID(const std::string &servername)
+       {
+               return "";
+       }
+
+       std::string SIDToServer(const std::string &SID)
+       {
+               return "";
+       }
+
+       userrec* FindByID(const std::string &UID)
+       {
+               return NULL;
+       }
+};
+
+
 /* Each server in the tree is represented by one class of
  * type TreeServer. A locally connected TreeServer can
  * have a class of type TreeSocket associated with it, for
@@ -276,7 +330,7 @@ class TreeServer : public classbase
                        userrec* a = (userrec*)*n;
                        log(DEBUG,"Kill %s fd=%d",a->nick,a->fd);
                        if (!IS_LOCAL(a))
-                               kill_link(a,reason_s);
+                               userrec::QuitUser(a,reason_s);
                }
                return time_to_die.size();
        }
@@ -555,6 +609,7 @@ class cmd_rconnect : public command_t
        cmd_rconnect (Module* Callback) : command_t("RCONNECT", 'o', 2), Creator(Callback)
        {
                this->source = "m_spanningtree.so";
+               syntax = "<remote-server-mask> <servermask>";
        }
 
        void Handle (const char** parameters, int pcnt, userrec *user)
@@ -974,13 +1029,13 @@ class TreeSocket : public InspSocket
                                                         */
                                                        mh = ServerInstance->ModeGrok->FindMode(*x, chan ? MODETYPE_CHANNEL : MODETYPE_USER);
 
-                                                       if ((mh->GetNumParams(adding) > 0) && (!mh->IsListMode()))
+                                                       if ((mh) && (mh->GetNumParams(adding) > 0) && (!mh->IsListMode()))
                                                        {
                                                                /* We only want to do special things to
                                                                 * modes with parameters, we are going to rewrite
                                                                 * those parameters
                                                                 */
-                                                               std::pair<bool, std::string> ret;
+                                                               ModePair ret;
                                                                adding ? cur_change = '+' : cur_change = '-';
 
                                                                ret = mh->ModeSet(smode ? NULL : who, dst, chan, params[paramptr]);
@@ -1093,19 +1148,28 @@ class TreeSocket : public InspSocket
 
                        if (to_keep.length())
                        {
-                               n = 0;
+                               unsigned int n = 2;
+                               unsigned int q = 0;
                                modelist[0] = params[0].c_str();
                                modelist[1] = to_keep.c_str();
 
-                               for (unsigned int q = 2; (q < params.size()) && (q < 64); q++)
-                                               modelist[q] = params_to_keep[n++].c_str();
+                               if (params_to_keep.size() > 2)
+                               {
+                                       for (q = 2; (q < params_to_keep.size()) && (q < 64); q++)
+                                       {
+                                               log(DEBUG,"Item %d of %d", q, params_to_keep.size());
+                                               modelist[n++] = params_to_keep[q].c_str();
+                                       }
+                               }
 
                                if (smode)
                                {
+                                       log(DEBUG,"Send mode");
                                        Srv->SendMode(modelist, n+2, who);
                                }
                                else
                                {
+                                       log(DEBUG,"Send mode client");
                                        Srv->CallCommandHandler("MODE", modelist, n+2, who);
                                }
 
@@ -1167,7 +1231,7 @@ class TreeSocket : public InspSocket
                                                 */
                                                if (mh)
                                                {
-                                                       std::pair<bool, std::string> ret;
+                                                       ModePair ret;
                                                        std::string p = "";
 
                                                        /* Does the mode require a parameter right now?
@@ -1274,11 +1338,11 @@ class TreeSocket : public InspSocket
                                        userrec* user = Srv->FindNick(source);
                                        if (!user)
                                        {
-                                               WriteChannelWithServ(source.c_str(), c, "TOPIC %s :%s", c->name, c->topic);
+                                               c->WriteChannelWithServ(source.c_str(), "TOPIC %s :%s", c->name, c->topic);
                                        }
                                        else
                                        {
-                                               WriteChannel(c, user, "TOPIC %s :%s", c->name, c->topic);
+                                               c->WriteChannel(user, "TOPIC %s :%s", c->name, c->topic);
                                                nsource = user->server;
                                        }
                                        /* all done, send it on its way */
@@ -1304,7 +1368,7 @@ class TreeSocket : public InspSocket
                memset(&mode_users,0,sizeof(mode_users));
                mode_users[0] = first;
                mode_users[1] = modestring;
-               strcpy(first,"+");
+               strcpy(modestring,"+");
                unsigned int modectr = 2;
                
                userrec* who = NULL;
@@ -1366,7 +1430,7 @@ class TreeSocket : public InspSocket
                                who = Srv->FindNick(usr);
                                if (who)
                                {
-                                       Srv->JoinUserToChannel(who,channel,key);
+                                       chanrec::JoinUser(who, channel.c_str(), true, key);
                                        if (modectr >= (MAXMODES-1))
                                        {
                                                /* theres a mode for this user. push them onto the mode queue, and flush it
@@ -1379,8 +1443,9 @@ class TreeSocket : public InspSocket
                                                        Srv->SendMode((const char**)mode_users,modectr,who);
                                                        if (ourTS != TS)
                                                        {
-                                                               log(DEFAULT,"Channel TS for %s changed from %lu to %lu",us,ourTS,TS);
+                                                               log(DEFAULT,"Channel TS for %s changed from %lu to %lu",us->name,ourTS,TS);
                                                                us->age = TS;
+                                                               ourTS = TS;
                                                        }
                                                }
                                                else
@@ -1397,6 +1462,7 @@ class TreeSocket : public InspSocket
                                                                        params.push_back(ConvToStr(us->age));
                                                                }
                                                                params.push_back(mode_users[x]);
+                                                               
                                                        }
                                                        // tell everyone to bounce the modes. bad modes, bad!
                                                        DoOneToMany(Srv->GetServerName(),"FMODE",params);
@@ -1410,7 +1476,7 @@ class TreeSocket : public InspSocket
                /* there werent enough modes built up to flush it during FJOIN,
                 * or, there are a number left over. flush them out.
                 */
-               if ((modectr > 2) && (who))
+               if ((modectr > 2) && (who) && (us))
                {
                        if (ourTS >= TS)
                        {
@@ -1418,8 +1484,9 @@ class TreeSocket : public InspSocket
                                Srv->SendMode((const char**)mode_users,modectr,who);
                                if (ourTS != TS)
                                {
-                                       log(DEFAULT,"Channel TS for %s changed from %lu to %lu",us,ourTS,TS);
+                                       log(DEFAULT,"Channel TS for %s changed from %lu to %lu",us->name,ourTS,TS);
                                        us->age = TS;
+                                       ourTS = TS;
                                }
                        }
                        else
@@ -1472,7 +1539,7 @@ class TreeSocket : public InspSocket
                        return true;
                }
                // NICK age nick host dhost ident +modes ip :gecos
-               //   0   123  4 56   7
+               //       0    1   2     3     4      5   6     7
                time_t age = atoi(params[0].c_str());
                
                /* This used to have a pretty craq'y loop doing the same thing,
@@ -1501,16 +1568,20 @@ class TreeSocket : public InspSocket
                clientlist[tempnick]->server = FindServerNamePtr(source.c_str());
                strlcpy(clientlist[tempnick]->ident, params[4].c_str(),IDENTMAX);
                strlcpy(clientlist[tempnick]->fullname, params[7].c_str(),MAXGECOS);
-               clientlist[tempnick]->registered = 7;
+               clientlist[tempnick]->registered = REG_ALL;
                clientlist[tempnick]->signon = age;
                
                for (std::string::iterator v = params[5].begin(); v != params[5].end(); v++)
                {
                        clientlist[tempnick]->modes[(*v)-65] = 1;
                }
-               inet_aton(params[6].c_str(),&clientlist[tempnick]->ip4);
 
-               WriteOpers("*** Client connecting at %s: %s!%s@%s [%s]",clientlist[tempnick]->server,clientlist[tempnick]->nick,clientlist[tempnick]->ident,clientlist[tempnick]->host, inet_ntoa(clientlist[tempnick]->ip4));
+               if (params[6].find_first_of(":") != std::string::npos)
+                       clientlist[tempnick]->SetSockAddr(AF_INET6, params[6].c_str(), 0);
+               else
+                       clientlist[tempnick]->SetSockAddr(AF_INET, params[6].c_str(), 0);
+
+               WriteOpers("*** Client connecting at %s: %s!%s@%s [%s]",clientlist[tempnick]->server,clientlist[tempnick]->nick,clientlist[tempnick]->ident,clientlist[tempnick]->host, clientlist[tempnick]->GetIPString());
 
                params[7] = ":" + params[7];
                DoOneToAllButSender(source,"NICK",params,source);
@@ -1713,9 +1784,9 @@ class TreeSocket : public InspSocket
                int iterations = 0;
                for (user_hash::iterator u = clientlist.begin(); u != clientlist.end(); u++, iterations++)
                {
-                       if (u->second->registered == 7)
+                       if (u->second->registered == REG_ALL)
                        {
-                               snprintf(data,MAXBUF,":%s NICK %lu %s %s %s %s +%s %s :%s",u->second->server,(unsigned long)u->second->age,u->second->nick,u->second->host,u->second->dhost,u->second->ident,u->second->FormatModes(),inet_ntoa(u->second->ip4),u->second->fullname);
+                               snprintf(data,MAXBUF,":%s NICK %lu %s %s %s %s +%s %s :%s",u->second->server,(unsigned long)u->second->age,u->second->nick,u->second->host,u->second->dhost,u->second->ident,u->second->FormatModes(),u->second->GetIPString(),u->second->fullname);
                                this->WriteLine(data);
                                if (*u->second->oper)
                                {
@@ -1865,6 +1936,43 @@ class TreeSocket : public InspSocket
                return false;
        }
 
+       bool Stats(std::string prefix, std::deque<std::string> &params)
+       {
+               /* Get the reply to a STATS query if it matches this servername,
+                * and send it back as a load of PUSH queries
+                */
+               if (params.size() > 1)
+               {
+                       if (Srv->MatchText(Srv->GetServerName(), params[1]))
+                       {
+                               /* It's for our server */
+                               string_list results;
+                               userrec* source = Srv->FindNick(prefix);
+                               if (source)
+                               {
+                                       std::deque<std::string> par;
+                                       par.push_back(prefix);
+                                       par.push_back("");
+                                       DoStats(*(params[0].c_str()), source, results);
+                                       for (size_t i = 0; i < results.size(); i++)
+                                       {
+                                               par[1] = "::" + results[i];
+                                               DoOneToOne(Srv->GetServerName(), "PUSH",par, source->server);
+                                       }
+                               }
+                       }
+                       else
+                       {
+                               /* Pass it on */
+                               userrec* source = Srv->FindNick(prefix);
+                               if (source)
+                                       DoOneToOne(prefix, "STATS", params, params[1]);
+                       }
+               }
+               return true;
+       }
+
+
        /* Because the core won't let users or even SERVERS set +o,
         * we use the OPERTYPE command to do this.
         */
@@ -1906,7 +2014,11 @@ class TreeSocket : public InspSocket
                                /* This is not required as one is sent in OnUserPostNick below
                                 */
                                //DoOneToMany(u->nick,"NICK",par);
-                               Srv->ChangeUserNick(u,params[1]);
+                               if (!u->ForceNickChange(params[1].c_str()))
+                               {
+                                       userrec::QuitUser(u, "Nickname collision");
+                                       return true;
+                               }
                                u->age = atoi(params[2].c_str());
                        }
                }
@@ -1922,7 +2034,7 @@ class TreeSocket : public InspSocket
 
                if (u)
                {
-                       Srv->JoinUserToChannel(u,params[1],"");
+                       chanrec::JoinUser(u, params[1].c_str(), false);
                        DoOneToAllButSender(prefix,"SVSJOIN",params,prefix);
                }
                return true;
@@ -1970,7 +2082,7 @@ class TreeSocket : public InspSocket
                        params[1] = ":" + params[1];
                        DoOneToAllButSender(prefix,"KILL",params,sourceserv);
                        ::Write(who->fd, ":%s KILL %s :%s (%s)", sourceserv.c_str(), who->nick, sourceserv.c_str(), reason.c_str());
-                       Srv->QuitUser(who,reason);
+                       userrec::QuitUser(who,reason);
                }
                return true;
        }
@@ -2171,7 +2283,7 @@ class TreeSocket : public InspSocket
                        if (params.size() == 1)
                        {
                                userrec* x = Srv->FindNick(params[0]);
-                               if ((x) && (x->fd > -1))
+                               if ((x) && (IS_LOCAL(x)))
                                {
                                        userrec* x = Srv->FindNick(params[0]);
                                        log(DEBUG,"Got IDLE");
@@ -2197,14 +2309,14 @@ class TreeSocket : public InspSocket
                        {
                                std::string who_did_the_whois = params[0];
                                userrec* who_to_send_to = Srv->FindNick(who_did_the_whois);
-                               if ((who_to_send_to) && (who_to_send_to->fd > -1))
+                               if ((who_to_send_to) && (IS_LOCAL(who_to_send_to)))
                                {
                                        log(DEBUG,"Got final IDLE");
                                        // an incoming reply to a whois we sent out
                                        std::string nick_whoised = prefix;
                                        unsigned long signon = atoi(params[1].c_str());
                                        unsigned long idle = atoi(params[2].c_str());
-                                       if ((who_to_send_to) && (who_to_send_to->fd > -1))
+                                       if ((who_to_send_to) && (IS_LOCAL(who_to_send_to)))
                                                do_whois(who_to_send_to,u,signon,idle,nick_whoised.c_str());
                                }
                                else
@@ -2229,15 +2341,7 @@ class TreeSocket : public InspSocket
 
                if (IS_LOCAL(u))
                {
-                       // push the raw to the user
-                       if (Srv->IsUlined(prefix))
-                       {
-                               ::Write(u->fd,"%s",params[1].c_str());
-                       }
-                       else
-                       {
-                               log(DEBUG,"PUSH from non-ulined server dropped into the bit-bucket:  :%s PUSH %s :%s",prefix.c_str(),params[0].c_str(),params[1].c_str());
-                       }
+                       ::Write(u->fd,"%s",params[1].c_str());
                }
                else
                {
@@ -2679,6 +2783,10 @@ class TreeSocket : public InspSocket
                                {
                                        return this->ForceJoin(prefix,params);
                                }
+                               else if (command == "STATS")
+                               {
+                                       return this->Stats(prefix, params);
+                               }
                                else if (command == "SERVER")
                                {
                                        return this->RemoteServer(prefix,params);
@@ -2792,7 +2900,9 @@ class TreeSocket : public InspSocket
                                                chanrec* chan = Srv->FindChannel(params[0]);
                                                if (user && chan)
                                                {
-                                                       server_kick_channel(user,chan,(char*)params[2].c_str(),false);
+                                                       if (!chan->ServerKickUser(user, params[2].c_str(), false))
+                                                               /* Yikes, the channels gone! */
+                                                               delete chan;
                                                }
                                        }
                                        if (this->InboundServerName != "")
@@ -2859,11 +2969,11 @@ class TreeSocket : public InspSocket
                                                                p.push_back(prefix);
                                                                p.push_back("Nickname collision");
                                                                DoOneToMany(Srv->GetServerName(),"KILL",p);
-                                                               Srv->QuitUser(x,"Nickname collision ("+prefix+" -> "+params[0]+")");
+                                                               userrec::QuitUser(x,"Nickname collision ("+prefix+" -> "+params[0]+")");
                                                                userrec* y = Srv->FindNick(prefix);
                                                                if (y)
                                                                {
-                                                                       Srv->QuitUser(y,"Nickname collision");
+                                                                       userrec::QuitUser(y,"Nickname collision");
                                                                }
                                                                return DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params);
                                                        }
@@ -2946,39 +3056,99 @@ class TreeSocket : public InspSocket
                 * IPs for which we don't have a link block.
                 */
                bool found = false;
-               char resolved_host[MAXBUF];
-               vector<Link>::iterator i;
-               for (i = LinkBlocks.begin(); i != LinkBlocks.end(); i++)
+
+               found = (std::find(ValidIPs.begin(), ValidIPs.end(), ip) != ValidIPs.end());
+               if (!found)
                {
-                       if (i->IPAddr == ip)
-                       {
-                               found = true;
-                               break;
-                       }
-                       /* XXX: Fixme: blocks for a very short amount of time,
-                        * we should cache these on rehash/startup
-                        */
-                       if (CleanAndResolve(resolved_host,i->IPAddr.c_str(),true))
-                       {
-                               if (std::string(resolved_host) == ip)
-                               {
+                       for (vector<std::string>::iterator i = ValidIPs.begin(); i != ValidIPs.end(); i++)
+                               if (MatchCIDR(ip, (*i).c_str()))
                                        found = true;
-                                       break;
-                               }
+
+                       if (!found)
+                       {
+                               WriteOpers("Server connection from %s denied (no link blocks with that IP address)", ip);
+                               close(newsock);
+                               return false;
                        }
                }
-               if (!found)
-               {
-                       WriteOpers("Server connection from %s denied (no link blocks with that IP address)", ip);
-                       close(newsock);
-                       return false;
-               }
                TreeSocket* s = new TreeSocket(newsock, ip);
                Srv->AddSocket(s);
                return true;
        }
 };
 
+/** This class is used to resolve server hostnames during /connect and autoconnect.
+ * As of 1.1, the resolver system is seperated out from InspSocket, so we must do this
+ * resolver step first ourselves if we need it. This is totally nonblocking, and will
+ * callback to OnLookupComplete or OnError when completed. Once it has completed we
+ * will have an IP address which we can then use to continue our connection.
+ */
+class ServernameResolver : public Resolver
+{       
+ private:
+       /** A copy of the Link tag info for what we're connecting to.
+        * We take a copy, rather than using a pointer, just in case the
+        * admin takes the tag away and rehashes while the domain is resolving.
+        */
+       Link MyLink;
+ public:        
+       ServernameResolver(const std::string &hostname, Link x) : Resolver(hostname, DNS_QUERY_FORWARD), MyLink(x)
+       {
+               /* Nothing in here, folks */
+       }
+        
+       void OnLookupComplete(const std::string &result)
+       {
+               /* Initiate the connection, now that we have an IP to use.
+                * Passing a hostname directly to InspSocket causes it to
+                * just bail and set its FD to -1.
+                */
+               TreeServer* CheckDupe = FindServer(MyLink.Name.c_str());
+               if (!CheckDupe) /* Check that nobody tried to connect it successfully while we were resolving */
+               {
+                       TreeSocket* newsocket = new TreeSocket(result,MyLink.Port,false,10,MyLink.Name.c_str());
+                       if (newsocket->GetFd() > -1)
+                       {
+                               /* We're all OK */
+                               Srv->AddSocket(newsocket);
+                       }
+                       else
+                       {
+                               /* Something barfed, show the opers */
+                               WriteOpers("*** CONNECT: Error connecting \002%s\002: %s.",MyLink.Name.c_str(),strerror(errno));
+                               delete newsocket;
+                       }
+               }
+       }
+
+       void OnError(ResolverError e, const std::string &errormessage)
+       {
+               /* Ooops! */
+               WriteOpers("*** CONNECT: Error connecting \002%s\002: Unable to resolve hostname - %s",MyLink.Name.c_str(),errormessage.c_str());
+       }
+};
+
+class SecurityIPResolver : public Resolver
+{
+ private:
+       Link MyLink;
+ public:
+       SecurityIPResolver(const std::string &hostname, Link x) : Resolver(hostname, DNS_QUERY_FORWARD), MyLink(x)
+       {
+       }
+
+       void OnLookupComplete(const std::string &result)
+       {
+               log(DEBUG,"Security IP cache: Adding IP address '%s' for Link '%s'",result.c_str(),MyLink.Name.c_str());
+               ValidIPs.push_back(result);
+       }
+
+       void OnError(ResolverError e, const std::string &errormessage)
+       {
+               log(DEBUG,"Could not resolve IP associated with Link '%s': %s",MyLink.Name.c_str(),errormessage.c_str());
+       }
+};
+
 void AddThisServer(TreeServer* server, std::deque<TreeServer*> &list)
 {
        for (unsigned int c = 0; c < list.size(); c++)
@@ -3184,9 +3354,11 @@ void ReadConfiguration(bool rebind)
        FlatLinks = Conf->ReadFlag("options","flatlinks",0);
        HideULines = Conf->ReadFlag("options","hideulines",0);
        LinkBlocks.clear();
+       ValidIPs.clear();
        for (int j =0; j < Conf->Enumerate("link"); j++)
        {
                Link L;
+               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.Port = Conf->ReadInteger("link","port",j,true);
@@ -3199,6 +3371,26 @@ void ReadConfiguration(bool rebind)
                /* Bugfix by brain, do not allow people to enter bad configurations */
                if ((L.IPAddr != "") && (L.RecvPass != "") && (L.SendPass != "") && (L.Name != "") && (L.Port))
                {
+                       ValidIPs.push_back(L.IPAddr);
+
+                       if (Allow.length())
+                               ValidIPs.push_back(Allow);
+
+                       /* Needs resolving */
+                       insp_inaddr binip;
+                       if (insp_aton(L.IPAddr.c_str(), &binip) < 1)
+                       {
+                               try
+                               {
+                                       SecurityIPResolver* sr = new SecurityIPResolver(L.IPAddr, L);
+                                       Srv->AddResolver(sr);
+                               }
+                               catch (ModuleException& e)
+                               {
+                                       log(DEBUG,"Error in resolver: %s",e.GetReason());
+                               }
+                       }
+
                        LinkBlocks.push_back(L);
                        log(DEBUG,"m_spanningtree: Read server %s with host %s:%d",L.Name.c_str(),L.IPAddr.c_str(),L.Port);
                }
@@ -3379,6 +3571,30 @@ class ModuleSpanningTree : public Module
                }
        }
 
+       int HandleStats(const char** parameters, int pcnt, userrec* user)
+       {
+               if (pcnt > 1)
+               {
+                       /* Remote STATS, the server is within the 2nd parameter */
+                       std::deque<std::string> params;
+                       params.push_back(parameters[0]);
+                       params.push_back(parameters[1]);
+                       /* Send it out remotely, generate no reply yet */
+                       TreeServer* s = FindServerMask(parameters[1]);
+                       if (s)
+                       {
+                               params[1] = s->GetName();
+                               DoOneToOne(user->nick, "STATS", params, s->GetName());
+                       }
+                       else
+                       {
+                               WriteServ(user->fd, "402 %s %s :No such server", user->nick, parameters[0]);
+                       }
+                       return 1;
+               }
+               return 0;
+       }
+
        // Ok, prepare to be confused.
        // After much mulling over how to approach this, it struck me that
        // the 'usual' way of doing a /MAP isnt the best way. Instead of
@@ -3476,7 +3692,7 @@ class ModuleSpanningTree : public Module
 
        int HandleTime(const char** parameters, int pcnt, userrec* user)
        {
-               if ((user->fd > -1) && (pcnt))
+               if ((IS_LOCAL(user)) && (pcnt))
                {
                        TreeServer* found = FindServerMask(parameters[0]);
                        if (found)
@@ -3500,7 +3716,7 @@ class ModuleSpanningTree : public Module
 
        int HandleRemoteWhois(const char** parameters, int pcnt, userrec* user)
        {
-               if ((user->fd > -1) && (pcnt > 1))
+               if ((IS_LOCAL(user)) && (pcnt > 1))
                {
                        userrec* remote = Srv->FindNick(parameters[1]);
                        if ((remote) && (remote->fd < 0))
@@ -3561,16 +3777,36 @@ class ModuleSpanningTree : public Module
                                {
                                        // an autoconnected server is not connected. Check if its time to connect it
                                        WriteOpers("*** AUTOCONNECT: Auto-connecting server \002%s\002 (%lu seconds until next attempt)",x->Name.c_str(),x->AutoConnect);
-                                       TreeSocket* newsocket = new TreeSocket(x->IPAddr,x->Port,false,10,x->Name.c_str());
-                                       if (newsocket->GetFd() > -1)
+
+                                       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)
                                        {
-                                               Srv->AddSocket(newsocket);
+                                               TreeSocket* newsocket = new TreeSocket(x->IPAddr,x->Port,false,10,x->Name.c_str());
+                                               if (newsocket->GetFd() > -1)
+                                               {
+                                                       Srv->AddSocket(newsocket);
+                                               }
+                                               else
+                                               {
+                                                       WriteOpers("*** AUTOCONNECT: Error autoconnecting \002%s\002: %s.",x->Name.c_str(),strerror(errno));
+                                                       delete newsocket;
+                                               }
                                        }
                                        else
                                        {
-                                               WriteOpers("*** AUTOCONNECT: Error autoconnecting \002%s\002: %s.",x->Name.c_str(),strerror(errno));
-                                               DELETE(newsocket);
+                                               try
+                                               {
+                                                       ServernameResolver* snr = new ServernameResolver(x->IPAddr, *x);
+                                                       Srv->AddResolver(snr);
+                                               }
+                                               catch (ModuleException& e)
+                                               {
+                                                       log(DEBUG,"Error in resolver: %s",e.GetReason());
+                                               }
                                        }
+
                                }
                        }
                }
@@ -3623,15 +3859,33 @@ class ModuleSpanningTree : public Module
                                if (!CheckDupe)
                                {
                                        WriteServ(user->fd,"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);
-                                       TreeSocket* newsocket = new TreeSocket(x->IPAddr,x->Port,false,10,x->Name.c_str());
-                                       if (newsocket->GetFd() > -1)
+                                       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)
                                        {
-                                               Srv->AddSocket(newsocket);
+                                               TreeSocket* newsocket = new TreeSocket(x->IPAddr,x->Port,false,10,x->Name.c_str());
+                                               if (newsocket->GetFd() > -1)
+                                               {
+                                                       Srv->AddSocket(newsocket);
+                                               }
+                                               else
+                                               {
+                                                       WriteOpers("*** CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(),strerror(errno));
+                                                       delete newsocket;
+                                               }
                                        }
                                        else
                                        {
-                                               WriteServ(user->fd,"NOTICE %s :*** CONNECT: Error connecting \002%s\002: %s.",user->nick,x->Name.c_str(),strerror(errno));
-                                               DELETE(newsocket);
+                                               try
+                                               {
+                                                       ServernameResolver* snr = new ServernameResolver(x->IPAddr, *x);
+                                                       Srv->AddResolver(snr);
+                                               }
+                                               catch (ModuleException& e)
+                                               {
+                                                       log(DEBUG,"Error in resolver: %s",e.GetReason());
+                                               }
                                        }
                                        return 1;
                                }
@@ -3646,17 +3900,17 @@ class ModuleSpanningTree : public Module
                return 1;
        }
 
-       virtual int OnStats(char statschar, userrec* user)
+       virtual int OnStats(char statschar, userrec* user, string_list &results)
        {
                if (statschar == 'c')
                {
                        for (unsigned int i = 0; i < LinkBlocks.size(); i++)
                        {
-                               WriteServ(user->fd,"213 %s C *@%s * %s %d 0 %c%c%c",user->nick,(LinkBlocks[i].HiddenFromStats ? "<hidden>" : LinkBlocks[i].IPAddr).c_str(),LinkBlocks[i].Name.c_str(),LinkBlocks[i].Port,(LinkBlocks[i].EncryptionKey != "" ? 'e' : '-'),(LinkBlocks[i].AutoConnect ? 'a' : '-'),'s');
-                               WriteServ(user->fd,"244 %s H * * %s",user->nick,LinkBlocks[i].Name.c_str());
+                               results.push_back(Srv->GetServerName()+" 213 "+user->nick+" C *@"+(LinkBlocks[i].HiddenFromStats ? "<hidden>" : LinkBlocks[i].IPAddr)+" * "+LinkBlocks[i].Name.c_str()+" "+ConvToStr(LinkBlocks[i].Port)+" "+(LinkBlocks[i].EncryptionKey != "" ? 'e' : '-')+(LinkBlocks[i].AutoConnect ? 'a' : '-')+'s');
+                               results.push_back(Srv->GetServerName()+" 244 "+user->nick+" H * * "+LinkBlocks[i].Name.c_str());
                        }
-                       WriteServ(user->fd,"219 %s %c :End of /STATS report",user->nick,statschar);
-                       WriteOpers("*** Notice: Stats '%c' requested by %s (%s@%s)",statschar,user->nick,user->ident,user->host);
+                       results.push_back(Srv->GetServerName()+" 219 "+user->nick+" "+statschar+" :End of /STATS report");
+                       WriteOpers("*** Notice: %s '%c' requested by %s (%s@%s)",(!strcmp(user->server,Config->ServerName) ? "Stats" : "Remote stats"),statschar,user->nick,user->ident,user->host);
                        return 1;
                }
                return 0;
@@ -3672,6 +3926,10 @@ class ModuleSpanningTree : public Module
                {
                        return this->HandleConnect(parameters,pcnt,user);
                }
+               else if (command == "STATS")
+               {
+                       return this->HandleStats(parameters,pcnt,user);
+               }
                else if (command == "SQUIT")
                {
                        return this->HandleSquit(parameters,pcnt,user);
@@ -3745,7 +4003,7 @@ class ModuleSpanningTree : public Module
 
        virtual void OnUserInvite(userrec* source,userrec* dest,chanrec* channel)
        {
-               if (source->fd > -1)
+               if (IS_LOCAL(source))
                {
                        std::deque<std::string> params;
                        params.push_back(dest->nick);
@@ -3764,7 +4022,7 @@ class ModuleSpanningTree : public Module
 
        virtual void OnWallops(userrec* user, const std::string &text)
        {
-               if (user->fd > -1)
+               if (IS_LOCAL(user))
                {
                        std::deque<std::string> params;
                        params.push_back(":"+text);
@@ -3777,7 +4035,7 @@ class ModuleSpanningTree : public Module
                if (target_type == TYPE_USER)
                {
                        userrec* d = (userrec*)dest;
-                       if ((d->fd < 0) && (user->fd > -1))
+                       if ((d->fd < 0) && (IS_LOCAL(user)))
                        {
                                std::deque<std::string> params;
                                params.clear();
@@ -3788,7 +4046,7 @@ class ModuleSpanningTree : public Module
                }
                else if (target_type == TYPE_CHANNEL)
                {
-                       if (user->fd > -1)
+                       if (IS_LOCAL(user))
                        {
                                chanrec *c = (chanrec*)dest;
                                std::string cname = c->name;
@@ -3807,7 +4065,7 @@ class ModuleSpanningTree : public Module
                }
                 else if (target_type == TYPE_SERVER)
                {
-                       if (user->fd > -1)
+                       if (IS_LOCAL(user))
                        {
                                char* target = (char*)dest;
                                std::deque<std::string> par;
@@ -3825,7 +4083,7 @@ class ModuleSpanningTree : public Module
                        // route private messages which are targetted at clients only to the server
                        // which needs to receive them
                        userrec* d = (userrec*)dest;
-                       if ((d->fd < 0) && (user->fd > -1))
+                       if ((d->fd < 0) && (IS_LOCAL(user)))
                        {
                                std::deque<std::string> params;
                                params.clear();
@@ -3836,7 +4094,7 @@ class ModuleSpanningTree : public Module
                }
                else if (target_type == TYPE_CHANNEL)
                {
-                       if (user->fd > -1)
+                       if (IS_LOCAL(user))
                        {
                                chanrec *c = (chanrec*)dest;
                                std::string cname = c->name;
@@ -3855,7 +4113,7 @@ class ModuleSpanningTree : public Module
                }
                else if (target_type == TYPE_SERVER)
                {
-                       if (user->fd > -1)
+                       if (IS_LOCAL(user))
                        {
                                char* target = (char*)dest;
                                std::deque<std::string> par;
@@ -3875,7 +4133,7 @@ class ModuleSpanningTree : public Module
        virtual void OnUserJoin(userrec* user, chanrec* channel)
        {
                // Only do this for local users
-               if (user->fd > -1)
+               if (IS_LOCAL(user))
                {
                        std::deque<std::string> params;
                        params.clear();
@@ -3904,7 +4162,7 @@ class ModuleSpanningTree : public Module
        virtual void OnChangeHost(userrec* user, const std::string &newhost)
        {
                // only occurs for local clients
-               if (user->registered != 7)
+               if (user->registered != REG_ALL)
                        return;
                std::deque<std::string> params;
                params.push_back(newhost);
@@ -3914,7 +4172,7 @@ class ModuleSpanningTree : public Module
        virtual void OnChangeName(userrec* user, const std::string &gecos)
        {
                // only occurs for local clients
-               if (user->registered != 7)
+               if (user->registered != REG_ALL)
                        return;
                std::deque<std::string> params;
                params.push_back(gecos);
@@ -3923,7 +4181,7 @@ class ModuleSpanningTree : public Module
 
        virtual void OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage)
        {
-               if (user->fd > -1)
+               if (IS_LOCAL(user))
                {
                        std::deque<std::string> params;
                        params.push_back(channel->name);
@@ -3936,7 +4194,7 @@ class ModuleSpanningTree : public Module
        virtual void OnUserConnect(userrec* user)
        {
                char agestr[MAXBUF];
-               if (user->fd > -1)
+               if (IS_LOCAL(user))
                {
                        std::deque<std::string> params;
                        snprintf(agestr,MAXBUF,"%lu",(unsigned long)user->age);
@@ -3946,7 +4204,7 @@ class ModuleSpanningTree : public Module
                        params.push_back(user->dhost);
                        params.push_back(user->ident);
                        params.push_back("+"+std::string(user->FormatModes()));
-                       params.push_back((char*)inet_ntoa(user->ip4));
+                       params.push_back(user->GetIPString());
                        params.push_back(":"+std::string(user->fullname));
                        DoOneToMany(Srv->GetServerName(),"NICK",params);
 
@@ -3962,7 +4220,7 @@ class ModuleSpanningTree : public Module
 
        virtual void OnUserQuit(userrec* user, const std::string &reason)
        {
-               if ((user->fd > -1) && (user->registered == 7))
+               if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
                {
                        std::deque<std::string> params;
                        params.push_back(":"+reason);
@@ -3979,7 +4237,7 @@ class ModuleSpanningTree : public Module
 
        virtual void OnUserPostNick(userrec* user, const std::string &oldnick)
        {
-               if (user->fd > -1)
+               if (IS_LOCAL(user))
                {
                        std::deque<std::string> params;
                        params.push_back(user->nick);
@@ -3989,7 +4247,7 @@ class ModuleSpanningTree : public Module
 
        virtual void OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason)
        {
-               if ((source) && (source->fd > -1))
+               if ((source) && (IS_LOCAL(source)))
                {
                        std::deque<std::string> params;
                        params.push_back(chan->name);
@@ -4037,7 +4295,7 @@ class ModuleSpanningTree : public Module
        // locally.
        virtual void OnOper(userrec* user, const std::string &opertype)
        {
-               if (user->fd > -1)
+               if (IS_LOCAL(user))
                {
                        std::deque<std::string> params;
                        params.push_back(opertype);
@@ -4047,7 +4305,7 @@ class ModuleSpanningTree : public Module
 
        void OnLine(userrec* source, const std::string &host, bool adding, char linetype, long duration, const std::string &reason)
        {
-               if (source->fd > -1)
+               if (IS_LOCAL(source))
                {
                        char type[8];
                        snprintf(type,8,"%cLINE",linetype);
@@ -4113,7 +4371,7 @@ class ModuleSpanningTree : public Module
 
        virtual void OnMode(userrec* user, void* dest, int target_type, const std::string &text)
        {
-               if ((user->fd > -1) && (user->registered == 7))
+               if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
                {
                        if (target_type == TYPE_USER)
                        {