Example #1
0
void TreeSocket::ProcessConnectedLine(std::string& prefix, std::string& command, parameterlist& params)
{
	User* who = ServerInstance->FindUUID(prefix);
	std::string direction;

	if (!who)
	{
		TreeServer* ServerSource = Utils->FindServer(prefix);
		if (prefix.empty())
			ServerSource = MyRoot;

		if (ServerSource)
		{
			who = ServerSource->ServerUser;
		}
		else
		{
			/* It is important that we don't close the link here, unknown prefix can occur
			 * due to various race conditions such as the KILL message for a user somehow
			 * crossing the users QUIT further upstream from the server. Thanks jilles!
			 */

			if ((prefix.length() == UUID_LENGTH-1) && (isdigit(prefix[0])) &&
				((command == "FMODE") || (command == "MODE") || (command == "KICK") || (command == "TOPIC") || (command == "KILL") || (command == "ADDLINE") || (command == "DELLINE")))
			{
				/* Special case, we cannot drop these commands as they've been committed already on a
				 * part of the network by the time we receive them, so in this scenario pretend the
				 * command came from a server to avoid desync.
				 */

				who = ServerInstance->FindUUID(prefix.substr(0, 3));
				if (!who)
					who = this->MyRoot->ServerUser;
			}
			else
			{
				ServerInstance->Logs->Log("m_spanningtree", DEBUG, "Command '%s' from unknown prefix '%s'! Dropping entire command.",
					command.c_str(), prefix.c_str());
				return;
			}
		}
	}

	// Make sure prefix is still good
	direction = who->server;
	prefix = who->uuid;

	/*
	 * Check for fake direction here, and drop any instances that are found.
	 * What is fake direction? Imagine the following server setup:
	 *    0AA <-> 0AB <-> 0AC
	 * Fake direction would be 0AC sending a message to 0AB claiming to be from
	 * 0AA, or something similar. Basically, a message taking a path that *cannot*
	 * be correct.
	 *
	 * When would this be seen?
	 * Well, hopefully never. It could be caused by race conditions, bugs, or
	 * "miscreant" servers, though, so let's check anyway. -- w
	 *
	 * We also check here for totally invalid prefixes (prefixes that are neither
	 * a valid SID or a valid UUID, so that invalid UUID or SID never makes it
	 * to the higher level functions. -- B
	 */
	TreeServer* route_back_again = Utils->BestRouteTo(direction);
	if ((!route_back_again) || (route_back_again->GetSocket() != this))
	{
		if (route_back_again)
			ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Protocol violation: Fake direction '%s' from connection '%s'",
				prefix.c_str(),linkID.c_str());
		return;
	}

	/*
	 * First up, check for any malformed commands (e.g. MODE without a timestamp)
	 * and rewrite commands where necessary (SVSMODE -> MODE for services). -- w
	 */
	if (command == "SVSMODE") // This isn't in an "else if" so we still force FMODE for changes on channels.
		command = "MODE";

	// TODO move all this into Commands
	if (command == "MAP")
	{
		Utils->Creator->HandleMap(params, who);
	}
	else if (command == "SERVER")
	{
		this->RemoteServer(prefix,params);
	}
	else if (command == "ERROR")
	{
		this->Error(params);
	}
	else if (command == "AWAY")
	{
		this->Away(prefix,params);
	}
	else if (command == "PING")
	{
		this->LocalPing(prefix,params);
	}
	else if (command == "PONG")
	{
		TreeServer *s = Utils->FindServer(prefix);
		if (s && s->bursting)
		{
			ServerInstance->SNO->WriteGlobalSno('l',"Server \002%s\002 has not finished burst, forcing end of burst (send ENDBURST!)", prefix.c_str());
			s->FinishBurst();
		}
		this->LocalPong(prefix,params);
	}
	else if (command == "VERSION")
	{
		this->ServerVersion(prefix,params);
	}
	else if (command == "ADDLINE")
	{
		this->AddLine(prefix,params);
	}
	else if (command == "DELLINE")
	{
		this->DelLine(prefix,params);
	}
	else if (command == "SAVE")
	{
		this->ForceNick(prefix,params);
	}
	else if (command == "OPERQUIT")
	{
		this->OperQuit(prefix,params);
	}
	else if (command == "IDLE")
	{
		this->Whois(prefix,params);
	}
	else if (command == "PUSH")
	{
		this->Push(prefix,params);
	}
	else if (command == "SQUIT")
	{
		if (params.size() == 2)
		{
			this->Squit(Utils->FindServer(params[0]),params[1]);
		}
	}
	else if (command == "SNONOTICE")
	{
		if (params.size() >= 2)
		{
			ServerInstance->SNO->WriteToSnoMask(params[0][0], "From " + who->nick + ": "+ params[1]);
			params[1] = ":" + params[1];
			Utils->DoOneToAllButSender(prefix, command, params, prefix);
		}
	}
	else if (command == "BURST")
	{
		// Set prefix server as bursting
		TreeServer* ServerSource = Utils->FindServer(prefix);
		if (!ServerSource)
		{
			ServerInstance->SNO->WriteGlobalSno('l', "WTF: Got BURST from a non-server(?): %s", prefix.c_str());
			return;
		}

		ServerSource->bursting = true;
		Utils->DoOneToAllButSender(prefix, command, params, prefix);
	}
	else if (command == "ENDBURST")
	{
		TreeServer* ServerSource = Utils->FindServer(prefix);
		if (!ServerSource)
		{
			ServerInstance->SNO->WriteGlobalSno('l', "WTF: Got ENDBURST from a non-server(?): %s", prefix.c_str());
			return;
		}

		ServerSource->FinishBurst();
		Utils->DoOneToAllButSender(prefix, command, params, prefix);
	}
	else if (command == "ENCAP")
	{
		this->Encap(who, params);
	}
	else if (command == "NICK")
	{
		if (params.size() != 2)
		{
			SendError("Protocol violation: Wrong number of parameters for NICK message");
			return;
		}

		if (IS_SERVER(who))
		{
			SendError("Protocol violation: Server changing nick");
			return;
		}

		if ((isdigit(params[0][0])) && (params[0] != who->uuid))
		{
			SendError("Protocol violation: User changing nick to an invalid UID - " + params[0]);
			return;
		}

		/* Update timestamp on user when they change nicks */
		who->age = atoi(params[1].c_str());

		/*
		 * On nick messages, check that the nick doesnt already exist here.
		 * If it does, perform collision logic.
		 */
		User* x = ServerInstance->FindNickOnly(params[0]);
		if ((x) && (x != who))
		{
			int collideret = 0;
			/* x is local, who is remote */
			collideret = this->DoCollision(x, who->age, who->ident, who->GetIPString(), who->uuid);
			if (collideret != 1)
			{
				/*
				 * Remote client lost, or both lost, parsing or passing on this
				 * nickchange would be pointless, as the incoming client's server will
				 * soon recieve SVSNICK to change its nick to its UID. :) -- w00t
				 */
				return;
			}
		}
		who->ForceNickChange(params[0].c_str());
		Utils->RouteCommand(route_back_again, command, params, who);
	}
	else
	{
		Command* cmd = ServerInstance->Parser->GetHandler(command);

		if (!cmd)
		{
			irc::stringjoiner pmlist(" ", params, 0, params.size() - 1);
			ServerInstance->Logs->Log("m_spanningtree", SPARSE, "Unrecognised S2S command :%s %s %s",
				who->uuid.c_str(), command.c_str(), pmlist.GetJoined().c_str());
			SendError("Unrecognised command '" + command + "' -- possibly loaded mismatched modules");
			return;
		}

		if (params.size() < cmd->min_params)
		{
			irc::stringjoiner pmlist(" ", params, 0, params.size() - 1);
			ServerInstance->Logs->Log("m_spanningtree", SPARSE, "Insufficient parameters for S2S command :%s %s %s",
				who->uuid.c_str(), command.c_str(), pmlist.GetJoined().c_str());
			SendError("Insufficient parameters for command '" + command + "'");
			return;
		}

		if ((!params.empty()) && (params.back().empty()) && (!cmd->allow_empty_last_param))
		{
			// the last param is empty and the command handler doesn't allow that, check if there will be enough params if we drop the last
			if (params.size()-1 < cmd->min_params)
				return;
			params.pop_back();
		}

		CmdResult res = cmd->Handle(params, who);

		if (res == CMD_INVALID)
		{
			irc::stringjoiner pmlist(" ", params, 0, params.size() - 1);
			ServerInstance->Logs->Log("m_spanningtree", SPARSE, "Error handling S2S command :%s %s %s",
				who->uuid.c_str(), command.c_str(), pmlist.GetJoined().c_str());
			SendError("Error handling '" + command + "' -- possibly loaded mismatched modules");
		}
		else if (res == CMD_SUCCESS)
			Utils->RouteCommand(route_back_again, command, params, who);
	}
}