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