Пример #1
0
/**
 * Compress data in ZIP buffer and move result to the write buffer of
 * the connection.
 * This function closes the connection on error.
 * @param Idx Connection handle.
 * @return true on success, false otherwise.
 */
GLOBAL bool
Zip_Flush( CONN_ID Idx )
{
	int result;
	unsigned char zipbuf[WRITEBUFFER_SLINK_LEN];
	int zipbuf_used = 0;
	z_stream *out;

	out = &My_Connections[Idx].zip.out;

	out->avail_in = (uInt)array_bytes(&My_Connections[Idx].zip.wbuf);
	if (!out->avail_in)
		return true;	/* nothing to do. */

	out->next_in = array_start(&My_Connections[Idx].zip.wbuf);
	assert(out->next_in != NULL);

	out->next_out = zipbuf;
	out->avail_out = (uInt)sizeof zipbuf;

#ifdef DEBUG_ZIP
	Log(LOG_DEBUG, "out->avail_in %d, out->avail_out %d",
		out->avail_in, out->avail_out);
#endif
	result = deflate( out, Z_SYNC_FLUSH );
	if(( result != Z_OK ) || ( out->avail_in > 0 ))
	{
		Log( LOG_ALERT, "Compression error: code %d!?", result );
		Conn_Close( Idx, "Compression error!", NULL, false );
		return false;
	}

	if (out->avail_out <= 0) {
		/* Not all data was compressed, because data became
		 * bigger while compressing it. */
		Log(LOG_ALERT, "Compression error: buffer overflow!?");
		Conn_Close(Idx, "Compression error!", NULL, false);
		return false;
	}

	assert(out->avail_out <= WRITEBUFFER_SLINK_LEN);

	zipbuf_used = WRITEBUFFER_SLINK_LEN - out->avail_out;
#ifdef DEBUG_ZIP
	Log(LOG_DEBUG, "zipbuf_used: %d", zipbuf_used);
#endif
	if (!array_catb(&My_Connections[Idx].wbuf,
			(char *)zipbuf, (size_t) zipbuf_used)) {
		Log (LOG_ALERT, "Compression error: can't copy data!?");
		Conn_Close(Idx, "Compression error!", NULL, false);
		return false;
	}

	My_Connections[Idx].bytes_out += zipbuf_used;
	My_Connections[Idx].zip.bytes_out += array_bytes(&My_Connections[Idx].zip.wbuf); 
	array_trunc(&My_Connections[Idx].zip.wbuf);

	return true;
} /* Zip_Flush */
Пример #2
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 */
Пример #3
0
GLOBAL bool
Client_CheckID( CLIENT *Client, char *ID )
{
	char str[COMMAND_LEN];
	CLIENT *c;

	assert( Client != NULL );
	assert( Client->conn_id > NONE );
	assert( ID != NULL );

	/* ID too long? */
	if (strlen(ID) > CLIENT_ID_LEN) {
		IRC_WriteErrClient(Client, ERR_ERRONEUSNICKNAME_MSG,
				   Client_ID(Client), ID);
		return false;
	}

	/* ID already in use? */
	c = My_Clients;
	while (c) {
		if (strcasecmp(c->id, ID) == 0) {
			snprintf(str, sizeof(str), "ID \"%s\" already registered", ID);
			if (c->conn_id != NONE)
				Log(LOG_ERR, "%s (on connection %d)!", str, c->conn_id);
			else
				Log(LOG_ERR, "%s (via network)!", str);
			Conn_Close(Client->conn_id, str, str, true);
			return false;
		}
		c = (CLIENT *)c->next;
	}

	return true;
} /* Client_CheckID */
Пример #4
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 */
Пример #5
0
/**
 * Copy data to the compression buffer of a connection. We do collect
 * some data there until it's full so that we can achieve better
 * compression ratios.
 * If the (pre-)compression buffer is full, we try to flush it ("actually
 * compress some data") and to add the new (uncompressed) data afterwards.
 * This function closes the connection on error.
 * @param Idx Connection handle.
 * @param Data Pointer to the data.
 * @param Len Length of the data to add.
 * @return true on success, false otherwise.
 */
GLOBAL bool
Zip_Buffer( CONN_ID Idx, const char *Data, size_t Len )
{
	size_t buflen;

	assert( Idx > NONE );
	assert( Data != NULL );
	assert( Len > 0 );

	buflen = array_bytes(&My_Connections[Idx].zip.wbuf);
	if (buflen + Len >= WRITEBUFFER_SLINK_LEN) {
		/* compression buffer is full, flush */
		if( ! Zip_Flush( Idx )) return false;
	}

	/* check again; if zip buf is still too large do not append data:
	 * otherwise the zip wbuf would grow too large */
	buflen = array_bytes(&My_Connections[Idx].zip.wbuf);
	if (buflen + Len >= WRITEBUFFER_SLINK_LEN) {
		Log(LOG_ALERT, "Zip Write buffer space exhausted: %lu bytes", buflen + Len);
		Conn_Close(Idx, "Zip Write buffer space exhausted", NULL, false);
		return false;
	}
	return array_catb(&My_Connections[Idx].zip.wbuf, Data, Len);
} /* Zip_Buffer */
Пример #6
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;
}
Пример #7
0
/**
 * 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);
}
Пример #8
0
/**
 * 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 */
Пример #9
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 */
Пример #10
0
/**
 * uncompress data and copy it to read buffer.
 * Returns true if data has been unpacked or no
 * compressed data is currently pending in the zread buffer.
 * This function closes the connection on error.
 * @param Idx Connection handle.
 * @return true on success, false otherwise.
 */
GLOBAL bool
Unzip_Buffer( CONN_ID Idx )
{
	int result;
	unsigned char unzipbuf[READBUFFER_LEN];
	int unzipbuf_used = 0;
	unsigned int z_rdatalen;
	unsigned int in_len;
	
	z_stream *in;

	assert( Idx > NONE );

	z_rdatalen = (unsigned int)array_bytes(&My_Connections[Idx].zip.rbuf);
	if (z_rdatalen == 0)
		return true;

	in = &My_Connections[Idx].zip.in;

	in->next_in = array_start(&My_Connections[Idx].zip.rbuf);
	assert(in->next_in != NULL);

	in->avail_in = z_rdatalen;
	in->next_out = unzipbuf;
	in->avail_out = (uInt)sizeof unzipbuf;

#ifdef DEBUG_ZIP
	Log(LOG_DEBUG, "in->avail_in %d, in->avail_out %d",
		in->avail_in, in->avail_out);
#endif
	result = inflate( in, Z_SYNC_FLUSH );
	if( result != Z_OK )
	{
		Log(LOG_ALERT, "Decompression error: %s (code=%d, ni=%d, ai=%d, no=%d, ao=%d)!?", in->msg, result, in->next_in, in->avail_in, in->next_out, in->avail_out);
		Conn_Close(Idx, "Decompression error!", NULL, false);
		return false;
	}

	assert(z_rdatalen >= in->avail_in);
	in_len = z_rdatalen - in->avail_in;
	unzipbuf_used = READBUFFER_LEN - in->avail_out;
#ifdef DEBUG_ZIP
	Log(LOG_DEBUG, "unzipbuf_used: %d - %d = %d", READBUFFER_LEN,
		in->avail_out, unzipbuf_used);
#endif
	assert(unzipbuf_used <= READBUFFER_LEN);
	if (!array_catb(&My_Connections[Idx].rbuf, (char*) unzipbuf,
			(size_t)unzipbuf_used)) {
		Log (LOG_ALERT, "Decompression error: can't copy data!?");
		Conn_Close(Idx, "Decompression error!", NULL, false);
		return false;
	}
	if( in->avail_in > 0 ) {
		array_moveleft(&My_Connections[Idx].zip.rbuf, 1, in_len );
	} else {
		array_trunc( &My_Connections[Idx].zip.rbuf );
		My_Connections[Idx].zip.bytes_in += unzipbuf_used;
	}

	return true;
} /* Unzip_Buffer */
Пример #11
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 */
Пример #12
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 */
Пример #13
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 */