2001-12-22 00:53:16 +01:00
|
|
|
/*
|
|
|
|
* ngIRCd -- The Next Generation IRC Daemon
|
2013-08-06 23:37:21 +02:00
|
|
|
* Copyright (c)2001-2013 Alexander Barton (alex@barton.de) and Contributors.
|
2001-12-22 00:53:16 +01:00
|
|
|
*
|
2002-12-12 13:24:18 +01:00
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
* Please read the file COPYING, README and AUTHORS for more information.
|
2001-12-22 00:53:16 +01:00
|
|
|
*/
|
|
|
|
|
2002-03-12 15:37:51 +01:00
|
|
|
#include "portab.h"
|
2001-12-22 00:53:16 +01:00
|
|
|
|
2005-06-01 23:52:18 +02:00
|
|
|
/**
|
|
|
|
* @file
|
|
|
|
* IRC command parser and validator.
|
|
|
|
*/
|
2002-12-12 13:24:18 +01:00
|
|
|
|
2001-12-22 00:53:16 +01:00
|
|
|
#include <assert.h>
|
2002-01-06 00:23:20 +01:00
|
|
|
#include <stdlib.h>
|
2001-12-22 00:53:16 +01:00
|
|
|
#include <string.h>
|
2004-01-17 04:17:49 +01:00
|
|
|
#include <strings.h>
|
2001-12-22 00:53:16 +01:00
|
|
|
|
2002-01-18 12:12:11 +01:00
|
|
|
#include "ngircd.h"
|
2002-12-30 18:15:42 +01:00
|
|
|
#include "conn-func.h"
|
2014-03-17 02:28:39 +01:00
|
|
|
#include "conf.h"
|
2002-05-27 15:09:26 +02:00
|
|
|
#include "channel.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include "messages.h"
|
|
|
|
|
|
|
|
#include "parse.h"
|
|
|
|
|
2001-12-23 22:56:47 +01:00
|
|
|
#include "irc.h"
|
2012-03-31 15:59:06 +02:00
|
|
|
#include "irc-cap.h"
|
2002-03-03 18:17:38 +01:00
|
|
|
#include "irc-channel.h"
|
2014-03-17 02:28:39 +01:00
|
|
|
#ifdef ICONV
|
|
|
|
# include "irc-encoding.h"
|
|
|
|
#endif
|
2002-11-30 18:39:56 +01:00
|
|
|
#include "irc-info.h"
|
2002-02-28 00:25:31 +01:00
|
|
|
#include "irc-login.h"
|
2012-11-05 23:34:21 +01:00
|
|
|
#include "irc-metadata.h"
|
2002-02-28 00:25:31 +01:00
|
|
|
#include "irc-mode.h"
|
2002-05-27 15:09:26 +02:00
|
|
|
#include "irc-op.h"
|
2002-03-03 18:17:38 +01:00
|
|
|
#include "irc-oper.h"
|
|
|
|
#include "irc-server.h"
|
2002-02-28 00:25:31 +01:00
|
|
|
#include "irc-write.h"
|
2007-11-21 13:16:33 +01:00
|
|
|
#include "numeric.h"
|
2001-12-22 00:53:16 +01:00
|
|
|
|
2008-01-13 17:12:49 +01:00
|
|
|
struct _NUMERIC {
|
|
|
|
int numeric;
|
|
|
|
bool (*function) PARAMS(( CLIENT *Client, REQUEST *Request ));
|
|
|
|
};
|
|
|
|
|
2001-12-22 00:53:16 +01:00
|
|
|
|
2008-01-13 17:12:49 +01:00
|
|
|
static COMMAND My_Commands[] =
|
2002-11-30 16:04:57 +01:00
|
|
|
{
|
2013-11-07 17:29:21 +01:00
|
|
|
#define _CMD(name, func, type, min, max, penalty) \
|
|
|
|
{ (name), (func), (type), (min), (max), (penalty), 0, 0, 0 }
|
|
|
|
_CMD("ADMIN", IRC_ADMIN, CLIENT_USER|CLIENT_SERVER, 0, 1, 1),
|
|
|
|
_CMD("AWAY", IRC_AWAY, CLIENT_USER, 0, 1, 0),
|
|
|
|
_CMD("CAP", IRC_CAP, CLIENT_ANY, 1, 2, 0),
|
|
|
|
_CMD("CONNECT", IRC_CONNECT, CLIENT_USER|CLIENT_SERVER, 0, -1, 0),
|
2013-09-05 18:01:49 +02:00
|
|
|
#ifdef STRICT_RFC
|
2013-11-07 17:29:21 +01:00
|
|
|
_CMD("DIE", IRC_DIE, CLIENT_USER, 0, 0, 0),
|
2013-09-05 18:01:49 +02:00
|
|
|
#else
|
2013-11-07 17:29:21 +01:00
|
|
|
_CMD("DIE", IRC_DIE, CLIENT_USER, 0, 1, 0),
|
2013-09-05 18:01:49 +02:00
|
|
|
#endif
|
2013-11-07 17:29:21 +01:00
|
|
|
_CMD("DISCONNECT", IRC_DISCONNECT, CLIENT_USER, 1, 1, 0),
|
|
|
|
_CMD("ERROR", IRC_ERROR, CLIENT_ANY, 0, -1, 0),
|
|
|
|
_CMD("GLINE", IRC_xLINE, CLIENT_USER|CLIENT_SERVER, 0, -1, 0),
|
|
|
|
_CMD("HELP", IRC_HELP, CLIENT_USER, 0, 1, 2),
|
|
|
|
_CMD("INFO", IRC_INFO, CLIENT_USER|CLIENT_SERVER, 0, 1, 2),
|
|
|
|
_CMD("INVITE", IRC_INVITE, CLIENT_USER|CLIENT_SERVER, 2, 2, 0),
|
|
|
|
_CMD("ISON", IRC_ISON, CLIENT_USER, 1, -1, 0),
|
|
|
|
_CMD("JOIN", IRC_JOIN, CLIENT_USER|CLIENT_SERVER, 1, 2, 0),
|
|
|
|
_CMD("KICK", IRC_KICK, CLIENT_USER|CLIENT_SERVER, 2, 3, 0),
|
|
|
|
_CMD("KILL", IRC_KILL, CLIENT_USER|CLIENT_SERVER, 2, 2, 0),
|
|
|
|
_CMD("KLINE", IRC_xLINE, CLIENT_USER|CLIENT_SERVER, 0, -1, 0),
|
|
|
|
_CMD("LINKS", IRC_LINKS, CLIENT_USER|CLIENT_SERVER, 0, 2, 1),
|
|
|
|
_CMD("LIST", IRC_LIST, CLIENT_USER|CLIENT_SERVER, 0, 2, 2),
|
|
|
|
_CMD("LUSERS", IRC_LUSERS, CLIENT_USER|CLIENT_SERVER, 0, 2, 1),
|
|
|
|
_CMD("METADATA", IRC_METADATA, CLIENT_SERVER, 3, 3, 0),
|
2013-11-07 17:53:29 +01:00
|
|
|
_CMD("MODE", IRC_MODE, CLIENT_USER|CLIENT_SERVER, 1, -1, 1),
|
2013-11-07 17:29:21 +01:00
|
|
|
_CMD("MOTD", IRC_MOTD, CLIENT_USER|CLIENT_SERVER, 0, 1, 3),
|
|
|
|
_CMD("NAMES", IRC_NAMES, CLIENT_USER|CLIENT_SERVER, 0, 2, 1),
|
|
|
|
_CMD("NICK", IRC_NICK, CLIENT_ANY, 0, -1, 0),
|
|
|
|
_CMD("NJOIN", IRC_NJOIN, CLIENT_SERVER, 2, 2, 0),
|
|
|
|
_CMD("NOTICE", IRC_NOTICE, CLIENT_ANY, 0, -1, 0),
|
|
|
|
_CMD("OPER", IRC_OPER, CLIENT_USER, 2, 2, 0),
|
|
|
|
_CMD("PART", IRC_PART, CLIENT_USER|CLIENT_SERVER, 1, 2, 0),
|
|
|
|
_CMD("PASS", IRC_PASS, CLIENT_ANY, 0, -1, 0),
|
|
|
|
_CMD("PING", IRC_PING, CLIENT_USER|CLIENT_SERVER, 0, -1, 0),
|
|
|
|
_CMD("PONG", IRC_PONG, CLIENT_ANY, 0, -1, 0),
|
|
|
|
_CMD("PRIVMSG", IRC_PRIVMSG, CLIENT_USER|CLIENT_SERVER, 0, 2, 0),
|
|
|
|
_CMD("QUIT", IRC_QUIT, CLIENT_ANY, 0, 1, 0),
|
|
|
|
_CMD("REHASH", IRC_REHASH, CLIENT_USER, 0, 0, 0),
|
|
|
|
_CMD("RESTART", IRC_RESTART, CLIENT_USER, 0, 0, 0),
|
|
|
|
_CMD("SERVER", IRC_SERVER, CLIENT_ANY, 0, -1, 0),
|
|
|
|
_CMD("SERVICE", IRC_SERVICE, CLIENT_ANY, 6, 6, 0),
|
|
|
|
_CMD("SERVLIST", IRC_SERVLIST, CLIENT_USER, 0, 2, 1),
|
|
|
|
_CMD("SQUERY", IRC_SQUERY, CLIENT_USER|CLIENT_SERVER, 0, 2, 0),
|
|
|
|
_CMD("SQUIT", IRC_SQUIT, CLIENT_USER|CLIENT_SERVER, 2, 2, 0),
|
|
|
|
_CMD("STATS", IRC_STATS, CLIENT_USER|CLIENT_SERVER, 0, 2, 2),
|
|
|
|
_CMD("SVSNICK", IRC_SVSNICK, CLIENT_SERVER, 2, 2, 0),
|
|
|
|
_CMD("SUMMON", IRC_SUMMON, CLIENT_USER|CLIENT_SERVER, 0, -1, 0),
|
|
|
|
_CMD("TIME", IRC_TIME, CLIENT_USER|CLIENT_SERVER, 0, 1, 1),
|
|
|
|
_CMD("TOPIC", IRC_TOPIC, CLIENT_USER|CLIENT_SERVER, 1, 2, 1),
|
|
|
|
_CMD("TRACE", IRC_TRACE, CLIENT_USER|CLIENT_SERVER, 0, 1, 3),
|
|
|
|
_CMD("USER", IRC_USER, CLIENT_ANY, 0, -1, 0),
|
|
|
|
_CMD("USERHOST", IRC_USERHOST, CLIENT_USER, 1, -1, 1),
|
|
|
|
_CMD("USERS", IRC_USERS, CLIENT_USER|CLIENT_SERVER, 0, -1, 0),
|
|
|
|
_CMD("VERSION", IRC_VERSION, CLIENT_USER|CLIENT_SERVER, 0, 1, 1),
|
|
|
|
_CMD("WALLOPS", IRC_WALLOPS, CLIENT_USER|CLIENT_SERVER, 1, 1, 0),
|
|
|
|
_CMD("WEBIRC", IRC_WEBIRC, CLIENT_UNKNOWN, 4, 4, 0),
|
|
|
|
_CMD("WHO", IRC_WHO, CLIENT_USER, 0, 2, 1),
|
2013-11-07 17:53:29 +01:00
|
|
|
_CMD("WHOIS", IRC_WHOIS, CLIENT_USER|CLIENT_SERVER, 0, -1, 1),
|
2013-11-07 17:29:21 +01:00
|
|
|
_CMD("WHOWAS", IRC_WHOWAS, CLIENT_USER|CLIENT_SERVER, 0, -1, 0),
|
2012-09-16 14:08:52 +02:00
|
|
|
|
2002-11-30 16:04:57 +01:00
|
|
|
#ifdef IRCPLUS
|
2013-11-07 17:29:21 +01:00
|
|
|
_CMD("CHANINFO", IRC_CHANINFO, CLIENT_SERVER, 0, -1, 0),
|
2012-09-17 00:36:10 +02:00
|
|
|
# ifdef ICONV
|
2013-11-07 17:29:21 +01:00
|
|
|
_CMD("CHARCONV", IRC_CHARCONV, CLIENT_USER, 1, 1, 0),
|
2012-09-17 00:36:10 +02:00
|
|
|
# endif
|
2011-03-16 23:58:39 +01:00
|
|
|
#endif
|
2012-09-16 14:08:52 +02:00
|
|
|
|
2011-03-16 23:58:39 +01:00
|
|
|
#ifndef STRICT_RFC
|
2013-11-07 17:29:21 +01:00
|
|
|
_CMD("GET", IRC_QUIT_HTTP, CLIENT_UNKNOWN, 0, -1, 0),
|
|
|
|
_CMD("POST", IRC_QUIT_HTTP, CLIENT_UNKNOWN, 0, -1, 0),
|
2002-11-30 16:04:57 +01:00
|
|
|
#endif
|
2013-11-07 17:29:21 +01:00
|
|
|
_CMD(NULL, NULL, 0, 0, 0, 0) /* End-Mark */
|
2013-11-07 14:20:08 +01:00
|
|
|
#undef _CMD
|
2002-11-30 16:04:57 +01:00
|
|
|
};
|
|
|
|
|
2005-07-31 22:13:07 +02:00
|
|
|
static void Init_Request PARAMS(( REQUEST *Req ));
|
2001-12-22 00:53:16 +01:00
|
|
|
|
2005-07-31 22:13:07 +02:00
|
|
|
static bool Validate_Prefix PARAMS(( CONN_ID Idx, REQUEST *Req, bool *Closed ));
|
|
|
|
static bool Validate_Command PARAMS(( CONN_ID Idx, REQUEST *Req, bool *Closed ));
|
|
|
|
static bool Validate_Args PARAMS(( CONN_ID Idx, REQUEST *Req, bool *Closed ));
|
2001-12-22 00:53:16 +01:00
|
|
|
|
2005-07-31 22:13:07 +02:00
|
|
|
static bool Handle_Request PARAMS(( CONN_ID Idx, REQUEST *Req ));
|
2001-12-22 00:53:16 +01:00
|
|
|
|
New option to scrub incoming CTCP commands
This patch makes it possible to scrub incomming CTCP commands from
other servers and clients alike. The ngircd oper can enable it from
the config file, by adding "ScrubCTCP = yes" under [OPTIONS]. It is
default off.
CTCP can be used to profile IRC users (get user clients name and
version, and also their IP addresses). This is not something we like
to happen when user pseudonymity/secrecy is important.
The server silently drops incomming CTCP requests from both other
servers and from users. The server that scrubs CTCP will not forward
the CTCP requests to other servers in the network either, which can
spell trouble if not every oper knows about the CTCP-scrubbing.
Scrubbing CTCP commands also means that it is not possible to send
files between users.
There is one exception to the CTCP scrubbing performed: ACTION ("/me
commands") requests are not scrubbed. ACTION is not dangerous to users
(unless they use OTR, which does not encrypt CTCP requests) and most
users would be confused if they were just dropped.
A CTCP request looks like this:
ctcp_char, COMMAND, arg0, arg1, arg2, .. argN, ctcp_char
ctcp_char is 0x01. (just like bold is 0x02 and color is 0x03.)
They are sent as part of a message and can be delivered to channels
and users alike.
2011-06-10 21:39:01 +02:00
|
|
|
static bool ScrubCTCP PARAMS((char *Request));
|
|
|
|
|
2005-06-01 23:52:18 +02:00
|
|
|
/**
|
|
|
|
* Return the pointer to the global "IRC command structure".
|
|
|
|
* This structure, an array of type "COMMAND" describes all the IRC commands
|
|
|
|
* implemented by ngIRCd and how to handle them.
|
|
|
|
* @return Pointer to the global command structure.
|
|
|
|
*/
|
2002-12-18 14:53:20 +01:00
|
|
|
GLOBAL COMMAND *
|
2005-03-19 19:43:48 +01:00
|
|
|
Parse_GetCommandStruct( void )
|
2002-12-18 14:53:20 +01:00
|
|
|
{
|
|
|
|
return My_Commands;
|
|
|
|
} /* Parse_GetCommandStruct */
|
|
|
|
|
|
|
|
|
2005-06-01 23:52:18 +02:00
|
|
|
/**
|
|
|
|
* Parse a command ("request") received from a client.
|
|
|
|
*
|
|
|
|
* This function is called after the connection layer received a valid CR+LF
|
2013-08-04 13:33:10 +02:00
|
|
|
* terminated line of text: we assume that this is a valid IRC command and
|
2005-06-01 23:52:18 +02:00
|
|
|
* try to do something useful with it :-)
|
|
|
|
*
|
|
|
|
* All errors are reported to the client from which the command has been
|
|
|
|
* received, and if the error is fatal this connection is closed down.
|
|
|
|
*
|
|
|
|
* This function is able to parse the syntax as described in RFC 2812,
|
|
|
|
* section 2.3.
|
|
|
|
*
|
|
|
|
* @param Idx Index of the connection from which the command has been received.
|
|
|
|
* @param Request NULL terminated line of text (the "command").
|
|
|
|
* @return true on success (valid command or "regular" error), false if a
|
2013-08-04 13:33:10 +02:00
|
|
|
* fatal error occurred and the connection has been shut down.
|
2005-06-01 23:52:18 +02:00
|
|
|
*/
|
2005-03-19 19:43:48 +01:00
|
|
|
GLOBAL bool
|
|
|
|
Parse_Request( CONN_ID Idx, char *Request )
|
2001-12-22 00:53:16 +01:00
|
|
|
{
|
|
|
|
REQUEST req;
|
2005-03-19 19:43:48 +01:00
|
|
|
char *start, *ptr;
|
|
|
|
bool closed;
|
2001-12-22 00:53:16 +01:00
|
|
|
|
|
|
|
assert( Idx >= 0 );
|
|
|
|
assert( Request != NULL );
|
2001-12-23 22:56:47 +01:00
|
|
|
|
2001-12-25 23:04:26 +01:00
|
|
|
#ifdef SNIFFER
|
2002-01-18 12:12:11 +01:00
|
|
|
if( NGIRCd_Sniffer ) Log( LOG_DEBUG, " <- connection %d: '%s'.", Idx, Request );
|
2001-12-23 22:56:47 +01:00
|
|
|
#endif
|
2002-07-26 23:12:24 +02:00
|
|
|
|
2001-12-22 00:53:16 +01:00
|
|
|
Init_Request( &req );
|
|
|
|
|
2009-04-21 08:40:10 +02:00
|
|
|
/* remove leading & trailing whitespace */
|
2001-12-29 04:08:19 +01:00
|
|
|
ngt_TrimStr( Request );
|
|
|
|
|
New option to scrub incoming CTCP commands
This patch makes it possible to scrub incomming CTCP commands from
other servers and clients alike. The ngircd oper can enable it from
the config file, by adding "ScrubCTCP = yes" under [OPTIONS]. It is
default off.
CTCP can be used to profile IRC users (get user clients name and
version, and also their IP addresses). This is not something we like
to happen when user pseudonymity/secrecy is important.
The server silently drops incomming CTCP requests from both other
servers and from users. The server that scrubs CTCP will not forward
the CTCP requests to other servers in the network either, which can
spell trouble if not every oper knows about the CTCP-scrubbing.
Scrubbing CTCP commands also means that it is not possible to send
files between users.
There is one exception to the CTCP scrubbing performed: ACTION ("/me
commands") requests are not scrubbed. ACTION is not dangerous to users
(unless they use OTR, which does not encrypt CTCP requests) and most
users would be confused if they were just dropped.
A CTCP request looks like this:
ctcp_char, COMMAND, arg0, arg1, arg2, .. argN, ctcp_char
ctcp_char is 0x01. (just like bold is 0x02 and color is 0x03.)
They are sent as part of a message and can be delivered to channels
and users alike.
2011-06-10 21:39:01 +02:00
|
|
|
if (Conf_ScrubCTCP && ScrubCTCP(Request))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (Request[0] == ':') {
|
2009-04-21 08:40:10 +02:00
|
|
|
/* Prefix */
|
2001-12-22 00:53:16 +01:00
|
|
|
req.prefix = Request + 1;
|
|
|
|
ptr = strchr( Request, ' ' );
|
2002-07-26 23:12:24 +02:00
|
|
|
if( ! ptr )
|
|
|
|
{
|
2009-01-10 00:44:34 +01:00
|
|
|
LogDebug("Connection %d: Parse error: prefix without command!?", Idx);
|
2011-01-29 16:05:55 +01:00
|
|
|
return Conn_WriteStr(Idx, "ERROR :Prefix without command");
|
2002-07-26 23:12:24 +02:00
|
|
|
}
|
2001-12-22 00:53:16 +01:00
|
|
|
*ptr = '\0';
|
2002-01-09 02:08:42 +01:00
|
|
|
#ifndef STRICT_RFC
|
2009-04-21 08:40:10 +02:00
|
|
|
/* ignore multiple spaces between prefix and command */
|
2002-01-09 02:08:42 +01:00
|
|
|
while( *(ptr + 1) == ' ' ) ptr++;
|
|
|
|
#endif
|
2001-12-22 00:53:16 +01:00
|
|
|
start = ptr + 1;
|
|
|
|
}
|
|
|
|
else start = Request;
|
|
|
|
|
|
|
|
ptr = strchr( start, ' ' );
|
2002-01-09 02:08:42 +01:00
|
|
|
if( ptr )
|
|
|
|
{
|
|
|
|
*ptr = '\0';
|
|
|
|
#ifndef STRICT_RFC
|
2009-04-21 08:40:10 +02:00
|
|
|
/* ignore multiple spaces between parameters */
|
2002-01-09 02:08:42 +01:00
|
|
|
while( *(ptr + 1) == ' ' ) ptr++;
|
|
|
|
#endif
|
|
|
|
}
|
2001-12-22 00:53:16 +01:00
|
|
|
req.command = start;
|
|
|
|
|
2009-04-21 08:40:10 +02:00
|
|
|
/* Arguments, Parameters */
|
2001-12-22 00:53:16 +01:00
|
|
|
if( ptr )
|
|
|
|
{
|
|
|
|
start = ptr + 1;
|
|
|
|
while( start )
|
|
|
|
{
|
2001-12-23 22:56:47 +01:00
|
|
|
if( start[0] == ':' )
|
|
|
|
{
|
|
|
|
req.argv[req.argc] = start + 1;
|
|
|
|
ptr = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
req.argv[req.argc] = start;
|
|
|
|
ptr = strchr( start, ' ' );
|
2002-01-09 02:08:42 +01:00
|
|
|
if( ptr )
|
|
|
|
{
|
|
|
|
*ptr = '\0';
|
|
|
|
#ifndef STRICT_RFC
|
|
|
|
while( *(ptr + 1) == ' ' ) ptr++;
|
|
|
|
#endif
|
|
|
|
}
|
2001-12-23 22:56:47 +01:00
|
|
|
}
|
2002-07-26 23:12:24 +02:00
|
|
|
|
2001-12-22 00:53:16 +01:00
|
|
|
req.argc++;
|
|
|
|
|
|
|
|
if( start[0] == ':' ) break;
|
|
|
|
if( req.argc > 14 ) break;
|
2002-07-26 23:12:24 +02:00
|
|
|
|
2001-12-22 00:53:16 +01:00
|
|
|
if( ptr ) start = ptr + 1;
|
|
|
|
else start = NULL;
|
|
|
|
}
|
|
|
|
}
|
2002-07-26 23:12:24 +02:00
|
|
|
|
2002-11-28 12:02:50 +01:00
|
|
|
if( ! Validate_Prefix( Idx, &req, &closed )) return ! closed;
|
|
|
|
if( ! Validate_Command( Idx, &req, &closed )) return ! closed;
|
2002-07-26 23:12:24 +02:00
|
|
|
if( ! Validate_Args( Idx, &req, &closed )) return ! closed;
|
2002-01-03 03:24:49 +01:00
|
|
|
|
2001-12-22 00:53:16 +01:00
|
|
|
return Handle_Request( Idx, &req );
|
|
|
|
} /* Parse_Request */
|
|
|
|
|
|
|
|
|
2005-06-01 23:52:18 +02:00
|
|
|
/**
|
|
|
|
* Initialize request structure.
|
|
|
|
* @param Req Request structure to be initialized.
|
|
|
|
*/
|
2005-07-31 22:13:07 +02:00
|
|
|
static void
|
2002-05-27 15:09:26 +02:00
|
|
|
Init_Request( REQUEST *Req )
|
2001-12-22 00:53:16 +01:00
|
|
|
{
|
2005-03-19 19:43:48 +01:00
|
|
|
int i;
|
2002-07-26 23:12:24 +02:00
|
|
|
|
2001-12-22 00:53:16 +01:00
|
|
|
assert( Req != NULL );
|
|
|
|
|
|
|
|
Req->prefix = NULL;
|
|
|
|
Req->command = NULL;
|
|
|
|
for( i = 0; i < 15; Req->argv[i++] = NULL );
|
|
|
|
Req->argc = 0;
|
|
|
|
} /* Init_Request */
|
|
|
|
|
|
|
|
|
2005-07-31 22:13:07 +02:00
|
|
|
static bool
|
2005-03-19 19:43:48 +01:00
|
|
|
Validate_Prefix( CONN_ID Idx, REQUEST *Req, bool *Closed )
|
2001-12-22 00:53:16 +01:00
|
|
|
{
|
2002-07-29 22:35:33 +02:00
|
|
|
CLIENT *client, *c;
|
2002-07-26 23:12:24 +02:00
|
|
|
|
2001-12-22 00:53:16 +01:00
|
|
|
assert( Idx >= 0 );
|
2002-07-26 23:12:24 +02:00
|
|
|
assert( Req != NULL );
|
2001-12-22 00:53:16 +01:00
|
|
|
|
2005-03-19 19:43:48 +01:00
|
|
|
*Closed = false;
|
2002-07-29 22:35:33 +02:00
|
|
|
|
2006-04-23 12:37:27 +02:00
|
|
|
client = Conn_GetClient( Idx );
|
2002-07-29 22:35:33 +02:00
|
|
|
assert( client != NULL );
|
|
|
|
|
2011-03-21 10:46:09 +01:00
|
|
|
if (!Req->prefix && Client_Type(client) == CLIENT_SERVER
|
2011-04-29 23:15:05 +02:00
|
|
|
&& !(Conn_Options(Idx) & CONN_RFC1459)
|
2011-03-21 10:46:09 +01:00
|
|
|
&& strcasecmp(Req->command, "ERROR") != 0
|
|
|
|
&& strcasecmp(Req->command, "PING") != 0)
|
|
|
|
{
|
|
|
|
Log(LOG_ERR,
|
|
|
|
"Received command without prefix (connection %d, command \"%s\")!?",
|
|
|
|
Idx, Req->command);
|
|
|
|
if (!Conn_WriteStr(Idx, "ERROR :Prefix missing"))
|
|
|
|
*Closed = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!Req->prefix)
|
|
|
|
return true;
|
|
|
|
|
2009-04-21 08:40:10 +02:00
|
|
|
/* only validate if this connection is already registered */
|
2011-01-29 16:05:55 +01:00
|
|
|
if (Client_Type(client) != CLIENT_USER
|
|
|
|
&& Client_Type(client) != CLIENT_SERVER
|
|
|
|
&& Client_Type(client) != CLIENT_SERVICE) {
|
2009-04-21 08:40:10 +02:00
|
|
|
/* not registered, ignore prefix */
|
2002-07-29 22:35:33 +02:00
|
|
|
Req->prefix = NULL;
|
2005-03-19 19:43:48 +01:00
|
|
|
return true;
|
2002-07-29 22:35:33 +02:00
|
|
|
}
|
|
|
|
|
2009-04-21 08:40:10 +02:00
|
|
|
/* check if client in prefix is known */
|
2013-09-26 02:26:24 +02:00
|
|
|
c = Client_Search(Req->prefix);
|
2011-01-29 16:05:55 +01:00
|
|
|
if (!c) {
|
2013-09-26 02:26:24 +02:00
|
|
|
if (Client_Type(client) != CLIENT_SERVER) {
|
|
|
|
Log(LOG_ERR,
|
|
|
|
"Ignoring command with invalid prefix \"%s\" from \"%s\" (connection %d, command \"%s\")!",
|
|
|
|
Req->prefix, Client_ID(client), Idx, Req->command);
|
|
|
|
if (!Conn_WriteStr(Idx,
|
|
|
|
"ERROR :Invalid prefix \"%s\"",
|
|
|
|
Req->prefix))
|
|
|
|
*Closed = true;
|
|
|
|
IRC_SetPenalty(client, 2);
|
|
|
|
} else
|
|
|
|
LogDebug("Ignoring command with invalid prefix \"%s\" from \"%s\" (connection %d, command \"%s\")!",
|
|
|
|
Req->prefix, Client_ID(client), Idx, Req->command);
|
2005-03-19 19:43:48 +01:00
|
|
|
return false;
|
2002-07-26 23:12:24 +02:00
|
|
|
}
|
2002-07-29 22:35:33 +02:00
|
|
|
|
2009-04-21 08:40:10 +02:00
|
|
|
/* check if the client named in the prefix is expected
|
|
|
|
* to come from that direction */
|
2011-01-29 16:05:55 +01:00
|
|
|
if (Client_NextHop(c) != client) {
|
2011-07-19 16:00:55 +02:00
|
|
|
if (Client_Type(c) != CLIENT_SERVER) {
|
|
|
|
Log(LOG_ERR,
|
2013-09-26 02:26:24 +02:00
|
|
|
"Spoofed prefix \"%s\" from \"%s\" (connection %d, command \"%s\"), closing connection!",
|
|
|
|
Req->prefix, Client_ID(client), Idx, Req->command);
|
2011-07-19 16:00:55 +02:00
|
|
|
Conn_Close(Idx, NULL, "Spoofed prefix", true);
|
|
|
|
*Closed = true;
|
|
|
|
} else {
|
2013-09-26 02:26:24 +02:00
|
|
|
Log(LOG_WARNING,
|
|
|
|
"Ignoring command with spoofed prefix \"%s\" from \"%s\" (connection %d, command \"%s\")!",
|
|
|
|
Req->prefix, Client_ID(client), Idx, Req->command);
|
2011-07-19 16:00:55 +02:00
|
|
|
}
|
2005-03-19 19:43:48 +01:00
|
|
|
return false;
|
2002-07-26 23:12:24 +02:00
|
|
|
}
|
2001-12-22 00:53:16 +01:00
|
|
|
|
2005-03-19 19:43:48 +01:00
|
|
|
return true;
|
2001-12-22 00:53:16 +01:00
|
|
|
} /* Validate_Prefix */
|
|
|
|
|
|
|
|
|
2005-07-31 22:13:07 +02:00
|
|
|
static bool
|
2005-07-22 23:31:05 +02:00
|
|
|
Validate_Command( UNUSED CONN_ID Idx, UNUSED REQUEST *Req, bool *Closed )
|
2001-12-22 00:53:16 +01:00
|
|
|
{
|
2002-07-26 23:12:24 +02:00
|
|
|
assert( Idx >= 0 );
|
2001-12-22 00:53:16 +01:00
|
|
|
assert( Req != NULL );
|
2005-03-19 19:43:48 +01:00
|
|
|
*Closed = false;
|
2002-07-26 23:12:24 +02:00
|
|
|
|
2005-03-19 19:43:48 +01:00
|
|
|
return true;
|
2013-08-04 13:33:10 +02:00
|
|
|
} /* Validate_Command */
|
2001-12-22 00:53:16 +01:00
|
|
|
|
|
|
|
|
2005-07-31 22:13:07 +02:00
|
|
|
static bool
|
2008-07-27 20:35:01 +02:00
|
|
|
#ifdef STRICT_RFC
|
2008-05-30 14:49:56 +02:00
|
|
|
Validate_Args(CONN_ID Idx, REQUEST *Req, bool *Closed)
|
2008-07-27 20:35:01 +02:00
|
|
|
#else
|
|
|
|
Validate_Args(UNUSED CONN_ID Idx, UNUSED REQUEST *Req, bool *Closed)
|
|
|
|
#endif
|
2001-12-22 00:53:16 +01:00
|
|
|
{
|
2008-06-11 16:00:38 +02:00
|
|
|
#ifdef STRICT_RFC
|
2008-05-30 14:49:56 +02:00
|
|
|
int i;
|
2008-06-11 16:00:38 +02:00
|
|
|
#endif
|
2008-05-30 14:49:56 +02:00
|
|
|
|
2005-03-19 19:43:48 +01:00
|
|
|
*Closed = false;
|
2002-07-26 23:12:24 +02:00
|
|
|
|
2008-06-11 16:00:38 +02:00
|
|
|
#ifdef STRICT_RFC
|
2008-07-27 20:35:01 +02:00
|
|
|
assert( Idx >= 0 );
|
|
|
|
assert( Req != NULL );
|
|
|
|
|
2008-06-11 16:00:38 +02:00
|
|
|
/* CR and LF are never allowed in command parameters.
|
|
|
|
* But since we do accept lines terminated only with CR or LF in
|
|
|
|
* "non-RFC-compliant mode" (besides the correct CR+LF combination),
|
|
|
|
* this check can only trigger in "strict RFC" mode; therefore we
|
|
|
|
* optimize it away otherwise ... */
|
2008-05-30 14:49:56 +02:00
|
|
|
for (i = 0; i < Req->argc; i++) {
|
|
|
|
if (strchr(Req->argv[i], '\r') || strchr(Req->argv[i], '\n')) {
|
|
|
|
Log(LOG_ERR,
|
|
|
|
"Invalid character(s) in parameter (connection %d, command %s)!?",
|
|
|
|
Idx, Req->command);
|
|
|
|
if (!Conn_WriteStr(Idx,
|
|
|
|
"ERROR :Invalid character(s) in parameter!"))
|
|
|
|
*Closed = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2008-06-11 16:00:38 +02:00
|
|
|
#endif
|
|
|
|
|
2005-03-19 19:43:48 +01:00
|
|
|
return true;
|
2001-12-22 00:53:16 +01:00
|
|
|
} /* Validate_Args */
|
|
|
|
|
|
|
|
|
2008-01-13 17:12:49 +01:00
|
|
|
/* Command is a status code ("numeric") from another server */
|
|
|
|
static bool
|
|
|
|
Handle_Numeric(CLIENT *client, REQUEST *Req)
|
|
|
|
{
|
|
|
|
static const struct _NUMERIC Numerics[] = {
|
2008-11-11 23:10:52 +01:00
|
|
|
{ 5, IRC_Num_ISUPPORT },
|
|
|
|
{ 20, NULL },
|
2008-01-13 17:12:49 +01:00
|
|
|
{ 376, IRC_Num_ENDOFMOTD }
|
|
|
|
};
|
|
|
|
int i, num;
|
2013-10-17 23:10:53 +02:00
|
|
|
char str[COMMAND_LEN];
|
2008-01-13 17:12:49 +01:00
|
|
|
CLIENT *prefix, *target = NULL;
|
|
|
|
|
|
|
|
/* Determine target */
|
2008-11-11 23:10:52 +01:00
|
|
|
if (Req->argc > 0) {
|
|
|
|
if (strcmp(Req->argv[0], "*") != 0)
|
|
|
|
target = Client_Search(Req->argv[0]);
|
|
|
|
else
|
|
|
|
target = Client_ThisServer();
|
|
|
|
}
|
2008-01-13 17:12:49 +01:00
|
|
|
|
|
|
|
if (!target) {
|
|
|
|
/* Status code without target!? */
|
|
|
|
if (Req->argc > 0)
|
|
|
|
Log(LOG_WARNING,
|
|
|
|
"Unknown target for status code %s: \"%s\"",
|
|
|
|
Req->command, Req->argv[0]);
|
|
|
|
else
|
|
|
|
Log(LOG_WARNING,
|
|
|
|
"Unknown target for status code %s!",
|
|
|
|
Req->command);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (target == Client_ThisServer()) {
|
|
|
|
/* This server is the target of the numeric */
|
|
|
|
num = atoi(Req->command);
|
|
|
|
|
2010-09-11 00:19:01 +02:00
|
|
|
for (i = 0; i < (int) C_ARRAY_SIZE(Numerics); i++) {
|
2008-11-11 23:10:52 +01:00
|
|
|
if (num == Numerics[i].numeric) {
|
|
|
|
if (!Numerics[i].function)
|
|
|
|
return CONNECTED;
|
2008-01-13 17:12:49 +01:00
|
|
|
return Numerics[i].function(client, Req);
|
2008-11-11 23:10:52 +01:00
|
|
|
}
|
2008-01-13 17:12:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
LogDebug("Ignored status code %s from \"%s\".",
|
|
|
|
Req->command, Client_ID(client));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Determine source */
|
|
|
|
if (! Req->prefix[0]) {
|
|
|
|
/* Oops, no prefix!? */
|
|
|
|
Log(LOG_WARNING, "Got status code %s from \"%s\" without prefix!?",
|
|
|
|
Req->command, Client_ID(client));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
prefix = Client_Search(Req->prefix);
|
|
|
|
if (! prefix) { /* Oops, unknown prefix!? */
|
|
|
|
Log(LOG_WARNING, "Got status code %s from unknown source: \"%s\"", Req->command, Req->prefix);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Forward status code */
|
|
|
|
strlcpy(str, Req->command, sizeof(str));
|
|
|
|
for (i = 0; i < Req->argc; i++) {
|
|
|
|
if (i < Req->argc - 1)
|
|
|
|
strlcat(str, " ", sizeof(str));
|
|
|
|
else
|
|
|
|
strlcat(str, " :", sizeof(str));
|
|
|
|
strlcat(str, Req->argv[i], sizeof(str));
|
|
|
|
}
|
|
|
|
return IRC_WriteStrClientPrefix(target, prefix, "%s", str);
|
|
|
|
}
|
|
|
|
|
2005-07-31 22:13:07 +02:00
|
|
|
static bool
|
2002-05-27 15:09:26 +02:00
|
|
|
Handle_Request( CONN_ID Idx, REQUEST *Req )
|
2001-12-22 00:53:16 +01:00
|
|
|
{
|
2008-01-13 17:12:49 +01:00
|
|
|
CLIENT *client;
|
|
|
|
bool result = true;
|
2008-02-05 14:07:14 +01:00
|
|
|
int client_type;
|
2002-11-30 16:04:57 +01:00
|
|
|
COMMAND *cmd;
|
2001-12-23 22:56:47 +01:00
|
|
|
|
2001-12-22 00:53:16 +01:00
|
|
|
assert( Idx >= 0 );
|
|
|
|
assert( Req != NULL );
|
|
|
|
assert( Req->command != NULL );
|
|
|
|
|
2006-04-23 12:37:27 +02:00
|
|
|
client = Conn_GetClient( Idx );
|
2001-12-23 22:56:47 +01:00
|
|
|
assert( client != NULL );
|
|
|
|
|
2007-11-21 13:16:33 +01:00
|
|
|
/* Numeric? */
|
2008-02-05 14:07:14 +01:00
|
|
|
client_type = Client_Type(client);
|
|
|
|
if ((client_type == CLIENT_SERVER ||
|
|
|
|
client_type == CLIENT_UNKNOWNSERVER)
|
2008-01-13 17:12:49 +01:00
|
|
|
&& strlen(Req->command) == 3 && atoi(Req->command) > 1)
|
|
|
|
return Handle_Numeric(client, Req);
|
2002-01-06 00:23:20 +01:00
|
|
|
|
2002-11-30 16:04:57 +01:00
|
|
|
cmd = My_Commands;
|
2008-01-13 17:12:49 +01:00
|
|
|
while (cmd->name) {
|
|
|
|
if (strcasecmp(Req->command, cmd->name) != 0) {
|
|
|
|
cmd++;
|
|
|
|
continue;
|
2002-11-30 16:04:57 +01:00
|
|
|
}
|
|
|
|
|
2013-01-22 10:54:06 +01:00
|
|
|
if (!(client_type & cmd->type)) {
|
|
|
|
if (client_type == CLIENT_USER
|
|
|
|
&& cmd->type & CLIENT_SERVER)
|
2013-08-06 23:37:21 +02:00
|
|
|
return IRC_WriteErrClient(client,
|
2013-01-22 10:54:06 +01:00
|
|
|
ERR_NOTREGISTEREDSERVER_MSG,
|
|
|
|
Client_ID(client));
|
|
|
|
else
|
2013-08-06 23:37:21 +02:00
|
|
|
return IRC_WriteErrClient(client,
|
2013-01-22 10:54:06 +01:00
|
|
|
ERR_NOTREGISTERED_MSG,
|
|
|
|
Client_ID(client));
|
|
|
|
}
|
2002-12-18 14:53:20 +01:00
|
|
|
|
2013-11-07 17:53:29 +01:00
|
|
|
if (cmd->penalty)
|
|
|
|
IRC_SetPenalty(client, cmd->penalty);
|
|
|
|
|
2013-09-05 18:01:49 +02:00
|
|
|
if (Req->argc < cmd->min_argc ||
|
2013-11-06 19:28:09 +01:00
|
|
|
(cmd->max_argc != -1 && Req->argc > cmd->max_argc))
|
|
|
|
return IRC_WriteErrClient(client, ERR_NEEDMOREPARAMS_MSG,
|
2013-09-05 18:01:49 +02:00
|
|
|
Client_ID(client), Req->command);
|
|
|
|
|
2013-01-22 10:54:06 +01:00
|
|
|
/* Command is allowed for this client: call it and count
|
|
|
|
* generated bytes in output */
|
2008-01-13 17:12:49 +01:00
|
|
|
Conn_ResetWCounter();
|
|
|
|
result = (cmd->function)(client, Req);
|
|
|
|
cmd->bytes += Conn_WCounter();
|
2002-12-18 14:53:20 +01:00
|
|
|
|
2008-01-13 17:12:49 +01:00
|
|
|
/* Adjust counters */
|
2008-02-05 14:07:14 +01:00
|
|
|
if (client_type != CLIENT_SERVER)
|
2008-01-13 17:12:49 +01:00
|
|
|
cmd->lcount++;
|
2002-11-30 16:04:57 +01:00
|
|
|
else
|
2008-01-13 17:12:49 +01:00
|
|
|
cmd->rcount++;
|
|
|
|
return result;
|
2002-11-30 16:04:57 +01:00
|
|
|
}
|
2005-06-24 22:56:46 +02:00
|
|
|
|
2008-02-05 14:07:14 +01:00
|
|
|
if (client_type != CLIENT_USER &&
|
|
|
|
client_type != CLIENT_SERVER &&
|
|
|
|
client_type != CLIENT_SERVICE )
|
2005-06-24 22:56:46 +02:00
|
|
|
return true;
|
2008-01-13 17:12:49 +01:00
|
|
|
|
2005-06-24 22:56:46 +02:00
|
|
|
/* Unknown command and registered connection: generate error: */
|
2008-01-13 17:12:49 +01:00
|
|
|
LogDebug("Connection %d: Unknown command \"%s\", %d %s,%s prefix.",
|
2005-06-24 22:56:46 +02:00
|
|
|
Client_Conn( client ), Req->command, Req->argc,
|
|
|
|
Req->argc == 1 ? "parameter" : "parameters",
|
|
|
|
Req->prefix ? "" : " no" );
|
|
|
|
|
2013-08-06 23:37:21 +02:00
|
|
|
if (Client_Type(client) != CLIENT_SERVER)
|
|
|
|
result = IRC_WriteErrClient(client, ERR_UNKNOWNCOMMAND_MSG,
|
2005-09-05 01:42:24 +02:00
|
|
|
Client_ID(client), Req->command);
|
2013-08-06 23:37:21 +02:00
|
|
|
|
2008-01-13 17:12:49 +01:00
|
|
|
return result;
|
2001-12-22 00:53:16 +01:00
|
|
|
} /* Handle_Request */
|
|
|
|
|
|
|
|
|
New option to scrub incoming CTCP commands
This patch makes it possible to scrub incomming CTCP commands from
other servers and clients alike. The ngircd oper can enable it from
the config file, by adding "ScrubCTCP = yes" under [OPTIONS]. It is
default off.
CTCP can be used to profile IRC users (get user clients name and
version, and also their IP addresses). This is not something we like
to happen when user pseudonymity/secrecy is important.
The server silently drops incomming CTCP requests from both other
servers and from users. The server that scrubs CTCP will not forward
the CTCP requests to other servers in the network either, which can
spell trouble if not every oper knows about the CTCP-scrubbing.
Scrubbing CTCP commands also means that it is not possible to send
files between users.
There is one exception to the CTCP scrubbing performed: ACTION ("/me
commands") requests are not scrubbed. ACTION is not dangerous to users
(unless they use OTR, which does not encrypt CTCP requests) and most
users would be confused if they were just dropped.
A CTCP request looks like this:
ctcp_char, COMMAND, arg0, arg1, arg2, .. argN, ctcp_char
ctcp_char is 0x01. (just like bold is 0x02 and color is 0x03.)
They are sent as part of a message and can be delivered to channels
and users alike.
2011-06-10 21:39:01 +02:00
|
|
|
/**
|
|
|
|
* Check if incoming messages contains CTCP commands and should be dropped.
|
|
|
|
*
|
|
|
|
* @param Request NULL terminated incoming command.
|
|
|
|
* @returns true, when the message should be dropped.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
ScrubCTCP(char *Request)
|
|
|
|
{
|
|
|
|
static const char me_cmd[] = "ACTION ";
|
|
|
|
static const char ctcp_char = 0x1;
|
|
|
|
bool dropCommand = false;
|
|
|
|
char *ptr = Request;
|
|
|
|
char *ptrEnd = strchr(Request, '\0');
|
|
|
|
|
|
|
|
if (Request[0] == ':' && ptrEnd > ptr)
|
|
|
|
ptr++;
|
|
|
|
|
|
|
|
while (ptr != ptrEnd && *ptr != ':')
|
|
|
|
ptr++;
|
|
|
|
|
|
|
|
if ((ptrEnd - ptr) > 1) {
|
|
|
|
ptr++;
|
|
|
|
if (*ptr == ctcp_char) {
|
|
|
|
dropCommand = true;
|
|
|
|
ptr++;
|
|
|
|
/* allow /me commands */
|
|
|
|
if ((size_t)(ptrEnd - ptr) >= strlen(me_cmd)
|
|
|
|
&& !strncmp(ptr, me_cmd, strlen(me_cmd)))
|
|
|
|
dropCommand = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return dropCommand;
|
|
|
|
}
|
|
|
|
|
2001-12-22 00:53:16 +01:00
|
|
|
/* -eof- */
|