ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/modules/core/m_message.c
Revision: 7864
Committed: Wed Nov 9 12:27:56 2016 UTC (9 years, 10 months ago) by michael
Content type: text/x-csrc
File size: 18866 byte(s)
Error occurred while calculating annotation data.
Log Message:
- m_message.c:flood_attack_channel(): don't rate-limit messages sent by opers

File Contents

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

Properties

Name Value
svn:eol-style native
svn:keywords Id Revision