]> jfr.im git - irc/rqf/shadowircd.git/blame - extensions/hurt.c
[svn] Backport from early 3.x:
[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"
17#include "event.h"
18#include "s_conf.h"
19#include "s_newconf.h"
20#include "hash.h"
21
22/* {{{ Structures */
23#define HURT_CUTOFF (10) /* protocol messages. */
24#define HURT_DEFAULT_EXPIRE (7 * 24 * 60) /* minutes. */
25#define HURT_EXIT_REASON "Hurt: Failed to identify to services"
26
27enum {
28 HEAL_NICK = 0,
29 HEAL_IP
30};
31
32typedef struct _hurt_state {
33 time_t start_time;
34 uint32_t n_hurts;
35 dlink_list hurt_clients;
36 uint16_t cutoff;
37 time_t default_expire;
38 const char *exit_reason;
39} hurt_state_t;
40
41typedef struct _hurt {
42 const char *ip;
43 struct sockaddr *saddr;
44 int saddr_bits;
45 const char *reason;
46 time_t expire;
47} hurt_t;
48/* }}} */
49
50/* {{{ Prototypes */
51static int mo_hurt(struct Client *, struct Client *, int, const char **);
52static int me_hurt(struct Client *, struct Client *, int, const char **);
53static int mo_heal(struct Client *, struct Client *, int, const char **);
54static int me_heal(struct Client *, struct Client *, int, const char **);
55
56static int modinit(void);
57static void modfini(void);
58
59static void client_exit_hook(hook_data_client_exit *);
60static void new_local_user_hook(struct Client *);
61static void doing_stats_hook(hook_data_int *hdata);
62
63static void hurt_check_event(void *);
64static void hurt_expire_event(void *);
65
66static hurt_t *hurt_new(time_t, const char *, const char *);
67static void hurt_add(hurt_t *);
68static void hurt_propagate(struct Client *, struct Client *, hurt_t *);
69static hurt_t *hurt_find(const char *ip);
70static hurt_t *hurt_find_exact(const char *ip);
71static void hurt_remove(const char *ip);
72static void hurt_destroy(void *hurt);
73
74static int heal_nick(struct Client *, struct Client *);
75
76static int nick_is_valid(const char *);
77/* }}} */
78
79/* {{{ State containers */
80
81dlink_list hurt_confs = { NULL, NULL, 0 };
82
83/* }}} */
84
85/* {{{ Messages */
86struct Message hurt_msgtab = {
87 "HURT", 0, 0, 0, MFLG_SLOW, {
88 mg_ignore, mg_ignore, mg_ignore,
89 mg_ignore, {me_hurt, 0}, {mo_hurt, 3}
90 }
91};
92
93struct Message heal_msgtab = {
94 "HEAL", 0, 0, 0, MFLG_SLOW, {
95 mg_ignore, mg_ignore, mg_ignore,
96 mg_ignore, {me_heal, 0}, {mo_heal, 2}
97 }
98};
99/* }}} */
100
101/* {{{ Misc module stuff */
102mapi_hfn_list_av1 hurt_hfnlist[] = {
103 {"client_exit", (hookfn) client_exit_hook},
104 {"new_local_user", (hookfn) new_local_user_hook},
105 {"doing_stats", (hookfn) doing_stats_hook},
106 {NULL, NULL},
107};
108
109mapi_clist_av1 hurt_clist[] = { &hurt_msgtab, &heal_msgtab, NULL };
110
111DECLARE_MODULE_AV1(
112 hurt,
113 modinit,
114 modfini,
115 hurt_clist,
116 NULL,
117 hurt_hfnlist,
5366977b 118 "$Revision: 3161 $"
212380e3 119);
120/* }}} */
121
122hurt_state_t hurt_state = {
123 .cutoff = HURT_CUTOFF,
124 .default_expire = HURT_DEFAULT_EXPIRE,
125 .exit_reason = HURT_EXIT_REASON,
126};
127
128/*
129 * Module constructor/destructor.
130 */
131
132/* {{{ static int modinit() */
133static int
134modinit(void)
135{
136 /* set-up hurt_state. */
137 hurt_state.start_time = CurrentTime;
138
139 /* add our event handlers. */
140 eventAdd("hurt_expire", hurt_expire_event, NULL, 60);
141 eventAdd("hurt_check", hurt_check_event, NULL, 5);
142
143 return 0;
144}
145/* }}} */
146
147/* {{{ static void modfini() */
148static void
149modfini(void)
150{
151 dlink_node *ptr, *next_ptr;
152
153 /* and delete our events. */
154 eventDelete(hurt_expire_event, NULL);
155 eventDelete(hurt_check_event, NULL);
156
157 DLINK_FOREACH_SAFE (ptr, next_ptr, hurt_state.hurt_clients.head)
158 {
159 dlinkDestroy(ptr, &hurt_state.hurt_clients);
160 }
161}
162/* }}} */
163
164/*
165 * Message handlers.
166 */
167
168/* {{{ static int mo_hurt()
169 *
170 * HURT [<expire>] <ip> <reason>
171 *
172 * parv[1] - expire or ip
173 * parv[2] - ip or reason
174 * parv[3] - reason or NULL
175 */
176static int
177mo_hurt(struct Client *client_p, struct Client *source_p,
178 int parc, const char **parv)
179{
180 const char *ip, *expire, *reason;
181 int expire_time;
182 hurt_t *hurt;
183 struct Client *target_p;
184
185 if (!IsOperK(source_p)) {
186 sendto_one(source_p, form_str(ERR_NOPRIVS), me.name,
187 source_p->name, "kline");
188 return 0;
189 }
190
191 if (parc == 3)
192 expire = NULL, ip = parv[1], reason = parv[2];
193 else
194 expire = parv[1], ip = parv[2], reason = parv[3];
195
196 if (!expire)
197 expire_time = HURT_DEFAULT_EXPIRE;
198 if (expire && (expire_time = valid_temp_time(expire)) < 1) {
5366977b 199 sendto_one_notice(source_p, ":Permanent HURTs are not supported");
212380e3 200 return 0;
201 }
202 if (EmptyString(reason)) {
5366977b 203 sendto_one_notice(source_p, ":Empty HURT reasons are bad for business");
212380e3 204 return 0;
205 }
206
207 /* Is this a client? */
208 if (strchr(ip, '.') == NULL && strchr(ip, ':') == NULL)
209 {
210 target_p = find_named_person(ip);
211 if (target_p == NULL)
212 {
213 sendto_one_numeric(source_p, ERR_NOSUCHNICK,
214 form_str(ERR_NOSUCHNICK), ip);
215 return 0;
216 }
217 ip = target_p->orighost;
218 }
219 else
220 {
221 if (!strncmp(ip, "*@", 2))
222 ip += 2;
223 if (strchr(ip, '!') || strchr(ip, '@'))
224 {
225 sendto_one_notice(source_p, ":Invalid HURT mask [%s]",
226 ip);
227 return 0;
228 }
229 }
230
231 if (hurt_find(ip) != NULL) {
5366977b 232 sendto_one(source_p, ":[%s] already HURT", ip);
212380e3 233 return 0;
234 }
235
236 /*
237 * okay, we've got this far, now it's time to add the the HURT locally
238 * and propagate it to other servers on the network.
239 */
240 sendto_realops_snomask(SNO_GENERAL, L_ALL,
241 "%s added HURT on [%s] for %ld minutes with reason [%s]",
242 get_oper_name(source_p), ip, (long) expire_time / 60, reason);
243
244 hurt = hurt_new(expire_time, ip, reason);
245 hurt_add(hurt);
246 hurt_propagate(NULL, source_p, hurt);
247
248 return 0;
249}
250/* }}} */
251
252/* {{{ static int me_hurt()
253 *
254 * [ENCAP mask] HURT <target> <expire> <ip> <reason>
255 *
256 * parv[1] - expire
257 * parv[2] - ip
258 * parv[3] - reason
259 */
260static int
261me_hurt(struct Client *client_p, struct Client *source_p,
262 int parc, const char **parv)
263{
264 time_t expire_time;
265 hurt_t *hurt;
266
267 /*
268 * right... if we don't get enough arguments, or if we get any invalid
269 * arguments, just ignore this request - shit happens, and it's not worth
270 * dropping a server over.
271 */
272 if (parc < 4 || !IsPerson(source_p))
273 return 0;
274 if ((expire_time = atoi(parv[1])) < 1)
275 return 0;
276 if (hurt_find(parv[2]) != NULL)
277 return 0;
278 if (EmptyString(parv[3]))
279 return 0;
280
281 sendto_realops_snomask(SNO_GENERAL, L_ALL,
282 "%s added HURT on [%s] for %ld minutes with reason [%s]",
283 get_oper_name(source_p), parv[2], (long) expire_time / 60, parv[3]);
284 hurt = hurt_new(expire_time, parv[2], parv[3]);
285 hurt_add(hurt);
286
287 return 0;
288}
289/* }}} */
290
291/* {{{ static int mo_heal()
292 *
293 * HURT <nick>|<ip>
294 *
295 * parv[1] - nick or ip
296 */
297static int
298mo_heal(struct Client *client_p, struct Client *source_p,
299 int parc, const char **parv)
300{
301 struct Client *target_p;
302
303 if (!IsOperUnkline(source_p))
304 {
305 sendto_one(source_p, form_str(ERR_NOPRIVS),
306 me.name, source_p->name, "unkline");
307 return 0;
308 }
309
310 if (nick_is_valid(parv[1]))
311 {
312 target_p = find_named_person(parv[1]);
313 if (target_p == NULL)
314 {
315 sendto_one_numeric(source_p, ERR_NOSUCHNICK,
316 form_str(ERR_NOSUCHNICK), parv[1]);
317 return 0;
318 }
319 if (MyConnect(target_p))
320 heal_nick(source_p, target_p);
321 else
322 sendto_one(target_p, ":%s ENCAP %s HEAL %s",
323 get_id(source_p, target_p),
324 target_p->servptr->name,
325 get_id(target_p, target_p));
326 }
327 else if (strchr(parv[1], '.'))
328 {
329 if (hurt_find_exact(parv[1]) == NULL)
330 {
5366977b 331 sendto_one_notice(source_p, ":Mask [%s] is not HURT", parv[1]);
212380e3 332 return 0;
333 }
334 hurt_remove(parv[1]);
335 sendto_realops_snomask(SNO_GENERAL, L_ALL, "%s removed HURT on %s",
336 get_oper_name(source_p), parv[1]);
337 sendto_server(NULL, NULL, NOCAPS, NOCAPS, ":%s ENCAP * HEAL %s",
338 source_p->name, parv[1]);
339 }
340 else
341 {
5366977b 342 sendto_one(source_p, ":[%s] is not a valid IP address/nick", parv[1]);
212380e3 343 return 0;
344 }
345
346 return 0;
347}
348/* }}} */
349
350static int
351me_heal(struct Client *client_p, struct Client *source_p,
352 int parc, const char **parv)
353{
354 struct Client *target_p;
355
356 /* as noted in me_hurt(), if we don't get sufficient arguments...
357 * *poof*, it's dropped...
358 */
359 if (parc < 2)
360 return 0;
361
362 if (nick_is_valid(parv[1]))
363 {
364 target_p = find_person(parv[1]);
365 if (target_p != NULL && MyConnect(target_p))
366 heal_nick(source_p, target_p);
367 }
368 else if (strchr(parv[1], '.')) /* host or mask to remove ban for */
369 {
370 if (hurt_find_exact(parv[1]) == NULL)
371 return 0;
372
373 hurt_remove(parv[1]);
374 sendto_realops_snomask(SNO_GENERAL, L_ALL, "%s removed HURT on %s",
375 get_oper_name(source_p), parv[1]);
376 }
377 else
378 return 0;
379
380 return 0;
381}
382
383/*
384 * Event handlers.
385 */
386
387/* {{{ static void hurt_check_event() */
388static void
389hurt_check_event(void *arg)
390{
391 dlink_node *ptr, *next_ptr;
392 struct Client *client_p;
393
394 DLINK_FOREACH_SAFE (ptr, next_ptr, hurt_state.hurt_clients.head) {
395 client_p = ptr->data;
396 if (!EmptyString(client_p->user->suser))
397 {
398 dlinkDestroy(ptr, &hurt_state.hurt_clients);
399 sendto_one_notice(client_p, ":HURT restriction removed for this session");
400 USED_TARGETS(client_p) = 0;
401 client_p->localClient->target_last = CurrentTime; /* don't ask --nenolod */
402 }
403 else if (client_p->localClient->receiveM > hurt_state.cutoff)
404 exit_client(NULL, client_p, &me, hurt_state.exit_reason);
405 }
406}
407/* }}} */
408
409/* {{{ static void hurt_expire_event() */
410static void
411hurt_expire_event(void *unused)
412{
413 dlink_node *ptr, *next_ptr;
414 hurt_t *hurt;
415
416 DLINK_FOREACH_SAFE (ptr, next_ptr, hurt_confs.head)
417 {
418 hurt = (hurt_t *) ptr->data;
419
420 if (hurt->expire <= CurrentTime)
421 {
422 dlinkFindDestroy(hurt, &hurt_confs);
423 hurt_destroy(hurt);
424 }
425 }
426}
427/* }}} */
428
429/*
430 * Hook functions.
431 */
432
433/* {{{ static void client_exit_hook() */
434static void
435client_exit_hook(hook_data_client_exit *data)
436{
437 s_assert(data != NULL);
438 s_assert(data->target != NULL);
439
440 dlinkFindDestroy(data->target, &hurt_state.hurt_clients);
441}
442/* }}} */
443
444/* {{{ static void new_local_user_hook() */
445static void
446new_local_user_hook(struct Client *source_p)
447{
448 if (IsAnyDead(source_p) || !EmptyString(source_p->user->suser) ||
449 IsExemptKline(source_p))
450 return;
451
452 if (hurt_find(source_p->sockhost) || hurt_find(source_p->orighost))
453 {
454 USED_TARGETS(source_p) = 10;
455 source_p->localClient->target_last = CurrentTime + 600; /* don't ask --nenolod */
456 SetTGChange(source_p);
457 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() */
464static void
465doing_stats_hook(hook_data_int *hdata)
466{
467 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 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 */
518static void
519hurt_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 - CurrentTime),
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 - CurrentTime),
532 hurt->ip, hurt->reason);
533}
534/* }}} */
535
536/* {{{ static hurt_t *hurt_new() */
537static hurt_t *
538hurt_new(time_t expire, const char *ip, const char *reason)
539{
540 hurt_t *hurt;
541
542 hurt = MyMalloc(sizeof(hurt_t));
543
544 DupString(hurt->ip, ip);
545 DupString(hurt->reason, reason);
546 hurt->expire = CurrentTime + expire;
547
548 return hurt;
549}
550/* }}} */
551
552/* {{{ static void hurt_destroy() */
553static void
554hurt_destroy(void *hurt)
555{
556 hurt_t *h;
557
558 if (!hurt)
559 return;
560
561 h = (hurt_t *) hurt;
562 MyFree((char *) h->ip);
563 MyFree((char *) h->reason);
564 MyFree(h);
565}
566/* }}} */
567
568static void
569hurt_add(hurt_t *hurt)
570{
571 dlinkAddAlloc(hurt, &hurt_confs);
572}
573
574static hurt_t *
575hurt_find_exact(const char *ip)
576{
577 dlink_node *ptr;
578 hurt_t *hurt;
579
580 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
591static hurt_t *
592hurt_find(const char *ip)
593{
594 dlink_node *ptr;
595 hurt_t *hurt;
596
597 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
608static void
609hurt_remove(const char *ip)
610{
611 hurt_t *hurt = hurt_find_exact(ip);
612
613 dlinkFindDestroy(hurt, &hurt_confs);
614 hurt_destroy(hurt);
615}
616
617/* {{{ static int heal_nick() */
618static int
619heal_nick(struct Client *source_p, struct Client *target_p)
620{
621 if (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 USED_TARGETS(target_p) = 0;
628 target_p->localClient->target_last = CurrentTime; /* don't ask --nenolod */
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 */