/** If you make a change which breaks the protocol, increment this.
* If you completely change the protocol, completely change the number.
+ *
+ * IMPORTANT: If you make changes, document your changes here, without fail:
+ * http://www.inspircd.org/wiki/List_of_protocol_changes_between_versions
+ *
+ * Failure to document your protocol changes will result in a painfully
+ * painful death by pain. You have been warned.
*/
-const long ProtocolVersion = 1101;
+const long ProtocolVersion = 1102;
/*
* The server list in InspIRCd is maintained as two structures
*/
typedef nspace::hash_map<std::string, TreeServer*, nspace::hash<string>, irc::StrHashComp> server_hash;
+
+/** The Link class might as well be a struct,
+ * but this is C++ and we don't believe in structs (!).
+ * It holds the entire information of one <link>
+ * tag from the main config file. We maintain a list
+ * of them, and populate the list on rehash/load.
+ */
+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;
+ std::string FailOver;
+ int Timeout;
+};
+
/** Contains helper functions and variables for this module,
* and keeps them out of the global namespace
*/
/** Announce TS changes to channels on merge
*/
bool AnnounceTSChange;
+ /** Synchronize timestamps between servers
+ */
+ bool EnableTimeSync;
/** Socket bindings for listening sockets
*/
std::vector<TreeSocket*> Bindings;
~SpanningTreeUtilities();
/** Send a message from this server to one other local or remote
*/
- bool DoOneToOne(std::string prefix, std::string command, std::deque<std::string> ¶ms, std::string target);
+ bool DoOneToOne(const std::string &prefix, const std::string &command, std::deque<std::string> ¶ms, std::string target);
/** Send a message from this server to all but one other, local or remote
*/
- bool DoOneToAllButSender(std::string prefix, std::string command, std::deque<std::string> ¶ms, std::string omit);
+ bool DoOneToAllButSender(const std::string &prefix, const std::string &command, std::deque<std::string> ¶ms, std::string omit);
/** Send a message from this server to all but one other, local or remote
*/
bool DoOneToAllButSender(const char* prefix, const char* command, std::deque<std::string> ¶ms, std::string omit);
/** Send a message from this server to all others
*/
- bool DoOneToMany(std::string prefix, std::string command, std::deque<std::string> ¶ms);
+ bool DoOneToMany(const std::string &prefix, const std::string &command, std::deque<std::string> ¶ms);
/** Send a message from this server to all others
*/
bool DoOneToMany(const char* prefix, const char* command, std::deque<std::string> ¶ms);
/** Send a message from this server to all others, without doing any processing on the command (e.g. send it as-is with colons and all)
*/
- bool DoOneToAllButSenderRaw(std::string data, std::string omit, std::string prefix, irc::string command, std::deque<std::string> ¶ms);
+ bool DoOneToAllButSenderRaw(const std::string &data, const std::string &omit, const std::string &prefix, const irc::string &command, std::deque<std::string> ¶ms);
/** Read the spanningtree module's tags from the config file
*/
void ReadConfiguration(bool rebind);
void GetListOfServersForChannel(chanrec* c, std::deque<TreeServer*> &list);
/** Find a server by name
*/
- TreeServer* FindServer(std::string ServerName);
+ TreeServer* FindServer(const std::string &ServerName);
/** Find a route to a server by name
*/
- TreeServer* BestRouteTo(std::string ServerName);
+ TreeServer* BestRouteTo(const std::string &ServerName);
/** Find a server by glob mask
*/
- TreeServer* FindServerMask(std::string ServerName);
+ TreeServer* FindServerMask(const std::string &ServerName);
/** Returns true if this is a server name we recognise
*/
- bool IsServer(std::string ServerName);
+ bool IsServer(const std::string &ServerName);
/** Attempt to connect to the failover link of link x
*/
void DoFailOver(Link* x);
{
Parent = NULL;
VersionString = "";
- UserCount = OperCount = 0;
+ UserCount = ServerInstance->UserCount();
+ OperCount = ServerInstance->OperCount();
VersionString = ServerInstance->GetVersionString();
Route = NULL;
Socket = NULL; /* Fix by brain */
return Parent;
}
- void SetVersion(std::string Version)
+ void SetVersion(const std::string &Version)
{
VersionString = Version;
}
}
};
-/** The Link class might as well be a struct,
- * but this is C++ and we don't believe in structs (!).
- * It holds the entire information of one <link>
- * tag from the main config file. We maintain a list
- * of them, and populate the list on rehash/load.
- */
-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 FailOver;
-};
-
/** Yay for fast searches!
* This is hundreds of times faster than recursion
* or even scanning a linked list, especially when
* there are more than a few servers to deal with.
* (read as: lots).
*/
-TreeServer* SpanningTreeUtilities::FindServer(std::string ServerName)
+TreeServer* SpanningTreeUtilities::FindServer(const std::string &ServerName)
{
server_hash::iterator iter;
iter = serverlist.find(ServerName.c_str());
* See the comments for the constructor of TreeServer
* for more details.
*/
-TreeServer* SpanningTreeUtilities::BestRouteTo(std::string ServerName)
+TreeServer* SpanningTreeUtilities::BestRouteTo(const std::string &ServerName)
{
if (ServerName.c_str() == TreeRoot->GetName())
return NULL;
* and match each one until we get a hit. Yes its slow,
* deal with it.
*/
-TreeServer* SpanningTreeUtilities::FindServerMask(std::string ServerName)
+TreeServer* SpanningTreeUtilities::FindServerMask(const std::string &ServerName)
{
for (server_hash::iterator i = serverlist.begin(); i != serverlist.end(); i++)
{
}
/* A convenient wrapper that returns true if a server exists */
-bool SpanningTreeUtilities::IsServer(std::string ServerName)
+bool SpanningTreeUtilities::IsServer(const std::string &ServerName)
{
return (FindServer(ServerName) != NULL);
}
return result;
}
- bool Capab(std::deque<std::string> params)
+ bool Capab(const std::deque<std::string> ¶ms)
{
if (params.size() < 1)
{
* does some validation first and passes on the SQUIT to all
* other remaining servers.
*/
- void Squit(TreeServer* Current,std::string reason)
+ void Squit(TreeServer* Current, const std::string &reason)
{
if ((Current) && (Current != Utils->TreeRoot))
{
+ Event rmode((char*)Current->GetName().c_str(), (Module*)Utils->Creator, "lost_server");
+ rmode.Send(Instance);
+
std::deque<std::string> params;
params.push_back(Current->GetName());
params.push_back(":"+reason);
}
/** FMODE command - server mode with timestamp checks */
- bool ForceMode(std::string source, std::deque<std::string> ¶ms)
+ bool ForceMode(const std::string &source, std::deque<std::string> ¶ms)
{
/* Chances are this is a 1.0 FMODE without TS */
if (params.size() < 3)
}
/** FTOPIC command */
- bool ForceTopic(std::string source, std::deque<std::string> ¶ms)
+ bool ForceTopic(const std::string &source, std::deque<std::string> ¶ms)
{
if (params.size() != 4)
return true;
}
/** FJOIN, similar to TS6 SJOIN, but not quite. */
- bool ForceJoin(std::string source, std::deque<std::string> ¶ms)
+ bool ForceJoin(const std::string &source, std::deque<std::string> ¶ms)
{
/* 1.1 FJOIN works as follows:
*
/* default TS is a high value, which if we dont have this
* channel will let the other side apply their modes.
*/
- time_t ourTS = time(NULL)+600;
+ time_t ourTS = Instance->Time(true)+600;
/* Does this channel exist? if it does, get its REAL timestamp */
if (chan)
/* Lower the TS here */
if (Utils->AnnounceTSChange && chan)
chan->WriteChannelWithServ(Instance->Config->ServerName,
- "TS for %s changed from %lu to %lu", chan->name, ourTS, TS);
+ "NOTICE %s :TS for %s changed from %lu to %lu", chan->name, chan->name, ourTS, TS);
ourTS = TS;
param_list.push_back(channel);
}
/** NICK command */
- bool IntroduceClient(std::string source, std::deque<std::string> ¶ms)
+ bool IntroduceClient(const std::string &source, std::deque<std::string> ¶ms)
{
if (params.size() < 8)
return true;
*/
void SendFJoins(TreeServer* Current, chanrec* c)
{
+ std::string buffer;
+
Instance->Log(DEBUG,"Sending FJOINs to other server for %s",c->name);
char list[MAXBUF];
std::string individual_halfops = std::string(":")+this->Instance->Config->ServerName+" FMODE "+c->name+" "+ConvToStr(c->age);
char* ptr = list + dlen;
CUList *ulist = c->GetUsers();
- std::vector<userrec*> specific_halfop;
- std::vector<userrec*> specific_voice;
std::string modes = "";
std::string params = "";
if (curlen > (480-NICKMAX))
{
- this->WriteLine(list);
+ buffer.append(list).append("\r\n");
+
dlen = curlen = snprintf(list,MAXBUF,":%s FJOIN %s %lu",this->Instance->Config->ServerName,c->name,(unsigned long)c->age);
ptr = list + dlen;
ptrlen = 0;
}
if (numusers)
- this->WriteLine(list);
+ buffer.append(list).append("\r\n");
+
+ /* Sorry for the hax. Because newly created channels assume +nt,
+ * if this channel doesnt have +nt, explicitly send -n and -t for the missing modes.
+ */
+ bool inverted = false;
+ if (!c->IsModeSet('n'))
+ {
+ modes.append("-n");
+ inverted = true;
+ }
+ if (!c->IsModeSet('t'))
+ {
+ modes.append("-t");
+ inverted = true;
+ }
+ if (inverted)
+ {
+ modes.append("+");
+ }
- for (BanList::iterator b = c->bans.begin(); b != c->bans.end(); b++)
- {
+ for (BanList::iterator b = c->bans.begin(); b != c->bans.end(); b++)
+ {
modes.append("b");
- params.append(b->data).append(" ");
- }
- this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" FMODE "+c->name+" "+ConvToStr(c->age)+" +"+c->ChanModes(true)+modes+" "+params);
+ params.append(" ").append(b->data);
+
+ if (params.length() >= MAXMODES)
+ {
+ /* Wrap at MAXMODES */
+ buffer.append(":").append(this->Instance->Config->ServerName).append(" FMODE ").append(c->name).append(" ").append(ConvToStr(c->age)).append(" +").append(modes).append(params).append("\r\n");
+ modes = "";
+ params = "";
+ }
+ }
+
+ buffer.append(":").append(this->Instance->Config->ServerName).append(" FMODE ").append(c->name).append(" ").append(ConvToStr(c->age)).append(" +").append(c->ChanModes(true));
+
+ /* Only send these if there are any */
+ if (!modes.empty())
+ buffer.append("\r\n").append(":").append(this->Instance->Config->ServerName).append(" FMODE ").append(c->name).append(" ").append(ConvToStr(c->age)).append(" +").append(modes).append(params);
+
+ this->WriteLine(buffer);
}
/** Send G, Q, Z and E lines */
void SendXLines(TreeServer* Current)
{
char data[MAXBUF];
+ std::string buffer;
std::string n = this->Instance->Config->ServerName;
const char* sn = n.c_str();
int iterations = 0;
/* Yes, these arent too nice looking, but they get the job done */
for (std::vector<ZLine*>::iterator i = Instance->XLines->zlines.begin(); i != Instance->XLines->zlines.end(); i++, iterations++)
{
- snprintf(data,MAXBUF,":%s ADDLINE Z %s %s %lu %lu :%s",sn,(*i)->ipaddr,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
- this->WriteLine(data);
+ snprintf(data,MAXBUF,":%s ADDLINE Z %s %s %lu %lu :%s\r\n",sn,(*i)->ipaddr,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
+ buffer.append(data);
}
for (std::vector<QLine*>::iterator i = Instance->XLines->qlines.begin(); i != Instance->XLines->qlines.end(); i++, iterations++)
{
- snprintf(data,MAXBUF,":%s ADDLINE Q %s %s %lu %lu :%s",sn,(*i)->nick,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
- this->WriteLine(data);
+ snprintf(data,MAXBUF,":%s ADDLINE Q %s %s %lu %lu :%s\r\n",sn,(*i)->nick,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
+ buffer.append(data);
}
for (std::vector<GLine*>::iterator i = Instance->XLines->glines.begin(); i != Instance->XLines->glines.end(); i++, iterations++)
{
- snprintf(data,MAXBUF,":%s ADDLINE G %s %s %lu %lu :%s",sn,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
- this->WriteLine(data);
+ snprintf(data,MAXBUF,":%s ADDLINE G %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
+ buffer.append(data);
}
for (std::vector<ELine*>::iterator i = Instance->XLines->elines.begin(); i != Instance->XLines->elines.end(); i++, iterations++)
{
- snprintf(data,MAXBUF,":%s ADDLINE E %s %s %lu %lu :%s",sn,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
- this->WriteLine(data);
+ snprintf(data,MAXBUF,":%s ADDLINE E %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
+ buffer.append(data);
}
for (std::vector<ZLine*>::iterator i = Instance->XLines->pzlines.begin(); i != Instance->XLines->pzlines.end(); i++, iterations++)
{
- snprintf(data,MAXBUF,":%s ADDLINE Z %s %s %lu %lu :%s",sn,(*i)->ipaddr,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
- this->WriteLine(data);
+ snprintf(data,MAXBUF,":%s ADDLINE Z %s %s %lu %lu :%s\r\n",sn,(*i)->ipaddr,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
+ buffer.append(data);
}
for (std::vector<QLine*>::iterator i = Instance->XLines->pqlines.begin(); i != Instance->XLines->pqlines.end(); i++, iterations++)
{
- snprintf(data,MAXBUF,":%s ADDLINE Q %s %s %lu %lu :%s",sn,(*i)->nick,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
- this->WriteLine(data);
+ snprintf(data,MAXBUF,":%s ADDLINE Q %s %s %lu %lu :%s\r\n",sn,(*i)->nick,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
+ buffer.append(data);
}
for (std::vector<GLine*>::iterator i = Instance->XLines->pglines.begin(); i != Instance->XLines->pglines.end(); i++, iterations++)
{
- snprintf(data,MAXBUF,":%s ADDLINE G %s %s %lu %lu :%s",sn,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
- this->WriteLine(data);
+ snprintf(data,MAXBUF,":%s ADDLINE G %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
+ buffer.append(data);
}
for (std::vector<ELine*>::iterator i = Instance->XLines->pelines.begin(); i != Instance->XLines->pelines.end(); i++, iterations++)
{
- snprintf(data,MAXBUF,":%s ADDLINE E %s %s %lu %lu :%s",sn,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
- this->WriteLine(data);
+ snprintf(data,MAXBUF,":%s ADDLINE E %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
+ buffer.append(data);
}
+
+ if (!buffer.empty())
+ this->WriteLine(buffer);
}
/** Send channel modes and topics */
{
char data[MAXBUF];
std::deque<std::string> list;
+ std::string dataline;
int iterations = 0;
for (user_hash::iterator u = this->Instance->clientlist.begin(); u != this->Instance->clientlist.end(); u++, iterations++)
{
this->WriteLine(data);
if (*u->second->oper)
{
- this->WriteLine(":"+std::string(u->second->nick)+" OPERTYPE "+std::string(u->second->oper));
+ snprintf(data,MAXBUF,":%s OPERTYPE %s", u->second->nick, u->second->oper);
+ this->WriteLine(data);
}
if (*u->second->awaymsg)
{
- this->WriteLine(":"+std::string(u->second->nick)+" AWAY :"+std::string(u->second->awaymsg));
+ snprintf(data,MAXBUF,":%s AWAY :%s", u->second->nick, u->second->awaymsg);
+ this->WriteLine(data);
}
FOREACH_MOD_I(this->Instance,I_OnSyncUser,OnSyncUser(u->second,(Module*)Utils->Creator,(void*)this));
list.clear();
*/
void DoBurst(TreeServer* s)
{
- std::string burst = "BURST "+ConvToStr(time(NULL));
+ std::string burst = "BURST "+ConvToStr(Instance->Time(true));
std::string endburst = "ENDBURST";
// Because by the end of the netburst, it could be gone!
std::string name = s->GetName();
{
std::string ret = in_buffer.substr(0,in_buffer.find("\n")-1);
in_buffer = in_buffer.substr(in_buffer.find("\n")+1,in_buffer.length()-in_buffer.find("\n"));
+ /* Use rfind here not find, as theres more
+ * chance of the \r being near the end of the
+ * string, not the start.
+ */
if (ret.find("\r") != std::string::npos)
ret = in_buffer.substr(0,in_buffer.find("\r")-1);
/* Process this one, abort if it
{
ctx_in->Decrypt(out, result, nbytes, 0);
for (int t = 0; t < nbytes; t++)
- if (result[t] == '\7') result[t] = 0;
+ {
+ if (result[t] == '\7')
+ {
+ /* We only need to stick a \0 on the
+ * first \7, the rest will be lost
+ */
+ result[t] = 0;
+ break;
+ }
+ }
ret = result;
}
}
}
if (!this->ProcessLine(ret))
{
- Instance->Log(DEBUG,"ProcessLine says no!");
return false;
}
}
to64frombits((unsigned char*)result64,(unsigned char*)result,ll);
line = result64;
}
- return this->Write(line + "\r\n");
+ line.append("\r\n");
+ return this->Write(line);
}
/* Handle ERROR command */
}
/** remote MOTD. leet, huh? */
- bool Motd(std::string prefix, std::deque<std::string> ¶ms)
+ bool Motd(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() > 0)
{
}
/** remote ADMIN. leet, huh? */
- bool Admin(std::string prefix, std::deque<std::string> ¶ms)
+ bool Admin(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() > 0)
{
return true;
}
- bool Stats(std::string prefix, std::deque<std::string> ¶ms)
+ bool Stats(const std::string &prefix, std::deque<std::string> ¶ms)
{
/* Get the reply to a STATS query if it matches this servername,
* and send it back as a load of PUSH queries
/** Because the core won't let users or even SERVERS set +o,
* we use the OPERTYPE command to do this.
*/
- bool OperType(std::string prefix, std::deque<std::string> ¶ms)
+ bool OperType(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() != 1)
{
/** Because Andy insists that services-compatible servers must
* implement SVSNICK and SVSJOIN, that's exactly what we do :p
*/
- bool ForceNick(std::string prefix, std::deque<std::string> ¶ms)
+ bool ForceNick(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 3)
return true;
return true;
}
- bool ServiceJoin(std::string prefix, std::deque<std::string> ¶ms)
+ bool ServiceJoin(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 2)
return true;
return true;
}
- bool RemoteRehash(std::string prefix, std::deque<std::string> ¶ms)
+ bool RemoteRehash(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 1)
return false;
return true;
}
- bool RemoteKill(std::string prefix, std::deque<std::string> ¶ms)
+ bool RemoteKill(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() != 2)
return true;
return true;
}
- bool LocalPong(std::string prefix, std::deque<std::string> ¶ms)
+ bool LocalPong(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 1)
return true;
return true;
}
- bool MetaData(std::string prefix, std::deque<std::string> ¶ms)
+ bool MetaData(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 3)
return true;
return true;
}
- bool ServerVersion(std::string prefix, std::deque<std::string> ¶ms)
+ bool ServerVersion(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 1)
return true;
return true;
}
- bool ChangeHost(std::string prefix, std::deque<std::string> ¶ms)
+ bool ChangeHost(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 1)
return true;
return true;
}
- bool AddLine(std::string prefix, std::deque<std::string> ¶ms)
+ bool AddLine(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 6)
return true;
return true;
}
- bool ChangeName(std::string prefix, std::deque<std::string> ¶ms)
+ bool ChangeName(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 1)
return true;
return true;
}
- bool Whois(std::string prefix, std::deque<std::string> ¶ms)
+ bool Whois(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 1)
return true;
char idle[MAXBUF];
snprintf(signon,MAXBUF,"%lu",(unsigned long)x->signon);
- snprintf(idle,MAXBUF,"%lu",(unsigned long)abs((x->idle_lastmsg)-time(NULL)));
+ snprintf(idle,MAXBUF,"%lu",(unsigned long)abs((x->idle_lastmsg)-Instance->Time(true)));
std::deque<std::string> par;
par.push_back(prefix);
par.push_back(signon);
return true;
}
- bool Push(std::string prefix, std::deque<std::string> ¶ms)
+ bool Push(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 2)
return true;
return true;
}
- bool Time(std::string prefix, std::deque<std::string> ¶ms)
+ bool HandleSetTime(const std::string &prefix, std::deque<std::string> ¶ms)
+ {
+ if (!params.size() || !Utils->EnableTimeSync)
+ return true;
+
+ bool force = false;
+
+ if ((params.size() == 2) && (params[1] == "FORCE"))
+ force = true;
+
+ time_t rts = atoi(params[0].c_str());
+ time_t us = Instance->Time(true);
+
+ if (rts == us)
+ {
+ Instance->Log(DEBUG, "Timestamp from %s is equal", prefix.c_str());
+
+ Utils->DoOneToAllButSender(prefix, "TIMESET", params, prefix);
+ }
+ else if (force || (rts < us))
+ {
+ int old = Instance->SetTimeDelta(rts - us);
+ Instance->Log(DEBUG, "%s TS (diff %d) from %s applied (old delta was %d)", (force) ? "Forced" : "Lower", rts - us, prefix.c_str(), old);
+
+ Utils->DoOneToAllButSender(prefix, "TIMESET", params, prefix);
+ }
+ else
+ {
+ Instance->Log(DEBUG, "Higher TS (diff %d) from %s overridden", us - rts, prefix.c_str());
+
+ std::deque<std::string> oparams;
+ oparams.push_back(ConvToStr(us));
+
+ Utils->DoOneToMany(prefix, "TIMESET", oparams);
+ }
+
+ return true;
+ }
+
+ bool Time(const std::string &prefix, std::deque<std::string> ¶ms)
{
// :source.server TIME remote.server sendernick
// :remote.server TIME source.server sendernick TS
userrec* u = this->Instance->FindNick(params[1]);
if (u)
{
- char curtime[256];
- snprintf(curtime,256,"%lu",(unsigned long)time(NULL));
- params.push_back(curtime);
+ params.push_back(ConvToStr(Instance->Time(false)));
params[0] = prefix;
Utils->DoOneToOne(this->Instance->Config->ServerName,"TIME",params,params[0]);
}
return true;
}
- bool LocalPing(std::string prefix, std::deque<std::string> ¶ms)
+ bool LocalPing(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 1)
return true;
}
}
- bool RemoveStatus(std::string prefix, std::deque<std::string> ¶ms)
+ bool RemoveStatus(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 1)
return true;
return true;
}
- bool RemoteServer(std::string prefix, std::deque<std::string> ¶ms)
+ bool RemoteServer(const std::string &prefix, std::deque<std::string> ¶ms)
{
if (params.size() < 4)
return false;
return false;
}
- void Split(std::string line, std::deque<std::string> &n)
+ void Split(const std::string &line, std::deque<std::string> &n)
{
n.clear();
irc::tokenstream tokens(line);
return;
}
- bool ProcessLine(std::string line)
+ bool ProcessLine(std::string &line)
{
std::deque<std::string> params;
irc::string command;
}
else if (command == "BURST")
{
- if (params.size())
+ if (params.size() && Utils->EnableTimeSync)
{
- /* If a time stamp is provided, try and check syncronization */
- time_t THEM = atoi(params[0].c_str());
- long delta = THEM-time(NULL);
+ /* If a time stamp is provided, apply synchronization */
+ bool force = false;
+ time_t them = atoi(params[0].c_str());
+ time_t us = Instance->Time(true);
+ int delta = them - us;
+
+ if ((params.size() == 2) && (params[1] == "FORCE"))
+ force = true;
+
if ((delta < -600) || (delta > 600))
{
this->Instance->SNO->WriteToSnoMask('l',"\2ERROR\2: Your clocks are out by %d seconds (this is more than ten minutes). Link aborted, \2PLEASE SYNC YOUR CLOCKS!\2",abs(delta));
this->WriteLine("ERROR :Your clocks are out by "+ConvToStr(abs(delta))+" seconds (this is more than ten minutes). Link aborted, PLEASE SYNC YOUR CLOCKS!");
return false;
}
- else if ((delta < -60) || (delta > 60))
+
+ if (us == them)
{
- this->Instance->SNO->WriteToSnoMask('l',"\2WARNING\2: Your clocks are out by %d seconds, please consider synching your clocks.",abs(delta));
+ this->Instance->Log(DEBUG, "Timestamps are equal; pat yourself on the back");
+ }
+ else if (force || (us > them))
+ {
+ this->Instance->Log(DEBUG, "Remote server has lower TS (%d seconds)", them - us);
+ this->Instance->SetTimeDelta(them - us);
+ // Send this new timestamp to any other servers
+ Utils->DoOneToMany(Utils->TreeRoot->GetName(), "TIMESET", params);
+ }
+ else
+ {
+ // Override the timestamp
+ this->Instance->Log(DEBUG, "We have a higher timestamp (by %d seconds), not updating delta", us - them);
+ this->WriteLine(":" + Utils->TreeRoot->GetName() + " TIMESET " + ConvToStr(us));
}
}
this->LinkState = CONNECTED;
{
return this->Push(prefix,params);
}
+ else if (command == "TIMESET")
+ {
+ return this->HandleSetTime(prefix, params);
+ }
else if (command == "TIME")
{
return this->Time(prefix,params);
}
return true;
}
+ else if (command == "OPERNOTICE")
+ {
+ std::string sourceserv = this->myhost;
+
+ if (this->InboundServerName != "")
+ sourceserv = this->InboundServerName;
+
+ if (params.size() >= 1)
+ Instance->WriteOpers("*** From " + sourceserv + ": " + params[0]);
+
+ return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params);
+ }
+ else if (command == "MODENOTICE")
+ {
+ std::string sourceserv = this->myhost;
+ if (this->InboundServerName != "")
+ sourceserv = this->InboundServerName;
+ if (params.size() >= 2)
+ {
+ Instance->WriteMode(params[0].c_str(), WM_AND, "*** From %s: %s", sourceserv.c_str(), params[1].c_str());
+ }
+
+ return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params);
+ }
+ else if (command == "SNONOTICE")
+ {
+ std::string sourceserv = this->myhost;
+ if (this->InboundServerName != "")
+ sourceserv = this->InboundServerName;
+ if (params.size() >= 2)
+ {
+ Instance->SNO->WriteToSnoMask(*(params[0].c_str()), "From " + sourceserv + ": "+ params[1]);
+ }
+
+ return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params);
+ }
else if (command == "ENDBURST")
{
this->bursting = false;
sourceserv = this->InboundServerName;
}
this->Instance->SNO->WriteToSnoMask('l',"Received end of netburst from \2%s\2",sourceserv.c_str());
+
+ Event rmode((char*)sourceserv.c_str(), (Module*)Utils->Creator, "new_server");
+ rmode.Send(Instance);
+
return true;
}
else
{
sourceserv = this->InboundServerName;
}
+ if ((!who) && (command == "MODE"))
+ {
+ if (Utils->IsServer(prefix))
+ {
+ const char* modelist[127];
+ for (size_t i = 0; i < params.size(); i++)
+ modelist[i] = params[i].c_str();
+
+ userrec* fake = new userrec(Instance);
+ fake->SetFd(FD_MAGIC_NUMBER);
+
+ this->Instance->SendMode(modelist, params.size(), fake);
+
+ delete fake;
+
+ /* Hot potato! pass it on! */
+ return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params);
+ }
+ }
if (who)
{
if ((command == "NICK") && (params.size() > 0))
{
Squit(s,"Remote host closed the connection");
}
- this->Instance->SNO->WriteToSnoMask('l',"Connection to '\2%s\2' failed.",quitserver.c_str());
+
+ if (quitserver != "")
+ this->Instance->SNO->WriteToSnoMask('l',"Connection to '\2%s\2' failed.",quitserver.c_str());
}
virtual int OnIncomingConnection(int newsock, char* ip)
Link MyLink;
SpanningTreeUtilities* Utils;
public:
- ServernameResolver(SpanningTreeUtilities* Util, InspIRCd* Instance, const std::string &hostname, Link x) : Resolver(Instance, hostname, DNS_QUERY_FORWARD), MyLink(x), Utils(Util)
+ ServernameResolver(Module* me, SpanningTreeUtilities* Util, InspIRCd* Instance, const std::string &hostname, Link x) : Resolver(Instance, hostname, DNS_QUERY_FORWARD, me), MyLink(x), Utils(Util)
{
/* Nothing in here, folks */
}
TreeServer* CheckDupe = Utils->FindServer(MyLink.Name.c_str());
if (!CheckDupe) /* Check that nobody tried to connect it successfully while we were resolving */
{
- TreeSocket* newsocket = new TreeSocket(this->Utils, ServerInstance, result,MyLink.Port,false,10,MyLink.Name.c_str());
+ TreeSocket* newsocket = new TreeSocket(this->Utils, ServerInstance, result,MyLink.Port,false,MyLink.Timeout ? MyLink.Timeout : 10,MyLink.Name.c_str());
if (newsocket->GetFd() > -1)
{
/* We're all OK */
Link MyLink;
SpanningTreeUtilities* Utils;
public:
- SecurityIPResolver(SpanningTreeUtilities* U, InspIRCd* Instance, const std::string &hostname, Link x) : Resolver(Instance, hostname, DNS_QUERY_FORWARD), MyLink(x), Utils(U)
+ SecurityIPResolver(Module* me, SpanningTreeUtilities* U, InspIRCd* Instance, const std::string &hostname, Link x) : Resolver(Instance, hostname, DNS_QUERY_FORWARD, me), MyLink(x), Utils(U)
{
}
DELETE(sock);
}
}
+ delete TreeRoot;
}
void SpanningTreeUtilities::AddThisServer(TreeServer* server, std::deque<TreeServer*> &list)
return;
}
-bool SpanningTreeUtilities::DoOneToAllButSenderRaw(std::string data, std::string omit, std::string prefix, irc::string command, std::deque<std::string> ¶ms)
+bool SpanningTreeUtilities::DoOneToAllButSenderRaw(const std::string &data, const std::string &omit, const std::string &prefix, const irc::string &command, std::deque<std::string> ¶ms)
{
TreeServer* omitroute = this->BestRouteTo(omit);
if ((command == "NOTICE") || (command == "PRIVMSG"))
return true;
}
-bool SpanningTreeUtilities::DoOneToAllButSender(std::string prefix, std::string command, std::deque<std::string> ¶ms, std::string omit)
+bool SpanningTreeUtilities::DoOneToAllButSender(const std::string &prefix, const std::string &command, std::deque<std::string> ¶ms, std::string omit)
{
TreeServer* omitroute = this->BestRouteTo(omit);
std::string FullLine = ":" + prefix + " " + command;
return true;
}
-bool SpanningTreeUtilities::DoOneToMany(std::string prefix, std::string command, std::deque<std::string> ¶ms)
+bool SpanningTreeUtilities::DoOneToMany(const std::string &prefix, const std::string &command, std::deque<std::string> ¶ms)
{
std::string FullLine = ":" + prefix + " " + command;
unsigned int words = params.size();
return this->DoOneToAllButSender(spfx, scmd, params, omit);
}
-bool SpanningTreeUtilities::DoOneToOne(std::string prefix, std::string command, std::deque<std::string> ¶ms, std::string target)
+bool SpanningTreeUtilities::DoOneToOne(const std::string &prefix, const std::string &command, std::deque<std::string> ¶ms, std::string target)
{
TreeServer* Route = this->BestRouteTo(target);
if (Route)
{
std::string Type = Conf->ReadValue("bind","type",j);
std::string IP = Conf->ReadValue("bind","address",j);
- int Port = Conf->ReadInteger("bind","port",j,true);
+ std::string Port = Conf->ReadValue("bind","port",j);
if (Type == "servers")
{
- ServerInstance->Log(DEBUG,"m_spanningtree: Binding server port %s:%d", IP.c_str(), Port);
- if (IP == "*")
- {
- IP = "";
- }
- TreeSocket* listener = new TreeSocket(this, ServerInstance, IP.c_str(),Port,true,10);
- if (listener->GetState() == I_LISTENING)
+ irc::portparser portrange(Port, false);
+ int portno = -1;
+ while ((portno = portrange.GetToken()))
{
- ServerInstance->Log(DEFAULT,"m_spanningtree: Binding server port %s:%d successful!", IP.c_str(), Port);
- Bindings.push_back(listener);
- }
- else
- {
- ServerInstance->Log(DEFAULT,"m_spanningtree: Warning: Failed to bind server port %d",Port);
- listener->Close();
- DELETE(listener);
+ ServerInstance->Log(DEBUG,"m_spanningtree: Binding server port %s:%d", IP.c_str(), portno);
+ if (IP == "*")
+ IP = "";
+
+ TreeSocket* listener = new TreeSocket(this, ServerInstance, IP.c_str(), portno, true, 10);
+ if (listener->GetState() == I_LISTENING)
+ {
+ ServerInstance->Log(DEFAULT,"m_spanningtree: Binding server port %s:%d successful!", IP.c_str(), portno);
+ Bindings.push_back(listener);
+ }
+ else
+ {
+ ServerInstance->Log(DEFAULT,"m_spanningtree: Warning: Failed to bind server port %s:%d",IP.c_str(), portno);
+ listener->Close();
+ DELETE(listener);
+ }
+ ServerInstance->Log(DEBUG,"Done with this binding");
}
- ServerInstance->Log(DEBUG,"Done with this binding");
}
}
}
FlatLinks = Conf->ReadFlag("options","flatlinks",0);
HideULines = Conf->ReadFlag("options","hideulines",0);
AnnounceTSChange = Conf->ReadFlag("options","announcets",0);
+ EnableTimeSync = !(Conf->ReadFlag("options","notimesync",0));
LinkBlocks.clear();
ValidIPs.clear();
for (int j =0; j < Conf->Enumerate("link"); j++)
L.AutoConnect = Conf->ReadInteger("link","autoconnect",j,true);
L.EncryptionKey = Conf->ReadValue("link","encryptionkey",j);
L.HiddenFromStats = Conf->ReadFlag("link","hidden",j);
+ L.Timeout = Conf->ReadInteger("link","timeout",j,true);
L.NextConnectTime = time(NULL) + L.AutoConnect;
/* Bugfix by brain, do not allow people to enter bad configurations */
if (L.Name != ServerInstance->Config->ServerName)
{
try
{
- SecurityIPResolver* sr = new SecurityIPResolver(this, ServerInstance, L.IPAddr, L);
+ SecurityIPResolver* sr = new SecurityIPResolver((Module*)this->Creator, this, ServerInstance, L.IPAddr, L);
ServerInstance->AddResolver(sr);
}
catch (ModuleException& e)
DELETE(Conf);
}
-
+/** To create a timer which recurs every second, we inherit from InspTimer.
+ * InspTimer is only one-shot however, so at the end of each Tick() we simply
+ * insert another of ourselves into the pending queue :)
+ */
+class TimeSyncTimer : public InspTimer
+{
+ private:
+ InspIRCd *Instance;
+ ModuleSpanningTree *Module;
+ public:
+ TimeSyncTimer(InspIRCd *Instance, ModuleSpanningTree *Mod);
+ virtual void Tick(time_t TIME);
+};
class ModuleSpanningTree : public Module
{
SpanningTreeUtilities* Utils;
public:
+ TimeSyncTimer *SyncTimer;
ModuleSpanningTree(InspIRCd* Me)
: Module::Module(Me), max_local(0), max_global(0)
command_rconnect = new cmd_rconnect(ServerInstance, this, Utils);
ServerInstance->AddCommand(command_rconnect);
+
+ if (Utils->EnableTimeSync)
+ {
+ SyncTimer = new TimeSyncTimer(ServerInstance, this);
+ ServerInstance->Timers->AddTimer(SyncTimer);
+ }
+ else
+ SyncTimer = NULL;
}
void ShowLinks(TreeServer* Current, userrec* user, int hops)
if (n_users > max_global)
max_global = n_users;
- user->WriteServ("251 %s :There are %d users and %d invisible on %d servers",user->nick,n_users-ServerInstance->InvisibleUserCount(),ServerInstance->InvisibleUserCount(),this->CountServs());
+ unsigned int ulined_count = 0;
+ unsigned int ulined_local_count = 0;
+
+ /* If ulined are hidden and we're not an oper, count the number of ulined servers hidden,
+ * locally and globally (locally means directly connected to us)
+ */
+ if ((Utils->HideULines) && (!*user->oper))
+ {
+ for (server_hash::iterator q = Utils->serverlist.begin(); q != Utils->serverlist.end(); q++)
+ {
+ if (ServerInstance->ULine(q->second->GetName().c_str()))
+ {
+ ulined_count++;
+ if (q->second->GetParent() == Utils->TreeRoot)
+ ulined_local_count++;
+ }
+ }
+ }
+
+ user->WriteServ("251 %s :There are %d users and %d invisible on %d servers",user->nick,n_users-ServerInstance->InvisibleUserCount(),ServerInstance->InvisibleUserCount(),ulined_count ? this->CountServs() - ulined_count : this->CountServs());
if (ServerInstance->OperCount())
user->WriteServ("252 %s %d :operator(s) online",user->nick,ServerInstance->OperCount());
if (ServerInstance->UnregisteredUserCount())
user->WriteServ("253 %s %d :unknown connections",user->nick,ServerInstance->UnregisteredUserCount());
if (ServerInstance->ChannelCount())
user->WriteServ("254 %s %d :channels formed",user->nick,ServerInstance->ChannelCount());
- user->WriteServ("254 %s :I have %d clients and %d servers",user->nick,ServerInstance->LocalUserCount(),this->CountLocalServs());
+ user->WriteServ("254 %s :I have %d clients and %d servers",user->nick,ServerInstance->LocalUserCount(),ulined_local_count ? this->CountLocalServs() - ulined_local_count : this->CountLocalServs());
user->WriteServ("265 %s :Current Local Users: %d Max: %d",user->nick,ServerInstance->LocalUserCount(),max_local);
user->WriteServ("266 %s :Current Global Users: %d Max: %d",user->nick,n_users,max_global);
return;
/* 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(Utils, ServerInstance, x->IPAddr,x->Port,false,10,x->Name.c_str());
+ TreeSocket* newsocket = new TreeSocket(Utils, ServerInstance, x->IPAddr,x->Port,false,x->Timeout ? x->Timeout : 10,x->Name.c_str());
if (newsocket->GetFd() > -1)
{
/* Handled automatically on success */
{
try
{
- ServernameResolver* snr = new ServernameResolver(Utils, ServerInstance,x->IPAddr, *x);
+ ServernameResolver* snr = new ServernameResolver((Module*)this, Utils, ServerInstance,x->IPAddr, *x);
ServerInstance->AddResolver(snr);
}
catch (ModuleException& e)
return 1;
}
+ void BroadcastTimeSync()
+ {
+ std::deque<std::string> params;
+ params.push_back(ConvToStr(ServerInstance->Time(true)));
+ Utils->DoOneToMany(Utils->TreeRoot->GetName(), "TIMESET", params);
+ }
+
virtual int OnStats(char statschar, userrec* user, string_list &results)
{
if (statschar == 'c')
params.push_back(ConvToStr(channel->age));
params.push_back(std::string(channel->GetAllPrefixChars(user))+","+std::string(user->nick));
Utils->DoOneToMany(ServerInstance->Config->ServerName,"FJOIN",params);
+ if (channel->GetUserCounter() == 1)
+ {
+ /* First user in, sync the modes for the channel */
+ params.pop_back();
+ /* This is safe, all inspircd servers default to +nt */
+ params.push_back("+nt");
+ Utils->DoOneToMany(ServerInstance->Config->ServerName,"FMODE",params);
+ }
}
}
void OnLine(userrec* source, const std::string &host, bool adding, char linetype, long duration, const std::string &reason)
{
- if (IS_LOCAL(source))
+ if (!source)
{
- char type[8];
- snprintf(type,8,"%cLINE",linetype);
- std::string stype = type;
- if (adding)
- {
- char sduration[MAXBUF];
- snprintf(sduration,MAXBUF,"%ld",duration);
- std::deque<std::string> params;
- params.push_back(host);
- params.push_back(sduration);
- params.push_back(":"+reason);
- Utils->DoOneToMany(source->nick,stype,params);
- }
- else
+ /* Server-set lines */
+ char data[MAXBUF];
+ snprintf(data,MAXBUF,"%c %s %s %lu %lu :%s", linetype, host.c_str(), ServerInstance->Config->ServerName, (unsigned long)ServerInstance->Time(false),
+ (unsigned long)duration, reason.c_str());
+ std::deque<std::string> params;
+ params.push_back(data);
+ Utils->DoOneToMany(ServerInstance->Config->ServerName, "ADDLINE", params);
+ }
+ else
+ {
+ if (IS_LOCAL(source))
{
- std::deque<std::string> params;
- params.push_back(host);
- Utils->DoOneToMany(source->nick,stype,params);
+ char type[8];
+ snprintf(type,8,"%cLINE",linetype);
+ std::string stype = type;
+ if (adding)
+ {
+ char sduration[MAXBUF];
+ snprintf(sduration,MAXBUF,"%ld",duration);
+ std::deque<std::string> params;
+ params.push_back(host);
+ params.push_back(sduration);
+ params.push_back(":"+reason);
+ Utils->DoOneToMany(source->nick,stype,params);
+ }
+ else
+ {
+ std::deque<std::string> params;
+ params.push_back(host);
+ Utils->DoOneToMany(source->nick,stype,params);
+ }
}
}
}
return;
(*params)[1] = ":" + (*params)[1];
params->insert(params->begin() + 1,ServerInstance->Config->ServerName);
- params->insert(params->begin() + 1,ConvToStr(ServerInstance->Time()));
+ params->insert(params->begin() + 1,ConvToStr(ServerInstance->Time(true)));
Utils->DoOneToMany(ServerInstance->Config->ServerName,"FTOPIC",*params);
}
else if (event->GetEventID() == "send_mode")
params->insert(params->begin() + 1,ConvToStr(ourTS));
Utils->DoOneToMany(ServerInstance->Config->ServerName,"FMODE",*params);
}
+ else if (event->GetEventID() == "send_mode_explicit")
+ {
+ if (params->size() < 2)
+ return;
+ Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODE",*params);
+ }
+ else if (event->GetEventID() == "send_opers")
+ {
+ if (params->size() < 1)
+ return;
+ (*params)[0] = ":" + (*params)[0];
+ Utils->DoOneToMany(ServerInstance->Config->ServerName,"OPERNOTICE",*params);
+ }
+ else if (event->GetEventID() == "send_modeset")
+ {
+ if (params->size() < 2)
+ return;
+ (*params)[1] = ":" + (*params)[1];
+ Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODENOTICE",*params);
+ }
+ else if (event->GetEventID() == "send_snoset")
+ {
+ if (params->size() < 2)
+ return;
+ (*params)[1] = ":" + (*params)[1];
+ Utils->DoOneToMany(ServerInstance->Config->ServerName,"SNONOTICE",*params);
+ }
+ else if (event->GetEventID() == "send_push")
+ {
+ if (params->size() < 2)
+ return;
+
+ userrec *a = ServerInstance->FindNick((*params)[0]);
+
+ if (!a)
+ return;
+
+ (*params)[1] = ":" + (*params)[1];
+ Utils->DoOneToOne(ServerInstance->Config->ServerName, "PUSH", *params, a->server);
+ }
}
virtual ~ModuleSpanningTree()
ServerInstance->Log(DEBUG,"Performing unload of spanningtree!");
/* This will also free the listeners */
delete Utils;
+ if (SyncTimer)
+ ServerInstance->Timers->DelTimer(SyncTimer);
}
virtual Version GetVersion()
}
};
+TimeSyncTimer::TimeSyncTimer(InspIRCd *Inst, ModuleSpanningTree *Mod) : InspTimer(43200, Inst->Time()), Instance(Inst), Module(Mod)
+{
+}
+
+void TimeSyncTimer::Tick(time_t TIME)
+{
+ Module->BroadcastTimeSync();
+ Module->SyncTimer = new TimeSyncTimer(Instance, Module);
+ Instance->Timers->AddTimer(Module->SyncTimer);
+}
+
void SpanningTreeUtilities::DoFailOver(Link* x)
{
if (x->FailOver.length())
return NULL;
}
-
class ModuleSpanningTreeFactory : public ModuleFactory
{
public: