2 * charybdis: an advanced Internet Relay Chat Daemon(ircd).
4 * Copyright (C) 2006 charybdis development team
7 * $Id: hurt.c 1905 2006-08-29 14:51:31Z jilles $
19 #include "s_newconf.h"
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"
32 typedef struct _hurt_state
{
35 dlink_list hurt_clients
;
37 time_t default_expire
;
38 const char *exit_reason
;
41 typedef struct _hurt
{
43 struct sockaddr
*saddr
;
51 static int mo_hurt(struct Client
*, struct Client
*, int, const char **);
52 static int me_hurt(struct Client
*, struct Client
*, int, const char **);
53 static int mo_heal(struct Client
*, struct Client
*, int, const char **);
54 static int me_heal(struct Client
*, struct Client
*, int, const char **);
56 static int modinit(void);
57 static void modfini(void);
59 static void client_exit_hook(hook_data_client_exit
*);
60 static void new_local_user_hook(struct Client
*);
61 static void doing_stats_hook(hook_data_int
*hdata
);
63 static void hurt_check_event(void *);
64 static void hurt_expire_event(void *);
66 static hurt_t
*hurt_new(time_t, const char *, const char *);
67 static void hurt_add(hurt_t
*);
68 static void hurt_propagate(struct Client
*, struct Client
*, hurt_t
*);
69 static hurt_t
*hurt_find(const char *ip
);
70 static hurt_t
*hurt_find_exact(const char *ip
);
71 static void hurt_remove(const char *ip
);
72 static void hurt_destroy(void *hurt
);
74 static int heal_nick(struct Client
*, struct Client
*);
76 static int nick_is_valid(const char *);
79 /* {{{ State containers */
81 dlink_list hurt_confs
= { NULL
, NULL
, 0 };
86 struct 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}
93 struct 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}
101 /* {{{ Misc module stuff */
102 mapi_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
},
109 mapi_clist_av1 hurt_clist
[] = { &hurt_msgtab
, &heal_msgtab
, NULL
};
122 hurt_state_t hurt_state
= {
123 .cutoff
= HURT_CUTOFF
,
124 .default_expire
= HURT_DEFAULT_EXPIRE
,
125 .exit_reason
= HURT_EXIT_REASON
,
129 * Module constructor/destructor.
132 /* {{{ static int modinit() */
136 /* set-up hurt_state. */
137 hurt_state
.start_time
= CurrentTime
;
139 /* add our event handlers. */
140 eventAdd("hurt_expire", hurt_expire_event
, NULL
, 60);
141 eventAdd("hurt_check", hurt_check_event
, NULL
, 5);
147 /* {{{ static void modfini() */
151 dlink_node
*ptr
, *next_ptr
;
153 /* and delete our events. */
154 eventDelete(hurt_expire_event
, NULL
);
155 eventDelete(hurt_check_event
, NULL
);
157 DLINK_FOREACH_SAFE (ptr
, next_ptr
, hurt_state
.hurt_clients
.head
)
159 dlinkDestroy(ptr
, &hurt_state
.hurt_clients
);
168 /* {{{ static int mo_hurt()
170 * HURT [<expire>] <ip> <reason>
172 * parv[1] - expire or ip
173 * parv[2] - ip or reason
174 * parv[3] - reason or NULL
177 mo_hurt(struct Client
*client_p
, struct Client
*source_p
,
178 int parc
, const char **parv
)
180 const char *ip
, *expire
, *reason
;
183 struct Client
*target_p
;
185 if (!IsOperK(source_p
)) {
186 sendto_one(source_p
, form_str(ERR_NOPRIVS
), me
.name
,
187 source_p
->name
, "kline");
192 expire
= NULL
, ip
= parv
[1], reason
= parv
[2];
194 expire
= parv
[1], ip
= parv
[2], reason
= parv
[3];
197 expire_time
= HURT_DEFAULT_EXPIRE
;
198 if (expire
&& (expire_time
= valid_temp_time(expire
)) < 1) {
200 ":%s NOTICE %s :Permanent HURTs are not supported",
201 me
.name
, source_p
->name
);
204 if (EmptyString(reason
)) {
206 ":%s NOTICE %s :Empty HURT reasons are bad for business",
207 me
.name
, source_p
->name
);
211 /* Is this a client? */
212 if (strchr(ip
, '.') == NULL
&& strchr(ip
, ':') == NULL
)
214 target_p
= find_named_person(ip
);
215 if (target_p
== NULL
)
217 sendto_one_numeric(source_p
, ERR_NOSUCHNICK
,
218 form_str(ERR_NOSUCHNICK
), ip
);
221 ip
= target_p
->orighost
;
225 if (!strncmp(ip
, "*@", 2))
227 if (strchr(ip
, '!') || strchr(ip
, '@'))
229 sendto_one_notice(source_p
, ":Invalid HURT mask [%s]",
235 if (hurt_find(ip
) != NULL
) {
237 ":%s NOTICE %s :[%s] already HURT",
238 me
.name
, source_p
->name
, ip
);
243 * okay, we've got this far, now it's time to add the the HURT locally
244 * and propagate it to other servers on the network.
246 sendto_realops_snomask(SNO_GENERAL
, L_ALL
,
247 "%s added HURT on [%s] for %ld minutes with reason [%s]",
248 get_oper_name(source_p
), ip
, (long) expire_time
/ 60, reason
);
250 hurt
= hurt_new(expire_time
, ip
, reason
);
252 hurt_propagate(NULL
, source_p
, hurt
);
258 /* {{{ static int me_hurt()
260 * [ENCAP mask] HURT <target> <expire> <ip> <reason>
267 me_hurt(struct Client
*client_p
, struct Client
*source_p
,
268 int parc
, const char **parv
)
274 * right... if we don't get enough arguments, or if we get any invalid
275 * arguments, just ignore this request - shit happens, and it's not worth
276 * dropping a server over.
278 if (parc
< 4 || !IsPerson(source_p
))
280 if ((expire_time
= atoi(parv
[1])) < 1)
282 if (hurt_find(parv
[2]) != NULL
)
284 if (EmptyString(parv
[3]))
287 sendto_realops_snomask(SNO_GENERAL
, L_ALL
,
288 "%s added HURT on [%s] for %ld minutes with reason [%s]",
289 get_oper_name(source_p
), parv
[2], (long) expire_time
/ 60, parv
[3]);
290 hurt
= hurt_new(expire_time
, parv
[2], parv
[3]);
297 /* {{{ static int mo_heal()
301 * parv[1] - nick or ip
304 mo_heal(struct Client
*client_p
, struct Client
*source_p
,
305 int parc
, const char **parv
)
307 struct Client
*target_p
;
309 if (!IsOperUnkline(source_p
))
311 sendto_one(source_p
, form_str(ERR_NOPRIVS
),
312 me
.name
, source_p
->name
, "unkline");
316 if (nick_is_valid(parv
[1]))
318 target_p
= find_named_person(parv
[1]);
319 if (target_p
== NULL
)
321 sendto_one_numeric(source_p
, ERR_NOSUCHNICK
,
322 form_str(ERR_NOSUCHNICK
), parv
[1]);
325 if (MyConnect(target_p
))
326 heal_nick(source_p
, target_p
);
328 sendto_one(target_p
, ":%s ENCAP %s HEAL %s",
329 get_id(source_p
, target_p
),
330 target_p
->servptr
->name
,
331 get_id(target_p
, target_p
));
333 else if (strchr(parv
[1], '.'))
335 if (hurt_find_exact(parv
[1]) == NULL
)
337 sendto_one(source_p
, ":%s NOTICE %s :Mask [%s] is not HURT",
338 me
.name
, source_p
->name
, parv
[1]);
341 hurt_remove(parv
[1]);
342 sendto_realops_snomask(SNO_GENERAL
, L_ALL
, "%s removed HURT on %s",
343 get_oper_name(source_p
), parv
[1]);
344 sendto_server(NULL
, NULL
, NOCAPS
, NOCAPS
, ":%s ENCAP * HEAL %s",
345 source_p
->name
, parv
[1]);
350 ":%s NOTICE %s :[%s] is not a valid IP address/nick",
351 me
.name
, source_p
->name
, parv
[1]);
360 me_heal(struct Client
*client_p
, struct Client
*source_p
,
361 int parc
, const char **parv
)
363 struct Client
*target_p
;
365 /* as noted in me_hurt(), if we don't get sufficient arguments...
366 * *poof*, it's dropped...
371 if (nick_is_valid(parv
[1]))
373 target_p
= find_person(parv
[1]);
374 if (target_p
!= NULL
&& MyConnect(target_p
))
375 heal_nick(source_p
, target_p
);
377 else if (strchr(parv
[1], '.')) /* host or mask to remove ban for */
379 if (hurt_find_exact(parv
[1]) == NULL
)
382 hurt_remove(parv
[1]);
383 sendto_realops_snomask(SNO_GENERAL
, L_ALL
, "%s removed HURT on %s",
384 get_oper_name(source_p
), parv
[1]);
396 /* {{{ static void hurt_check_event() */
398 hurt_check_event(void *arg
)
400 dlink_node
*ptr
, *next_ptr
;
401 struct Client
*client_p
;
403 DLINK_FOREACH_SAFE (ptr
, next_ptr
, hurt_state
.hurt_clients
.head
) {
404 client_p
= ptr
->data
;
405 if (!EmptyString(client_p
->user
->suser
))
407 dlinkDestroy(ptr
, &hurt_state
.hurt_clients
);
408 sendto_one_notice(client_p
, ":HURT restriction removed for this session");
409 USED_TARGETS(client_p
) = 0;
410 client_p
->localClient
->target_last
= CurrentTime
; /* don't ask --nenolod */
412 else if (client_p
->localClient
->receiveM
> hurt_state
.cutoff
)
413 exit_client(NULL
, client_p
, &me
, hurt_state
.exit_reason
);
418 /* {{{ static void hurt_expire_event() */
420 hurt_expire_event(void *unused
)
422 dlink_node
*ptr
, *next_ptr
;
425 DLINK_FOREACH_SAFE (ptr
, next_ptr
, hurt_confs
.head
)
427 hurt
= (hurt_t
*) ptr
->data
;
429 if (hurt
->expire
<= CurrentTime
)
431 dlinkFindDestroy(hurt
, &hurt_confs
);
442 /* {{{ static void client_exit_hook() */
444 client_exit_hook(hook_data_client_exit
*data
)
446 s_assert(data
!= NULL
);
447 s_assert(data
->target
!= NULL
);
449 dlinkFindDestroy(data
->target
, &hurt_state
.hurt_clients
);
453 /* {{{ static void new_local_user_hook() */
455 new_local_user_hook(struct Client
*source_p
)
457 if (IsAnyDead(source_p
) || !EmptyString(source_p
->user
->suser
) ||
458 IsExemptKline(source_p
))
461 if (hurt_find(source_p
->sockhost
) || hurt_find(source_p
->orighost
))
463 USED_TARGETS(source_p
) = 10;
464 source_p
->localClient
->target_last
= CurrentTime
+ 600; /* don't ask --nenolod */
465 SetTGChange(source_p
);
466 dlinkAddAlloc(source_p
, &hurt_state
.hurt_clients
);
467 sendto_one_notice(source_p
, ":You are hurt. Please identify to services immediately, or use /stats p for assistance.");
472 /* {{{ static void doing_stats_hook() */
474 doing_stats_hook(hook_data_int
*hdata
)
478 struct Client
*source_p
;
481 s_assert(hdata
->client
);
483 source_p
= hdata
->client
;
484 if(hdata
->arg2
!= (int) 's')
486 if((ConfigFileEntry
.stats_k_oper_only
== 2) && !IsOper(source_p
))
488 if ((ConfigFileEntry
.stats_k_oper_only
== 1) && !IsOper(source_p
))
490 hurt
= hurt_find(source_p
->sockhost
);
493 sendto_one_numeric(source_p
, RPL_STATSKLINE
,
494 form_str(RPL_STATSKLINE
), 's',
495 "*", hurt
->ip
, hurt
->reason
, "", "");
499 hurt
= hurt_find(source_p
->orighost
);
502 sendto_one_numeric(source_p
, RPL_STATSKLINE
,
503 form_str(RPL_STATSKLINE
), 's',
504 "*", hurt
->ip
, hurt
->reason
, "", "");
510 DLINK_FOREACH(ptr
, hurt_confs
.head
)
512 hurt
= (hurt_t
*) ptr
->data
;
513 sendto_one_numeric(source_p
, RPL_STATSKLINE
,
514 form_str(RPL_STATSKLINE
), 's',
515 "*", hurt
->ip
, hurt
->reason
, "", "");
520 /* {{{ static void hurt_propagate()
522 * client_p - specific server to propagate HURT to, or NULL to propagate to all
524 * source_p - source (oper who added the HURT)
525 * hurt - HURT to be propagated
528 hurt_propagate(struct Client
*client_p
, struct Client
*source_p
, hurt_t
*hurt
)
532 ":%s ENCAP %s HURT %ld %s :%s",
533 source_p
->name
, client_p
->name
,
534 (long)(hurt
->expire
- CurrentTime
),
535 hurt
->ip
, hurt
->reason
);
537 sendto_server(&me
, NULL
, NOCAPS
, NOCAPS
,
538 ":%s ENCAP * HURT %ld %s :%s",
540 (long)(hurt
->expire
- CurrentTime
),
541 hurt
->ip
, hurt
->reason
);
545 /* {{{ static hurt_t *hurt_new() */
547 hurt_new(time_t expire
, const char *ip
, const char *reason
)
551 hurt
= MyMalloc(sizeof(hurt_t
));
553 DupString(hurt
->ip
, ip
);
554 DupString(hurt
->reason
, reason
);
555 hurt
->expire
= CurrentTime
+ expire
;
561 /* {{{ static void hurt_destroy() */
563 hurt_destroy(void *hurt
)
571 MyFree((char *) h
->ip
);
572 MyFree((char *) h
->reason
);
578 hurt_add(hurt_t
*hurt
)
580 dlinkAddAlloc(hurt
, &hurt_confs
);
584 hurt_find_exact(const char *ip
)
589 DLINK_FOREACH(ptr
, hurt_confs
.head
)
591 hurt
= (hurt_t
*) ptr
->data
;
593 if (!strcasecmp(ip
, hurt
->ip
))
601 hurt_find(const char *ip
)
606 DLINK_FOREACH(ptr
, hurt_confs
.head
)
608 hurt
= (hurt_t
*) ptr
->data
;
610 if (match(hurt
->ip
, ip
))
618 hurt_remove(const char *ip
)
620 hurt_t
*hurt
= hurt_find_exact(ip
);
622 dlinkFindDestroy(hurt
, &hurt_confs
);
626 /* {{{ static int heal_nick() */
628 heal_nick(struct Client
*source_p
, struct Client
*target_p
)
630 if (dlinkFindDestroy(target_p
, &hurt_state
.hurt_clients
))
632 sendto_realops_snomask(SNO_GENERAL
, L_ALL
, "%s used HEAL on %s",
633 get_oper_name(source_p
), get_client_name(target_p
, HIDE_IP
));
634 sendto_one_notice(target_p
, ":HURT restriction temporarily removed by operator");
635 sendto_one_notice(source_p
, ":HURT restriction on %s temporarily removed", target_p
->name
);
636 USED_TARGETS(target_p
) = 0;
637 target_p
->localClient
->target_last
= CurrentTime
; /* don't ask --nenolod */
642 sendto_one_notice(source_p
, ":%s was not hurt", target_p
->name
);
652 /* {{{ static int nick_is_valid() */
654 nick_is_valid(const char *nick
)
656 const char *s
= nick
;
658 for (; *s
!= '\0'; s
++) {
668 * vim: ts=8 sw=8 noet fdm=marker tw=80