1 /*
2  *  ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3  *
4  *  Copyright (c) 1997-2021 ircd-hybrid development team
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 2 of the License, or
9  *  (at your option) 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19  *  USA
20  */
21 
22 /*! \file m_message.c
23  * \brief Includes required functions for processing the PRIVMSG/NOTICE command.
24  * \version $Id: m_message.c 9858 2021-01-01 04:43:42Z michael $
25  */
26 
27 #include "stdinc.h"
28 #include "list.h"
29 #include "client.h"
30 #include "ircd.h"
31 #include "numeric.h"
32 #include "conf.h"
33 #include "server.h"
34 #include "send.h"
35 #include "parse.h"
36 #include "modules.h"
37 #include "channel.h"
38 #include "channel_mode.h"
39 #include "irc_string.h"
40 #include "hash.h"
41 #include "packet.h"
42 
43 
44 enum
45 {
46   ENTITY_NONE,
47   ENTITY_CHANNEL,
48   ENTITY_CLIENT
49 };
50 
51 static const char *const command[] =
52 {
53   [false] = "PRIVMSG",
54   [true] = "NOTICE"
55 };
56 
57 static struct
58 {
59   void *ptr;
60   unsigned int type;
61   unsigned int flags;
62 } targets[IRCD_BUFSIZE];
63 
64 static unsigned int ntargets;
65 
66 
67 /* duplicate_ptr()
68  *
69  * inputs       - pointer to check
70  *              - pointer to table of entities
71  *              - number of valid entities so far
72  * output       - YES if duplicate pointer in table, NO if not.
73  *                note, this does the canonize using pointers
74  * side effects - NONE
75  */
76 static bool
duplicate_ptr(const void * const ptr)77 duplicate_ptr(const void *const ptr)
78 {
79   for (unsigned int i = 0; i < ntargets; ++i)
80     if (targets[i].ptr == ptr)
81       return true;
82 
83   return false;
84 }
85 
86 /* flood_attack_client()
87  *
88  * inputs       - flag 0 if PRIVMSG 1 if NOTICE. RFC
89  *                say NOTICE must not auto reply
90  *              - pointer to source Client
91  *              - pointer to target Client
92  * output       - 1 if target is under flood attack
93  * side effects - check for flood attack on target target_p
94  */
95 static bool
flood_attack_client(bool notice,struct Client * source_p,struct Client * target_p)96 flood_attack_client(bool notice, struct Client *source_p, struct Client *target_p)
97 {
98   assert(MyClient(target_p));
99   assert(IsClient(source_p));
100 
101   if (!(GlobalSetOptions.floodcount && GlobalSetOptions.floodtime))
102     return false;
103 
104   if (HasUMode(source_p, UMODE_OPER))
105     return false;
106 
107   if (HasFlag(source_p, FLAGS_SERVICE | FLAGS_CANFLOOD))
108     return false;
109 
110   if (target_p->connection->first_received_message_time + GlobalSetOptions.floodtime < event_base->time.sec_monotonic)
111   {
112     if (target_p->connection->received_number_of_privmsgs)
113       target_p->connection->received_number_of_privmsgs = 0;
114     else
115       DelFlag(target_p, FLAGS_FLOOD_NOTICED);
116 
117     target_p->connection->first_received_message_time = event_base->time.sec_monotonic;
118   }
119 
120   if (target_p->connection->received_number_of_privmsgs >= GlobalSetOptions.floodcount)
121   {
122     if (!HasFlag(target_p, FLAGS_FLOOD_NOTICED))
123     {
124       sendto_realops_flags(UMODE_FLOOD, L_ALL, SEND_NOTICE, "Possible Flooder %s on %s target: %s",
125                            client_get_name(source_p, HIDE_IP),
126                            source_p->servptr->name, target_p->name);
127       AddFlag(target_p, FLAGS_FLOOD_NOTICED);
128     }
129 
130     if (notice == false)
131       sendto_one_notice(source_p, &me, ":*** Message to %s throttled due to flooding",
132                         target_p->name);
133     return true;
134   }
135 
136   ++target_p->connection->received_number_of_privmsgs;
137   return false;
138 }
139 
140 /* flood_attack_channel()
141  *
142  * inputs       - flag 0 if PRIVMSG 1 if NOTICE. RFC
143  *                says NOTICE must not auto reply
144  *              - pointer to source Client
145  *              - pointer to target channel
146  * output       - 1 if target is under flood attack
147  * side effects - check for flood attack on target channel
148  */
149 static bool
flood_attack_channel(bool notice,struct Client * source_p,struct Channel * channel)150 flood_attack_channel(bool notice, struct Client *source_p, struct Channel *channel)
151 {
152   if (!(GlobalSetOptions.floodcount && GlobalSetOptions.floodtime))
153     return false;
154 
155   if (HasUMode(source_p, UMODE_OPER))
156     return false;
157 
158   if (HasFlag(source_p, FLAGS_SERVICE | FLAGS_CANFLOOD))
159     return false;
160 
161   if (channel->first_received_message_time + GlobalSetOptions.floodtime < event_base->time.sec_monotonic)
162   {
163     if (channel->received_number_of_privmsgs)
164       channel->received_number_of_privmsgs = 0;
165     else
166       ClearFloodNoticed(channel);
167 
168     channel->first_received_message_time = event_base->time.sec_monotonic;
169   }
170 
171   if (channel->received_number_of_privmsgs >= GlobalSetOptions.floodcount)
172   {
173     if (!IsSetFloodNoticed(channel))
174     {
175       sendto_realops_flags(UMODE_FLOOD, L_ALL, SEND_NOTICE, "Possible Flooder %s on %s target: %s",
176                            client_get_name(source_p, HIDE_IP),
177                            source_p->servptr->name, channel->name);
178       SetFloodNoticed(channel);
179     }
180 
181     if (MyClient(source_p))
182     {
183       if (notice == false)
184         sendto_one_notice(source_p, &me, ":*** Message to %s throttled due to flooding",
185                           channel->name);
186       return true;
187     }
188   }
189 
190   ++channel->received_number_of_privmsgs;
191   return false;
192 }
193 
194 /* msg_channel_flags()
195  *
196  * inputs	- flag 0 if PRIVMSG 1 if NOTICE. RFC
197  *		  say NOTICE must not auto reply
198  *		- pointer to source_p
199  *		- pointer to channel
200  *		- flags
201  *		- pointer to text to send
202  * output	- NONE
203  * side effects	- message given channel either chanop or voice
204  */
205 static void
msg_channel(bool notice,struct Client * source_p,struct Channel * channel,unsigned int flags,const char * text)206 msg_channel(bool notice, struct Client *source_p, struct Channel *channel,
207             unsigned int flags, const char *text)
208 {
209   unsigned int type = 0;
210   const char *prefix = "";
211 
212   if (flags & CHFL_VOICE)
213   {
214     type = CHFL_CHANOP | CHFL_HALFOP | CHFL_VOICE;
215     prefix = "+";
216   }
217   else if (flags & CHFL_HALFOP)
218   {
219     type = CHFL_CHANOP | CHFL_HALFOP;
220     prefix = "%";
221   }
222   else if (flags & CHFL_CHANOP)
223   {
224     type = CHFL_CHANOP;
225     prefix = "@";
226   }
227 
228   /* Chanops and voiced can flood their own channel with impunity */
229   int ret = can_send(channel, source_p, NULL, text, notice);
230   if (ret < 0)
231   {
232     if (ret == CAN_SEND_OPV || flood_attack_channel(notice, source_p, channel) == false)
233       sendto_channel_butone(source_p, source_p, channel, type, "%s %s%s :%s",
234                             command[notice], prefix, channel->name, text);
235   }
236   else if (notice == false)
237     sendto_one_numeric(source_p, &me, ret, channel->name, text);
238 }
239 
240 /* msg_client()
241  *
242  * inputs	- flag 0 if PRIVMSG 1 if NOTICE. RFC
243  *		  say NOTICE must not auto reply
244  * 		- pointer to source_p source (struct Client *)
245  *		- pointer to target_p target (struct Client *)
246  *		- pointer to text
247  * output	- NONE
248  * side effects	- message given channel either chanop or voice
249  */
250 static void
msg_client(bool notice,struct Client * source_p,struct Client * target_p,const char * text)251 msg_client(bool notice, struct Client *source_p, struct Client *target_p,
252            const char *text)
253 {
254   if (MyClient(source_p))
255   {
256     if (target_p->away[0] && notice == false)
257       sendto_one_numeric(source_p, &me, RPL_AWAY, target_p->name, target_p->away);
258 
259     if (HasUMode(target_p, UMODE_REGONLY) && target_p != source_p)
260     {
261       if (!HasUMode(source_p, UMODE_REGISTERED | UMODE_OPER))
262       {
263         if (notice == false)
264           sendto_one_numeric(source_p, &me, ERR_NONONREG, target_p->name);
265         return;
266       }
267     }
268   }
269 
270   if (MyClient(target_p) && IsClient(source_p))
271   {
272     if (HasUMode(target_p, UMODE_CALLERID | UMODE_SOFTCALLERID) &&
273         accept_message(source_p, target_p) == false)
274     {
275       bool callerid = HasUMode(target_p, UMODE_CALLERID) != 0;
276 
277       /* check for accept, flag recipient incoming message */
278       if (notice == false)
279         sendto_one_numeric(source_p, &me, RPL_TARGUMODEG,
280                            target_p->name,
281                            callerid ? "+g" : "+G",
282                            callerid ? "server side ignore" :
283                                       "server side ignore with the exception of common channels");
284 
285       if ((target_p->connection->last_caller_id_time +
286            ConfigGeneral.caller_id_wait) < event_base->time.sec_monotonic)
287       {
288         if (notice == false)
289           sendto_one_numeric(source_p, &me, RPL_TARGNOTIFY, target_p->name);
290 
291         sendto_one_numeric(target_p, &me, RPL_UMODEGMSG,
292                            source_p->name, source_p->username, source_p->host,
293                            callerid ? "+g" : "+G");
294         target_p->connection->last_caller_id_time = event_base->time.sec_monotonic;
295       }
296 
297       /* Only so opers can watch for floods */
298       flood_attack_client(notice, source_p, target_p);
299       return;
300     }
301 
302     if (flood_attack_client(notice, source_p, target_p) == true)
303       return;
304   }
305 
306   sendto_anywhere(target_p, source_p, command[notice], ":%s", text);
307 }
308 
309 /* handle_special()
310  *
311  * inputs	- client pointer
312  *		- nick stuff to grok for opers
313  *		- text to send if grok
314  * output	- none
315  * side effects	- old style nick@server is handled here for non opers
316  *		  all the traditional oper type messages are also parsed here.
317  *		  i.e. "/msg #some.host."
318  *		  However, syntax has been changed.
319  *		  previous syntax "/msg #some.host.mask"
320  *		  now becomes     "/msg $#some.host.mask"
321  *		  previous syntax of: "/msg $some.server.mask" remains
322  *		  This disambiguates the syntax.
323  */
324 static void
handle_special(bool notice,struct Client * source_p,const char * nick,const char * text)325 handle_special(bool notice, struct Client *source_p,
326                const char *nick, const char *text)
327 {
328   /*
329    * nick@server addressed?
330    */
331   const char *server = strchr(nick, '@');
332   if (server)
333   {
334     struct Client *target_p = hash_find_server(server + 1);
335     if (target_p == NULL)
336     {
337       sendto_one_numeric(source_p, &me, ERR_NOSUCHSERVER, server + 1);
338       return;
339     }
340 
341     if (!IsMe(target_p))
342     {
343       sendto_one(target_p, ":%s %s %s :%s", source_p->id, command[notice], nick, text);
344       return;
345     }
346 
347     sendto_one_numeric(source_p, &me, ERR_NOSUCHNICK, nick);
348     return;
349   }
350 
351   if (!HasUMode(source_p, UMODE_OPER))
352   {
353     sendto_one_numeric(source_p, &me, ERR_NOPRIVILEGES);
354     return;
355   }
356 
357   /*
358    * The following two cases allow masks in NOTICEs
359    * (for OPERs only)
360    *
361    * Armin, 8Jun90 (gruner@informatik.tu-muenchen.de)
362    */
363   if (*nick == '$')
364   {
365     if (*(nick + 1) == '$' || *(nick + 1) == '#')
366       ++nick;
367     else if (MyClient(source_p))
368     {
369       sendto_one_notice(source_p, &me, ":The command %s %s is no longer supported, please use $%s",
370                         command[notice], nick, nick);
371       return;
372     }
373 
374     const char *s = strrchr(nick, '.');
375     if (s == NULL)
376     {
377       sendto_one_numeric(source_p, &me, ERR_NOTOPLEVEL, nick);
378       return;
379     }
380 
381     while (*++s)
382       if (*s == '.' || *s == '*' || *s == '?')
383         break;
384 
385     if (*s == '*' || *s == '?')
386     {
387       sendto_one_numeric(source_p, &me, ERR_WILDTOPLEVEL, nick);
388       return;
389     }
390 
391     sendto_match_butone(IsServer(source_p->from) ? source_p->from : NULL, source_p,
392                         nick + 1, (*nick == '#') ? MATCH_HOST : MATCH_SERVER,
393                         "%s $%s :%s", command[notice], nick, text);
394   }
395 }
396 
397 /* build_target_list()
398  *
399  * inputs	- pointer to given source (oper/client etc.)
400  *		- pointer to list of nicks/channels
401  *		- pointer to table to place results
402  *		- pointer to text (only used if source_p is an oper)
403  * output	- number of valid entities
404  * side effects	- target_table is modified to contain a list of
405  *		  pointers to channels or clients
406  *		  if source client is an oper
407  *		  all the classic old bizzare oper privmsg tricks
408  *		  are parsed and sent as is, if prefixed with $
409  *		  to disambiguate.
410  *
411  */
412 static void
build_target_list(bool notice,struct Client * source_p,char * list,const char * text)413 build_target_list(bool notice, struct Client *source_p, char *list, const char *text)
414 {
415   char *p = NULL;
416   void *target = NULL;
417 
418   ntargets = 0;
419 
420   for (const char *name = strtok_r(list, ",", &p); name;
421                    name = strtok_r(NULL, ",", &p))
422   {
423     /*
424      * Channels are privmsg'd a lot more than other clients, moved up
425      * here plain old channel msg?
426      */
427     if (IsChanPrefix(*name))
428     {
429       if ((target = hash_find_channel(name)))
430       {
431         if (duplicate_ptr(target) == false)
432         {
433           if (ntargets >= ConfigGeneral.max_targets)
434           {
435             sendto_one_numeric(source_p, &me, ERR_TOOMANYTARGETS,
436                                name, ConfigGeneral.max_targets);
437             return;
438           }
439 
440           targets[ntargets].ptr = target;
441           targets[ntargets].type = ENTITY_CHANNEL;
442           targets[ntargets++].flags = 0;
443         }
444       }
445       else if (notice == false)
446         sendto_one_numeric(source_p, &me, ERR_NOSUCHNICK, name);
447 
448       continue;
449     }
450 
451     /* Look for a PRIVMSG/NOTICE to another client */
452     if ((target = find_person(source_p, name)))
453     {
454       if (duplicate_ptr(target) == false)
455       {
456         if (ntargets >= ConfigGeneral.max_targets)
457         {
458           sendto_one_numeric(source_p, &me, ERR_TOOMANYTARGETS,
459                              name, ConfigGeneral.max_targets);
460           return;
461         }
462 
463         targets[ntargets].ptr = target;
464         targets[ntargets].type = ENTITY_CLIENT;
465         targets[ntargets++].flags = 0;
466       }
467 
468       continue;
469     }
470 
471     /* @#channel or +#channel message ? */
472     unsigned int type = 0;
473     const char *with_prefix = name;
474 
475     /* Allow %+@ if someone wants to do that */
476     while (true)
477     {
478       if (*name == '@')
479         type |= CHFL_CHANOP;
480       else if (*name == '%')
481         type |= CHFL_CHANOP | CHFL_HALFOP;
482       else if (*name == '+')
483         type |= CHFL_CHANOP | CHFL_HALFOP | CHFL_VOICE;
484       else
485         break;
486       ++name;
487     }
488 
489     if (type)
490     {
491       if (EmptyString(name))  /* If it's a '\0' dump it, there is no recipient */
492       {
493         sendto_one_numeric(source_p, &me, ERR_NORECIPIENT, command[notice]);
494         continue;
495       }
496 
497       /*
498        * At this point, name should be a channel name i.e. #foo. If the channel
499        * is found, fine, if not report an error.
500        */
501       if ((target = hash_find_channel(name)))
502       {
503         if (IsClient(source_p) && !HasFlag(source_p, FLAGS_SERVICE))
504         {
505           if (member_has_flags(member_find_link(source_p, target),
506                                CHFL_CHANOP|CHFL_HALFOP|CHFL_VOICE) == false)
507           {
508             sendto_one_numeric(source_p, &me, ERR_CHANOPRIVSNEEDED, with_prefix);
509             continue;
510           }
511         }
512 
513         if (duplicate_ptr(target) == false)
514         {
515           if (ntargets >= ConfigGeneral.max_targets)
516           {
517             sendto_one_numeric(source_p, &me, ERR_TOOMANYTARGETS,
518                                name, ConfigGeneral.max_targets);
519             return;
520           }
521 
522           targets[ntargets].ptr = target;
523           targets[ntargets].type = ENTITY_CHANNEL;
524           targets[ntargets++].flags = type;
525         }
526       }
527       else if (notice == false)
528         sendto_one_numeric(source_p, &me, ERR_NOSUCHNICK, name);
529 
530       continue;
531     }
532 
533     if (*name == '$' || strchr(name, '@'))
534       handle_special(notice, source_p, name, text);
535     else if (notice == false)
536     {
537       if (!IsDigit(*name) || MyClient(source_p))
538         sendto_one_numeric(source_p, &me, ERR_NOSUCHNICK, name);
539     }
540   }
541 }
542 
543 /*
544  * inputs       - flag privmsg or notice
545  *              - pointer to source_p
546  *              - pointer to channel
547  */
548 static void
m_message(bool notice,struct Client * source_p,int parc,char * parv[])549 m_message(bool notice, struct Client *source_p, int parc, char *parv[])
550 {
551   if (EmptyString(parv[1]))
552   {
553     if (notice == false)
554       sendto_one_numeric(source_p, &me, ERR_NORECIPIENT, command[notice]);
555     return;
556   }
557 
558   if (EmptyString(parv[2]))
559   {
560     if (notice == false)
561       sendto_one_numeric(source_p, &me, ERR_NOTEXTTOSEND);
562     return;
563   }
564 
565   build_target_list(notice, source_p, parv[1], parv[2]);
566 
567   for (unsigned int i = 0; i < ntargets; ++i)
568   {
569     switch (targets[i].type)
570     {
571       case ENTITY_CLIENT:
572         msg_client(notice, source_p, targets[i].ptr, parv[2]);
573         break;
574 
575       case ENTITY_CHANNEL:
576         msg_channel(notice, source_p, targets[i].ptr, targets[i].flags, parv[2]);
577         break;
578     }
579   }
580 }
581 
582 static void
m_privmsg(struct Client * source_p,int parc,char * parv[])583 m_privmsg(struct Client *source_p, int parc, char *parv[])
584 {
585   /*
586    * Servers have no reason to send privmsgs, yet sometimes there is cause
587    * for a notice.. (for example remote kline replies) --fl_
588    */
589   if (!IsClient(source_p))
590     return;
591 
592   if (MyConnect(source_p))
593     source_p->connection->last_privmsg = event_base->time.sec_monotonic;
594 
595   m_message(false, source_p, parc, parv);
596 }
597 
598 static void
m_notice(struct Client * source_p,int parc,char * parv[])599 m_notice(struct Client *source_p, int parc, char *parv[])
600 {
601   m_message(true, source_p, parc, parv);
602 }
603 
604 static struct Message privmsg_msgtab =
605 {
606   .cmd = "PRIVMSG",
607   .handlers[UNREGISTERED_HANDLER] = { .handler = m_unregistered },
608   .handlers[CLIENT_HANDLER] = { .handler = m_privmsg, .end_grace_period = true },
609   .handlers[SERVER_HANDLER] = { .handler = m_privmsg },
610   .handlers[ENCAP_HANDLER] = { .handler = m_ignore },
611   .handlers[OPER_HANDLER] = { .handler = m_privmsg, .end_grace_period = true }
612 };
613 
614 static struct Message notice_msgtab =
615 {
616   .cmd = "NOTICE",
617   .handlers[UNREGISTERED_HANDLER] = { .handler = m_unregistered },
618   .handlers[CLIENT_HANDLER] = { .handler = m_notice },
619   .handlers[SERVER_HANDLER] = { .handler = m_notice },
620   .handlers[ENCAP_HANDLER] = { .handler = m_ignore },
621   .handlers[OPER_HANDLER] = { .handler = m_notice }
622 };
623 
624 static void
module_init(void)625 module_init(void)
626 {
627   mod_add_cmd(&privmsg_msgtab);
628   mod_add_cmd(&notice_msgtab);
629 }
630 
631 static void
module_exit(void)632 module_exit(void)
633 {
634   mod_del_cmd(&privmsg_msgtab);
635   mod_del_cmd(&notice_msgtab);
636 }
637 
638 struct module module_entry =
639 {
640   .version = "$Revision: 9858 $",
641   .modinit = module_init,
642   .modexit = module_exit,
643   .is_core = true
644 };
645