Exemplo n.º 1
0
Arquivo: client.c Projeto: kazcw/pande
/**
 * Introduce a new user or service client in the network.
 *
 * @param From Remote server introducing the client or NULL (local).
 * @param Client New client.
 * @param Type Type of the client (CLIENT_USER or CLIENT_SERVICE).
 */
GLOBAL void
Client_Introduce(CLIENT *From, CLIENT *Client, int Type)
{
	/* Set client type (user or service) */
	Client_SetType(Client, Type);

	if (From) {
		if (Conf_NickIsService(Conf_GetServer(Client_Conn(From)),
				   Client_ID(Client)))
			Client_SetType(Client, CLIENT_SERVICE);
		LogDebug("%s \"%s\" (+%s) registered (via %s, on %s, %d hop%s).",
			 Client_TypeText(Client), Client_Mask(Client),
			 Client_Modes(Client), Client_ID(From),
			 Client_ID(Client_Introducer(Client)),
			 Client_Hops(Client), Client_Hops(Client) > 1 ? "s": "");
	} else {
		Log(LOG_NOTICE, "%s \"%s\" registered (connection %d).",
		    Client_TypeText(Client), Client_Mask(Client),
		    Client_Conn(Client));
		Log_ServerNotice('c', "Client connecting: %s (%s@%s) [%s] - %s",
			         Client_ID(Client), Client_User(Client),
				 Client_Hostname(Client),
				 Conn_IPA(Client_Conn(Client)),
				 Client_TypeText(Client));
	}

	/* Inform other servers */
	IRC_WriteStrServersPrefixFlag_CB(From,
				From != NULL ? From : Client_ThisServer(),
				'\0', cb_introduceClient, (void *)Client);
} /* Client_Introduce */
Exemplo n.º 2
0
/**
 * Handler for the IRC "PART" command.
 *
 * @param Client The client from which this command has been received.
 * @param Req Request structure with prefix and all parameters.
 * @return CONNECTED or DISCONNECTED.
 */
GLOBAL bool
IRC_PART(CLIENT * Client, REQUEST * Req)
{
	CLIENT *target;
	char *chan;

	assert(Client != NULL);
	assert(Req != NULL);

	_IRC_GET_SENDER_OR_RETURN_(target, Req, Client)

	/* Loop over all the given channel names */
	chan = strtok(Req->argv[0], ",");

	/* Make sure that "chan" is not the empty string ("PART :") */
	if (!chan)
		return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
					  Client_ID(Client), Req->command);

	while (chan) {
		Channel_Part(target, Client, chan,
			     Req->argc > 1 ? Req->argv[1] : Client_ID(target));
		chan = strtok(NULL, ",");
	}

	/* Update idle time, if local client */
	if (Client_Conn(Client) > NONE)
		Conn_UpdateIdle(Client_Conn(Client));

	return CONNECTED;
} /* IRC_PART */
Exemplo n.º 3
0
/**
 * Kill an client identified by its nick name.
 *
 * Please note that after killig a client, its CLIENT cond CONNECTION
 * structures are invalid. So the caller must make sure on its own not to
 * access data of probably killed clients after calling this function!
 *
 * @param Client The client from which the command leading to the KILL has
 *		been received, or NULL. The KILL will no be forwarded in this
 *		direction. Only relevant when From is set, too.
 * @param From The client from which the command originated, or NULL for
		the local server.
 * @param Nick The nick name to kill.
 * @param Reason Text to send as reason to the client and other servers.
 */
GLOBAL bool
IRC_KillClient(CLIENT *Client, CLIENT *From, const char *Nick, const char *Reason)
{
	const char *msg;
	CONN_ID my_conn, conn;
	CLIENT *c;

	/* Do we know such a client in the network? */
	c = Client_Search(Nick);
	if (!c) {
		LogDebug("Client with nick \"%s\" is unknown, not forwaring.", Nick);
		return CONNECTED;
	}

	/* Inform other servers */
	IRC_WriteStrServersPrefix(From ? Client : NULL,
				  From ? From : Client_ThisServer(),
				  "KILL %s :%s", Nick, Reason);

	if (Client_Type(c) != CLIENT_USER && Client_Type(c) != CLIENT_GOTNICK) {
		/* Target of this KILL is not a regular user, this is
		 * invalid! So we ignore this case if we received a
		 * regular KILL from the network and try to kill the
		 * client/connection anyway (but log an error!) if the
		 * origin is the local server. */

		if (Client != Client_ThisServer()) {
			/* Invalid KILL received from remote */
			if (Client_Type(c) == CLIENT_SERVER)
				msg = ERR_CANTKILLSERVER_MSG;
			else
				msg = ERR_NOPRIVILEGES_MSG;
			return IRC_WriteErrClient(Client, msg, Client_ID(Client));
		}

		Log(LOG_ERR,
		    "Got KILL for invalid client type: %d, \"%s\"!",
		    Client_Type(c), Nick);
	}

	/* Save ID of this connection */
	my_conn = Client_Conn(Client);

	/* Kill the client NOW:
	 *  - Close the local connection (if there is one),
	 *  - Destroy the CLIENT structure for remote clients.
	 * Note: Conn_Close() removes the CLIENT structure as well. */
	conn = Client_Conn(c);
	if(conn > NONE)
		Conn_Close(conn, NULL, Reason, true);
	else
		Client_Destroy(c, NULL, Reason, false);

	/* Are we still connected or were we killed, too? */
	if (my_conn > NONE && Conn_GetClient(my_conn))
		return CONNECTED;
	else
		return DISCONNECTED;
}
Exemplo n.º 4
0
/**
 * Handler for the IRC "QUIT" command.
 *
 * See RFC 2812, 3.1.7 "Quit", and RFC 2813, 4.1.5 "Quit".
 *
 * @param Client	The client from which this command has been received.
 * @param Req		Request structure with prefix and all parameters.
 * @returns		CONNECTED or DISCONNECTED.
 */
GLOBAL bool
IRC_QUIT( CLIENT *Client, REQUEST *Req )
{
	CLIENT *target;
	char quitmsg[LINE_LEN];

	assert(Client != NULL);
	assert(Req != NULL);

	/* Wrong number of arguments? */
	if (Req->argc > 1)
		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
					  Client_ID(Client), Req->command);

	if (Req->argc == 1)
		strlcpy(quitmsg, Req->argv[0], sizeof quitmsg);

	if (Client_Type(Client) == CLIENT_SERVER) {
		/* Server */
		target = Client_Search(Req->prefix);
		if (!target) {
			Log(LOG_WARNING,
			    "Got QUIT from %s for unknown client!?",
			    Client_ID(Client));
			return CONNECTED;
		}

		if (target != Client) {
			Client_Destroy(target, "Got QUIT command.",
				       Req->argc == 1 ? quitmsg : NULL, true);
			return CONNECTED;
		} else {
			Conn_Close(Client_Conn(Client), "Got QUIT command.",
				   Req->argc == 1 ? quitmsg : NULL, true);
			return DISCONNECTED;
		}
	} else {
		if (Req->argc == 1 && quitmsg[0] != '\"') {
			/* " " to avoid confusion */
			strlcpy(quitmsg, "\"", sizeof quitmsg);
			strlcat(quitmsg, Req->argv[0], sizeof quitmsg-1);
			strlcat(quitmsg, "\"", sizeof quitmsg );
		}

		/* User, Service, or not yet registered */
		Conn_Close(Client_Conn(Client), "Got QUIT command.",
			   Req->argc == 1 ? quitmsg : NULL, true);

		return DISCONNECTED;
	}
} /* IRC_QUIT */
Exemplo n.º 5
0
Arquivo: client.c Projeto: kazcw/pande
GLOBAL const char *
Client_IPAText(CLIENT *Client)
{
	assert(Client != NULL);

	/* Not a local client? */
	if (Client_Conn(Client) <= NONE)
		return "0.0.0.0";

	if (!Client->ipa_text)
		return Conn_GetIPAInfo(Client_Conn(Client));
	else
		return Client->ipa_text;
}
Exemplo n.º 6
0
/**
 * Handle ISUPPORT (005) numeric.
 */
GLOBAL bool
IRC_Num_ISUPPORT(CLIENT * Client, REQUEST * Req)
{
	int i;
	char *key, *value;

	for (i = 1; i < Req->argc - 1; i++) {
		key = Req->argv[i];
		value = strchr(key, '=');
		if (value)
			*value++ = '\0';
		else
			value = "";

		if (strcmp("NICKLEN", key) == 0) {
			if ((unsigned int)atol(value) == Conf_MaxNickLength - 1)
				continue;

			/* Nickname length settings are different! */
			Log(LOG_ERR,
			    "Peer uses incompatible nickname length (%d/%d)! Disconnecting ...",
			    Conf_MaxNickLength - 1, atoi(value));
			Conn_Close(Client_Conn(Client),
				   "Incompatible nickname length",
				   NULL, false);
			return DISCONNECTED;
		}
	}

	return CONNECTED;
} /* IRC_Num_ISUPPORT */
Exemplo n.º 7
0
/**
 * Announce new server in the network
 * @param Client New server
 * @param Server Existing server in the network
 */
static bool
Announce_Server(CLIENT * Client, CLIENT * Server)
{
	CLIENT *c;

	if (Client_Conn(Server) > NONE) {
		/* Announce the new server to the one already registered
		 * which is directly connected to the local server */
		if (!IRC_WriteStrClient
		    (Server, "SERVER %s %d %d :%s", Client_ID(Client),
		     Client_Hops(Client) + 1, Client_MyToken(Client),
		     Client_Info(Client)))
			return DISCONNECTED;
	}

	if (Client_Hops(Server) == 1)
		c = Client_ThisServer();
	else
		c = Client_TopServer(Server);

	/* Inform new server about the one already registered in the network */
	return IRC_WriteStrClientPrefix(Client, c, "SERVER %s %d %d :%s",
		Client_ID(Server), Client_Hops(Server) + 1,
		Client_MyToken(Server), Client_Info(Server));
} /* Announce_Server */
Exemplo n.º 8
0
Arquivo: op.c Projeto: LucentW/ngircd
/**
 * Check that the originator of a request is an IRC operator and allowed
 * to administer this server.
 *
 * @param CLient Client from which the command has been received.
 * @param Req Request structure.
 * @return CLIENT structure of the client that initiated the command or
 *	   NULL if client is not allowed to execute operator commands.
 */
GLOBAL CLIENT *
Op_Check(CLIENT * Client, REQUEST * Req)
{
	CLIENT *c;

	assert(Client != NULL);
	assert(Req != NULL);

	if (Client_Type(Client) == CLIENT_SERVER && Req->prefix)
		c = Client_Search(Req->prefix);
	else
		c = Client;

	if (!c)
		return NULL;
	if (Client_Type(Client) == CLIENT_SERVER
	    && Client_Type(c) == CLIENT_SERVER)
		return c;
	if (!Client_HasMode(c, 'o'))
		return NULL;
	if (Client_Conn(c) <= NONE && !Conf_AllowRemoteOper)
		return NULL;

	/* The client is an local IRC operator, or this server is configured
	 * to trust remote operators. */
	return c;
} /* Op_Check */
Exemplo n.º 9
0
Arquivo: pam.c Projeto: LucentW/ngircd
/**
 * Authenticate a connecting client using PAM.
 * @param Client The client to authenticate.
 * @return true when authentication succeeded, false otherwise.
 */
GLOBAL bool
PAM_Authenticate(CLIENT *Client) {
	pam_handle_t *pam;
	int retval = PAM_SUCCESS;

	LogDebug("PAM: Authenticate \"%s\" (%s) ...",
		 Client_OrigUser(Client), Client_Mask(Client));

	/* Set supplied client password */
	if (password)
		free(password);
	password = strdup(Conn_Password(Client_Conn(Client)));
	conv.appdata_ptr = Conn_Password(Client_Conn(Client));

	/* Initialize PAM */
	retval = pam_start(Conf_PAMServiceName, Client_OrigUser(Client), &conv, &pam);
	if (retval != PAM_SUCCESS) {
		Log(LOG_ERR, "PAM: Failed to create authenticator! (%d)", retval);
		return false;
	}

	pam_set_item(pam, PAM_RUSER, Client_User(Client));
	pam_set_item(pam, PAM_RHOST, Client_Hostname(Client));
#if defined(HAVE_PAM_FAIL_DELAY) && !defined(NO_PAM_FAIL_DELAY)
	pam_fail_delay(pam, 0);
#endif

	/* PAM authentication ... */
	retval = pam_authenticate(pam, 0);

	/* Success? */
	if (retval == PAM_SUCCESS)
		Log(LOG_INFO, "PAM: Authenticated \"%s\" (%s).",
		    Client_OrigUser(Client), Client_Mask(Client));
	else
		Log(LOG_ERR, "PAM: Error on \"%s\" (%s): %s",
		    Client_OrigUser(Client), Client_Mask(Client),
		    pam_strerror(pam, retval));

	/* Free PAM structures */
	if (pam_end(pam, retval) != PAM_SUCCESS)
		Log(LOG_ERR, "PAM: Failed to release authenticator!");

	return (retval == PAM_SUCCESS);
}
Exemplo n.º 10
0
Arquivo: client.c Projeto: kazcw/pande
/**
 * Reject a client when logging in.
 *
 * This function is called when a client isn't allowed to connect to this
 * server. Possible reasons are bad server password, bad PAM password,
 * or that the client is G/K-Line'd.
 *
 * After calling this function, the client isn't connected any more.
 *
 * @param Client The client to reject.
 * @param Reason The reason why the client has been rejected.
 * @param InformClient If true, send the exact reason to the client.
 */
GLOBAL void
Client_Reject(CLIENT *Client, const char *Reason, bool InformClient)
{
	char info[COMMAND_LEN];

	assert(Client != NULL);
	assert(Reason != NULL);

	if (InformClient)
		snprintf(info, sizeof(info), "Access denied: %s", Reason);
	else
		strcpy(info, "Access denied: Bad password?");

	Log(LOG_ERR,
	    "User \"%s\" rejected (connection %d): %s!",
	    Client_Mask(Client), Client_Conn(Client), Reason);
	Conn_Close(Client_Conn(Client), Reason, info, true);
}
Exemplo n.º 11
0
GLOBAL bool
Channel_Write(CHANNEL *Chan, CLIENT *From, CLIENT *Client, const char *Command,
	      bool SendErrors, const char *Text)
{
	if (!Can_Send_To_Channel(Chan, From)) {
		if (! SendErrors)
			return CONNECTED;	/* no error, see RFC 2812 */
		if (strchr(Channel_Modes(Chan), 'M'))
			return IRC_WriteStrClient(From, ERR_NEEDREGGEDNICK_MSG,
						  Client_ID(From), Channel_Name(Chan));
		else
			return IRC_WriteStrClient(From, ERR_CANNOTSENDTOCHAN_MSG,
					  Client_ID(From), Channel_Name(Chan));
	}

	if (Client_Conn(From) > NONE)
		Conn_UpdateIdle(Client_Conn(From));

	return IRC_WriteStrChannelPrefix(Client, Chan, From, true,
			"%s %s :%s", Command, Channel_Name(Chan), Text);
}
Exemplo n.º 12
0
Arquivo: irc.c Projeto: ngircd/ngircd
/**
 * Handler for the IRC "ERROR" command.
 *
 * @param Client The client from which this command has been received.
 * @param Req Request structure with prefix and all parameters.
 * @return CONNECTED or DISCONNECTED.
*/
GLOBAL bool
IRC_ERROR(CLIENT *Client, REQUEST *Req)
{
	char *msg;

	assert( Client != NULL );
	assert( Req != NULL );

	if (Client_Type(Client) != CLIENT_GOTPASS
	    && Client_Type(Client) != CLIENT_GOTPASS_2813
	    && Client_Type(Client) != CLIENT_UNKNOWNSERVER
	    && Client_Type(Client) != CLIENT_SERVER
	    && Client_Type(Client) != CLIENT_SERVICE) {
		LogDebug("Ignored ERROR command from \"%s\" ...",
			 Client_Mask(Client));
		IRC_SetPenalty(Client, 2);
		return CONNECTED;
	}

	if (Req->argc < 1) {
		msg = "Got ERROR command";
		Log(LOG_NOTICE, "Got ERROR from \"%s\"!",
		    Client_Mask(Client));
	} else {
		msg = Req->argv[0];
		Log(LOG_NOTICE, "Got ERROR from \"%s\": \"%s\"!",
		    Client_Mask(Client), msg);
	}

	if (Client_Conn(Client) != NONE) {
		Conn_Close(Client_Conn(Client), NULL, msg, false);
		return DISCONNECTED;
	}

	return CONNECTED;
} /* IRC_ERROR */
Exemplo n.º 13
0
Arquivo: client.c Projeto: kazcw/pande
GLOBAL void
Client_DebugDump(void)
{
	CLIENT *c;

	Log(LOG_DEBUG, "Client status:");
	c = My_Clients;
	while (c) {
		Log(LOG_DEBUG,
		    " - %s: type=%d, host=%s, user=%s, conn=%d, start=%ld, flags=%s",
                   Client_ID(c), Client_Type(c), Client_Hostname(c),
                   Client_User(c), Client_Conn(c), Client_StartTime(c),
                   Client_Flags(c));
		c = (CLIENT *)c->next;
	}
} /* Client_DumpClients */
Exemplo n.º 14
0
/**
 * Handler for the IRC "WEBIRC" command.
 *
 * See doc/Protocol.txt, section II.4:
 * "Update webchat/proxy client information".
 *
 * @param Client	The client from which this command has been received.
 * @param Req		Request structure with prefix and all parameters.
 * @returns		CONNECTED or DISCONNECTED.
 */
GLOBAL bool
IRC_WEBIRC(CLIENT *Client, REQUEST *Req)
{
	/* Exactly 4 parameters are requited */
	if (Req->argc != 4)
		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
					  Client_ID(Client), Req->command);

	if (!Conf_WebircPwd[0] || strcmp(Req->argv[0], Conf_WebircPwd) != 0)
		return IRC_WriteStrClient(Client, ERR_PASSWDMISMATCH_MSG,
					  Client_ID(Client));

	LogDebug("Connection %d: got valid WEBIRC command: user=%s, host=%s, ip=%s",
		 Client_Conn(Client), Req->argv[1], Req->argv[2], Req->argv[3]);

	Client_SetUser(Client, Req->argv[1], true);
	Client_SetOrigUser(Client, Req->argv[1]);
	Client_SetHostname(Client, Req->argv[2]);
	return CONNECTED;
} /* IRC_WEBIRC */
Exemplo n.º 15
0
/**
 * Forward JOIN command to a specific server
 *
 * This function differentiates between servers using RFC 2813 mode that
 * support the JOIN command with appended ASCII 7 character and channel
 * modes, and servers using RFC 1459 protocol which require separate JOIN
 * and MODE commands.
 *
 * @param To		Forward JOIN (and MODE) command to this peer server
 * @param Prefix	Client used to prefix the genrated commands
 * @param Data		Parameters of JOIN command to forward, probably
 *			containing channel modes separated by ASCII 7.
 */
static void
cb_join_forward(CLIENT *To, CLIENT *Prefix, void *Data)
{
	CONN_ID conn;
	char str[COMMAND_LEN], *ptr = NULL;

	strlcpy(str, (char *)Data, sizeof(str));
	conn = Client_Conn(To);

	if (Conn_Options(conn) & CONN_RFC1459) {
		/* RFC 1459 compatibility mode, appended modes are NOT
		 * supported, so strip them off! */
		ptr = strchr(str, 0x7);
		if (ptr)
			*ptr++ = '\0';
	}

	IRC_WriteStrClientPrefix(To, Prefix, "JOIN %s", str);
	if (ptr && *ptr)
		IRC_WriteStrClientPrefix(To, Prefix, "MODE %s +%s %s", str, ptr,
					 Client_ID(Prefix));
} /* cb_join_forward */
Exemplo n.º 16
0
/**
 * Handler for the IRC "KILL" command.
 *
 * This function implements the IRC command "KILL" wich is used to selectively
 * disconnect clients. It can be used by IRC operators and servers, for example
 * to "solve" nick collisions after netsplits. See RFC 2812 section 3.7.1.
 *
 * Please note that this function is also called internally, without a real
 * KILL command being received over the network! Client is Client_ThisServer()
 * in this case, and the prefix in Req is NULL.
 *
 * @param Client	The client from which this command has been received
 *			or Client_ThisServer() when generated interanlly.
 * @param Req		Request structure with prefix and all parameters.
 * @returns		CONNECTED or DISCONNECTED.
 */
GLOBAL bool
IRC_KILL( CLIENT *Client, REQUEST *Req )
{
	CLIENT *prefix, *c;
	char reason[COMMAND_LEN], *msg;
	CONN_ID my_conn, conn;

	assert (Client != NULL);
	assert (Req != NULL);

	if (Client_Type(Client) != CLIENT_SERVER && !Client_OperByMe(Client))
		return IRC_WriteStrClient(Client, ERR_NOPRIVILEGES_MSG,
					  Client_ID(Client));

	if (Req->argc != 2)
		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
					  Client_ID(Client), Req->command);

	/* Get prefix (origin); use the client if no prefix is given. */
	if (Req->prefix)
		prefix = Client_Search(Req->prefix);
	else
		prefix = Client;

	/* Log a warning message and use this server as origin when the
	 * prefix (origin) is invalid. */
	if (!prefix) {
		Log(LOG_WARNING, "Got KILL with invalid prefix: \"%s\"!",
		    Req->prefix );
		prefix = Client_ThisServer();
	}

	if (Client != Client_ThisServer())
		Log(LOG_NOTICE|LOG_snotice,
		    "Got KILL command from \"%s\" for \"%s\": %s",
		    Client_Mask(prefix), Req->argv[0], Req->argv[1]);

	/* Build reason string: Prefix the "reason" if the originator is a
	 * regular user, so users can't spoof KILLs of servers. */
	if (Client_Type(Client) == CLIENT_USER)
		snprintf(reason, sizeof(reason), "KILLed by %s: %s",
			 Client_ID(Client), Req->argv[1]);
	else
		strlcpy(reason, Req->argv[1], sizeof(reason));

	/* Inform other servers */
	IRC_WriteStrServersPrefix(Client, prefix, "KILL %s :%s",
				  Req->argv[0], reason);

	/* Save ID of this connection */
	my_conn = Client_Conn( Client );

	/* Do we host such a client? */
	c = Client_Search( Req->argv[0] );
	if( c )
	{
		if(( Client_Type( c ) != CLIENT_USER ) &&
		   ( Client_Type( c ) != CLIENT_GOTNICK ))
		{
			/* Target of this KILL is not a regular user, this is
			 * invalid! So we ignore this case if we received a
			 * regular KILL from the network and try to kill the
			 * client/connection anyway (but log an error!) if the
			 * origin is the local server. */

			if( Client != Client_ThisServer( ))
			{
				/* Invalid KILL received from remote */
				if( Client_Type( c ) == CLIENT_SERVER )
					msg = ERR_CANTKILLSERVER_MSG;
				else
					msg = ERR_NOPRIVILEGES_MSG;
				return IRC_WriteStrClient( Client, msg,
					Client_ID( Client ));
			}

			Log( LOG_ERR, "Got KILL for invalid client type: %d, \"%s\"!",
			     Client_Type( c ), Req->argv[0] );
		}

		/* Kill the client NOW:
		 *  - Close the local connection (if there is one),
		 *  - Destroy the CLIENT structure for remote clients.
		 * Note: Conn_Close() removes the CLIENT structure as well. */
		conn = Client_Conn( c );
		if(conn > NONE)
			Conn_Close(conn, NULL, reason, true);
		else
			Client_Destroy(c, NULL, reason, false);
	}
	else
		Log( LOG_NOTICE, "Client with nick \"%s\" is unknown here.", Req->argv[0] );

	/* Are we still connected or were we killed, too? */
	if(( my_conn > NONE ) && ( Conn_GetClient( my_conn )))
		return CONNECTED;
	else
		return DISCONNECTED;
} /* IRC_KILL */
Exemplo n.º 17
0
/**
 * Handle client mode requests
 *
 * @param Client The client from which this command has been received.
 * @param Req Request structure with prefix and all parameters.
 * @param Origin The originator of the MODE command (prefix).
 * @param Target The target (client) of this MODE command.
 * @return CONNECTED or DISCONNECTED.
 */
static bool
Client_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target )
{
	char the_modes[COMMAND_LEN], x[2], *mode_ptr;
	bool ok, set;
	bool send_RPL_HOSTHIDDEN_MSG = false;
	int mode_arg;
	size_t len;

	/* Is the client allowed to request or change the modes? */
	if (Client_Type(Client) == CLIENT_USER) {
		/* Users are only allowed to manipulate their own modes! */
		if (Target != Client)
			return IRC_WriteErrClient(Client,
						  ERR_USERSDONTMATCH_MSG,
						  Client_ID(Client));
	}

	/* Mode request: let's answer it :-) */
	if (Req->argc == 1)
		return IRC_WriteStrClient(Origin, RPL_UMODEIS_MSG,
					  Client_ID(Target),
					  Client_Modes(Target));

	mode_arg = 1;
	mode_ptr = Req->argv[mode_arg];

	/* Initial state: set or unset modes? */
	if (*mode_ptr == '+') {
		set = true;
		strcpy(the_modes, "+");
	} else if (*mode_ptr == '-') {
		set = false;
		strcpy(the_modes, "-");
	} else
		return IRC_WriteErrClient(Origin, ERR_UMODEUNKNOWNFLAG_MSG,
					  Client_ID(Origin));

	x[1] = '\0';
	ok = CONNECTED;
	while (mode_ptr) {
		mode_ptr++;
		if (!*mode_ptr) {
			/* Try next argument if there's any */
			mode_arg++;
			if (mode_arg < Req->argc)
				mode_ptr = Req->argv[mode_arg];
			else
				break;
		}

		switch(*mode_ptr) {
		  case '+':
		  case '-':
			if ((*mode_ptr == '+' && !set)
			    || (*mode_ptr == '-' && set)) {
				/* Action modifier ("+"/"-") must be changed */
				len = strlen(the_modes) - 1;
				if (the_modes[len] == '+'
				    || the_modes[len] == '-') {
					/* Last character in the "result
					 * string" was an "action", so just
					 * overwrite it with the new action */
					the_modes[len] = *mode_ptr;
				} else {
					/* Append new modifier character to
					 * the resulting mode string */
					x[0] = *mode_ptr;
					strlcat(the_modes, x,
						sizeof(the_modes));
				}
				if (*mode_ptr == '+')
					set = true;
				else
					set = false;
			}
			continue;
		}

		/* Validate modes */
		x[0] = '\0';
		switch (*mode_ptr) {
		case 'b': /* Block private msgs */
		case 'C': /* Only messages from clients sharing a channel */
		case 'i': /* Invisible */
		case 'I': /* Hide channel list from WHOIS */
		case 's': /* Server messages */
		case 'w': /* Wallops messages */
			x[0] = *mode_ptr;
			break;
		case 'a': /* Away */
			if (Client_Type(Client) == CLIENT_SERVER) {
				x[0] = 'a';
				Client_SetAway(Origin, DEFAULT_AWAY_MSG);
			} else
				ok = IRC_WriteErrClient(Origin,
							ERR_NOPRIVILEGES_MSG,
							Client_ID(Origin));
			break;
		case 'B': /* Bot */
			if (Client_HasMode(Client, 'r'))
				ok = IRC_WriteErrClient(Origin,
							ERR_RESTRICTED_MSG,
							Client_ID(Origin));
			else
				x[0] = 'B';
			break;
		case 'c': /* Receive connect notices */
		case 'q': /* KICK-protected user */
		case 'F': /* disable flood protection */
			  /* (only settable by IRC operators!) */
			if (!set || Client_Type(Client) == CLIENT_SERVER
			    || Client_HasMode(Origin, 'o'))
				x[0] = *mode_ptr;
			else
				ok = IRC_WriteErrClient(Origin,
							ERR_NOPRIVILEGES_MSG,
							Client_ID(Origin));
			break;
		case 'o': /* IRC operator (only unsettable!) */
			if (!set || Client_Type(Client) == CLIENT_SERVER) {
				x[0] = 'o';
			} else
				ok = IRC_WriteErrClient(Origin,
							ERR_NOPRIVILEGES_MSG,
							Client_ID(Origin));
			break;
		case 'r': /* Restricted (only settable) */
			if (set || Client_Type(Client) == CLIENT_SERVER)
				x[0] = 'r';
			else
				ok = IRC_WriteErrClient(Origin,
							ERR_RESTRICTED_MSG,
							Client_ID(Origin));
			break;
		case 'R': /* Registered (not [un]settable by clients) */
			if (Client_Type(Client) == CLIENT_SERVER)
				x[0] = 'R';
			else
				ok = IRC_WriteErrClient(Origin,
							ERR_NICKREGISTER_MSG,
							Client_ID(Origin));
			break;
		case 'x': /* Cloak hostname */
			if (Client_HasMode(Client, 'r'))
				ok = IRC_WriteErrClient(Origin,
							ERR_RESTRICTED_MSG,
							Client_ID(Origin));
			else if (!set || Conf_CloakHostModeX[0]
				 || Client_Type(Client) == CLIENT_SERVER
				 || Client_HasMode(Origin, 'o')) {
				x[0] = 'x';
				send_RPL_HOSTHIDDEN_MSG = true;
			} else
				ok = IRC_WriteErrClient(Origin,
							ERR_NOPRIVILEGES_MSG,
							Client_ID(Origin));
			break;
		default:
			if (Client_Type(Client) != CLIENT_SERVER) {
				Log(LOG_DEBUG,
				    "Unknown mode \"%c%c\" from \"%s\"!?",
				    set ? '+' : '-', *mode_ptr,
				    Client_ID(Origin));
				ok = IRC_WriteErrClient(Origin,
							ERR_UMODEUNKNOWNFLAG2_MSG,
							Client_ID(Origin),
							set ? '+' : '-',
							*mode_ptr);
				x[0] = '\0';
			} else {
				Log(LOG_DEBUG,
				    "Handling unknown mode \"%c%c\" from \"%s\" for \"%s\" ...",
				    set ? '+' : '-', *mode_ptr,
				    Client_ID(Origin), Client_ID(Target));
				x[0] = *mode_ptr;
			}
		}

		if (!ok)
			break;

		/* Is there a valid mode change? */
		if (!x[0])
			continue;

		if (set) {
			if (Client_ModeAdd(Target, x[0]))
				strlcat(the_modes, x, sizeof(the_modes));
		} else {
			if (Client_ModeDel(Target, x[0]))
				strlcat(the_modes, x, sizeof(the_modes));
		}
	}

	/* Are there changed modes? */
	if (the_modes[1]) {
		/* Remove needless action modifier characters */
		len = strlen(the_modes) - 1;
		if (the_modes[len] == '+' || the_modes[len] == '-')
			the_modes[len] = '\0';

		if (Client_Type(Client) == CLIENT_SERVER) {
			/* Forward modes to other servers */
			if (Client_Conn(Target) != NONE) {
				/* Remote server (service?) changed modes
				 * for one of our clients. Inform it! */
				IRC_WriteStrClientPrefix(Target, Origin,
							 "MODE %s :%s",
							 Client_ID(Target),
							 the_modes);
			}
			IRC_WriteStrServersPrefix(Client, Origin,
						  "MODE %s :%s",
						  Client_ID(Target),
						  the_modes);
		} else {
			/* Send reply to client and inform other servers */
			ok = IRC_WriteStrClientPrefix(Client, Origin,
						      "MODE %s :%s",
						      Client_ID(Target),
						      the_modes);
			IRC_WriteStrServersPrefix(Client, Origin,
						  "MODE %s :%s",
						  Client_ID(Target),
						  the_modes);
		}

		if (send_RPL_HOSTHIDDEN_MSG && Client_Conn(Target) > NONE) {
			/* A new (cloaked) hostname must be announced */
			IRC_WriteStrClientPrefix(Target, Origin,
						 RPL_HOSTHIDDEN_MSG,
						 Client_ID(Target),
						 Client_HostnameDisplayed(Target));

		}

		LogDebug("%s \"%s\": Mode change, now \"%s\".",
			 Client_TypeText(Target), Client_Mask(Target),
			 Client_Modes(Target));
	}

	return ok;
} /* Client_Mode */
Exemplo n.º 18
0
/**
 * Handler for the IRC command "INVITE".
 *
 * @param Client The client from which this command has been received.
 * @param Req Request structure with prefix and all parameters.
 * @return CONNECTED or DISCONNECTED.
 */
GLOBAL bool
IRC_INVITE(CLIENT *Client, REQUEST *Req)
{
	CHANNEL *chan;
	CLIENT *target, *from;
	const char *colon_if_necessary;
	bool remember = false;

	assert( Client != NULL );
	assert( Req != NULL );

	_IRC_ARGC_EQ_OR_RETURN_(Client, Req, 2)
	_IRC_GET_SENDER_OR_RETURN_(from, Req, Client)

	/* Search user */
	target = Client_Search(Req->argv[0]);
	if (!target || (Client_Type(target) != CLIENT_USER))
		return IRC_WriteStrClient(from, ERR_NOSUCHNICK_MSG,
				Client_ID(Client), Req->argv[0]);

	chan = Channel_Search(Req->argv[1]);
	if (chan) {
		/* Channel exists. Is the user a valid member of the channel? */
		if (!Channel_IsMemberOf(chan, from))
			return IRC_WriteStrClient(from, ERR_NOTONCHANNEL_MSG, Client_ID(Client), Req->argv[1]);

		/* Is the channel "invite-disallow"? */
		if (strchr(Channel_Modes(chan), 'V'))
			return IRC_WriteStrClient(from, ERR_NOINVITE_MSG,
				Client_ID(from), Channel_Name(chan));

		/* Is the channel "invite-only"? */
		if (strchr(Channel_Modes(chan), 'i')) {
			/* Yes. The user must be channel owner/admin/operator/halfop! */
			if (!strchr(Channel_UserModes(chan, from), 'q') &&
			    !strchr(Channel_UserModes(chan, from), 'a') &&
			    !strchr(Channel_UserModes(chan, from), 'o') &&
			    !strchr(Channel_UserModes(chan, from), 'h'))
				return IRC_WriteStrClient(from, ERR_CHANOPRIVSNEEDED_MSG,
						Client_ID(from), Channel_Name(chan));
			remember = true;
		}

		/* Is the target user already member of the channel? */
		if (Channel_IsMemberOf(chan, target))
			return IRC_WriteStrClient(from, ERR_USERONCHANNEL_MSG,
					Client_ID(from), Req->argv[0], Req->argv[1]);

		/* If the target user is banned on that channel: remember invite */
		if (Lists_Check(Channel_GetListBans(chan), target))
			remember = true;

		if (remember) {
			/* We must remember this invite */
			if (!Channel_AddInvite(chan, Client_Mask(target), true))
				return CONNECTED;
		}
	}

	LogDebug("User \"%s\" invites \"%s\" to \"%s\" ...", Client_Mask(from),
		 Req->argv[0], Req->argv[1]);

	/*
	 * RFC 2812 says:
	 * 'There is no requirement that the channel [..] must exist or be a valid channel'
	 * The problem with this is that this allows the "channel" to contain spaces,
	 * in which case we must prefix its name with a colon to make it clear that
	 * it is only a single argument.
	 */
	colon_if_necessary = strchr(Req->argv[1], ' ') ? ":":"";
	/* Inform target client */
	IRC_WriteStrClientPrefix(target, from, "INVITE %s %s%s", Req->argv[0],
					colon_if_necessary, Req->argv[1]);

	if (Client_Conn(target) > NONE) {
		/* The target user is local, so we have to send the status code */
		if (!IRC_WriteStrClientPrefix(from, target, RPL_INVITING_MSG,
			Client_ID(from), Req->argv[0], colon_if_necessary, Req->argv[1]))
			return DISCONNECTED;

		if (strchr(Client_Modes(target), 'a') &&
			!IRC_WriteStrClient(from, RPL_AWAY_MSG, Client_ID(from),
					Client_ID(target), Client_Away(target)))
				return DISCONNECTED;
	}
	return CONNECTED;
} /* IRC_INVITE */
Exemplo n.º 19
0
/**
 * Check weather a local client is allowed to join an already existing
 * channel or not.
 *
 * @param Client	Client that sent the JOIN command
 * @param chan		Channel to check
 * @param channame	Name of the channel
 * @param key		Provided channel key (or NULL)
 * @returns		true if client is allowed to join, false otherwise
 */
static bool
join_allowed(CLIENT *Client, CHANNEL *chan, const char *channame,
	     const char *key)
{
	bool is_invited, is_banned, is_exception;

	/* Allow IRC operators to overwrite channel limits */
	if (Client_HasMode(Client, 'o'))
		return true;

	is_banned = Lists_Check(Channel_GetListBans(chan), Client);
	is_exception = Lists_Check(Channel_GetListExcepts(chan), Client);
	is_invited = Lists_Check(Channel_GetListInvites(chan), Client);

	if (is_banned && !is_invited && !is_exception) {
		/* Client is banned from channel (and not on invite list) */
		IRC_WriteErrClient(Client, ERR_BANNEDFROMCHAN_MSG,
				   Client_ID(Client), channame);
		return false;
	}

	if (Channel_HasMode(chan, 'i') && !is_invited) {
		/* Channel is "invite-only" and client is not on invite list */
		IRC_WriteErrClient(Client, ERR_INVITEONLYCHAN_MSG,
				   Client_ID(Client), channame);
		return false;
	}

	if (!Channel_CheckKey(chan, Client, key ? key : "")) {
		/* Channel is protected by a channel key and the client
		 * didn't specify the correct one */
		IRC_WriteErrClient(Client, ERR_BADCHANNELKEY_MSG,
				   Client_ID(Client), channame);
		return false;
	}

	if (Channel_HasMode(chan, 'l') &&
	    (Channel_MaxUsers(chan) <= Channel_MemberCount(chan))) {
		/* There are more clints joined to this channel than allowed */
		IRC_WriteErrClient(Client, ERR_CHANNELISFULL_MSG,
				   Client_ID(Client), channame);
		return false;
	}

	if (Channel_HasMode(chan, 'z') && !Conn_UsesSSL(Client_Conn(Client))) {
		/* Only "secure" clients are allowed, but clients doesn't
		 * use SSL encryption */
		IRC_WriteErrClient(Client, ERR_SECURECHANNEL_MSG,
				   Client_ID(Client), channame);
		return false;
	}

	if (Channel_HasMode(chan, 'O') && !Client_HasMode(Client, 'o')) {
		/* Only IRC operators are allowed! */
		IRC_WriteErrClient(Client, ERR_OPONLYCHANNEL_MSG,
				   Client_ID(Client), channame);
		return false;
	}

	if (Channel_HasMode(chan, 'R') && !Client_HasMode(Client, 'R')) {
		/* Only registered users are allowed! */
		IRC_WriteErrClient(Client, ERR_REGONLYCHANNEL_MSG,
				   Client_ID(Client), channame);
		return false;
	}

	return true;
} /* join_allowed */
Exemplo n.º 20
0
/**
 * Handler for the IRC command "SQUIT".
 * See RFC 2813 section 4.1.2 and RFC 2812 section 3.1.8.
 */
GLOBAL bool
IRC_SQUIT(CLIENT * Client, REQUEST * Req)
{
	char msg[COMMAND_LEN], logmsg[COMMAND_LEN];
	CLIENT *from, *target;
	CONN_ID con;
	int loglevel;

	assert(Client != NULL);
	assert(Req != NULL);

	if (Client_Type(Client) != CLIENT_SERVER
	    && !Client_HasMode(Client, 'o'))
		return Op_NoPrivileges(Client, Req);

	/* Bad number of arguments? */
	if (Req->argc != 2)
		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
					  Client_ID(Client), Req->command);

	if (Client_Type(Client) == CLIENT_SERVER && Req->prefix) {
		from = Client_Search(Req->prefix);
		if (Client_Type(from) != CLIENT_SERVER
		    && !Op_Check(Client, Req))
			return Op_NoPrivileges(Client, Req);
	} else
		from = Client;
	if (!from)
		return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
					  Client_ID(Client), Req->prefix);

	if (Client_Type(Client) == CLIENT_USER)
		loglevel = LOG_NOTICE | LOG_snotice;
	else
		loglevel = LOG_DEBUG;
	Log(loglevel, "Got SQUIT from %s for \"%s\": \"%s\" ...",
	    Client_ID(from), Req->argv[0], Req->argv[1]);

	target = Client_Search(Req->argv[0]);
	if (Client_Type(Client) != CLIENT_SERVER &&
	    target == Client_ThisServer())
		return Op_NoPrivileges(Client, Req);
	if (!target) {
		/* The server is (already) unknown */
		Log(LOG_WARNING,
		    "Got SQUIT from %s for unknown server \"%s\"!?",
		    Client_ID(Client), Req->argv[0]);
		return CONNECTED;
	}

	con = Client_Conn(target);

	if (Req->argv[1][0])
		if (Client_NextHop(from) != Client || con > NONE)
			snprintf(msg, sizeof(msg), "%s (SQUIT from %s)",
				 Req->argv[1], Client_ID(from));
		else
			strlcpy(msg, Req->argv[1], sizeof(msg));
	else
		snprintf(msg, sizeof(msg), "Got SQUIT from %s",
			 Client_ID(from));

	if (con > NONE) {
		/* We are directly connected to the target server, so we
		 * have to tear down the connection and to inform all the
		 * other remaining servers in the network */
		IRC_SendWallops(Client_ThisServer(), Client_ThisServer(),
				"Received SQUIT %s from %s: %s",
				Req->argv[0], Client_ID(from),
				Req->argv[1][0] ? Req->argv[1] : "-");
		Conn_Close(con, NULL, msg, true);
		if (con == Client_Conn(Client))
			return DISCONNECTED;
	} else {
		/* This server is not directly connected, so the SQUIT must
		 * be forwarded ... */
		if (Client_Type(from) != CLIENT_SERVER) {
			/* The origin is not an IRC server, so don't evaluate
			 * this SQUIT but simply forward it */
			IRC_WriteStrClientPrefix(Client_NextHop(target),
			    from, "SQUIT %s :%s", Req->argv[0], Req->argv[1]);
		} else {
			/* SQUIT has been generated by another server, so
			 * remove the target server from the network! */
			logmsg[0] = '\0';
			if (!strchr(msg, '('))
				snprintf(logmsg, sizeof(logmsg),
					 "%s (SQUIT from %s)", Req->argv[1],
					 Client_ID(from));
			Client_Destroy(target, logmsg[0] ? logmsg : msg,
				       msg, false);
		}
	}
	return CONNECTED;
} /* IRC_SQUIT */
Exemplo n.º 21
0
Arquivo: client.c Projeto: kazcw/pande
/**
 * Announce an user or service to a server.
 *
 * This function differentiates between RFC1459 and RFC2813 server links and
 * generates the appropriate commands to register the user or service.
 *
 * @param Client	Server
 * @param Prefix	Prefix for the generated commands
 * @param User		User to announce
 */
GLOBAL bool
Client_Announce(CLIENT * Client, CLIENT * Prefix, CLIENT * User)
{
	CONN_ID conn;
	char *modes, *user, *host;

	modes = Client_Modes(User);
	user = Client_User(User) ? Client_User(User) : "-";
	host = Client_Hostname(User) ? Client_Hostname(User) : "-";

	conn = Client_Conn(Client);
	if (Conn_Options(conn) & CONN_RFC1459) {
		/* RFC 1459 mode: separate NICK and USER commands */
		if (! Conn_WriteStr(conn, "NICK %s :%d",
				    Client_ID(User), Client_Hops(User) + 1))
			return DISCONNECTED;
		if (! Conn_WriteStr(conn, ":%s USER %s %s %s :%s",
				     Client_ID(User), user, host,
				     Client_ID(Client_Introducer(User)),
				     Client_Info(User)))
			return DISCONNECTED;
		if (modes[0]) {
			if (! Conn_WriteStr(conn, ":%s MODE %s +%s",
				     Client_ID(User), Client_ID(User),
				     modes))
				return DISCONNECTED;
		}
	} else {
		/* RFC 2813 mode: one combined NICK or SERVICE command */
		if (Client_Type(User) == CLIENT_SERVICE
		    && Client_HasFlag(Client, 'S')) {
			if (!IRC_WriteStrClientPrefix(Client, Prefix,
					"SERVICE %s %d * +%s %d :%s",
					Client_Mask(User),
					Client_MyToken(Client_Introducer(User)),
					modes, Client_Hops(User) + 1,
					Client_Info(User)))
				return DISCONNECTED;
		} else {
			if (!IRC_WriteStrClientPrefix(Client, Prefix,
					"NICK %s %d %s %s %d +%s :%s",
					Client_ID(User), Client_Hops(User) + 1,
					user, host,
					Client_MyToken(Client_Introducer(User)),
					modes, Client_Info(User)))
				return DISCONNECTED;
		}
	}

	if (Client_HasFlag(Client, 'M')) {
		/* Synchronize metadata */
		if (Client_HostnameCloaked(User)) {
			if (!IRC_WriteStrClientPrefix(Client, Prefix,
					"METADATA %s cloakhost :%s",
					Client_ID(User),
					Client_HostnameCloaked(User)))
				return DISCONNECTED;
		}

		if (Client_AccountName(User)) {
			if (!IRC_WriteStrClientPrefix(Client, Prefix,
					"METADATA %s accountname :%s",
					Client_ID(User),
					Client_AccountName(User)))
				return DISCONNECTED;
		}

		if (Conn_GetCertFp(Client_Conn(User))) {
			if (!IRC_WriteStrClientPrefix(Client, Prefix,
					"METADATA %s certfp :%s",
					Client_ID(User),
					Conn_GetCertFp(Client_Conn(User))))
				return DISCONNECTED;
		}
	}

	return CONNECTED;
} /* Client_Announce */
Exemplo n.º 22
0
static bool
Remove_Client( int Type, CHANNEL *Chan, CLIENT *Client, CLIENT *Origin, const char *Reason, bool InformServer )
{
	CL2CHAN *cl2chan, *last_cl2chan;
	CHANNEL *c;

	assert( Chan != NULL );
	assert( Client != NULL );
	assert( Origin != NULL );
	assert( Reason != NULL );

	/* Do not inform other servers if the channel is local to this server,
	 * regardless of what the caller requested! */
	if(InformServer)
		InformServer = !Channel_IsLocal(Chan);

	last_cl2chan = NULL;
	cl2chan = My_Cl2Chan;
	while( cl2chan )
	{
		if(( cl2chan->channel == Chan ) && ( cl2chan->client == Client )) break;
		last_cl2chan = cl2chan;
		cl2chan = cl2chan->next;
	}
	if( ! cl2chan ) return false;

	c = cl2chan->channel;
	assert( c != NULL );

	/* maintain cl2chan list */
	if( last_cl2chan ) last_cl2chan->next = cl2chan->next;
	else My_Cl2Chan = cl2chan->next;
	free( cl2chan );

	switch( Type )
	{
		case REMOVE_QUIT:
			/* QUIT: other servers have already been notified, 
			 * see Client_Destroy(); so only inform other clients
			 * in same channel. */
			assert( InformServer == false );
			LogDebug("User \"%s\" left channel \"%s\" (%s).",
					Client_Mask( Client ), c->name, Reason );
			break;
		case REMOVE_KICK:
			/* User was KICKed: inform other servers (public
			 * channels) and all users in the channel */
			if( InformServer )
				IRC_WriteStrServersPrefix( Client_NextHop( Origin ),
					Origin, "KICK %s %s :%s", c->name, Client_ID( Client ), Reason);
			IRC_WriteStrChannelPrefix(Client, c, Origin, false, "KICK %s %s :%s",
							c->name, Client_ID( Client ), Reason );
			if ((Client_Conn(Client) > NONE) &&
					(Client_Type(Client) == CLIENT_USER))
			{
				IRC_WriteStrClientPrefix(Client, Origin, "KICK %s %s :%s",
								c->name, Client_ID( Client ), Reason);
			}
			LogDebug("User \"%s\" has been kicked off \"%s\" by \"%s\": %s.",
				Client_Mask( Client ), c->name, Client_ID(Origin), Reason);
			break;
		default: /* PART */
			if (Conf_MorePrivacy)
				Reason = "";

			if (InformServer)
				IRC_WriteStrServersPrefix(Origin, Client, "PART %s :%s", c->name, Reason);

			IRC_WriteStrChannelPrefix(Origin, c, Client, false, "PART %s :%s",
									c->name, Reason);

			if ((Client_Conn(Origin) > NONE) &&
					(Client_Type(Origin) == CLIENT_USER))
			{
				IRC_WriteStrClientPrefix( Origin, Client, "PART %s :%s", c->name, Reason);
				LogDebug("User \"%s\" left channel \"%s\" (%s).",
					Client_Mask(Client), c->name, Reason);
			}
	}

	/* When channel is empty and is not pre-defined, delete */
	if( ! Channel_HasMode( Chan, 'P' ))
	{
		if( ! Get_First_Cl2Chan( NULL, Chan )) Delete_Channel( Chan );
	}

	return true;
} /* Remove_Client */
Exemplo n.º 23
0
/**
 * Handler for the IRC command "SERVER".
 * See RFC 2813 section 4.1.2.
 */
GLOBAL bool
IRC_SERVER( CLIENT *Client, REQUEST *Req )
{
	char str[LINE_LEN];
	CLIENT *from, *c;
	int i;
	CONN_ID con;
	
	assert( Client != NULL );
	assert( Req != NULL );

	/* Return an error if this is not a local client */
	if (Client_Conn(Client) <= NONE)
		return IRC_WriteStrClient(Client, ERR_UNKNOWNCOMMAND_MSG,
					  Client_ID(Client), Req->command);

	if (Client_Type(Client) == CLIENT_GOTPASS ||
	    Client_Type(Client) == CLIENT_GOTPASS_2813) {
		/* We got a PASS command from the peer, and now a SERVER
		 * command: the peer tries to register itself as a server. */
		LogDebug("Connection %d: got SERVER command (new server link) ...",
			Client_Conn(Client));

		if(( Req->argc != 2 ) && ( Req->argc != 3 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );

		/* Ist this server configured on out side? */
		for( i = 0; i < MAX_SERVERS; i++ ) if( strcasecmp( Req->argv[0], Conf_Server[i].name ) == 0 ) break;
		if( i >= MAX_SERVERS )
		{
			Log( LOG_ERR, "Connection %d: Server \"%s\" not configured here!", Client_Conn( Client ), Req->argv[0] );
			Conn_Close( Client_Conn( Client ), NULL, "Server not configured here", true);
			return DISCONNECTED;
		}
		if( strcmp( Client_Password( Client ), Conf_Server[i].pwd_in ) != 0 )
		{
			/* wrong password */
			Log( LOG_ERR, "Connection %d: Got bad password from server \"%s\"!", Client_Conn( Client ), Req->argv[0] );
			Conn_Close( Client_Conn( Client ), NULL, "Bad password", true);
			return DISCONNECTED;
		}
		
		/* Is there a registered server with this ID? */
		if( ! Client_CheckID( Client, Req->argv[0] )) return DISCONNECTED;

		Client_SetID( Client, Req->argv[0] );
		Client_SetHops( Client, 1 );
		Client_SetInfo( Client, Req->argv[Req->argc - 1] );

		/* Is this server registering on our side, or are we connecting to
		 * a remote server? */
		con = Client_Conn(Client);
		if (Client_Token(Client) != TOKEN_OUTBOUND) {
			/* Incoming connection, send user/pass */
			if (!IRC_WriteStrClient(Client, "PASS %s %s",
						Conf_Server[i].pwd_out,
						NGIRCd_ProtoID)
			    || !IRC_WriteStrClient(Client, "SERVER %s 1 :%s",
						   Conf_ServerName,
						   Conf_ServerInfo)) {
				    Conn_Close(con, "Unexpected server behavior!",
					       NULL, false);
				    return DISCONNECTED;
			}
			Client_SetIntroducer(Client, Client);
			Client_SetToken(Client, 1);
		} else {
			/* outgoing connect, we already sent a SERVER and PASS
			 * command to the peer */
			Client_SetToken(Client, atoi(Req->argv[1]));
		}

		/* Mark this connection as belonging to an configured server */
		Conf_SetServer(i, con);

		/* Check protocol level */
		if (Client_Type(Client) == CLIENT_GOTPASS) {
			/* We got a "simple" PASS command, so the peer is
			 * using the protocol as defined in RFC 1459. */
			if (! (Conn_Options(con) & CONN_RFC1459))
				Log(LOG_INFO,
				    "Switching connection %d (\"%s\") to RFC 1459 compatibility mode.",
				    con, Client_ID(Client));
			Conn_SetOption(con, CONN_RFC1459);
		}

		Client_SetType(Client, CLIENT_UNKNOWNSERVER);

#ifdef ZLIB
		if (strchr(Client_Flags(Client), 'Z') && !Zip_InitConn(con)) {
			Conn_Close( con, "Can't inizialize compression (zlib)!", NULL, false );
			return DISCONNECTED;
		}
#endif

#ifdef IRCPLUS
		if (strchr(Client_Flags(Client), 'H')) {
			LogDebug("Peer supports IRC+ extended server handshake ...");
			if (!IRC_Send_ISUPPORT(Client))
				return DISCONNECTED;
			return IRC_WriteStrClient(Client, RPL_ENDOFMOTD_MSG,
						  Client_ID(Client));
		} else {
#endif
			if (Conf_MaxNickLength != CLIENT_NICK_LEN_DEFAULT)
				Log(LOG_CRIT,
				    "Attention: this server uses a non-standard nick length, but the peer doesn't support the IRC+ extended server handshake!");
#ifdef IRCPLUS
		}
#endif

		return IRC_Num_ENDOFMOTD(Client, Req);
	}
	else if( Client_Type( Client ) == CLIENT_SERVER )
	{
		/* New server is being introduced to the network */

		if( Req->argc != 4 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );

		/* check for existing server with same ID */
		if( ! Client_CheckID( Client, Req->argv[0] )) return DISCONNECTED;

		from = Client_Search( Req->prefix );
		if( ! from )
		{
			/* Uh, Server, that introduced the new server is unknown?! */
			Log( LOG_ALERT, "Unknown ID in prefix of SERVER: \"%s\"! (on connection %d)", Req->prefix, Client_Conn( Client ));
			Conn_Close( Client_Conn( Client ), NULL, "Unknown ID in prefix of SERVER", true);
			return DISCONNECTED;
		}

		c = Client_NewRemoteServer(Client, Req->argv[0], from, atoi(Req->argv[1]), atoi(Req->argv[2]), Req->argv[3], true);
		if (!c) {
			Log( LOG_ALERT, "Can't create client structure for server! (on connection %d)", Client_Conn( Client ));
			Conn_Close( Client_Conn( Client ), NULL, "Can't allocate client structure for remote server", true);
			return DISCONNECTED;
		}

		if(( Client_Hops( c ) > 1 ) && ( Req->prefix[0] )) snprintf( str, sizeof( str ), "connected to %s, ", Client_ID( from ));
		else strcpy( str, "" );
		Log( LOG_NOTICE|LOG_snotice, "Server \"%s\" registered (via %s, %s%d hop%s).", Client_ID( c ), Client_ID( Client ), str, Client_Hops( c ), Client_Hops( c ) > 1 ? "s": "" );

		/* notify other servers */
		IRC_WriteStrServersPrefix( Client, from, "SERVER %s %d %d :%s", Client_ID( c ), Client_Hops( c ) + 1, Client_MyToken( c ), Client_Info( c ));

		return CONNECTED;
	} else
		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
					  Client_ID(Client), Req->command);
} /* IRC_SERVER */
Exemplo n.º 24
0
/**
 * Initiate client login.
 *
 * This function is called after the daemon received the required NICK and
 * USER commands of a new client. If the daemon is compiled with support for
 * PAM, the authentication sub-processs is forked; otherwise the global server
 * password is checked.
 *
 * @param Client The client logging in.
 * @returns CONNECTED or DISCONNECTED.
 */
GLOBAL bool
Login_User(CLIENT * Client)
{
#ifdef PAM
	int pipefd[2], result;
	pid_t pid;
#endif
	CONN_ID conn;

	assert(Client != NULL);
	conn = Client_Conn(Client);

#ifndef STRICT_RFC
	if (Conf_AuthPing) {
		/* Did we receive the "auth PONG" already? */
		if (Conn_GetAuthPing(conn)) {
			Client_SetType(Client, CLIENT_WAITAUTHPING);
			LogDebug("Connection %d: Waiting for AUTH PONG ...", conn);
			return CONNECTED;
		}
	}
#endif

	/* Still waiting for "CAP END" command? */
	if (Client_Cap(Client) & CLIENT_CAP_PENDING) {
		Client_SetType(Client, CLIENT_WAITCAPEND);
		LogDebug("Connection %d: Waiting for CAP END ...", conn);
		return CONNECTED;
	}

#ifdef PAM
	if (!Conf_PAM) {
		/* Don't do any PAM authentication at all if PAM is not
		 * enabled, instead emulate the behavior of the daemon
		 * compiled without PAM support. */
		if (strcmp(Conn_Password(conn), Conf_ServerPwd) == 0)
			return Login_User_PostAuth(Client);
		Client_Reject(Client, "Bad server password", false);
		return DISCONNECTED;
	}

	if (Conf_PAMIsOptional &&
	    strcmp(Conn_Password(conn), "") == 0) {
		/* Clients are not required to send a password and to be PAM-
		 * authenticated at all. If not, they won't become "identified"
		 * and keep the "~" in their supplied user name.
		 * Therefore it is sensible to either set Conf_PAMisOptional or
		 * to enable IDENT lookups -- not both. */
		return Login_User_PostAuth(Client);
	}

	if (Conf_PAM) {
		/* Fork child process for PAM authentication; and make sure that the
		 * process timeout is set higher than the login timeout! */
		pid = Proc_Fork(Conn_GetProcStat(conn), pipefd,
				cb_Read_Auth_Result, Conf_PongTimeout + 1);
		if (pid > 0) {
			LogDebug("Authenticator for connection %d created (PID %d).",
				 conn, pid);
			return CONNECTED;
		} else {
			/* Sub process */
			Log_Init_Subprocess("Auth");
			Conn_CloseAllSockets(NONE);
			result = PAM_Authenticate(Client);
			if (write(pipefd[1], &result, sizeof(result)) != sizeof(result))
				Log_Subprocess(LOG_ERR,
					       "Failed to pipe result to parent!");
			Log_Exit_Subprocess("Auth");
			exit(0);
		}
	} else return CONNECTED;
#else
	/* Check global server password ... */
	if (strcmp(Conn_Password(conn), Conf_ServerPwd) != 0) {
		/* Bad password! */
		Client_Reject(Client, "Bad server password", false);
		return DISCONNECTED;
	}
	return Login_User_PostAuth(Client);
#endif
}
Exemplo n.º 25
0
/**
 * Handle ENDOFMOTD (376) numeric and login remote server.
 * The peer is either an IRC server (no IRC+ protocol), or we got the
 * ENDOFMOTD numeric from an IRC+ server. We have to register the new server.
 */
GLOBAL bool
IRC_Num_ENDOFMOTD(CLIENT * Client, UNUSED REQUEST * Req)
{
	int max_hops, i;
	CLIENT *c;
	CHANNEL *chan;

	Client_SetType(Client, CLIENT_SERVER);

	Log(LOG_NOTICE | LOG_snotice,
	    "Server \"%s\" registered (connection %d, 1 hop - direct link).",
	    Client_ID(Client), Client_Conn(Client));

	/* Get highest hop count */
	max_hops = 0;
	c = Client_First();
	while (c) {
		if (Client_Hops(c) > max_hops)
			max_hops = Client_Hops(c);
		c = Client_Next(c);
	}

	/* Inform the new server about all other servers, and announce the
	 * new server to all the already registered ones. Important: we have
	 * to do this "in order" and can't introduce servers of which the
	 * "toplevel server" isn't known already. */
	for (i = 0; i < (max_hops + 1); i++) {
		for (c = Client_First(); c != NULL; c = Client_Next(c)) {
			if (Client_Type(c) != CLIENT_SERVER)
				continue;	/* not a server */
			if (Client_Hops(c) != i)
				continue;	/* not actual "nesting level" */
			if (c == Client || c == Client_ThisServer())
				continue;	/* that's us or the peer! */

			if (!Announce_Server(Client, c))
				return DISCONNECTED;
		}
	}

	/* Announce all the users to the new server */
	c = Client_First();
	while (c) {
		if (Client_Type(c) == CLIENT_USER ||
		    Client_Type(c) == CLIENT_SERVICE) {
			if (!Client_Announce(Client, Client_ThisServer(), c))
				return DISCONNECTED;
		}
		c = Client_Next(c);
	}

	/* Announce all channels to the new server */
	chan = Channel_First();
	while (chan) {
		if (Channel_IsLocal(chan)) {
			chan = Channel_Next(chan);
			continue;
		}
#ifdef IRCPLUS
		/* Send CHANINFO if the peer supports it */
		if (Client_HasFlag(Client, 'C')) {
			if (!Send_CHANINFO(Client, chan))
				return DISCONNECTED;
		}
#endif

		if (!Announce_Channel(Client, chan))
			return DISCONNECTED;

		/* Get next channel ... */
		chan = Channel_Next(chan);
	}

#ifdef IRCPLUS
	if (Client_HasFlag(Client, 'L')) {
		LogDebug("Synchronizing INVITE- and BAN-lists ...");
		if (!Synchronize_Lists(Client))
			return DISCONNECTED;
	}
#endif

	if (!IRC_WriteStrClient(Client, "PING :%s",
	    Client_ID(Client_ThisServer())))
		return DISCONNECTED;

	return CONNECTED;
} /* IRC_Num_ENDOFMOTD */
Exemplo n.º 26
0
Arquivo: irc.c Projeto: ngircd/ngircd
/**
 * Get pointer to a static string representing the connection "options".
 *
 * @param Idx Connection index.
 * @return Pointer to static (global) string buffer.
 */
static char *
#if defined(SSL_SUPPORT) || defined(ZLIB)
Option_String(CONN_ID Idx)
{
	static char option_txt[8];
	UINT16 options;

	assert(Idx != NONE);

	options = Conn_Options(Idx);
	strcpy(option_txt, "F");	/* No idea what this means, but the
					 * original ircd sends it ... */
#ifdef SSL_SUPPORT
	if(options & CONN_SSL)		/* SSL encrypted link */
		strlcat(option_txt, "s", sizeof(option_txt));
#endif
#ifdef ZLIB
	if(options & CONN_ZIP)		/* zlib compression enabled */
		strlcat(option_txt, "z", sizeof(option_txt));
#endif

	return option_txt;
#else
Option_String(UNUSED CONN_ID Idx)
{
	return "";
#endif
} /* Option_String */

/**
 * Send a message to target(s).
 *
 * This function is used by IRC_{PRIVMSG|NOTICE|SQUERY} to actualy
 * send the message(s).
 *
 * @param Client The client from which this command has been received.
 * @param Req Request structure with prefix and all parameters.
 * @param ForceType Required type of the destination of the message(s).
 * @param SendErrors Whether to report errors back to the client or not.
 * @return CONNECTED or DISCONNECTED.
 */
static bool
Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
{
	CLIENT *cl, *from;
	CL2CHAN *cl2chan;
	CHANNEL *chan;
	char *currentTarget = Req->argv[0];
	char *strtok_last = NULL;
	char *message = NULL;
	char *targets[MAX_HNDL_TARGETS];
	int i, target_nr = 0;

	assert(Client != NULL);
	assert(Req != NULL);

	if (Req->argc == 0) {
		if (!SendErrors)
			return CONNECTED;
		return IRC_WriteErrClient(Client, ERR_NORECIPIENT_MSG,
					  Client_ID(Client), Req->command);
	}
	if (Req->argc == 1) {
		if (!SendErrors)
			return CONNECTED;
		return IRC_WriteErrClient(Client, ERR_NOTEXTTOSEND_MSG,
					  Client_ID(Client));
	}
	if (Req->argc > 2) {
		if (!SendErrors)
			return CONNECTED;
		return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
					  Client_ID(Client), Req->command);
	}

	if (Client_Type(Client) == CLIENT_SERVER && Req->prefix)
		from = Client_Search(Req->prefix);
	else
		from = Client;
	if (!from)
		return IRC_WriteErrClient(Client, ERR_NOSUCHNICK_MSG,
					  Client_ID(Client), Req->prefix);

#ifdef ICONV
	if (Client_Conn(Client) > NONE)
		message = Conn_EncodingFrom(Client_Conn(Client), Req->argv[1]);
	else
#endif
		message = Req->argv[1];

	/* handle msgtarget = msgto *("," msgto) */
	currentTarget = strtok_r(currentTarget, ",", &strtok_last);
	ngt_UpperStr(Req->command);

	/* Please note that "currentTarget" is NULL when the target contains
	 * the separator character only, e. g. "," or ",,,," etc.! */
	while (currentTarget) {
		/* Make sure that there hasn't been such a target already: */
		targets[target_nr++] = currentTarget;
		for(i = 0; i < target_nr - 1; i++) {
			if (strcasecmp(currentTarget, targets[i]) == 0)
				goto send_next_target;
		}

		/* Check for and handle valid <msgto> of form:
		 * RFC 2812 2.3.1:
		 *   msgto =  channel / ( user [ "%" host ] "@" servername )
		 *   msgto =/ ( user "%" host ) / targetmask
		 *   msgto =/ nickname / ( nickname "!" user "@" host )
		 */
		if (strchr(currentTarget, '!') == NULL)
			/* nickname */
			cl = Client_Search(currentTarget);
		else
			cl = NULL;

		if (cl == NULL) {
			/* If currentTarget isn't a nickname check for:
			 * user ["%" host] "@" servername
			 * user "%" host
			 * nickname "!" user "@" host
			 */
			char target[COMMAND_LEN];
			char * nick = NULL;
			char * user = NULL;
			char * host = NULL;
			char * server = NULL;

			strlcpy(target, currentTarget, COMMAND_LEN);
			server = strchr(target, '@');
			if (server) {
				*server = '\0';
				server++;
			}
			host = strchr(target, '%');
			if (host) {
				*host = '\0';
				host++;
			}
			user = strchr(target, '!');
			if (user) {
				/* msgto form: nick!user@host */
				*user = '******';
				user++;
				nick = target;
				host = server; /* not "@server" but "@host" */
			} else {
				user = target;
			}

			for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) {
				if (Client_Type(cl) != CLIENT_USER &&
				    Client_Type(cl) != CLIENT_SERVICE)
					continue;
				if (nick != NULL && host != NULL) {
					if (strcasecmp(nick, Client_ID(cl)) == 0 &&
					    strcasecmp(user, Client_User(cl)) == 0 &&
					    strcasecmp(host, Client_HostnameDisplayed(cl)) == 0)
						break;
					else
						continue;
				}
				if (strcasecmp(user, Client_User(cl)) != 0)
					continue;
				if (host != NULL && strcasecmp(host,
						Client_HostnameDisplayed(cl)) != 0)
					continue;
				if (server != NULL && strcasecmp(server,
						Client_ID(Client_Introducer(cl))) != 0)
					continue;
				break;
			}
		}

		if (cl) {
			/* Target is a user, enforce type */
#ifndef STRICT_RFC
			if (Client_Type(cl) != ForceType &&
			    !(ForceType == CLIENT_USER &&
			      (Client_Type(cl) == CLIENT_USER ||
			       Client_Type(cl) == CLIENT_SERVICE))) {
#else
			if (Client_Type(cl) != ForceType) {
#endif
				if (SendErrors && !IRC_WriteErrClient(
				    from, ERR_NOSUCHNICK_MSG,Client_ID(from),
				    currentTarget))
					return DISCONNECTED;
				goto send_next_target;
			}

#ifndef STRICT_RFC
			if (ForceType == CLIENT_SERVICE &&
			    (Conn_Options(Client_Conn(Client_NextHop(cl)))
			     & CONN_RFC1459)) {
				/* SQUERY command but RFC 1459 link: convert
				 * request to PRIVMSG command */
				Req->command = "PRIVMSG";
			}
#endif
			if (Client_HasMode(cl, 'b') &&
			    !Client_HasMode(from, 'R') &&
			    !Client_HasMode(from, 'o') &&
			    !(Client_Type(from) == CLIENT_SERVER) &&
			    !(Client_Type(from) == CLIENT_SERVICE)) {
				if (SendErrors && !IRC_WriteErrClient(from,
						ERR_NONONREG_MSG,
						Client_ID(from), Client_ID(cl)))
					return DISCONNECTED;
				goto send_next_target;
			}

			if (Client_HasMode(cl, 'C') &&
			    !Client_HasMode(from, 'o') &&
			    !(Client_Type(from) == CLIENT_SERVER) &&
			    !(Client_Type(from) == CLIENT_SERVICE)) {
				cl2chan = Channel_FirstChannelOf(cl);
				while (cl2chan) {
					chan = Channel_GetChannel(cl2chan);
					if (Channel_IsMemberOf(chan, from))
						break;
					cl2chan = Channel_NextChannelOf(cl, cl2chan);
				}
				if (!cl2chan) {
					if (SendErrors && !IRC_WriteErrClient(
					    from, ERR_NOTONSAMECHANNEL_MSG,
					    Client_ID(from), Client_ID(cl)))
						return DISCONNECTED;
					goto send_next_target;
				}
			}

			if (SendErrors && (Client_Type(Client) != CLIENT_SERVER)
			    && Client_HasMode(cl, 'a')) {
				/* Target is away */
				if (!IRC_WriteStrClient(from, RPL_AWAY_MSG,
							Client_ID(from),
							Client_ID(cl),
							Client_Away(cl)))
					return DISCONNECTED;
			}
			if (Client_Conn(from) > NONE) {
				Conn_UpdateIdle(Client_Conn(from));
			}
			if (!IRC_WriteStrClientPrefix(cl, from, "%s %s :%s",
						      Req->command, Client_ID(cl),
						      message))
				return DISCONNECTED;
		} else if (ForceType != CLIENT_SERVICE
			   && (chan = Channel_Search(currentTarget))) {
			/* Target is a channel */
			if (!Channel_Write(chan, from, Client, Req->command,
					   SendErrors, message))
					return DISCONNECTED;
		} else if (ForceType != CLIENT_SERVICE
			   && strchr("$#", currentTarget[0])
			   && strchr(currentTarget, '.')) {
			/* $#: server/host mask, RFC 2812, sec. 3.3.1 */
			if (!Send_Message_Mask(from, Req->command, currentTarget,
					       message, SendErrors))
				return DISCONNECTED;
		} else {
			if (!SendErrors)
				return CONNECTED;
			if (!IRC_WriteErrClient(from, ERR_NOSUCHNICK_MSG,
						Client_ID(from), currentTarget))
				return DISCONNECTED;
		}

	send_next_target:
		currentTarget = strtok_r(NULL, ",", &strtok_last);
		if (!currentTarget)
			break;

		Conn_SetPenalty(Client_Conn(Client), 1);

		if (target_nr >= MAX_HNDL_TARGETS) {
			/* Too many targets given! */
			return IRC_WriteErrClient(Client,
						  ERR_TOOMANYTARGETS_MSG,
						  currentTarget);
		}
	}

	return CONNECTED;
} /* Send_Message */

/**
 * Send a message to "target mask" target(s).
 *
 * See RFC 2812, sec. 3.3.1 for details.
 *
 * @param from The client from which this command has been received.
 * @param command The command to use (PRIVMSG, NOTICE, ...).
 * @param targetMask The "target mask" (will be verified by this function).
 * @param message The message to send.
 * @param SendErrors Whether to report errors back to the client or not.
 * @return CONNECTED or DISCONNECTED.
 */
static bool
Send_Message_Mask(CLIENT * from, char * command, char * targetMask,
		  char * message, bool SendErrors)
{
	CLIENT *cl;
	bool client_match;
	char *mask = targetMask + 1;
	const char *check_wildcards;

	cl = NULL;

	if (!Client_HasMode(from, 'o')) {
		if (!SendErrors)
			return true;
		return IRC_WriteErrClient(from, ERR_NOPRIVILEGES_MSG,
					  Client_ID(from));
	}

	/*
	 * RFC 2812, sec. 3.3.1 requires that targetMask have at least one
	 * dot (".") and no wildcards ("*", "?") following the last one.
	 */
	check_wildcards = strrchr(targetMask, '.');
	if (!check_wildcards || check_wildcards[strcspn(check_wildcards, "*?")]) {
		if (!SendErrors)
			return true;
		return IRC_WriteErrClient(from, ERR_WILDTOPLEVEL_MSG,
					  targetMask);
	}

	if (targetMask[0] == '#') {
		/* #: hostmask, see RFC 2812, sec. 3.3.1 */
		for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) {
			if (Client_Type(cl) != CLIENT_USER)
				continue;
			client_match = MatchCaseInsensitive(mask, Client_Hostname(cl));
			if (client_match)
				if (!IRC_WriteStrClientPrefix(cl, from, "%s %s :%s",
						command, Client_ID(cl), message))
					return false;
		}
	} else {
		/* $: server mask, see RFC 2812, sec. 3.3.1 */
		assert(targetMask[0] == '$');
		for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) {
			if (Client_Type(cl) != CLIENT_USER)
				continue;
			client_match = MatchCaseInsensitive(mask,
					Client_ID(Client_Introducer(cl)));
			if (client_match)
				if (!IRC_WriteStrClientPrefix(cl, from, "%s %s :%s",
						command, Client_ID(cl), message))
					return false;
		}
	}
	return CONNECTED;
} /* Send_Message_Mask */
Exemplo n.º 27
0
/**
 * Announce a channel and its users in the network.
 */
static bool
Announce_Channel(CLIENT *Client, CHANNEL *Chan)
{
	CL2CHAN *cl2chan;
	CLIENT *cl;
	char str[COMMAND_LEN], *ptr;
	bool njoin, xop;

	/* Check features of remote server */
	njoin = Conn_Options(Client_Conn(Client)) & CONN_RFC1459 ? false : true;
	xop = Client_HasFlag(Client, 'X') ? true : false;

	/* Get all the members of this channel */
	cl2chan = Channel_FirstMember(Chan);
	snprintf(str, sizeof(str), "NJOIN %s :", Channel_Name(Chan));
	while (cl2chan) {
		cl = Channel_GetClient(cl2chan);
		assert(cl != NULL);

		if (njoin) {
			/* RFC 2813: send NJOIN with nicknames and modes
			 * (if user is channel operator or has voice) */
			if (str[strlen(str) - 1] != ':')
				strlcat(str, ",", sizeof(str));

			/* Prepare user prefix (ChanOp, voiced, ...) */
			if (xop && Channel_UserHasMode(Chan, cl, 'q'))
				strlcat(str, "~", sizeof(str));
			if (xop && Channel_UserHasMode(Chan, cl, 'a'))
				strlcat(str, "&", sizeof(str));
			if (Channel_UserHasMode(Chan, cl, 'o'))
				strlcat(str, "@", sizeof(str));
			if (xop && Channel_UserHasMode(Chan, cl, 'h'))
				strlcat(str, "%", sizeof(str));
			if (Channel_UserHasMode(Chan, cl, 'v'))
				strlcat(str, "+", sizeof(str));

			strlcat(str, Client_ID(cl), sizeof(str));

			/* Send the data if the buffer is "full" */
			if (strlen(str) > (sizeof(str) - CLIENT_NICK_LEN - 8)) {
				if (!IRC_WriteStrClient(Client, "%s", str))
					return DISCONNECTED;
				snprintf(str, sizeof(str), "NJOIN %s :",
					 Channel_Name(Chan));
			}
		} else {
			/* RFC 1459: no NJOIN, send JOIN and MODE */
			if (!IRC_WriteStrClientPrefix(Client, cl, "JOIN %s",
						Channel_Name(Chan)))
				return DISCONNECTED;
			ptr = Channel_UserModes(Chan, cl);
			while (*ptr) {
				if (!IRC_WriteStrClientPrefix(Client, cl,
						   "MODE %s +%c %s",
						   Channel_Name(Chan), ptr[0],
						   Client_ID(cl)))
					return DISCONNECTED;
				ptr++;
			}
		}

		cl2chan = Channel_NextMember(Chan, cl2chan);
	}

	/* Data left in the buffer? */
	if (str[strlen(str) - 1] != ':') {
		/* Yes, send it ... */
		if (!IRC_WriteStrClient(Client, "%s", str))
			return DISCONNECTED;
	}

	return CONNECTED;
} /* Announce_Channel */
Exemplo n.º 28
0
GLOBAL bool
IRC_TRACE( CLIENT *Client, REQUEST *Req )
{
	CLIENT *from, *target, *c;
	CONN_ID idx, idx2;
	char user[CLIENT_USER_LEN];

	assert( Client != NULL );
	assert( Req != NULL );

	/* Bad number of arguments? */
	if( Req->argc > 1 ) return IRC_WriteStrClient( Client, ERR_NORECIPIENT_MSG, Client_ID( Client ), Req->command );

	/* Search sender */
	if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_Search( Req->prefix );
	else from = Client;
	if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );

	/* Search target */
	if( Req->argc == 1 ) target = Client_Search( Req->argv[0] );
	else target = Client_ThisServer( );
	
	/* Forward command to other server? */
	if( target != Client_ThisServer( ))
	{
		if(( ! target ) || ( Client_Type( target ) != CLIENT_SERVER )) return IRC_WriteStrClient( from, ERR_NOSUCHSERVER_MSG, Client_ID( from ), Req->argv[0] );

		/* Send RPL_TRACELINK back to initiator */
		idx = Client_Conn( Client ); assert( idx > NONE );
		idx2 = Client_Conn( Client_NextHop( target )); assert( idx2 > NONE );
		if( ! IRC_WriteStrClient( from, RPL_TRACELINK_MSG, Client_ID( from ), PACKAGE_NAME, PACKAGE_VERSION, Client_ID( target ), Client_ID( Client_NextHop( target )), Option_String( idx2 ), time( NULL ) - Conn_StartTime( idx2 ), Conn_SendQ( idx ), Conn_SendQ( idx2 ))) return DISCONNECTED;

		/* Forward command */
		IRC_WriteStrClientPrefix( target, from, "TRACE %s", Req->argv[0] );
		return CONNECTED;
	}

	/* Infos about all connected servers */
	c = Client_First( );
	while( c )
	{
		if( Client_Conn( c ) > NONE )
		{
			/* Local client */
			if( Client_Type( c ) == CLIENT_SERVER )
			{
				/* Server link */
				strlcpy( user, Client_User( c ), sizeof( user ));
				if( user[0] == '~' ) strlcpy( user, "unknown", sizeof( user ));
				if( ! IRC_WriteStrClient( from, RPL_TRACESERVER_MSG, Client_ID( from ), Client_ID( c ), user, Client_Hostname( c ), Client_Mask( Client_ThisServer( )), Option_String( Client_Conn( c )))) return DISCONNECTED;
			}
			if(( Client_Type( c ) == CLIENT_USER ) && ( strchr( Client_Modes( c ), 'o' )))
			{
				/* IRC Operator */
				if( ! IRC_WriteStrClient( from, RPL_TRACEOPERATOR_MSG, Client_ID( from ), Client_ID( c ))) return DISCONNECTED;
			}
		}
		c = Client_Next( c );
	}

	IRC_SetPenalty( Client, 3 );
	return IRC_WriteStrClient( from, RPL_TRACEEND_MSG, Client_ID( from ), Conf_ServerName, PACKAGE_NAME, PACKAGE_VERSION, NGIRCd_DebugLevel );
} /* IRC_TRACE */
Exemplo n.º 29
0
static bool
Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
{
	CLIENT *cl, *from;
	CL2CHAN *cl2chan;
	CHANNEL *chan;
	char *currentTarget = Req->argv[0];
	char *lastCurrentTarget = NULL;

	assert(Client != NULL);
	assert(Req != NULL);

	if (Req->argc == 0) {
		if (!SendErrors)
			return CONNECTED;
		return IRC_WriteStrClient(Client, ERR_NORECIPIENT_MSG,
					  Client_ID(Client), Req->command);
	}
	if (Req->argc == 1) {
		if (!SendErrors)
			return CONNECTED;
		return IRC_WriteStrClient(Client, ERR_NOTEXTTOSEND_MSG,
					  Client_ID(Client));
	}
	if (Req->argc > 2) {
		if (!SendErrors)
			return CONNECTED;
		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
					  Client_ID(Client), Req->command);
	}

	if (Client_Type(Client) == CLIENT_SERVER)
		from = Client_Search(Req->prefix);
	else
		from = Client;
	if (!from)
		return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
					  Client_ID(Client), Req->prefix);

	/* handle msgtarget = msgto *("," msgto) */
	currentTarget = strtok_r(currentTarget, ",", &lastCurrentTarget);
	ngt_UpperStr(Req->command);

	while (currentTarget) {
		/* Check for and handle valid <msgto> of form:
		 * RFC 2812 2.3.1:
		 *   msgto =  channel / ( user [ "%" host ] "@" servername )
		 *   msgto =/ ( user "%" host ) / targetmask
		 *   msgto =/ nickname / ( nickname "!" user "@" host )
		 */
		if (strchr(currentTarget, '!') == NULL)
			/* nickname */
			cl = Client_Search(currentTarget);
		else
			cl = NULL;

		if (cl == NULL) {
			/* If currentTarget isn't a nickname check for:
			 * user ["%" host] "@" servername
			 * user "%" host
			 * nickname "!" user "@" host
			 */
			char target[COMMAND_LEN];
			char * nick = NULL;
			char * user = NULL;
			char * host = NULL;
			char * server = NULL;

			strlcpy(target, currentTarget, COMMAND_LEN);
			server = strchr(target, '@');
			if (server) {
				*server = '\0';
				server++;
			}
			host = strchr(target, '%');
			if (host) {
				*host = '\0';
				host++;
			}
			user = strchr(target, '!');
			if (user) {
				/* msgto form: nick!user@host */
				*user = '******';
				user++;
				nick = target;
				host = server; /* not "@server" but "@host" */
			} else {
				user = target;
			}

			for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) {
				if (Client_Type(cl) != CLIENT_USER &&
				    Client_Type(cl) != CLIENT_SERVICE)
					continue;
				if (nick != NULL && host != NULL) {
					if (strcasecmp(nick, Client_ID(cl)) == 0 &&
					    strcasecmp(user, Client_User(cl)) == 0 &&
					    strcasecmp(host, Client_HostnameCloaked(cl)) == 0)
						break;
					else
						continue;
				}
				if (strcasecmp(user, Client_User(cl)) != 0)
					continue;
				if (host != NULL && strcasecmp(host,
						Client_HostnameCloaked(cl)) != 0)
					continue;
				if (server != NULL && strcasecmp(server,
						Client_ID(Client_Introducer(cl))) != 0)
					continue;
				break;
			}
		}

		if (cl) {
			/* Target is a user, enforce type */
#ifndef STRICT_RFC
			if (Client_Type(cl) != ForceType &&
			    !(ForceType == CLIENT_USER &&
			      (Client_Type(cl) == CLIENT_USER ||
			       Client_Type(cl) == CLIENT_SERVICE))) {
#else
			if (Client_Type(cl) != ForceType) {
#endif
				if (SendErrors && !IRC_WriteStrClient(
				    from, ERR_NOSUCHNICK_MSG,Client_ID(from),
				    currentTarget))
					return DISCONNECTED;
				goto send_next_target;
			}

#ifndef STRICT_RFC
			if (ForceType == CLIENT_SERVICE &&
			    (Conn_Options(Client_Conn(Client_NextHop(cl)))
			     & CONN_RFC1459)) {
				/* SQUERY command but RFC 1459 link: convert
				 * request to PRIVMSG command */
				Req->command = "PRIVMSG";
			}
#endif

			if (Client_HasMode(cl, 'C')) {
				cl2chan = Channel_FirstChannelOf(cl);
				while (cl2chan) {
					chan = Channel_GetChannel(cl2chan);
					if (Channel_IsMemberOf(chan, from))
						break;
					cl2chan = Channel_NextChannelOf(cl, cl2chan);
				}
				if (!cl2chan) {
					if (SendErrors && !IRC_WriteStrClient(
					    from, ERR_NOTONSAMECHANNEL_MSG,
					    Client_ID(from), Client_ID(cl)))
						return DISCONNECTED;
					goto send_next_target;
				}
			}

			if (SendErrors && (Client_Type(Client) != CLIENT_SERVER)
			    && strchr(Client_Modes(cl), 'a')) {
				/* Target is away */
				if (!IRC_WriteStrClient(from, RPL_AWAY_MSG,
							Client_ID(from),
							Client_ID(cl),
							Client_Away(cl)))
					return DISCONNECTED;
			}
			if (Client_Conn(from) > NONE) {
				Conn_UpdateIdle(Client_Conn(from));
			}
			if (!IRC_WriteStrClientPrefix(cl, from, "%s %s :%s",
						      Req->command, Client_ID(cl),
						      Req->argv[1]))
				return DISCONNECTED;
		} else if (ForceType != CLIENT_SERVICE
			   && (chan = Channel_Search(currentTarget))) {
			if (!Channel_Write(chan, from, Client, Req->command,
					   SendErrors, Req->argv[1]))
					return DISCONNECTED;
		} else if (ForceType != CLIENT_SERVICE
			/* $#: server/target mask, RFC 2812, sec. 3.3.1 */
			   && strchr("$#", currentTarget[0])
			   && strchr(currentTarget, '.')) {
			/* targetmask */
			if (!Send_Message_Mask(from, Req->command, currentTarget,
					       Req->argv[1], SendErrors))
				return DISCONNECTED;
		} else {
			if (!SendErrors)
				return CONNECTED;
			if (!IRC_WriteStrClient(from, ERR_NOSUCHNICK_MSG,
						Client_ID(from), currentTarget))
				return DISCONNECTED;
		}

	send_next_target:
		currentTarget = strtok_r(NULL, ",", &lastCurrentTarget);
		if (currentTarget)
			Conn_SetPenalty(Client_Conn(Client), 1);
	}

	return CONNECTED;
} /* Send_Message */


static bool
Send_Message_Mask(CLIENT * from, char * command, char * targetMask,
		  char * message, bool SendErrors)
{
	CLIENT *cl;
	bool client_match;
	char *mask = targetMask + 1;
	const char *check_wildcards;

	cl = NULL;

	if (strchr(Client_Modes(from), 'o') == NULL) {
		if (!SendErrors)
			return true;
		return IRC_WriteStrClient(from, ERR_NOPRIVILEGES_MSG,
					  Client_ID(from));
	}

	/*
	 * RFC 2812, sec. 3.3.1 requires that targetMask have at least one
	 * dot (".") and no wildcards ("*", "?") following the last one.
	 */
	check_wildcards = strrchr(targetMask, '.');
	assert(check_wildcards != NULL);
	if (check_wildcards &&
		check_wildcards[strcspn(check_wildcards, "*?")])
	{
		if (!SendErrors)
			return true;
		return IRC_WriteStrClient(from, ERR_WILDTOPLEVEL, targetMask);
	}

	/* #: hostmask, see RFC 2812, sec. 3.3.1 */
	if (targetMask[0] == '#') {
		for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) {
			if (Client_Type(cl) != CLIENT_USER)
				continue;
			client_match = MatchCaseInsensitive(mask, Client_Hostname(cl));
			if (client_match)
				if (!IRC_WriteStrClientPrefix(cl, from, "%s %s :%s",
						command, Client_ID(cl), message))
					return false;
		}
	} else {
		assert(targetMask[0] == '$'); /* $: server mask, see RFC 2812, sec. 3.3.1 */
		for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) {
			if (Client_Type(cl) != CLIENT_USER)
				continue;
			client_match = MatchCaseInsensitive(mask,
					Client_ID(Client_Introducer(cl)));
			if (client_match)
				if (!IRC_WriteStrClientPrefix(cl, from, "%s %s :%s",
						command, Client_ID(cl), message))
					return false;
		}
	}
	return CONNECTED;
} /* Send_Message_Mask */
Exemplo n.º 30
0
Arquivo: irc.c Projeto: ngircd/ngircd
/*
 * Handler for the IRC "TRACE" command.
 *
 * @param Client The client from which this command has been received.
 * @param Req Request structure with prefix and all parameters.
 * @return CONNECTED or DISCONNECTED.
 */
 GLOBAL bool
IRC_TRACE(CLIENT *Client, REQUEST *Req)
{
	CLIENT *from, *target, *c;
	CONN_ID idx, idx2;
	char user[CLIENT_USER_LEN];

	assert(Client != NULL);
	assert(Req != NULL);

	_IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
	_IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, from)

	/* Forward command to other server? */
	if (target != Client_ThisServer()) {
		/* Send RPL_TRACELINK back to initiator */
		idx = Client_Conn(Client);
		assert(idx > NONE);
		idx2 = Client_Conn(Client_NextHop(target));
		assert(idx2 > NONE);

		if (!IRC_WriteStrClient(from, RPL_TRACELINK_MSG,
					Client_ID(from), PACKAGE_NAME,
					PACKAGE_VERSION, Client_ID(target),
					Client_ID(Client_NextHop(target)),
					Option_String(idx2),
					(long)(time(NULL) - Conn_StartTime(idx2)),
					Conn_SendQ(idx), Conn_SendQ(idx2)))
			return DISCONNECTED;

		/* Forward command */
		IRC_WriteStrClientPrefix(target, from, "TRACE %s", Req->argv[0]);
		return CONNECTED;
	}

	/* Infos about all connected servers */
	c = Client_First();
	while (c) {
		if (Client_Conn(c) > NONE) {
			/* Local client */
			if (Client_Type(c) == CLIENT_SERVER) {
				/* Server link */
				strlcpy(user, Client_User(c), sizeof(user));
				if (user[0] == '~')
					strlcpy(user, "unknown", sizeof(user));
				if (!IRC_WriteStrClient(from,
						RPL_TRACESERVER_MSG,
						Client_ID(from), Client_ID(c),
						user, Client_Hostname(c),
						Client_Mask(Client_ThisServer()),
						Option_String(Client_Conn(c))))
					return DISCONNECTED;
			}
			if (Client_Type(c) == CLIENT_USER
			    && Client_HasMode(c, 'o')) {
				/* IRC Operator */
				if (!IRC_WriteStrClient(from,
						RPL_TRACEOPERATOR_MSG,
						Client_ID(from), Client_ID(c)))
					return DISCONNECTED;
			}
		}
		c = Client_Next( c );
	}

	return IRC_WriteStrClient(from, RPL_TRACEEND_MSG, Client_ID(from),
				  Conf_ServerName, PACKAGE_NAME,
				  PACKAGE_VERSION, NGIRCd_DebugLevel);
} /* IRC_TRACE */