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