X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=ngircd-alex.git;a=blobdiff_plain;f=src%2Fngircd%2Firc.c;h=eae046d3eab11703c00fd695307abce7ede115d2;hp=34180aa7d48eb04130da17ffdd712909859204bf;hb=798de94d6556bdf2c6019f368ad7441fe6e2d1be;hpb=eccbd97e1f2599bfb76818fe6bc32967ed07e0c7 diff --git a/src/ngircd/irc.c b/src/ngircd/irc.c index 34180aa7..eae046d3 100644 --- a/src/ngircd/irc.c +++ b/src/ngircd/irc.c @@ -1,6 +1,6 @@ /* * ngIRCd -- The Next Generation IRC Daemon - * Copyright (c)2001-2013 Alexander Barton (alex@barton.de) and Contributors. + * Copyright (c)2001-2018 Alexander Barton (alex@barton.de) and Contributors. * * 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 @@ -16,17 +16,19 @@ * IRC commands */ -#include "imp.h" #include #include #include +#include +#include #include "ngircd.h" #include "conn-func.h" #include "conf.h" #include "channel.h" -#include "conn-encoding.h" -#include "defines.h" +#ifdef ICONV +# include "conn-encoding.h" +#endif #include "irc-macros.h" #include "irc-write.h" #include "log.h" @@ -34,9 +36,7 @@ #include "messages.h" #include "parse.h" #include "op.h" -#include "tool.h" -#include "exp.h" #include "irc.h" static char *Option_String PARAMS((CONN_ID Idx)); @@ -85,6 +85,8 @@ IRC_CheckListTooBig(CLIENT *From, const int Count, const int Limit, GLOBAL bool IRC_ERROR(CLIENT *Client, REQUEST *Req) { + char *msg; + assert( Client != NULL ); assert( Req != NULL ); @@ -99,12 +101,20 @@ IRC_ERROR(CLIENT *Client, REQUEST *Req) return CONNECTED; } - if (Req->argc < 1) + if (Req->argc < 1) { + msg = "Got ERROR command"; Log(LOG_NOTICE, "Got ERROR from \"%s\"!", Client_Mask(Client)); - else + } else { + msg = Req->argv[0]; Log(LOG_NOTICE, "Got ERROR from \"%s\": \"%s\"!", - Client_Mask(Client), Req->argv[0]); + Client_Mask(Client), msg); + } + + if (Client_Conn(Client) != NONE) { + Conn_Close(Client_Conn(Client), NULL, msg, false); + return DISCONNECTED; + } return CONNECTED; } /* IRC_ERROR */ @@ -128,9 +138,8 @@ IRC_ERROR(CLIENT *Client, REQUEST *Req) GLOBAL bool IRC_KILL(CLIENT *Client, REQUEST *Req) { - CLIENT *prefix, *c; - char reason[COMMAND_LEN], *msg; - CONN_ID my_conn, conn; + CLIENT *prefix; + char reason[COMMAND_LEN]; assert (Client != NULL); assert (Req != NULL); @@ -166,59 +175,8 @@ IRC_KILL(CLIENT *Client, REQUEST *Req) 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_WriteErrClient(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 */ + return IRC_KillClient(Client, prefix, Req->argv[0], reason); +} /** * Handler for the IRC "NOTICE" command. @@ -276,8 +234,6 @@ IRC_TRACE(CLIENT *Client, REQUEST *Req) assert(Client != NULL); assert(Req != NULL); - IRC_SetPenalty(Client, 3); - _IRC_GET_SENDER_OR_RETURN_(from, Req, Client) _IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, from) @@ -294,7 +250,7 @@ IRC_TRACE(CLIENT *Client, REQUEST *Req) PACKAGE_VERSION, Client_ID(target), Client_ID(Client_NextHop(target)), Option_String(idx2), - time(NULL) - Conn_StartTime(idx2), + (long)(time(NULL) - Conn_StartTime(idx2)), Conn_SendQ(idx), Conn_SendQ(idx2))) return DISCONNECTED; @@ -353,8 +309,6 @@ IRC_HELP(CLIENT *Client, REQUEST *Req) assert(Client != NULL); assert(Req != NULL); - IRC_SetPenalty(Client, 2); - if ((Req->argc == 0 && array_bytes(&Conf_Helptext) > 0) || (Req->argc >= 1 && strcasecmp(Req->argv[0], "Commands") != 0)) { /* Help text available and requested */ @@ -376,6 +330,87 @@ IRC_HELP(CLIENT *Client, REQUEST *Req) return CONNECTED; } /* IRC_HELP */ +/** + * 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 = NONE, conn; + CLIENT *c; + + assert(Nick != NULL); + assert(Reason != NULL); + + /* 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; + } + + if (Client_Type(c) != CLIENT_USER && Client_Type(c) != CLIENT_GOTNICK + && Client_Type(c) != CLIENT_SERVICE) { + /* 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); + } + + /* Inform other servers */ + IRC_WriteStrServersPrefix(From ? Client : NULL, + From ? From : Client_ThisServer(), + "KILL %s :%s", Nick, Reason); + + + /* Save ID of this connection */ + if (Client) + 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; +} + /** * Send help for a given topic to the client. * @@ -448,16 +483,11 @@ Help(CLIENT *Client, const char *Topic) * @return Pointer to static (global) string buffer. */ static char * -#ifdef ZLIB +#if defined(SSL_SUPPORT) || defined(ZLIB) Option_String(CONN_ID Idx) -#else -Option_String(UNUSED CONN_ID Idx) -#endif { static char option_txt[8]; -#ifdef ZLIB UINT16 options; -#endif assert(Idx != NONE); @@ -472,11 +502,27 @@ Option_String(UNUSED CONN_ID Idx) if(options & CONN_ZIP) /* zlib compression enabled */ strlcat(option_txt, "z", sizeof(option_txt)); #endif - LogDebug(" *** %d: %d = %s", Idx, options, option_txt); return option_txt; +#else +Option_String(UNUSED CONN_ID Idx) +{ + return ""; +#endif } /* Option_String */ +/** + * Send a message to target(s). + * + * This function is used by IRC_{PRIVMSG|NOTICE|SQUERY} to actualy + * send the message(s). + * + * @param Client The client from which this command has been received. + * @param Req Request structure with prefix and all parameters. + * @param ForceType Required type of the destination of the message(s). + * @param SendErrors Whether to report errors back to the client or not. + * @return CONNECTED or DISCONNECTED. + */ static bool Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors) { @@ -484,8 +530,10 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors) CL2CHAN *cl2chan; CHANNEL *chan; char *currentTarget = Req->argv[0]; - char *lastCurrentTarget = NULL; + char *strtok_last = NULL; char *message = NULL; + char *targets[MAX_HNDL_TARGETS]; + int i, target_nr = 0; assert(Client != NULL); assert(Req != NULL); @@ -505,12 +553,11 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors) if (Req->argc > 2) { if (!SendErrors) return CONNECTED; - IRC_SetPenalty(Client, 2); return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG, Client_ID(Client), Req->command); } - if (Client_Type(Client) == CLIENT_SERVER) + if (Client_Type(Client) == CLIENT_SERVER && Req->prefix) from = Client_Search(Req->prefix); else from = Client; @@ -526,10 +573,19 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors) message = Req->argv[1]; /* handle msgtarget = msgto *("," msgto) */ - currentTarget = strtok_r(currentTarget, ",", &lastCurrentTarget); + currentTarget = strtok_r(currentTarget, ",", &strtok_last); ngt_UpperStr(Req->command); + /* Please note that "currentTarget" is NULL when the target contains + * the separator character only, e. g. "," or ",,,," etc.! */ while (currentTarget) { + /* Make sure that there hasn't been such a target already: */ + targets[target_nr++] = currentTarget; + for(i = 0; i < target_nr - 1; i++) { + if (strcasecmp(currentTarget, targets[i]) == 0) + goto send_next_target; + } + /* Check for and handle valid of form: * RFC 2812 2.3.1: * msgto = channel / ( user [ "%" host ] "@" servername ) @@ -638,7 +694,10 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors) goto send_next_target; } - if (Client_HasMode(cl, 'C')) { + if (Client_HasMode(cl, 'C') && + !Client_HasMode(from, 'o') && + !(Client_Type(from) == CLIENT_SERVER) && + !(Client_Type(from) == CLIENT_SERVICE)) { cl2chan = Channel_FirstChannelOf(cl); while (cl2chan) { chan = Channel_GetChannel(cl2chan); @@ -673,14 +732,14 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors) return DISCONNECTED; } else if (ForceType != CLIENT_SERVICE && (chan = Channel_Search(currentTarget))) { + /* Target is a channel */ if (!Channel_Write(chan, from, Client, Req->command, SendErrors, message)) return DISCONNECTED; } else if (ForceType != CLIENT_SERVICE - /* $#: server/target mask, RFC 2812, sec. 3.3.1 */ && strchr("$#", currentTarget[0]) && strchr(currentTarget, '.')) { - /* targetmask */ + /* $#: server/host mask, RFC 2812, sec. 3.3.1 */ if (!Send_Message_Mask(from, Req->command, currentTarget, message, SendErrors)) return DISCONNECTED; @@ -693,14 +752,35 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors) } send_next_target: - currentTarget = strtok_r(NULL, ",", &lastCurrentTarget); - if (currentTarget) - Conn_SetPenalty(Client_Conn(Client), 1); + currentTarget = strtok_r(NULL, ",", &strtok_last); + if (!currentTarget) + break; + + Conn_SetPenalty(Client_Conn(Client), 1); + + if (target_nr >= MAX_HNDL_TARGETS) { + /* Too many targets given! */ + return IRC_WriteErrClient(Client, + ERR_TOOMANYTARGETS_MSG, + currentTarget); + } } return CONNECTED; } /* Send_Message */ +/** + * Send a message to "target mask" target(s). + * + * See RFC 2812, sec. 3.3.1 for details. + * + * @param from The client from which this command has been received. + * @param command The command to use (PRIVMSG, NOTICE, ...). + * @param targetMask The "target mask" (will be verified by this function). + * @param message The message to send. + * @param SendErrors Whether to report errors back to the client or not. + * @return CONNECTED or DISCONNECTED. + */ static bool Send_Message_Mask(CLIENT * from, char * command, char * targetMask, char * message, bool SendErrors) @@ -724,17 +804,15 @@ Send_Message_Mask(CLIENT * from, char * command, char * targetMask, * dot (".") and no wildcards ("*", "?") following the last one. */ check_wildcards = strrchr(targetMask, '.'); - assert(check_wildcards != NULL); - if (check_wildcards && - check_wildcards[strcspn(check_wildcards, "*?")]) - { + if (!check_wildcards || check_wildcards[strcspn(check_wildcards, "*?")]) { if (!SendErrors) return true; - return IRC_WriteErrClient(from, ERR_WILDTOPLEVEL, targetMask); + return IRC_WriteErrClient(from, ERR_WILDTOPLEVEL_MSG, + targetMask); } - /* #: hostmask, see RFC 2812, sec. 3.3.1 */ if (targetMask[0] == '#') { + /* #: hostmask, see RFC 2812, sec. 3.3.1 */ for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) { if (Client_Type(cl) != CLIENT_USER) continue; @@ -745,7 +823,8 @@ Send_Message_Mask(CLIENT * from, char * command, char * targetMask, return false; } } else { - assert(targetMask[0] == '$'); /* $: server mask, see RFC 2812, sec. 3.3.1 */ + /* $: server mask, see RFC 2812, sec. 3.3.1 */ + assert(targetMask[0] == '$'); for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) { if (Client_Type(cl) != CLIENT_USER) continue;