]> jfr.im git - irc/rqf/shadowircd.git/blame - extensions/hurt.c
Removal of ancient SVN ID's part one
[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 *
212380e3 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"
212380e3 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
25enum {
26 HEAL_NICK = 0,
27 HEAL_IP
28};
29
30typedef struct _hurt_state {
31 time_t start_time;
32 uint32_t n_hurts;
4103d13e 33 rb_dlink_list hurt_clients;
212380e3 34 uint16_t cutoff;
35 time_t default_expire;
36 const char *exit_reason;
37} hurt_state_t;
38
39typedef struct _hurt {
19fcdbd5 40 char *ip;
212380e3 41 struct sockaddr *saddr;
42 int saddr_bits;
19fcdbd5 43 char *reason;
212380e3 44 time_t expire;
45} hurt_t;
46/* }}} */
47
48/* {{{ Prototypes */
49static int mo_hurt(struct Client *, struct Client *, int, const char **);
50static int me_hurt(struct Client *, struct Client *, int, const char **);
51static int mo_heal(struct Client *, struct Client *, int, const char **);
52static int me_heal(struct Client *, struct Client *, int, const char **);
53
54static int modinit(void);
55static void modfini(void);
56
57static void client_exit_hook(hook_data_client_exit *);
58static void new_local_user_hook(struct Client *);
59static void doing_stats_hook(hook_data_int *hdata);
60
61static void hurt_check_event(void *);
62static void hurt_expire_event(void *);
63
64static hurt_t *hurt_new(time_t, const char *, const char *);
65static void hurt_add(hurt_t *);
66static void hurt_propagate(struct Client *, struct Client *, hurt_t *);
67static hurt_t *hurt_find(const char *ip);
68static hurt_t *hurt_find_exact(const char *ip);
69static void hurt_remove(const char *ip);
70static void hurt_destroy(void *hurt);
71
72static int heal_nick(struct Client *, struct Client *);
73
74static int nick_is_valid(const char *);
75/* }}} */
76
77/* {{{ State containers */
78
4103d13e 79rb_dlink_list hurt_confs = { NULL, NULL, 0 };
212380e3 80
81/* }}} */
82
83/* {{{ Messages */
84struct 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
91struct 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 */
100mapi_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
107mapi_clist_av1 hurt_clist[] = { &hurt_msgtab, &heal_msgtab, NULL };
108
109DECLARE_MODULE_AV1(
110 hurt,
111 modinit,
112 modfini,
113 hurt_clist,
114 NULL,
115 hurt_hfnlist,
5366977b 116 "$Revision: 3161 $"
212380e3 117);
118/* }}} */
119
120hurt_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() */
71f6ebfa
WP
131
132struct ev_entry *hurt_expire_ev = NULL;
133struct ev_entry *hurt_check_ev = NULL;
134
212380e3 135static int
136modinit(void)
137{
138 /* set-up hurt_state. */
c51d32ba 139 hurt_state.start_time = rb_current_time();
212380e3 140
141 /* add our event handlers. */
71f6ebfa
WP
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);
212380e3 144
145 return 0;
146}
147/* }}} */
148
149/* {{{ static void modfini() */
150static void
151modfini(void)
152{
4103d13e 153 rb_dlink_node *ptr, *next_ptr;
212380e3 154
155 /* and delete our events. */
71f6ebfa
WP
156 rb_event_delete(hurt_expire_ev);
157 rb_event_delete(hurt_check_ev);
212380e3 158
4103d13e 159 RB_DLINK_FOREACH_SAFE (ptr, next_ptr, hurt_state.hurt_clients.head)
212380e3 160 {
4103d13e 161 rb_dlinkDestroy(ptr, &hurt_state.hurt_clients);
212380e3 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 */
178static int
179mo_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) {
5366977b 201 sendto_one_notice(source_p, ":Permanent HURTs are not supported");
212380e3 202 return 0;
203 }
204 if (EmptyString(reason)) {
5366977b 205 sendto_one_notice(source_p, ":Empty HURT reasons are bad for business");
212380e3 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) {
5366977b 234 sendto_one(source_p, ":[%s] already HURT", ip);
212380e3 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 */
262static int
263me_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 */
299static int
300mo_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 {
5366977b 333 sendto_one_notice(source_p, ":Mask [%s] is not HURT", parv[1]);
212380e3 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 {
5366977b 344 sendto_one(source_p, ":[%s] is not a valid IP address/nick", parv[1]);
212380e3 345 return 0;
346 }
347
348 return 0;
349}
350/* }}} */
351
352static int
353me_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() */
390static void
391hurt_check_event(void *arg)
392{
4103d13e 393 rb_dlink_node *ptr, *next_ptr;
212380e3 394 struct Client *client_p;
395
4103d13e 396 RB_DLINK_FOREACH_SAFE (ptr, next_ptr, hurt_state.hurt_clients.head) {
212380e3 397 client_p = ptr->data;
398 if (!EmptyString(client_p->user->suser))
399 {
4103d13e 400 rb_dlinkDestroy(ptr, &hurt_state.hurt_clients);
212380e3 401 sendto_one_notice(client_p, ":HURT restriction removed for this session");
c51d32ba 402 client_p->localClient->target_last = rb_current_time(); /* don't ask --nenolod */
212380e3 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() */
411static void
412hurt_expire_event(void *unused)
413{
4103d13e 414 rb_dlink_node *ptr, *next_ptr;
212380e3 415 hurt_t *hurt;
416
4103d13e 417 RB_DLINK_FOREACH_SAFE (ptr, next_ptr, hurt_confs.head)
212380e3 418 {
419 hurt = (hurt_t *) ptr->data;
420
c51d32ba 421 if (hurt->expire <= rb_current_time())
212380e3 422 {
4103d13e 423 rb_dlinkFindDestroy(hurt, &hurt_confs);
212380e3 424 hurt_destroy(hurt);
425 }
426 }
427}
428/* }}} */
429
430/*
431 * Hook functions.
432 */
433
434/* {{{ static void client_exit_hook() */
435static void
436client_exit_hook(hook_data_client_exit *data)
437{
438 s_assert(data != NULL);
439 s_assert(data->target != NULL);
440
4103d13e 441 rb_dlinkFindDestroy(data->target, &hurt_state.hurt_clients);
212380e3 442}
443/* }}} */
444
445/* {{{ static void new_local_user_hook() */
446static void
447new_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 {
c51d32ba 455 source_p->localClient->target_last = rb_current_time() + 600; /* don't ask --nenolod */
212380e3 456 SetTGChange(source_p);
4103d13e 457 rb_dlinkAddAlloc(source_p, &hurt_state.hurt_clients);
212380e3 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{
4103d13e 467 rb_dlink_node *ptr;
212380e3 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
4103d13e 501 RB_DLINK_FOREACH(ptr, hurt_confs.head)
212380e3 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,
c51d32ba 525 (long)(hurt->expire - rb_current_time()),
212380e3 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,
c51d32ba 531 (long)(hurt->expire - rb_current_time()),
212380e3 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
1ec5bf14 542 hurt = rb_malloc(sizeof(hurt_t));
212380e3 543
1ec5bf14
VY
544 hurt->ip = rb_strdup(ip);
545 hurt->reason = rb_strdup(reason);
c51d32ba 546 hurt->expire = rb_current_time() + expire;
212380e3 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;
1ec5bf14
VY
562 rb_free(h->ip);
563 rb_free(h->reason);
564 rb_free(h);
212380e3 565}
566/* }}} */
567
568static void
569hurt_add(hurt_t *hurt)
570{
4103d13e 571 rb_dlinkAddAlloc(hurt, &hurt_confs);
212380e3 572}
573
574static hurt_t *
575hurt_find_exact(const char *ip)
576{
4103d13e 577 rb_dlink_node *ptr;
212380e3 578 hurt_t *hurt;
579
4103d13e 580 RB_DLINK_FOREACH(ptr, hurt_confs.head)
212380e3 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{
4103d13e 594 rb_dlink_node *ptr;
212380e3 595 hurt_t *hurt;
596
4103d13e 597 RB_DLINK_FOREACH(ptr, hurt_confs.head)
212380e3 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
4103d13e 613 rb_dlinkFindDestroy(hurt, &hurt_confs);
212380e3 614 hurt_destroy(hurt);
615}
616
617/* {{{ static int heal_nick() */
618static int
619heal_nick(struct Client *source_p, struct Client *target_p)
620{
4103d13e 621 if (rb_dlinkFindDestroy(target_p, &hurt_state.hurt_clients))
212380e3 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);
c51d32ba 627 target_p->localClient->target_last = rb_current_time(); /* don't ask --nenolod */
212380e3 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() */
643static int
644nick_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 */