Exemplo n.º 1
0
/**
 * Finish client registration.
 *
 * Introduce the new client to the network and send all "hello messages"
 * to it after authentication has been succeeded.
 *
 * @param Client The client logging in.
 * @return CONNECTED or DISCONNECTED.
 */
GLOBAL bool
Login_User_PostAuth(CLIENT *Client)
{
	REQUEST Req;
	char modes[CLIENT_MODE_LEN + 1];

	assert(Client != NULL);

	if (Class_HandleServerBans(Client) != CONNECTED)
		return DISCONNECTED;

	Client_Introduce(NULL, Client, CLIENT_USER);

	if (!IRC_WriteStrClient
	    (Client, RPL_WELCOME_MSG, Client_ID(Client), Client_Mask(Client)))
		return false;
	if (!IRC_WriteStrClient
	    (Client, RPL_YOURHOST_MSG, Client_ID(Client),
	     Client_ID(Client_ThisServer()), PACKAGE_VERSION, HOST_CPU,
	     HOST_VENDOR, HOST_OS))
		return false;
	if (!IRC_WriteStrClient
	    (Client, RPL_CREATED_MSG, Client_ID(Client), NGIRCd_StartStr))
		return false;
	if (!IRC_WriteStrClient
	    (Client, RPL_MYINFO_MSG, Client_ID(Client),
	     Client_ID(Client_ThisServer()), PACKAGE_VERSION, USERMODES,
	     CHANMODES))
		return false;

	/* Features supported by this server (005 numeric, ISUPPORT),
	 * see <http://www.irc.org/tech_docs/005.html> for details. */
	if (!IRC_Send_ISUPPORT(Client))
		return DISCONNECTED;

	if (!IRC_Send_LUSERS(Client))
		return DISCONNECTED;
	if (!IRC_Show_MOTD(Client))
		return DISCONNECTED;

	/* Set default user modes */
	if (Conf_DefaultUserModes[0]) {
		snprintf(modes, sizeof(modes), "+%s", Conf_DefaultUserModes);
		Req.prefix = Client_ID(Client_ThisServer());
		Req.command = "MODE";
		Req.argc = 2;
		Req.argv[0] = Client_ID(Client);
		Req.argv[1] = modes;
		IRC_MODE(Client, &Req);
	} else
		IRC_SetPenalty(Client, 1);

	return CONNECTED;
}
Exemplo n.º 2
0
/**
 * Handler for the IRC "USER" command.
 *
 * See RFC 2812, 3.1.3 "User message".
 *
 * @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_USER(CLIENT * Client, REQUEST * Req)
{
	CLIENT *c;
	char *ptr;

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

	if (Client_Type(Client) == CLIENT_GOTNICK ||
#ifndef STRICT_RFC
	    Client_Type(Client) == CLIENT_UNKNOWN ||
#endif
	    Client_Type(Client) == CLIENT_GOTPASS)
	{
		/* New connection */
		if (Req->argc != 4)
			return IRC_WriteStrClient(Client,
						  ERR_NEEDMOREPARAMS_MSG,
						  Client_ID(Client),
						  Req->command);

		/* User name: only alphanumeric characters and limited
		   punctuation is allowed.*/
		ptr = Req->argv[0];
		while (*ptr) {
			if (!isalnum(*ptr) &&
			    *ptr != '+' && *ptr != '-' &&
			    *ptr != '.' && *ptr != '_') {
				Conn_Close(Client_Conn(Client), NULL,
					   "Invalid user name", true);
				return DISCONNECTED;
			}
			ptr++;
		}

#ifdef IDENTAUTH
		ptr = Client_User(Client);
		if (!ptr || !*ptr || *ptr == '~')
			Client_SetUser(Client, Req->argv[0], false);
#else
		Client_SetUser(Client, Req->argv[0], false);
#endif
		Client_SetOrigUser(Client, Req->argv[0]);

		/* "Real name" or user info text: Don't set it to the empty
		 * string, the original ircd can't deal with such "real names"
		 * (e. g. "USER user * * :") ... */
		if (*Req->argv[3])
			Client_SetInfo(Client, Req->argv[3]);
		else
			Client_SetInfo(Client, "-");

		LogDebug("Connection %d: got valid USER command ...",
		    Client_Conn(Client));
		if (Client_Type(Client) == CLIENT_GOTNICK)
			return Login_User(Client);
		else
			Client_SetType(Client, CLIENT_GOTUSER);
		return CONNECTED;

	} else if (Client_Type(Client) == CLIENT_SERVER ||
		   Client_Type(Client) == CLIENT_SERVICE) {
		/* Server/service updating an user */
		if (Req->argc != 4)
			return IRC_WriteStrClient(Client,
						  ERR_NEEDMOREPARAMS_MSG,
						  Client_ID(Client),
						  Req->command);
		c = Client_Search(Req->prefix);
		if (!c)
			return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
						  Client_ID(Client),
						  Req->prefix);

		Client_SetUser(c, Req->argv[0], true);
		Client_SetOrigUser(c, Req->argv[0]);
		Client_SetHostname(c, Req->argv[1]);
		Client_SetInfo(c, Req->argv[3]);

		LogDebug("Connection %d: got valid USER command for \"%s\".",
			 Client_Conn(Client), Client_Mask(c));

		/* RFC 1459 style user registration?
		 * Introduce client to network: */
		if (Client_Type(c) == CLIENT_GOTNICK)
			Client_Introduce(Client, c, CLIENT_USER);

		return CONNECTED;
	} else if (Client_Type(Client) == CLIENT_USER) {
		/* Already registered connection */
		return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG,
					  Client_ID(Client));
	} else {
		/* Unexpected/invalid connection state? */
		return IRC_WriteStrClient(Client, ERR_NOTREGISTERED_MSG,
					  Client_ID(Client));
	}
} /* IRC_USER */
Exemplo n.º 3
0
/**
 * Handler for the IRC "SERVICE" command.
 *
 * This function implements IRC Services registration using the SERVICE command
 * defined in RFC 2812 3.1.6 and RFC 2813 4.1.4.
 *
 * At the moment ngIRCd doesn't support directly linked services, so this
 * function returns ERR_ERRONEUSNICKNAME when the SERVICE command has not been
 * received from a peer server.
 *
 * @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_SERVICE(CLIENT *Client, REQUEST *Req)
{
	CLIENT *c, *intr_c;
	char *nick, *user, *host, *info, *modes, *ptr;
	int token, hops;

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

	if (Client_Type(Client) != CLIENT_GOTPASS &&
	    Client_Type(Client) != CLIENT_SERVER)
		return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG,
					  Client_ID(Client));

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

	if (Client_Type(Client) != CLIENT_SERVER)
		return IRC_WriteStrClient(Client, ERR_ERRONEUSNICKNAME_MSG,
				  Client_ID(Client), Req->argv[0]);

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

	nick = Req->argv[0];
	user = NULL; host = NULL;
	token = atoi(Req->argv[1]);
	hops = atoi(Req->argv[4]);
	info = Req->argv[5];

	/* Validate service name ("nick name") */
	c = Client_Search(nick);
	if(c) {
		/* Nick name collission: disconnect (KILL) both clients! */
		Log(LOG_ERR, "Server %s introduces already registered service \"%s\"!",
		    Client_ID(Client), nick);
		Kill_Nick(nick, "Nick collision");
		return CONNECTED;
	}

	/* Get the server to which the service is connected */
	intr_c = Client_GetFromToken(Client, token);
	if (! intr_c) {
		Log(LOG_ERR, "Server %s introduces service \"%s\" on unknown server!?",
		    Client_ID(Client), nick);
		Kill_Nick(nick, "Unknown server");
		return CONNECTED;
	}

	/* Get user and host name */
	ptr = strchr(nick, '@');
	if (ptr) {
		*ptr = '\0';
		host = ++ptr;
	}
	if (!host)
		host = Client_Hostname(intr_c);
	ptr = strchr(nick, '!');
	if (ptr) {
		*ptr = '\0';
		user = ++ptr;
	}
	if (!user)
		user = nick;

	/* According to RFC 2812/2813 parameter 4 <type> "is currently reserved
	 * for future usage"; but we use it to transfer the modes and check
	 * that the first character is a '+' sign and ignore it otherwise. */
	modes = (Req->argv[3][0] == '+') ? ++Req->argv[3] : "";

	c = Client_NewRemoteUser(intr_c, nick, hops, user, host,
				 token, modes, info, true);
	if (! c) {
		/* Couldn't create client structure, so KILL the service to
		 * keep network status consistent ... */
		Log(LOG_ALERT, "Can't create client structure! (on connection %d)",
		    Client_Conn(Client));
		Kill_Nick(nick, "Server error");
		return CONNECTED;
	}

	Client_Introduce(Client, c, CLIENT_SERVICE);
	return CONNECTED;
} /* IRC_SERVICE */
Exemplo n.º 4
0
/**
 * Handler for the IRC "NICK" command.
 *
 * See RFC 2812, 3.1.2 "Nick message", and RFC 2813, 4.1.3 "Nick".
 *
 * This function implements the IRC command "NICK" which is used to register
 * with the server, to change already registered nicknames and to introduce
 * new users which are connected to other servers.
 *
 * @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_NICK( CLIENT *Client, REQUEST *Req )
{
	CLIENT *intr_c, *target, *c;
	char *nick, *user, *hostname, *modes, *info;
	int token, hops;

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

	/* Some IRC clients, for example BitchX, send the NICK and USER
	 * commands in the wrong order ... */
	if(Client_Type(Client) == CLIENT_UNKNOWN
	    || Client_Type(Client) == CLIENT_GOTPASS
	    || Client_Type(Client) == CLIENT_GOTNICK
#ifndef STRICT_RFC
	    || Client_Type(Client) == CLIENT_GOTUSER
#endif
	    || Client_Type(Client) == CLIENT_USER
	    || Client_Type(Client) == CLIENT_SERVICE
	    || (Client_Type(Client) == CLIENT_SERVER && Req->argc == 1))
	{
		/* User registration or change of nickname */

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

		/* Search "target" client */
		if( Client_Type( Client ) == CLIENT_SERVER )
		{
			target = Client_Search( Req->prefix );
			if( ! target )
				return IRC_WriteStrClient( Client,
							   ERR_NOSUCHNICK_MSG,
							   Client_ID( Client ),
							   Req->argv[0] );
		}
		else
		{
			/* Is this a restricted client? */
			if( Client_HasMode( Client, 'r' ))
				return IRC_WriteStrClient( Client,
							   ERR_RESTRICTED_MSG,
							   Client_ID( Client ));

			target = Client;
		}

#ifndef STRICT_RFC
		/* If the clients tries to change to its own nickname we won't
		 * do anything. This is how the original ircd behaves and some
		 * clients (for example Snak) expect it to be like this.
		 * But I doubt that this is "really the right thing" ... */
		if( strcmp( Client_ID( target ), Req->argv[0] ) == 0 )
			return CONNECTED;
#endif

		/* Check that the new nickname is available. Special case:
		 * the client only changes from/to upper to lower case. */
		if( strcasecmp( Client_ID( target ), Req->argv[0] ) != 0 )
		{
			if( ! Client_CheckNick( target, Req->argv[0] ))
				return CONNECTED;
		}

		if (Client_Type(target) != CLIENT_USER &&
		    Client_Type(target) != CLIENT_SERVICE &&
		    Client_Type(target) != CLIENT_SERVER) {
			/* New client */
			LogDebug("Connection %d: got valid NICK command ...",
			     Client_Conn( Client ));

			/* Register new nickname of this client */
			Client_SetID( target, Req->argv[0] );

#ifndef STRICT_RFC
			if (Conf_AuthPing) {
				Conn_SetAuthPing(Client_Conn(Client), rand());
				IRC_WriteStrClient(Client, "PING :%ld",
					Conn_GetAuthPing(Client_Conn(Client)));
				LogDebug("Connection %d: sent AUTH PING %ld ...",
					Client_Conn(Client),
					Conn_GetAuthPing(Client_Conn(Client)));
			}
#endif

			/* If we received a valid USER command already then
			 * register the new client! */
			if( Client_Type( Client ) == CLIENT_GOTUSER )
				return Login_User( Client );
			else
				Client_SetType( Client, CLIENT_GOTNICK );
		} else {
			/* Nickname change */
			if (Client_Conn(target) > NONE) {
				/* Local client */
				Log(LOG_INFO,
				    "%s \"%s\" changed nick (connection %d): \"%s\" -> \"%s\".",
				    Client_TypeText(target), Client_Mask(target),
				    Client_Conn(target), Client_ID(target),
				    Req->argv[0]);
				Conn_UpdateIdle(Client_Conn(target));
			} else {
				/* Remote client */
				LogDebug("%s \"%s\" changed nick: \"%s\" -> \"%s\".",
					 Client_TypeText(target),
					 Client_Mask(target), Client_ID(target),
					 Req->argv[0]);
			}

			/* Inform all users and servers (which have to know)
			 * of this nickname change */
			if( Client_Type( Client ) == CLIENT_USER )
				IRC_WriteStrClientPrefix( Client, Client,
							  "NICK :%s",
							  Req->argv[0] );
			IRC_WriteStrServersPrefix( Client, target,
						   "NICK :%s", Req->argv[0] );
			IRC_WriteStrRelatedPrefix( target, target, false,
						   "NICK :%s", Req->argv[0] );

			/* Register old nickname for WHOWAS queries */
			Client_RegisterWhowas( target );

			/* Save new nickname */
			Client_SetID( target, Req->argv[0] );

			IRC_SetPenalty( target, 2 );
		}

		return CONNECTED;
	} else if(Client_Type(Client) == CLIENT_SERVER ||
		  Client_Type(Client) == CLIENT_SERVICE) {
		/* Server or service introduces new client */

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

		if (Req->argc >= 7) {
			/* RFC 2813 compatible syntax */
			nick = Req->argv[0];
			hops = atoi(Req->argv[1]);
			user = Req->argv[2];
			hostname = Req->argv[3];
			token = atoi(Req->argv[4]);
			modes = Req->argv[5] + 1;
			info = Req->argv[6];
		} else {
			/* RFC 1459 compatible syntax */
			nick = Req->argv[0];
			hops = 1;
			user = Req->argv[0];
			hostname = Client_ID(Client);
			token = atoi(Req->argv[1]);
			modes = "";
			info = Req->argv[0];
		}

		c = Client_Search(nick);
		if(c) {
			/*
			 * the new nick is already present on this server:
			 * the new and the old one have to be disconnected now.
			 */
			Log( LOG_ERR, "Server %s introduces already registered nick \"%s\"!", Client_ID( Client ), Req->argv[0] );
			Kill_Nick( Req->argv[0], "Nick collision" );
			return CONNECTED;
		}

		/* Find the Server this client is connected to */
		intr_c = Client_GetFromToken(Client, token);
		if( ! intr_c )
		{
			Log( LOG_ERR, "Server %s introduces nick \"%s\" on unknown server!?", Client_ID( Client ), Req->argv[0] );
			Kill_Nick( Req->argv[0], "Unknown server" );
			return CONNECTED;
		}

		c = Client_NewRemoteUser(intr_c, nick, hops, user, hostname,
					 token, modes, info, true);
		if( ! c )
		{
			/* out of memory, need to disconnect client to keep network state consistent */
			Log( LOG_ALERT, "Can't create client structure! (on connection %d)", Client_Conn( Client ));
			Kill_Nick( Req->argv[0], "Server error" );
			return CONNECTED;
		}

		/* RFC 2813: client is now fully registered, inform all the
		 * other servers about the new user.
		 * RFC 1459: announce the new client only after receiving the
		 * USER command, first we need more information! */
		if (Req->argc < 7) {
			LogDebug("Client \"%s\" is being registered (RFC 1459) ...",
				 Client_Mask(c));
			Client_SetType(c, CLIENT_GOTNICK);
		} else
			Client_Introduce(Client, c, CLIENT_USER);

		return CONNECTED;
	}
	else return IRC_WriteStrClient( Client, ERR_ALREADYREGISTRED_MSG, Client_ID( Client ));
} /* IRC_NICK */