ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/branches/8.2.x/modules/core/m_message.c
Revision: 7351
Committed: Sun Feb 21 12:45:02 2016 UTC (8 years, 1 month ago) by michael
Content type: text/x-csrc
File size: 19419 byte(s)
Log Message:
- m_message.c:flood_attack_*(): incorporate fixes from p4 so 'can_flood' actually works

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

Properties

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