]> jfr.im git - irc/rqf/shadowircd.git/blob - extensions/hurt.c
Disallow mIRC italics in channel names when disable_fake_channels
[irc/rqf/shadowircd.git] / extensions / hurt.c
1 /*
2 * charybdis: an advanced Internet Relay Chat Daemon(ircd).
3 *
4 * Copyright (C) 2006 charybdis development team
5 * All rights reserved
6 *
7 */
8 #include "stdinc.h"
9 #include "modules.h"
10 #include "hook.h"
11 #include "client.h"
12 #include "ircd.h"
13 #include "send.h"
14 #include "numeric.h"
15 #include "hostmask.h"
16 #include "s_conf.h"
17 #include "s_newconf.h"
18 #include "hash.h"
19
20 /* {{{ Structures */
21 #define HURT_CUTOFF (10) /* protocol messages. */
22 #define HURT_DEFAULT_EXPIRE (7 * 24 * 60) /* minutes. */
23 #define HURT_EXIT_REASON "Hurt: Failed to identify to services"
24
25 enum {
26 HEAL_NICK = 0,
27 HEAL_IP
28 };
29
30 typedef struct _hurt_state {
31 time_t start_time;
32 uint32_t n_hurts;
33 rb_dlink_list hurt_clients;
34 uint16_t cutoff;
35 time_t default_expire;
36 const char *exit_reason;
37 } hurt_state_t;
38
39 typedef struct _hurt {
40 char *ip;
41 struct sockaddr *saddr;
42 int saddr_bits;
43 char *reason;
44 time_t expire;
45 } hurt_t;
46 /* }}} */
47
48 /* {{{ Prototypes */
49 static int mo_hurt(struct Client *, struct Client *, int, const char **);
50 static int me_hurt(struct Client *, struct Client *, int, const char **);
51 static int mo_heal(struct Client *, struct Client *, int, const char **);
52 static int me_heal(struct Client *, struct Client *, int, const char **);
53
54 static int modinit(void);
55 static void modfini(void);
56
57 static void client_exit_hook(hook_data_client_exit *);
58 static void new_local_user_hook(struct Client *);
59 static void doing_stats_hook(hook_data_int *hdata);
60
61 static void hurt_check_event(void *);
62 static void hurt_expire_event(void *);
63
64 static hurt_t *hurt_new(time_t, const char *, const char *);
65 static void hurt_add(hurt_t *);
66 static void hurt_propagate(struct Client *, struct Client *, hurt_t *);
67 static hurt_t *hurt_find(const char *ip);
68 static hurt_t *hurt_find_exact(const char *ip);
69 static void hurt_remove(const char *ip);
70 static void hurt_destroy(void *hurt);
71
72 static int heal_nick(struct Client *, struct Client *);
73
74 static int nick_is_valid(const char *);
75 /* }}} */
76
77 /* {{{ State containers */
78
79 rb_dlink_list hurt_confs = { NULL, NULL, 0 };
80
81 /* }}} */
82
83 /* {{{ Messages */
84 struct Message hurt_msgtab = {
85 "HURT", 0, 0, 0, MFLG_SLOW, {
86 mg_ignore, mg_ignore, mg_ignore,
87 mg_ignore, {me_hurt, 0}, {mo_hurt, 3}
88 }
89 };
90
91 struct Message heal_msgtab = {
92 "HEAL", 0, 0, 0, MFLG_SLOW, {
93 mg_ignore, mg_ignore, mg_ignore,
94 mg_ignore, {me_heal, 0}, {mo_heal, 2}
95 }
96 };
97 /* }}} */
98
99 /* {{{ Misc module stuff */
100 mapi_hfn_list_av1 hurt_hfnlist[] = {
101 {"client_exit", (hookfn) client_exit_hook},
102 {"new_local_user", (hookfn) new_local_user_hook},
103 {"doing_stats", (hookfn) doing_stats_hook},
104 {NULL, NULL},
105 };
106
107 mapi_clist_av1 hurt_clist[] = { &hurt_msgtab, &heal_msgtab, NULL };
108
109 DECLARE_MODULE_AV1(
110 hurt,
111 modinit,
112 modfini,
113 hurt_clist,
114 NULL,
115 hurt_hfnlist,
116 "$Revision: 3161 $"
117 );
118 /* }}} */
119
120 hurt_state_t hurt_state = {
121 .cutoff = HURT_CUTOFF,
122 .default_expire = HURT_DEFAULT_EXPIRE,
123 .exit_reason = HURT_EXIT_REASON,
124 };
125
126 /*
127 * Module constructor/destructor.
128 */
129
130 /* {{{ static int modinit() */
131
132 struct ev_entry *hurt_expire_ev = NULL;
133 struct ev_entry *hurt_check_ev = NULL;
134
135 static int
136 modinit(void)
137 {
138 /* set-up hurt_state. */
139 hurt_state.start_time = rb_current_time();
140
141 /* add our event handlers. */
142 hurt_expire_ev = rb_event_add("hurt_expire", hurt_expire_event, NULL, 60);
143 hurt_check_ev = rb_event_add("hurt_check", hurt_check_event, NULL, 5);
144
145 return 0;
146 }
147 /* }}} */
148
149 /* {{{ static void modfini() */
150 static void
151 modfini(void)
152 {
153 rb_dlink_node *ptr, *next_ptr;
154
155 /* and delete our events. */
156 rb_event_delete(hurt_expire_ev);
157 rb_event_delete(hurt_check_ev);
158
159 RB_DLINK_FOREACH_SAFE (ptr, next_ptr, hurt_state.hurt_clients.head)
160 {
161 rb_dlinkDestroy(ptr, &hurt_state.hurt_clients);
162 }
163 }
164 /* }}} */
165
166 /*
167 * Message handlers.
168 */
169
170 /* {{{ static int mo_hurt()
171 *
172 * HURT [<expire>] <ip> <reason>
173 *
174 * parv[1] - expire or ip
175 * parv[2] - ip or reason
176 * parv[3] - reason or NULL
177 */
178 static int
179 mo_hurt(struct Client *client_p, struct Client *source_p,
180 int parc, const char **parv)
181 {
182 const char *ip, *expire, *reason;
183 int expire_time;
184 hurt_t *hurt;
185 struct Client *target_p;
186
187 if (!IsOperK(source_p)) {
188 sendto_one(source_p, form_str(ERR_NOPRIVS), me.name,
189 source_p->name, "kline");
190 return 0;
191 }
192
193 if (parc == 3)
194 expire = NULL, ip = parv[1], reason = parv[2];
195 else
196 expire = parv[1], ip = parv[2], reason = parv[3];
197
198 if (!expire)
199 expire_time = HURT_DEFAULT_EXPIRE;
200 if (expire && (expire_time = valid_temp_time(expire)) < 1) {
201 sendto_one_notice(source_p, ":Permanent HURTs are not supported");
202 return 0;
203 }
204 if (EmptyString(reason)) {
205 sendto_one_notice(source_p, ":Empty HURT reasons are bad for business");
206 return 0;
207 }
208
209 /* Is this a client? */
210 if (strchr(ip, '.') == NULL && strchr(ip, ':') == NULL)
211 {
212 target_p = find_named_person(ip);
213 if (target_p == NULL)
214 {
215 sendto_one_numeric(source_p, ERR_NOSUCHNICK,
216 form_str(ERR_NOSUCHNICK), ip);
217 return 0;
218 }
219 ip = target_p->orighost;
220 }
221 else
222 {
223 if (!strncmp(ip, "*@", 2))
224 ip += 2;
225 if (strchr(ip, '!') || strchr(ip, '@'))
226 {
227 sendto_one_notice(source_p, ":Invalid HURT mask [%s]",
228 ip);
229 return 0;
230 }
231 }
232
233 if (hurt_find(ip) != NULL) {
234 sendto_one(source_p, ":[%s] already HURT", ip);
235 return 0;
236 }
237
238 /*
239 * okay, we've got this far, now it's time to add the the HURT locally
240 * and propagate it to other servers on the network.
241 */
242 sendto_realops_snomask(SNO_GENERAL, L_ALL,
243 "%s added HURT on [%s] for %ld minutes with reason [%s]",
244 get_oper_name(source_p), ip, (long) expire_time / 60, reason);
245
246 hurt = hurt_new(expire_time, ip, reason);
247 hurt_add(hurt);
248 hurt_propagate(NULL, source_p, hurt);
249
250 return 0;
251 }
252 /* }}} */
253
254 /* {{{ static int me_hurt()
255 *
256 * [ENCAP mask] HURT <target> <expire> <ip> <reason>
257 *
258 * parv[1] - expire
259 * parv[2] - ip
260 * parv[3] - reason
261 */
262 static int
263 me_hurt(struct Client *client_p, struct Client *source_p,
264 int parc, const char **parv)
265 {
266 time_t expire_time;
267 hurt_t *hurt;
268
269 /*
270 * right... if we don't get enough arguments, or if we get any invalid
271 * arguments, just ignore this request - shit happens, and it's not worth
272 * dropping a server over.
273 */
274 if (parc < 4 || !IsPerson(source_p))
275 return 0;
276 if ((expire_time = atoi(parv[1])) < 1)
277 return 0;
278 if (hurt_find(parv[2]) != NULL)
279 return 0;
280 if (EmptyString(parv[3]))
281 return 0;
282
283 sendto_realops_snomask(SNO_GENERAL, L_ALL,
284 "%s added HURT on [%s] for %ld minutes with reason [%s]",
285 get_oper_name(source_p), parv[2], (long) expire_time / 60, parv[3]);
286 hurt = hurt_new(expire_time, parv[2], parv[3]);
287 hurt_add(hurt);
288
289 return 0;
290 }
291 /* }}} */
292
293 /* {{{ static int mo_heal()
294 *
295 * HURT <nick>|<ip>
296 *
297 * parv[1] - nick or ip
298 */
299 static int
300 mo_heal(struct Client *client_p, struct Client *source_p,
301 int parc, const char **parv)
302 {
303 struct Client *target_p;
304
305 if (!IsOperUnkline(source_p))
306 {
307 sendto_one(source_p, form_str(ERR_NOPRIVS),
308 me.name, source_p->name, "unkline");
309 return 0;
310 }
311
312 if (nick_is_valid(parv[1]))
313 {
314 target_p = find_named_person(parv[1]);
315 if (target_p == NULL)
316 {
317 sendto_one_numeric(source_p, ERR_NOSUCHNICK,
318 form_str(ERR_NOSUCHNICK), parv[1]);
319 return 0;
320 }
321 if (MyConnect(target_p))
322 heal_nick(source_p, target_p);
323 else
324 sendto_one(target_p, ":%s ENCAP %s HEAL %s",
325 get_id(source_p, target_p),
326 target_p->servptr->name,
327 get_id(target_p, target_p));
328 }
329 else if (strchr(parv[1], '.'))
330 {
331 if (hurt_find_exact(parv[1]) == NULL)
332 {
333 sendto_one_notice(source_p, ":Mask [%s] is not HURT", parv[1]);
334 return 0;
335 }
336 hurt_remove(parv[1]);
337 sendto_realops_snomask(SNO_GENERAL, L_ALL, "%s removed HURT on %s",
338 get_oper_name(source_p), parv[1]);
339 sendto_server(NULL, NULL, NOCAPS, NOCAPS, ":%s ENCAP * HEAL %s",
340 source_p->name, parv[1]);
341 }
342 else
343 {
344 sendto_one(source_p, ":[%s] is not a valid IP address/nick", parv[1]);
345 return 0;
346 }
347
348 return 0;
349 }
350 /* }}} */
351
352 static int
353 me_heal(struct Client *client_p, struct Client *source_p,
354 int parc, const char **parv)
355 {
356 struct Client *target_p;
357
358 /* as noted in me_hurt(), if we don't get sufficient arguments...
359 * *poof*, it's dropped...
360 */
361 if (parc < 2)
362 return 0;
363
364 if (nick_is_valid(parv[1]))
365 {
366 target_p = find_person(parv[1]);
367 if (target_p != NULL && MyConnect(target_p))
368 heal_nick(source_p, target_p);
369 }
370 else if (strchr(parv[1], '.')) /* host or mask to remove ban for */
371 {
372 if (hurt_find_exact(parv[1]) == NULL)
373 return 0;
374
375 hurt_remove(parv[1]);
376 sendto_realops_snomask(SNO_GENERAL, L_ALL, "%s removed HURT on %s",
377 get_oper_name(source_p), parv[1]);
378 }
379 else
380 return 0;
381
382 return 0;
383 }
384
385 /*
386 * Event handlers.
387 */
388
389 /* {{{ static void hurt_check_event() */
390 static void
391 hurt_check_event(void *arg)
392 {
393 rb_dlink_node *ptr, *next_ptr;
394 struct Client *client_p;
395
396 RB_DLINK_FOREACH_SAFE (ptr, next_ptr, hurt_state.hurt_clients.head) {
397 client_p = ptr->data;
398 if (!EmptyString(client_p->user->suser))
399 {
400 rb_dlinkDestroy(ptr, &hurt_state.hurt_clients);
401 sendto_one_notice(client_p, ":HURT restriction removed for this session");
402 client_p->localClient->target_last = rb_current_time(); /* don't ask --nenolod */
403 }
404 else if (client_p->localClient->receiveM > hurt_state.cutoff)
405 exit_client(NULL, client_p, &me, hurt_state.exit_reason);
406 }
407 }
408 /* }}} */
409
410 /* {{{ static void hurt_expire_event() */
411 static void
412 hurt_expire_event(void *unused)
413 {
414 rb_dlink_node *ptr, *next_ptr;
415 hurt_t *hurt;
416
417 RB_DLINK_FOREACH_SAFE (ptr, next_ptr, hurt_confs.head)
418 {
419 hurt = (hurt_t *) ptr->data;
420
421 if (hurt->expire <= rb_current_time())
422 {
423 rb_dlinkFindDestroy(hurt, &hurt_confs);
424 hurt_destroy(hurt);
425 }
426 }
427 }
428 /* }}} */
429
430 /*
431 * Hook functions.
432 */
433
434 /* {{{ static void client_exit_hook() */
435 static void
436 client_exit_hook(hook_data_client_exit *data)
437 {
438 s_assert(data != NULL);
439 s_assert(data->target != NULL);
440
441 rb_dlinkFindDestroy(data->target, &hurt_state.hurt_clients);
442 }
443 /* }}} */
444
445 /* {{{ static void new_local_user_hook() */
446 static void
447 new_local_user_hook(struct Client *source_p)
448 {
449 if (IsAnyDead(source_p) || !EmptyString(source_p->user->suser) ||
450 IsExemptKline(source_p))
451 return;
452
453 if (hurt_find(source_p->sockhost) || hurt_find(source_p->orighost))
454 {
455 source_p->localClient->target_last = rb_current_time() + 600; /* don't ask --nenolod */
456 SetTGChange(source_p);
457 rb_dlinkAddAlloc(source_p, &hurt_state.hurt_clients);
458 sendto_one_notice(source_p, ":You are hurt. Please identify to services immediately, or use /stats p for assistance.");
459 }
460 }
461 /* }}} */
462
463 /* {{{ static void doing_stats_hook() */
464 static void
465 doing_stats_hook(hook_data_int *hdata)
466 {
467 rb_dlink_node *ptr;
468 hurt_t *hurt;
469 struct Client *source_p;
470
471 s_assert(hdata);
472 s_assert(hdata->client);
473
474 source_p = hdata->client;
475 if(hdata->arg2 != (int) 's')
476 return;
477 if((ConfigFileEntry.stats_k_oper_only == 2) && !IsOper(source_p))
478 return;
479 if ((ConfigFileEntry.stats_k_oper_only == 1) && !IsOper(source_p))
480 {
481 hurt = hurt_find(source_p->sockhost);
482 if (hurt != NULL)
483 {
484 sendto_one_numeric(source_p, RPL_STATSKLINE,
485 form_str(RPL_STATSKLINE), 's',
486 "*", hurt->ip, hurt->reason, "", "");
487 return;
488 }
489
490 hurt = hurt_find(source_p->orighost);
491 if (hurt != NULL)
492 {
493 sendto_one_numeric(source_p, RPL_STATSKLINE,
494 form_str(RPL_STATSKLINE), 's',
495 "*", hurt->ip, hurt->reason, "", "");
496 return;
497 }
498 return;
499 }
500
501 RB_DLINK_FOREACH(ptr, hurt_confs.head)
502 {
503 hurt = (hurt_t *) ptr->data;
504 sendto_one_numeric(source_p, RPL_STATSKLINE,
505 form_str(RPL_STATSKLINE), 's',
506 "*", hurt->ip, hurt->reason, "", "");
507 }
508 }
509 /* }}} */
510
511 /* {{{ static void hurt_propagate()
512 *
513 * client_p - specific server to propagate HURT to, or NULL to propagate to all
514 * servers.
515 * source_p - source (oper who added the HURT)
516 * hurt - HURT to be propagated
517 */
518 static void
519 hurt_propagate(struct Client *client_p, struct Client *source_p, hurt_t *hurt)
520 {
521 if (client_p)
522 sendto_one(client_p,
523 ":%s ENCAP %s HURT %ld %s :%s",
524 source_p->name, client_p->name,
525 (long)(hurt->expire - rb_current_time()),
526 hurt->ip, hurt->reason);
527 else
528 sendto_server(&me, NULL, NOCAPS, NOCAPS,
529 ":%s ENCAP * HURT %ld %s :%s",
530 source_p->name,
531 (long)(hurt->expire - rb_current_time()),
532 hurt->ip, hurt->reason);
533 }
534 /* }}} */
535
536 /* {{{ static hurt_t *hurt_new() */
537 static hurt_t *
538 hurt_new(time_t expire, const char *ip, const char *reason)
539 {
540 hurt_t *hurt;
541
542 hurt = rb_malloc(sizeof(hurt_t));
543
544 hurt->ip = rb_strdup(ip);
545 hurt->reason = rb_strdup(reason);
546 hurt->expire = rb_current_time() + expire;
547
548 return hurt;
549 }
550 /* }}} */
551
552 /* {{{ static void hurt_destroy() */
553 static void
554 hurt_destroy(void *hurt)
555 {
556 hurt_t *h;
557
558 if (!hurt)
559 return;
560
561 h = (hurt_t *) hurt;
562 rb_free(h->ip);
563 rb_free(h->reason);
564 rb_free(h);
565 }
566 /* }}} */
567
568 static void
569 hurt_add(hurt_t *hurt)
570 {
571 rb_dlinkAddAlloc(hurt, &hurt_confs);
572 }
573
574 static hurt_t *
575 hurt_find_exact(const char *ip)
576 {
577 rb_dlink_node *ptr;
578 hurt_t *hurt;
579
580 RB_DLINK_FOREACH(ptr, hurt_confs.head)
581 {
582 hurt = (hurt_t *) ptr->data;
583
584 if (!strcasecmp(ip, hurt->ip))
585 return hurt;
586 }
587
588 return NULL;
589 }
590
591 static hurt_t *
592 hurt_find(const char *ip)
593 {
594 rb_dlink_node *ptr;
595 hurt_t *hurt;
596
597 RB_DLINK_FOREACH(ptr, hurt_confs.head)
598 {
599 hurt = (hurt_t *) ptr->data;
600
601 if (match(hurt->ip, ip))
602 return hurt;
603 }
604
605 return NULL;
606 }
607
608 static void
609 hurt_remove(const char *ip)
610 {
611 hurt_t *hurt = hurt_find_exact(ip);
612
613 rb_dlinkFindDestroy(hurt, &hurt_confs);
614 hurt_destroy(hurt);
615 }
616
617 /* {{{ static int heal_nick() */
618 static int
619 heal_nick(struct Client *source_p, struct Client *target_p)
620 {
621 if (rb_dlinkFindDestroy(target_p, &hurt_state.hurt_clients))
622 {
623 sendto_realops_snomask(SNO_GENERAL, L_ALL, "%s used HEAL on %s",
624 get_oper_name(source_p), get_client_name(target_p, HIDE_IP));
625 sendto_one_notice(target_p, ":HURT restriction temporarily removed by operator");
626 sendto_one_notice(source_p, ":HURT restriction on %s temporarily removed", target_p->name);
627 target_p->localClient->target_last = rb_current_time(); /* don't ask --nenolod */
628 return 1;
629 }
630 else
631 {
632 sendto_one_notice(source_p, ":%s was not hurt", target_p->name);
633 return 0;
634 }
635 }
636 /* }}} */
637
638 /*
639 * Anything else...
640 */
641
642 /* {{{ static int nick_is_valid() */
643 static int
644 nick_is_valid(const char *nick)
645 {
646 const char *s = nick;
647
648 for (; *s != '\0'; s++) {
649 if (!IsNickChar(*s))
650 return 0;
651 }
652
653 return 1;
654 }
655 /* }}} */
656
657 /*
658 * vim: ts=8 sw=8 noet fdm=marker tw=80
659 */