unrealircd

- supernets unrealircd source & configuration
git clone git://git.acid.vegas/unrealircd.git
Log | Files | Refs | Archive | README | LICENSE

parse.c (24067B)

      1 /************************************************************************
      2  *   Unreal Internet Relay Chat Daemon, src/parse.c
      3  *   Copyright (C) 1990 Jarkko Oikarinen and
      4  *                      University of Oulu, Computing Center
      5  *
      6  *   This program is free software; you can redistribute it and/or modify
      7  *   it under the terms of the GNU General Public License as published by
      8  *   the Free Software Foundation; either version 1, or (at your option)
      9  *   any later version.
     10  *
     11  *   This program is distributed in the hope that it will be useful,
     12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14  *   GNU General Public License for more details.
     15  *
     16  *   You should have received a copy of the GNU General Public License
     17  *   along with this program; if not, write to the Free Software
     18  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     19  */
     20 
     21 /** @file
     22  * @brief Main line parsing functions - for incoming lines from clients.
     23  */
     24 #include "unrealircd.h"
     25 
     26 /** Last (or current) command that we processed. Useful for post-mortem. */
     27 char backupbuf[8192];
     28 
     29 static char *para[MAXPARA + 2];
     30 
     31 /* Forward declarations of functions that are local (static) */
     32 static int do_numeric(int, Client *, MessageTag *, int, const char **);
     33 static void cancel_clients(Client *, Client *, char *);
     34 static void remove_unknown(Client *, char *);
     35 static void parse2(Client *client, Client **fromptr, MessageTag *mtags, int mtags_bytes, char *ch);
     36 static void parse_addlag(Client *client, int command_bytes, int mtags_bytes);
     37 static int client_lagged_up(Client *client);
     38 static void ban_handshake_data_flooder(Client *client);
     39 
     40 /** Put a packet in the client receive queue and process the data (if
     41  * the 'fake lag' rules permit doing so).
     42  * @param client      The client
     43  * @param readbuf     The read buffer
     44  * @param length      The length of the data
     45  * @param killsafely  If 1 then we may call exit_client() if the client
     46  *                    is flooding. If 0 then we use dead_socket().
     47  * @returns 1 in normal circumstances, 0 if client was killed.
     48  * @note  If killsafely is 1 and the return value is 0 then
     49  *        the client was killed - IsDead() is true.
     50  *        If this is a problem, then set killsafely to 0 when calling.
     51  */
     52 int process_packet(Client *client, char *readbuf, int length, int killsafely)
     53 {
     54 	dbuf_put(&client->local->recvQ, readbuf, length);
     55 
     56 	/* parse some of what we have (inducing fakelag, etc) */
     57 	parse_client_queued(client);
     58 
     59 	/* We may be killed now, so check for it.. */
     60 	if (IsDead(client))
     61 		return 0;
     62 
     63 	/* flood from unknown connection */
     64 	if (IsUnknown(client) && (DBufLength(&client->local->recvQ) > iConf.handshake_data_flood_amount))
     65 	{
     66 		unreal_log(ULOG_INFO, "flood", "HANDSHAKE_DATA_FLOOD", client,
     67 		           "Handshake data flood detected from $client.details [$client.ip]");
     68 		if (!killsafely)
     69 			ban_handshake_data_flooder(client);
     70 		else
     71 			dead_socket(client, "Handshake data flood detected");
     72 		return 0;
     73 	}
     74 
     75 	/* excess flood check */
     76 	if (IsUser(client) && DBufLength(&client->local->recvQ) > get_recvq(client))
     77 	{
     78 		unreal_log(ULOG_INFO, "flood", "RECVQ_EXCEEDED", client,
     79 		           "Flood from $client.details [$client.ip] exceeds class::recvq ($recvq > $class_recvq) (Client sending too much data)",
     80 		           log_data_integer("recvq", DBufLength(&client->local->recvQ)),
     81 		           log_data_integer("class_recvq", get_recvq(client)));
     82 		if (!killsafely)
     83 			exit_client(client, NULL, "Excess Flood");
     84 		else
     85 			dead_socket(client, "Excess Flood");
     86 		return 0;
     87 	}
     88 
     89 	return 1;
     90 }
     91 
     92 /** Parse any queued data for 'client', if permitted.
     93  * @param client	The client.
     94  */
     95 void parse_client_queued(Client *client)
     96 {
     97 	int dolen = 0;
     98 	char buf[READBUFSIZE];
     99 
    100 	if (IsDNSLookup(client))
    101 		return; /* we delay processing of data until the host is resolved */
    102 
    103 	if (IsIdentLookup(client))
    104 		return; /* we delay processing of data until identd has replied */
    105 
    106 	if (!IsUser(client) && !IsServer(client) && (iConf.handshake_delay > 0) &&
    107 	    !IsNoHandshakeDelay(client) &&
    108 	    !IsUnixSocket(client) &&
    109 	    (TStime() - client->local->creationtime < iConf.handshake_delay))
    110 	{
    111 		return; /* we delay processing of data until set::handshake-delay is reached */
    112 	}
    113 
    114 	while (DBufLength(&client->local->recvQ) && !client_lagged_up(client))
    115 	{
    116 		dolen = dbuf_getmsg(&client->local->recvQ, buf);
    117 
    118 		if (dolen == 0)
    119 			return;
    120 
    121 		dopacket(client, buf, dolen);
    122 		
    123 		if (IsDead(client))
    124 			return;
    125 	}
    126 }
    127 
    128 /*
    129 ** dopacket
    130 **	client - pointer to client structure for which the buffer data
    131 **	       applies.
    132 **	buffer - pointr to the buffer containing the newly read data
    133 **	length - number of valid bytes of data in the buffer
    134 **
    135 ** Note:
    136 **	It is implicitly assumed that dopacket is called only
    137 **	with client of "local" variation, which contains all the
    138 **	necessary fields (buffer etc..)
    139 **
    140 ** Rewritten for linebufs, 19th May 2013. --kaniini
    141 */
    142 void dopacket(Client *client, char *buffer, int length)
    143 {
    144 	client->local->traffic.bytes_received += length;
    145 	me.local->traffic.bytes_received += length;
    146 
    147 	client->local->traffic.messages_received++;
    148 	me.local->traffic.messages_received++;
    149 
    150 	parse(client, buffer, length);
    151 }
    152 
    153 
    154 /** Parse an incoming line.
    155  * A line was received previously, buffered via dbuf, now popped from the dbuf stack,
    156  * and we should now process it.
    157  * @param cptr    The client from which the message was received
    158  * @param buffer  The buffer
    159  * @param length  The length of the buffer
    160  * @note parse() cannot not be called recusively by any other functions!
    161  */
    162 void parse(Client *cptr, char *buffer, int length)
    163 {
    164 	Hook *h;
    165 	Client *from = cptr;
    166 	char *ch;
    167 	int i, ret;
    168 	MessageTag *mtags = NULL;
    169 	int mtags_bytes = 0;
    170 
    171 	/* Take extreme care in this function, as messages can be up to READBUFSIZE
    172 	 * in size, which is 8192 at the time of writing.
    173 	 * This, while all the rest of the IRCd code assumes a maximum length
    174 	 * of BUFSIZE, which is 512 (including NUL byte).
    175 	 */
    176 	for (h = Hooks[HOOKTYPE_PACKET]; h; h = h->next)
    177 	{
    178 		(*(h->func.intfunc))(from, &me, NULL, &buffer, &length);
    179 		if (!buffer)
    180 			return;
    181 	}
    182 
    183 	if (IsDeadSocket(cptr))
    184 		return;
    185 
    186 	if ((cptr->local->traffic.bytes_received >= iConf.handshake_data_flood_amount) && IsUnknown(cptr))
    187 	{
    188 		unreal_log(ULOG_INFO, "flood", "HANDSHAKE_DATA_FLOOD", cptr,
    189 		           "Handshake data flood detected from $client.details [$client.ip]");
    190 		ban_handshake_data_flooder(cptr);
    191 		return;
    192 	}
    193 
    194 	/* This stores the last executed command in 'backupbuf', useful for debugging crashes */
    195 	strlcpy(backupbuf, buffer, sizeof(backupbuf));
    196 
    197 #if defined(RAWCMDLOGGING)
    198 	unreal_log(ULOG_INFO, "rawtraffic", "TRAFFIC_IN", cptr,
    199 		   "<- $client: $data",
    200 		   log_data_string("data", backupbuf));
    201 #endif
    202 
    203 	/* This poisons unused para elements that code should never access */
    204 	for (i = 0; i < MAXPARA+2; i++)
    205 		para[i] = (char *)DEADBEEF_ADDR;
    206 
    207 	/* First, skip any whitespace */
    208 	for (ch = buffer; *ch == ' '; ch++)
    209 		;
    210 
    211 	/* Now, parse message tags, if any */
    212 	if (*ch == '@')
    213 	{
    214 		char *start = ch;
    215 		parse_message_tags(cptr, &ch, &mtags);
    216 		if (ch - start > 0)
    217 			mtags_bytes = ch - start;
    218 		/* Skip whitespace again */
    219 		for (; *ch == ' '; ch++)
    220 			;
    221 	}
    222 
    223 	parse2(cptr, &from, mtags, mtags_bytes, ch);
    224 
    225 	if (IsDead(cptr))
    226 		RunHook(HOOKTYPE_POST_COMMAND, NULL, mtags, ch);
    227 	else
    228 		RunHook(HOOKTYPE_POST_COMMAND, from, mtags, ch);
    229 
    230 	free_message_tags(mtags);
    231 	return;
    232 }
    233 
    234 /** Parse the remaining line - helper function for parse().
    235  * @param cptr   	The client from which the message was received
    236  * @param from   	The sender, this may be changed by parse2() when
    237  *               	the message has a sender, eg :xyz PRIVMSG ..
    238  * @param mtags  	Message tags received for this message.
    239  * @param mtags_bytes	The length of all message tags.
    240  * @param ch		The incoming line received (buffer), excluding message tags.
    241  */
    242 static void parse2(Client *cptr, Client **fromptr, MessageTag *mtags, int mtags_bytes, char *ch)
    243 {
    244 	Client *from = cptr;
    245 	char *s;
    246 	int len, i, numeric = 0, paramcount;
    247 #ifdef DEBUGMODE
    248 	time_t then, ticks;
    249 	int retval;
    250 #endif
    251 	RealCommand *cmptr = NULL;
    252 	int bytes;
    253 
    254 	*fromptr = cptr; /* The default, unless a source is specified (and permitted) */
    255 
    256 	/* The remaining part should never be more than 510 bytes
    257 	 * (that is 512 minus CR LF, as specified in RFC1459 section 2.3).
    258 	 * If it is too long, then we cut it off here.
    259 	 */
    260 	if (strlen(ch) > 510)
    261 	{
    262 		ch[510] = '\0';
    263 	}
    264 
    265 	para[0] = (char *)DEADBEEF_ADDR; /* helps us catch bugs :) */
    266 
    267 	if (*ch == ':' || *ch == '@')
    268 	{
    269 		char sender[HOSTLEN + 1];
    270 		s = sender;
    271 		*s = '\0';
    272 
    273 		/* Deal with :sender ... */
    274 		for (++ch, i = 0; *ch && *ch != ' '; ++ch)
    275 		{
    276 			if (s < sender + sizeof(sender) - 1)
    277 				*s++ = *ch;
    278 		}
    279 		*s = '\0';
    280 
    281 		/* For servers we lookup the sender and change 'from' accordingly.
    282 		 * For other clients we ignore the sender.
    283 		 */
    284 		if (*sender && IsServer(cptr))
    285 		{
    286 			from = find_client(sender, NULL);
    287 
    288 			if (!from && strchr(sender, '@'))
    289 				from = hash_find_nickatserver(sender, NULL);
    290 
    291 			/* Sender not found. Possibly a ghost, so kill it.
    292 			 * This can happen in normal circumstances. For example
    293 			 * in case of A-B-C where we are B. If a KILL came from C
    294 			 * for a client on A and we processed it at B, then until
    295 			 * A has processed it we may still receive messages from A
    296 			 * about it's soon-to-be-killed-client (all due to lag).
    297 			 */
    298 			if (!from)
    299 			{
    300 				ircstats.is_unpf++;
    301 				remove_unknown(cptr, sender);
    302 				return;
    303 			}
    304 			/* This is more severe. The server gave a source of a client
    305 			 * that cannot exist from that direction.
    306 			 * Eg in case of a topology of A-B-C-D and we are B,
    307 			 * we got a message from A with ":D MODE...".
    308 			 * In that case we send a SQUIT to that direction telling to
    309 			 * unlink D from that side. This will likely lead to a
    310 			 * problematic situation, though.
    311 			 * This is, by the way, also why we try to prevent this situation
    312 			 * in the first place by using PROTOCTL SERVERS=...
    313 			 * in which case we reject such a flawed link very early
    314 			 * in the server handshake process. -- Syzop
    315 			 */
    316 			if (from->direction != cptr)
    317 			{
    318 				ircstats.is_wrdi++;
    319 				cancel_clients(cptr, from, ch);
    320 				return;
    321 			}
    322 			*fromptr = from; /* Update source client */
    323 		}
    324 		while (*ch == ' ')
    325 			ch++;
    326 	}
    327 
    328 	RunHook(HOOKTYPE_PRE_COMMAND, from, mtags, ch);
    329 
    330 	if (*ch == '\0')
    331 	{
    332 		if (!IsServer(cptr))
    333 			cptr->local->fake_lag++; /* 1s fake lag */
    334 		return;
    335 	}
    336 
    337 	/* Recalculate string length, now that we have skipped the sender */
    338 	bytes = strlen(ch);
    339 
    340 	/* Now let's figure out the command (or numeric)... */
    341 	s = strchr(ch, ' ');	/* s -> End of the command code */
    342 	len = (s) ? (s - ch) : 0;
    343 
    344 	if (len == 3 && isdigit(*ch) && isdigit(*(ch + 1)) && isdigit(*(ch + 2)))
    345 	{
    346 		/* Numeric (eg: 311) */
    347 		cmptr = NULL;
    348 		numeric = (*ch - '0') * 100 + (*(ch + 1) - '0') * 10 + (*(ch + 2) - '0');
    349 		paramcount = MAXPARA;
    350 		ircstats.is_num++;
    351 		parse_addlag(cptr, bytes, mtags_bytes);
    352 	}
    353 	else
    354 	{
    355 		/* Command (eg: PRIVMSG) */
    356 		int flags = 0;
    357 		if (s)
    358 			*s++ = '\0';
    359 
    360 		/* Set the appropriate flags for the command lookup */
    361 		if (!IsRegistered(from))
    362 			flags |= CMD_UNREGISTERED;
    363 		if (IsUser(from))
    364 			flags |= CMD_USER;
    365 		if (IsServer(from))
    366 			flags |= CMD_SERVER;
    367 		if (IsShunned(from))
    368 			flags |= CMD_SHUN;
    369 		if (IsVirus(from))
    370 			flags |= CMD_VIRUS;
    371 		if (IsOper(from))
    372 			flags |= CMD_OPER;
    373 		if (IsControl(from))
    374 			flags |= CMD_CONTROL;
    375 		cmptr = find_command(ch, flags);
    376 		if (!cmptr || !(cmptr->flags & CMD_NOLAG))
    377 		{
    378 			/* Add fake lag (doing this early in the code, so we don't forget) */
    379 			parse_addlag(cptr, bytes, mtags_bytes);
    380 		}
    381 		if (!cmptr)
    382 		{
    383 			if (IsControl(from))
    384 			{
    385 				sendto_one(from, NULL, "ERROR UNKNOWN_COMMAND: %s", ch);
    386 				sendto_one(from, NULL, "END 1");
    387 				return;
    388 			}
    389 			/* Don't send error messages in response to NOTICEs
    390 			 * in pre-connection state.
    391 			 */
    392 			if (!IsRegistered(cptr) && strcasecmp(ch, "NOTICE"))
    393 			{
    394 				sendnumericfmt(from, ERR_NOTREGISTERED, ":You have not registered");
    395 				return;
    396 			}
    397 			/* If the user is shunned then don't send anything back in case
    398 			 * of an unknown command, since we want to save data.
    399 			 */
    400 			if (IsShunned(cptr))
    401 				return;
    402 				
    403 			if (ch[0] != '\0')
    404 			{
    405 				if (IsUser(from))
    406 				{
    407 					sendto_one(from, NULL, ":%s %d %s %s :Unknown command",
    408 					                       me.name, ERR_UNKNOWNCOMMAND,
    409 					                       from->name, ch);
    410 				}
    411 			}
    412 			ircstats.is_unco++;
    413 			return;
    414 		}
    415 		if (cmptr->flags != 0) { /* temporary until all commands are updated */
    416 
    417 		/* Logic in comparisons below is a bit complicated, see notes */
    418 
    419 		/* If you're a user, and this command does not permit users or opers, deny */
    420 		if ((flags & CMD_USER) && !(cmptr->flags & CMD_USER) && !(cmptr->flags & CMD_OPER))
    421 		{
    422 			if (cmptr->flags & CMD_UNREGISTERED)
    423 				sendnumeric(cptr, ERR_ALREADYREGISTRED); /* only for unregistered phase */
    424 			else
    425 				sendnumeric(cptr, ERR_NOTFORUSERS, cmptr->cmd); /* really never for users */
    426 			return;
    427 		}
    428 
    429 		/* If you're a server, but command doesn't want servers, deny */
    430 		if ((flags & CMD_SERVER) && !(cmptr->flags & CMD_SERVER))
    431 			return;
    432 		}
    433 
    434 		/* If you're a user, but not an operator, and this requires operators, deny */
    435 		if ((cmptr->flags & CMD_OPER) && (flags & CMD_USER) && !(flags & CMD_OPER))
    436 		{
    437 			sendnumeric(cptr, ERR_NOPRIVILEGES);
    438 			return;
    439 		}
    440 		paramcount = cmptr->parameters;
    441 		cmptr->bytes += bytes;
    442 	}
    443 	/*
    444 	   ** Must the following loop really be so devious? On
    445 	   ** surface it splits the message to parameters from
    446 	   ** blank spaces. But, if paramcount has been reached,
    447 	   ** the rest of the message goes into this last parameter
    448 	   ** (about same effect as ":" has...) --msa
    449 	 */
    450 
    451 	/* Note initially true: s==NULL || *(s-1) == '\0' !! */
    452 
    453 	i = 0;
    454 	if (s)
    455 	{
    456 		/*
    457 		if (paramcount > MAXPARA)
    458 			paramcount = MAXPARA;
    459 		We now use functions to create commands, so we can just check this 
    460 		once when the command is created rather than each time the command
    461 		is used -- codemastr
    462 		*/
    463 		for (;;)
    464 		{
    465 			/*
    466 			   ** Never "FRANCE " again!! ;-) Clean
    467 			   ** out *all* blanks.. --msa
    468 			 */
    469 			while (*s == ' ')
    470 				*s++ = '\0';
    471 
    472 			if (*s == '\0')
    473 				break;
    474 			if (*s == ':')
    475 			{
    476 				/*
    477 				   ** The rest is single parameter--can
    478 				   ** include blanks also.
    479 				 */
    480 				para[++i] = s + 1;
    481 				break;
    482 			}
    483 			para[++i] = s;
    484 			if (i >= paramcount)
    485 				break;
    486 			for (; *s != ' ' && *s; s++)
    487 				;
    488 		}
    489 	}
    490 	para[++i] = NULL;
    491 
    492 	/* Check if one of the message tags are rejected by spamfilter */
    493 	if (MyConnect(from) && !IsServer(from) && match_spamfilter_mtags(from, mtags, cmptr ? cmptr->cmd : NULL))
    494 		return;
    495 
    496 	if (cmptr == NULL)
    497 	{
    498 		do_numeric(numeric, from, mtags, i, (const char **)para);
    499 		return;
    500 	}
    501 	cmptr->count++;
    502 	if (IsUser(cptr) && (cmptr->flags & CMD_RESETIDLE))
    503 		cptr->local->idle_since = TStime();
    504 
    505 	/* Now ready to execute the command */
    506 #ifndef DEBUGMODE
    507 	if (cmptr->flags & CMD_ALIAS)
    508 	{
    509 		(*cmptr->aliasfunc) (from, mtags, i, (const char **)para, cmptr->cmd);
    510 	} else {
    511 		if (!cmptr->overriders)
    512 			(*cmptr->func) (from, mtags, i, (const char **)para);
    513 		else
    514 			(*cmptr->overriders->func) (cmptr->overriders, from, mtags, i, (const char **)para);
    515 	}
    516 #else
    517 	then = clock();
    518 	if (cmptr->flags & CMD_ALIAS)
    519 	{
    520 		(*cmptr->aliasfunc) (from, mtags, i, (const char **)para, cmptr->cmd);
    521 	} else {
    522 		if (!cmptr->overriders)
    523 			(*cmptr->func) (from, mtags, i, (const char **)para);
    524 		else
    525 			(*cmptr->overriders->func) (cmptr->overriders, from, mtags, i, (const char **)para);
    526 	}
    527 	if (!IsDead(cptr))
    528 	{
    529 		ticks = (clock() - then);
    530 		if (IsServer(cptr))
    531 			cmptr->rticks += ticks;
    532 		else
    533 			cmptr->lticks += ticks;
    534 	}
    535 #endif
    536 }
    537 
    538 /** Ban user that is "flooding from an unknown connection".
    539  * This is basically a client sending lots of data but not registering.
    540  * Note that "lots" in terms of IRC is a few KB's, since more is rather unusual.
    541  * @param client The client.
    542  */
    543 static void ban_handshake_data_flooder(Client *client)
    544 {
    545 	if (find_tkl_exception(TKL_HANDSHAKE_DATA_FLOOD, client))
    546 	{
    547 		/* If the user is exempt we will still KILL the client, since it is
    548 		 * clearly misbehaving. We just won't ZLINE the host, so it won't
    549 		 * affect any other connections from the same IP address.
    550 		 */
    551 		exit_client(client, NULL, "Handshake data flood detected");
    552 	}
    553 	else
    554 	{
    555 		/* place_host_ban also takes care of removing any other clients with same host/ip */
    556 		place_host_ban(client, iConf.handshake_data_flood_ban_action, "Handshake data flood detected", iConf.handshake_data_flood_ban_time);
    557 	}
    558 }
    559 
    560 /** Add "fake lag" if needed.
    561  * The main purpose of fake lag is to create artificial lag when
    562  * processing incoming data from the client. So, if a client sends
    563  * a lot of commands, then next command will be processed at a rate
    564  * of 1 per second, or even slower. The exact algorithm is defined in this function.
    565  *
    566  * Servers are exempt from fake lag, so are IRCOps and clients tagged as
    567  * 'no fake lag' by services (rarely used). Finally, there is also an
    568  * option called class::options::nofakelag which exempts fakelag.
    569  * Exemptions should be granted with extreme care, since a client will
    570  * be able to flood at full speed causing potentially many Mbits or even
    571  * GBits of data to be sent out to other clients.
    572  *
    573  * @param client	The client.
    574  * @param command_bytes	Command length in bytes (excluding message tagss)
    575  * @param mtags_bytes	Length of message tags in bytes
    576  */
    577 void parse_addlag(Client *client, int command_bytes, int mtags_bytes)
    578 {
    579 	FloodSettings *settings = get_floodsettings_for_user(client, FLD_LAG_PENALTY);
    580 
    581 	if (!IsServer(client) && !IsNoFakeLag(client) &&
    582 #ifdef FAKELAG_CONFIGURABLE
    583 	    !(client->local->class && (client->local->class->options & CLASS_OPT_NOFAKELAG)) &&
    584 #endif
    585 	    !ValidatePermissionsForPath("immune:lag",client,NULL,NULL,NULL))
    586 	{
    587 		int lag_penalty = settings->period[FLD_LAG_PENALTY];
    588 		int lag_penalty_bytes = settings->limit[FLD_LAG_PENALTY];
    589 
    590 		client->local->fake_lag_msec += (1 + (command_bytes/lag_penalty_bytes) + (mtags_bytes/lag_penalty_bytes)) * lag_penalty;
    591 
    592 		/* This code takes into account not only the msecs we just calculated
    593 		 * but also any leftover msec from previous lagging up.
    594 		 */
    595 		client->local->fake_lag += (client->local->fake_lag_msec / 1000);
    596 		client->local->fake_lag_msec = client->local->fake_lag_msec % 1000;
    597 	}
    598 }
    599 
    600 /* Add extra fake lag to client, such as after a failed oper attempt.
    601  */
    602 void add_fake_lag(Client *client, long msec)
    603 {
    604 	if (!MyConnect(client))
    605 		return;
    606 
    607 	client->local->fake_lag_msec += msec;
    608 	client->local->fake_lag += (client->local->fake_lag_msec / 1000);
    609 	client->local->fake_lag_msec = client->local->fake_lag_msec % 1000;
    610 }
    611 
    612 /** Returns 1 if the client is lagged up and data should NOT be parsed.
    613  * See also parse_addlag() for more information on "fake lag".
    614  * @param client	The client to check
    615  * @returns 1 if client is lagged up and data should not be parsed, 0 otherwise.
    616  */
    617 static int client_lagged_up(Client *client)
    618 {
    619 	if (client->status < CLIENT_STATUS_UNKNOWN)
    620 		return 0;
    621 	if (IsServer(client))
    622 		return 0;
    623 	if (ValidatePermissionsForPath("immune:lag",client,NULL,NULL,NULL))
    624 		return 0;
    625 	if (client->local->fake_lag - TStime() < 10)
    626 		return 0;
    627 	return 1;
    628 }
    629 
    630 
    631 /** Numeric received from a connection.
    632  * @param numeric     The numeric code (range 000-999)
    633  * @param cptr        The client
    634  * @param recv_mtags  Received message tags
    635  * @param parc        Parameter count
    636  * @param parv        Parameters
    637  * @note  In general you should NOT send anything back if you receive
    638  *        a numeric, this to prevent creating loops.
    639  */
    640 static int do_numeric(int numeric, Client *client, MessageTag *recv_mtags, int parc, const char *parv[])
    641 {
    642 	Client *acptr;
    643 	Channel *channel;
    644 	char *nick, *p;
    645 	int i;
    646 	char buffer[BUFSIZE];
    647 	char targets[BUFSIZE];
    648 
    649 	if ((numeric < 0) || (numeric > 999))
    650 		return -1;
    651 
    652 	if (MyConnect(client) && !IsServer(client) && !IsUser(client) && IsHandshake(client) && client->server && !IsServerSent(client))
    653 	{
    654 		/* This is an outgoing server connect that is currently not yet IsServer() but in 'unknown' state.
    655 		 * We need to handle a few responses here.
    656 		 */
    657 
    658 		/* STARTTLS: unknown command */
    659 		if ((numeric == 451) && (parc > 2) && strstr(parv[1], "STARTTLS"))
    660 		{
    661 			if (client->server->conf && (client->server->conf->outgoing.options & CONNECT_INSECURE))
    662 				start_server_handshake(client);
    663 			else
    664 				reject_insecure_server(client);
    665 			return 0;
    666 		}
    667 
    668 		/* STARTTLS failed */
    669 		if (numeric == 691)
    670 		{
    671 			unreal_log(ULOG_WARNING, "link", "STARTTLS_FAILED", client,
    672 			           "Switching from plaintext to TLS via STARTTLS failed for server $client, this is unusual. "
    673 			           "Please check the other side of the link for errors.");
    674 			reject_insecure_server(client);
    675 			return 0;
    676 		}
    677 
    678 		/* STARTTLS OK */
    679 		if (numeric == 670)
    680 		{
    681 			int ret = client_starttls(client);
    682 			if (ret < 0)
    683 			{
    684 				unreal_log(ULOG_WARNING, "link", "STARTTLS_FAILED", client,
    685 					   "Switching from plaintext to TLS via STARTTLS failed for server $client, this is unusual.");
    686 				reject_insecure_server(client);
    687 				return ret;
    688 			}
    689 			/* We don't call start_server_handshake() here. First the TLS handshake will
    690 			 * be completed, then completed_connection() will be called for a second time,
    691 			 * which will call completed_connection() from there.
    692 			 */
    693 			return 0;
    694 		}
    695 	}
    696 
    697 	/* Other than the (strange) code from above, we actually
    698 	 * don't process numerics from non-servers. So return here.
    699 	 */
    700 	if ((parc < 2) || BadPtr(parv[1]) || !IsServer(client))
    701 		return 0;
    702 
    703 	/* Remap low number numerics. */
    704 	if (numeric < 100)
    705 		numeric += 100;
    706 
    707 	/* Convert parv[] back to a string 'buffer', since that is
    708 	 * what we use in the sendto_* functions below.
    709 	 */
    710 	concat_params(buffer, sizeof(buffer), parc, parv);
    711 
    712 	/* Now actually process the numeric, IOTW: send it on */
    713 	strlcpy(targets, parv[1], sizeof(targets));
    714 	for (nick = strtoken(&p, targets, ","); nick; nick = strtoken(&p, NULL, ","))
    715 	{
    716 		if ((acptr = find_client(nick, NULL)))
    717 		{
    718 			if (!IsMe(acptr) && IsUser(acptr))
    719 			{
    720 				if (MyConnect(acptr) && isdigit(*nick))
    721 				{
    722 					/* Hack to prevent leaking UID */
    723 					char *skip = strchr(buffer, ' ');
    724 					if (skip)
    725 					{
    726 						sendto_prefix_one(acptr, client, recv_mtags, ":%s %d %s %s",
    727 						    client->name, numeric, acptr->name, skip+1);
    728 					} /* else.. malformed (no content) */
    729 				} else {
    730 					sendto_prefix_one(acptr, client, recv_mtags, ":%s %d %s",
    731 					    client->name, numeric, buffer);
    732 				}
    733 			}
    734 			else if (IsServer(acptr) && acptr->direction != client->direction)
    735 				sendto_prefix_one(acptr, client, recv_mtags, ":%s %d %s",
    736 				    client->name, numeric, buffer);
    737 		}
    738 		else if ((acptr = find_server_quick(nick)))
    739 		{
    740 			if (!IsMe(acptr) && acptr->direction != client->direction)
    741 				sendto_prefix_one(acptr, client, recv_mtags, ":%s %d %s",
    742 				    client->name, numeric, buffer);
    743 		}
    744 		else if ((channel = find_channel(nick)))
    745 		{
    746 			sendto_channel(channel, client, client->direction,
    747 			               0, 0, SEND_ALL, recv_mtags,
    748 			               ":%s %d %s", client->name, numeric, buffer);
    749 		}
    750 	}
    751 
    752 	return 0;
    753 }
    754 
    755 // FIXME: aren't we exiting the wrong client?
    756 static void cancel_clients(Client *cptr, Client *client, char *cmd)
    757 {
    758 	if (IsServer(cptr) || IsServer(client) || IsMe(client))
    759 		return;
    760 	exit_client(cptr, NULL, "Fake prefix");
    761 }
    762 
    763 static void remove_unknown(Client *client, char *sender)
    764 {
    765 	if (!IsRegistered(client) || IsUser(client))
    766 		return;
    767 	/*
    768 	 * Not from a server so don't need to worry about it.
    769 	 */
    770 	if (!IsServer(client))
    771 		return;
    772 
    773 	/*
    774 	 * Do kill if it came from a server because it means there is a ghost
    775 	 * user on the other server which needs to be removed. -avalon
    776 	 */
    777 	if ((isdigit(*sender) && strlen(sender) <= SIDLEN) || strchr(sender, '.'))
    778 		sendto_one(client, NULL, ":%s SQUIT %s :Unknown prefix (%s) from %s",
    779 		    me.id, sender, sender, client->name);
    780 	else
    781 		sendto_one(client, NULL, ":%s KILL %s :Ghost user", me.id, sender);
    782 }