Ejemplo n.º 1
0
void ModuleSpanningTree::OnChangeName(User* user, const std::string &gecos)
{
	if (user->registered != REG_ALL || !IS_LOCAL(user))
		return;

	CmdBuilder(user, "FNAME").push_last(gecos).Broadcast();
}
Ejemplo n.º 2
0
void ModuleSpanningTree::OnChangeIdent(User* user, const std::string &ident)
{
	if ((user->registered != REG_ALL) || (!IS_LOCAL(user)))
		return;

	CmdBuilder(user, "FIDENT").push(ident).Broadcast();
}
Ejemplo n.º 3
0
void TreeServer::SQuitChild(TreeServer* server, const std::string& reason)
{
	stdalgo::erase(Children, server);

	if (IsRoot())
	{
		// Server split from us, generate a SQUIT message and broadcast it
		ServerInstance->SNO->WriteGlobalSno('l', "Server \002" + server->GetName() + "\002 split: " + reason);
		CmdBuilder("SQUIT").push(server->GetID()).push_last(reason).Broadcast();
	}
	else
	{
		ServerInstance->SNO->WriteToSnoMask('L', "Server \002" + server->GetName() + "\002 split from server \002" + GetName() + "\002 with reason: " + reason);
	}

	unsigned int num_lost_servers = 0;
	server->SQuitInternal(num_lost_servers);

	const std::string quitreason = GetName() + " " + server->GetName();
	unsigned int num_lost_users = QuitUsers(quitreason);

	ServerInstance->SNO->WriteToSnoMask(IsRoot() ? 'l' : 'L', "Netsplit complete, lost \002%u\002 user%s on \002%u\002 server%s.",
		num_lost_users, num_lost_users != 1 ? "s" : "", num_lost_servers, num_lost_servers != 1 ? "s" : "");

	// No-op if the socket is already closed (i.e. it called us)
	if (server->IsLocal())
		server->GetSocket()->Close();

	// Add the server to the cull list, the servers behind it are handled by cull() and the destructor
	ServerInstance->GlobalCulls.AddItem(server);
}
Ejemplo n.º 4
0
void ModuleSpanningTree::OnChangeHost(User* user, const std::string &newhost)
{
	if (user->registered != REG_ALL || !IS_LOCAL(user))
		return;

	CmdBuilder(user, "FHOST").push(newhost).Broadcast();
}
Ejemplo n.º 5
0
void ModuleSpanningTree::OnUserQuit(User* user, const std::string &reason, const std::string &oper_message)
{
	if (IS_LOCAL(user))
	{
		if (oper_message != reason)
			ServerInstance->PI->SendMetaData(user, "operquit", oper_message);

		CmdBuilder(user, "QUIT").push_last(reason).Broadcast();
	}
	else
	{
		// Hide the message if one of the following is true:
		// - User is being quit due to a netsplit and quietbursts is on
		// - Server is a silent uline
		TreeServer* server = TreeServer::Get(user);
		bool hide = (((server->IsDead()) && (Utils->quiet_bursts)) || (server->IsSilentULine()));
		if (!hide)
		{
			ServerInstance->SNO->WriteToSnoMask('Q', "Client exiting on server %s: %s (%s) [%s]",
				user->server->GetName().c_str(), user->GetFullRealHost().c_str(), user->GetIPString().c_str(), oper_message.c_str());
		}
	}

	// Regardless, We need to modify the user Counts..
	TreeServer::Get(user)->UserCount--;
}
Ejemplo n.º 6
0
void ModuleSpanningTree::DoPingChecks(time_t curtime)
{
	/*
	 * Cancel remote burst mode on any servers which still have it enabled due to latency/lack of data.
	 * This prevents lost REMOTECONNECT notices
	 */
	long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);

restart:
	for (server_hash::iterator i = Utils->serverlist.begin(); i != Utils->serverlist.end(); i++)
	{
		TreeServer *s = i->second;

		// Skip myself
		if (s->IsRoot())
			continue;

		// Do not ping servers that are not fully connected yet!
		// Servers which are connected to us have IsLocal() == true and if they're fully connected
		// then Socket->LinkState == CONNECTED. Servers that are linked to another server are always fully connected.
		if (s->IsLocal() && s->GetSocket()->GetLinkState() != CONNECTED)
			continue;

		// Now do PING checks on all servers
		// Only ping if this server needs one
		if (curtime >= s->NextPingTime())
		{
			// And if they answered the last
			if (s->AnsweredLastPing())
			{
				// They did, send a ping to them
				s->SetNextPingTime(curtime + Utils->PingFreq);
				s->GetSocket()->WriteLine(CmdBuilder("PING").push(s->GetID()));
				s->LastPingMsec = ts;
			}
			else
			{
				// They didn't answer the last ping, if they are locally connected, get rid of them.
				if (s->IsLocal())
				{
					TreeSocket* sock = s->GetSocket();
					sock->SendError("Ping timeout");
					sock->Close();
					goto restart;
				}
			}
		}

		// If warn on ping enabled and not warned and the difference is sufficient and they didn't answer the last ping...
		if ((Utils->PingWarnTime) && (!s->Warned) && (curtime >= s->NextPingTime() - (Utils->PingFreq - Utils->PingWarnTime)) && (!s->AnsweredLastPing()))
		{
			/* The server hasnt responded, send a warning to opers */
			ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 has not responded to PING for %d seconds, high latency.", s->GetName().c_str(), Utils->PingWarnTime);
			s->Warned = true;
		}
	}
}
Ejemplo n.º 7
0
void SpanningTreeProtocolInterface::BroadcastEncap(const std::string& cmd, const parameterlist& params, User* source, User* omit)
{
	if (!source)
		source = ServerInstance->FakeClient;

	// If omit is non-NULL we pass the route belonging to the user to Forward(),
	// otherwise we pass NULL, which is equivalent to Broadcast()
	TreeServer* server = (omit ? TreeServer::Get(omit)->GetRoute() : NULL);
	CmdBuilder(source, "ENCAP * ").push_raw(cmd).insert(params).Forward(server);
}
Ejemplo n.º 8
0
void ModuleSpanningTree::OnUserQuit(User* user, const std::string &reason, const std::string &oper_message)
{
	if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
	{
		if (oper_message != reason)
			ServerInstance->PI->SendMetaData(user, "operquit", oper_message);

		CmdBuilder(user, "QUIT").push_last(reason).Broadcast();
	}

	// Regardless, We need to modify the user Counts..
	TreeServer* SourceServer = Utils->FindServer(user->server);
	if (SourceServer)
	{
		SourceServer->UserCount--;
	}
}
Ejemplo n.º 9
0
ModResult ModuleSpanningTree::HandleRemoteWhois(const std::vector<std::string>& parameters, User* user)
{
	if ((IS_LOCAL(user)) && (parameters.size() > 1))
	{
		User* remote = ServerInstance->FindNickOnly(parameters[1]);
		if (remote && !IS_LOCAL(remote))
		{
			CmdBuilder(user, "IDLE").push(remote->uuid).Unicast(remote);
			return MOD_RES_DENY;
		}
		else if (!remote)
		{
			user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[1].c_str());
			user->WriteNumeric(318, "%s %s :End of /WHOIS list.",user->nick.c_str(), parameters[1].c_str());
			return MOD_RES_DENY;
		}
	}
	return MOD_RES_PASSTHRU;
}
Ejemplo n.º 10
0
CmdResult CommandIJoin::HandleRemote(RemoteUser* user, std::vector<std::string>& params)
{
	Channel* chan = ServerInstance->FindChan(params[0]);
	if (!chan)
	{
		// Desync detected, recover
		// Ignore the join and send RESYNC, this will result in the remote server sending all channel data to us
		ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Received IJOIN for non-existant channel: " + params[0]);

		CmdBuilder("RESYNC").push(params[0]).Unicast(user);

		return CMD_FAILURE;
	}

	bool apply_modes;
	if (params.size() > 1)
	{
		time_t RemoteTS = ConvToInt(params[1]);
		if (!RemoteTS)
		{
			ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Invalid TS in IJOIN: " + params[1]);
			return CMD_INVALID;
		}

		if (RemoteTS < chan->age)
		{
			ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Attempted to lower TS via IJOIN. Channel=" + params[0] + " RemoteTS=" + params[1] + " LocalTS=" + ConvToStr(chan->age));
			return CMD_INVALID;
		}
		apply_modes = ((params.size() > 2) && (RemoteTS == chan->age));
	}
	else
		apply_modes = false;

	chan->ForceJoin(user, apply_modes ? &params[2] : NULL);
	return CMD_SUCCESS;
}
Ejemplo n.º 11
0
void SpanningTreeProtocolInterface::SendSNONotice(char snomask, const std::string &text)
{
	CmdBuilder("SNONOTICE").push(snomask).push_last(text).Broadcast();
}
Ejemplo n.º 12
0
void TreeSocket::WriteLine(const std::string& original_line)
{
	if (LinkState == CONNECTED)
	{
		if (original_line.c_str()[0] != ':')
		{
			ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Sending line without server prefix!");
			WriteLine(":" + ServerInstance->Config->GetSID() + " " + original_line);
			return;
		}
		if (proto_version != ProtocolVersion)
		{
			std::string line = original_line;
			std::string::size_type a = line.find(' ');
			std::string::size_type b = line.find(' ', a + 1);
			std::string command = line.substr(a + 1, b-a-1);
			// now try to find a translation entry
			// TODO a more efficient lookup method will be needed later
			if (proto_version < 1205)
			{
				if (command == "IJOIN")
				{
					// Convert
					// :<uid> IJOIN <chan> <membid> [<ts> [<flags>]]
					// to
					// :<sid> FJOIN <chan> <ts> + [<flags>],<uuid>
					std::string::size_type c = line.find(' ', b + 1);
					if (c == std::string::npos)
						return;

					std::string::size_type d = line.find(' ', c + 1);
					// Erase membership id first
					line.erase(c, d-c);
					if (d == std::string::npos)
					{
						// No TS or modes in the command
						// :22DAAAAAB IJOIN #chan
						const std::string channame = line.substr(b+1, c-b-1);
						Channel* chan = ServerInstance->FindChan(channame);
						if (!chan)
							return;

						line.push_back(' ');
						line.append(ConvToStr(chan->age));
						line.append(" + ,");
					}
					else
					{
						d = line.find(' ', c + 1);
						if (d == std::string::npos)
						{
							// TS present, no modes
							// :22DAAAAAC IJOIN #chan 12345
							line.append(" + ,");
						}
						else
						{
							// Both TS and modes are present
							// :22DAAAAAC IJOIN #chan 12345 ov
							std::string::size_type e = line.find(' ', d + 1);
							if (e != std::string::npos)
								line.erase(e);

							line.insert(d, " +");
							line.push_back(',');
						}
					}

					// Move the uuid to the end and replace the I with an F
					line.append(line.substr(1, 9));
					line.erase(4, 6);
					line[5] = 'F';
				}
				else if (command == "RESYNC")
					return;
				else if (command == "METADATA")
				{
					// Drop TS for channel METADATA, translate METADATA operquit into an OPERQUIT command
					// :sid METADATA #target TS extname ...
					//     A        B       C  D
					if (b == std::string::npos)
						return;

					std::string::size_type c = line.find(' ', b + 1);
					if (c == std::string::npos)
						return;

					std::string::size_type d = line.find(' ', c + 1);
					if (d == std::string::npos)
						return;

					if (line[b + 1] == '#')
					{
						// We're sending channel metadata
						line.erase(c, d-c);
					}
					else if (!line.compare(c, d-c, " operquit", 9))
					{
						// ":22D METADATA 22DAAAAAX operquit :message" -> ":22DAAAAAX OPERQUIT :message"
						line = ":" + line.substr(b+1, c-b) + "OPERQUIT" + line.substr(d);
					}
				}
				else if (command == "FTOPIC")
				{
					// Drop channel TS for FTOPIC
					// :sid FTOPIC #target TS TopicTS setter :newtopic
					//     A      B       C  D       E      F
					// :uid FTOPIC #target TS TopicTS :newtopic
					//     A      B       C  D       E
					if (b == std::string::npos)
						return;

					std::string::size_type c = line.find(' ', b + 1);
					if (c == std::string::npos)
						return;

					std::string::size_type d = line.find(' ', c + 1);
					if (d == std::string::npos)
						return;

					std::string::size_type e = line.find(' ', d + 1);
					if (line[e+1] == ':')
					{
						line.erase(c, e-c);
						line.erase(a+1, 1);
					}
					else
						line.erase(c, d-c);
				}
				else if ((command == "PING") || (command == "PONG"))
				{
					// :22D PING 20D
					if (line.length() < 13)
						return;

					// Insert the source SID (and a space) between the command and the first parameter
					line.insert(10, line.substr(1, 4));
				}
				else if (command == "OPERTYPE")
				{
					std::string::size_type colon = line.find(':', b);
					if (colon != std::string::npos)
					{
						for (std::string::iterator i = line.begin()+colon; i != line.end(); ++i)
						{
							if (*i == ' ')
								*i = '_';
						}
						line.erase(colon, 1);
					}
				}
				else if (command == "INVITE")
				{
					// :22D INVITE 22DAAAAAN #chan TS ExpirationTime
					//     A      B         C     D  E
					if (b == std::string::npos)
						return;

					std::string::size_type c = line.find(' ', b + 1);
					if (c == std::string::npos)
						return;

					std::string::size_type d = line.find(' ', c + 1);
					if (d == std::string::npos)
						return;

					std::string::size_type e = line.find(' ', d + 1);
					// If there is no expiration time then everything will be erased from 'd'
					line.erase(d, e-d);
				}
				else if (command == "FJOIN")
				{
					// Strip membership ids
					// :22D FJOIN #chan 1234 +f 4:3 :o,22DAAAAAB:15 o,22DAAAAAA:15
					// :22D FJOIN #chan 1234 +f 4:3 o,22DAAAAAB:15
					// :22D FJOIN #chan 1234 +Pf 4:3 :

					// If the last parameter is prefixed by a colon then it's a userlist which may have 0 or more users;
					// if it isn't, then it is a single member
					std::string::size_type spcolon = line.find(" :");
					if (spcolon != std::string::npos)
					{
						spcolon++;
						// Loop while there is a ':' in the userlist, this is never true if the channel is empty
						std::string::size_type pos = std::string::npos;
						while ((pos = line.rfind(':', pos-1)) > spcolon)
						{
							// Find the next space after the ':'
							std::string::size_type sp = line.find(' ', pos);
							// Erase characters between the ':' and the next space after it, including the ':' but not the space;
							// if there is no next space, everything will be erased between pos and the end of the line
							line.erase(pos, sp-pos);
						}
					}
					else
					{
						// Last parameter is a single member
						std::string::size_type sp = line.rfind(' ');
						std::string::size_type colon = line.find(':', sp);
						line.erase(colon);
					}
				}
				else if (command == "KICK")
				{
					// Strip membership id if the KICK has one
					if (b == std::string::npos)
						return;

					std::string::size_type c = line.find(' ', b + 1);
					if (c == std::string::npos)
						return;

					std::string::size_type d = line.find(' ', c + 1);
					if ((d < line.size()-1) && (original_line[d+1] != ':'))
					{
						// There is a third parameter which doesn't begin with a colon, erase it
						std::string::size_type e = line.find(' ', d + 1);
						line.erase(d, e-d);
					}
				}
				else if (command == "SINFO")
				{
					// :22D SINFO version :InspIRCd-2.2
					//     A     B       C
					std::string::size_type c = line.find(' ', b + 1);
					if (c == std::string::npos)
						return;

					// Only translating SINFO version, discard everything else
					if (line.compare(b, 9, " version ", 9))
						return;

					line = line.substr(0, 5) + "VERSION" + line.substr(c);
				}
				else if (command == "SERVER")
				{
					// :001 SERVER inspircd.test 002 [<anything> ...] :gecos
					//     A      B             C
					std::string::size_type c = line.find(' ', b + 1);
					if (c == std::string::npos)
						return;

					std::string::size_type d = c + 4;
					std::string::size_type spcolon = line.find(" :", d);
					if (spcolon == std::string::npos)
						return;

					line.erase(d, spcolon-d);
					line.insert(c, " * 0");

					if (burstsent)
					{
						WriteLineNoCompat(line);

						// Synthesize a :<newserver> BURST <time> message
						spcolon = line.find(" :");
						line = CmdBuilder(line.substr(spcolon-3, 3), "BURST").push_int(ServerInstance->Time()).str();
					}
				}
			}
			WriteLineNoCompat(line);
			return;
		}
	}

	WriteLineNoCompat(original_line);
}
Ejemplo n.º 13
0
void TreeSocket::ProcessLine(std::string &line)
{
	std::string prefix;
	std::string command;
	parameterlist params;

	ServerInstance->Logs->Log(MODNAME, LOG_RAWIO, "S[%d] I %s", this->GetFd(), line.c_str());

	Split(line, prefix, command, params);

	if (command.empty())
		return;

	switch (this->LinkState)
	{
		case WAIT_AUTH_1:
			/*
			 * State WAIT_AUTH_1:
			 *  Waiting for SERVER command from remote server. Server initiating
			 *  the connection sends the first SERVER command, listening server
			 *  replies with theirs if its happy, then if the initiator is happy,
			 *  it starts to send its net sync, which starts the merge, otherwise
			 *  it sends an ERROR.
			 */
			if (command == "PASS")
			{
				/*
				 * Ignore this silently. Some services packages insist on sending PASS, even
				 * when it is not required (i.e. by us). We have to ignore this here, otherwise
				 * as it's an unknown command (effectively), it will cause the connection to be
				 * closed, which probably isn't what people want. -- w00t
				 */
			}
			else if (command == "SERVER")
			{
				this->Inbound_Server(params);
			}
			else if (command == "ERROR")
			{
				this->Error(params);
			}
			else if (command == "USER")
			{
				this->SendError("Client connections to this port are prohibited.");
			}
			else if (command == "CAPAB")
			{
				this->Capab(params);
			}
			else
			{
				this->SendError("Invalid command in negotiation phase: " + command);
			}
		break;
		case WAIT_AUTH_2:
			/*
			 * State WAIT_AUTH_2:
			 *  We have sent SERVER to the other side of the connection. Now we're waiting for them to start BURST.
			 *  The other option at this stage of things, of course, is for them to close our connection thanks
			 *  to invalid credentials.. -- w
			 */
			if (command == "SERVER")
			{
				/*
				 * Connection is either attempting to re-auth itself (stupid) or sending netburst without sending BURST.
				 * Both of these aren't allowable, so block them here. -- w
				 */
				this->SendError("You may not re-authenticate or commence netburst without sending BURST.");
			}
			else if (command == "BURST")
			{
				if (params.size())
				{
					time_t them = ConvToInt(params[0]);
					time_t delta = them - ServerInstance->Time();
					if ((delta < -600) || (delta > 600))
					{
						ServerInstance->SNO->WriteGlobalSno('l',"\2ERROR\2: Your clocks are out by %d seconds (this is more than five minutes). Link aborted, \2PLEASE SYNC YOUR CLOCKS!\2",abs((long)delta));
						SendError("Your clocks are out by "+ConvToStr(abs((long)delta))+" seconds (this is more than five minutes). Link aborted, PLEASE SYNC YOUR CLOCKS!");
						return;
					}
					else if ((delta < -30) || (delta > 30))
					{
						ServerInstance->SNO->WriteGlobalSno('l',"\2WARNING\2: Your clocks are out by %d seconds. Please consider synching your clocks.", abs((long)delta));
					}
				}

				// Check for duplicate server name/sid again, it's possible that a new
				// server was introduced while we were waiting for them to send BURST.
				// (we do not reserve their server name/sid when they send SERVER, we do it now)
				if (!CheckDuplicate(capab->name, capab->sid))
					return;

				this->LinkState = CONNECTED;
				Utils->timeoutlist.erase(this);

				linkID = capab->name;

				MyRoot = new TreeServer(capab->name, capab->description, capab->sid, Utils->TreeRoot, this, capab->hidden);
				Utils->TreeRoot->AddChild(MyRoot);

				MyRoot->bursting = true;
				this->DoBurst(MyRoot);

				CommandServer::Builder(MyRoot).Forward(MyRoot);
				CmdBuilder(MyRoot->GetID(), "BURST").insert(params).Forward(MyRoot);
			}
			else if (command == "ERROR")
			{
				this->Error(params);
			}
			else if (command == "CAPAB")
			{
				this->Capab(params);
			}

		break;
		case CONNECTING:
			/*
			 * State CONNECTING:
			 *  We're connecting (OUTGOING) to another server. They are in state WAIT_AUTH_1 until they verify
			 *  our credentials, when they proceed into WAIT_AUTH_2 and send SERVER to us. We then send BURST
			 *  + our netburst, which will put them into CONNECTED state. -- w
			 */
			if (command == "SERVER")
			{
				// Our credentials have been accepted, send netburst. (this puts US into the CONNECTED state)
				this->Outbound_Reply_Server(params);
			}
			else if (command == "ERROR")
			{
				this->Error(params);
			}
			else if (command == "CAPAB")
			{
				this->Capab(params);
			}
		break;
		case CONNECTED:
			/*
			 * State CONNECTED:
			 *  Credentials have been exchanged, we've gotten their 'BURST' (or sent ours).
			 *  Anything from here on should be accepted a little more reasonably.
			 */
			this->ProcessConnectedLine(prefix, command, params);
		break;
		case DYING:
		break;
	}
}
Ejemplo n.º 14
0
void SpanningTreeProtocolInterface::PushToClient(User* target, const std::string &rawline)
{
	CmdBuilder("PUSH").push(target->uuid).push_last(rawline).Unicast(target);
}