2 * charybdis: an advanced Internet Relay Chat Daemon(ircd).
4 * Copyright (C) 2006 charybdis development team
7 * $Id: hurt.c 3161 2007-01-25 07:23:01Z nenolod $
18 #include "s_newconf.h"
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"
33 typedef struct _hurt_state
{
36 rb_dlink_list hurt_clients
;
38 time_t default_expire
;
39 const char *exit_reason
;
42 typedef struct _hurt
{
44 struct sockaddr
*saddr
;
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 **);
57 static int modinit(void);
58 static void modfini(void);
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
);
64 static void hurt_check_event(void *);
65 static void hurt_expire_event(void *);
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
);
75 static int heal_nick(struct Client
*, struct Client
*);
77 static int nick_is_valid(const char *);
80 /* {{{ State containers */
82 rb_dlink_list hurt_confs
= { NULL
, NULL
, 0 };
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}
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}
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
},
110 mapi_clist_av1 hurt_clist
[] = { &hurt_msgtab
, &heal_msgtab
, NULL
};
123 hurt_state_t hurt_state
= {
124 .cutoff
= HURT_CUTOFF
,
125 .default_expire
= HURT_DEFAULT_EXPIRE
,
126 .exit_reason
= HURT_EXIT_REASON
,
130 * Module constructor/destructor.
133 /* {{{ static int modinit() */
135 struct ev_entry
*hurt_expire_ev
= NULL
;
136 struct ev_entry
*hurt_check_ev
= NULL
;
141 /* set-up hurt_state. */
142 hurt_state
.start_time
= rb_current_time();
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);
152 /* {{{ static void modfini() */
156 rb_dlink_node
*ptr
, *next_ptr
;
158 /* and delete our events. */
159 rb_event_delete(hurt_expire_ev
);
160 rb_event_delete(hurt_check_ev
);
162 RB_DLINK_FOREACH_SAFE (ptr
, next_ptr
, hurt_state
.hurt_clients
.head
)
164 rb_dlinkDestroy(ptr
, &hurt_state
.hurt_clients
);
173 /* {{{ static int mo_hurt()
175 * HURT [<expire>] <ip> <reason>
177 * parv[1] - expire or ip
178 * parv[2] - ip or reason
179 * parv[3] - reason or NULL
182 mo_hurt(struct Client
*client_p
, struct Client
*source_p
,
183 int parc
, const char **parv
)
185 const char *ip
, *expire
, *reason
;
188 struct Client
*target_p
;
190 if (!IsOperK(source_p
)) {
191 sendto_one(source_p
, form_str(ERR_NOPRIVS
), me
.name
,
192 source_p
->name
, "kline");
197 expire
= NULL
, ip
= parv
[1], reason
= parv
[2];
199 expire
= parv
[1], ip
= parv
[2], reason
= parv
[3];
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");
207 if (EmptyString(reason
)) {
208 sendto_one_notice(source_p
, ":Empty HURT reasons are bad for business");
212 /* Is this a client? */
213 if (strchr(ip
, '.') == NULL
&& strchr(ip
, ':') == NULL
)
215 target_p
= find_named_person(ip
);
216 if (target_p
== NULL
)
218 sendto_one_numeric(source_p
, ERR_NOSUCHNICK
,
219 form_str(ERR_NOSUCHNICK
), ip
);
222 ip
= target_p
->orighost
;
226 if (!strncmp(ip
, "*@", 2))
228 if (strchr(ip
, '!') || strchr(ip
, '@'))
230 sendto_one_notice(source_p
, ":Invalid HURT mask [%s]",
236 if (hurt_find(ip
) != NULL
) {
237 sendto_one(source_p
, ":[%s] already HURT", ip
);
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.
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
);
249 hurt
= hurt_new(expire_time
, ip
, reason
);
251 hurt_propagate(NULL
, source_p
, hurt
);
257 /* {{{ static int me_hurt()
259 * [ENCAP mask] HURT <target> <expire> <ip> <reason>
266 me_hurt(struct Client
*client_p
, struct Client
*source_p
,
267 int parc
, const char **parv
)
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.
277 if (parc
< 4 || !IsPerson(source_p
))
279 if ((expire_time
= atoi(parv
[1])) < 1)
281 if (hurt_find(parv
[2]) != NULL
)
283 if (EmptyString(parv
[3]))
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]);
296 /* {{{ static int mo_heal()
300 * parv[1] - nick or ip
303 mo_heal(struct Client
*client_p
, struct Client
*source_p
,
304 int parc
, const char **parv
)
306 struct Client
*target_p
;
308 if (!IsOperUnkline(source_p
))
310 sendto_one(source_p
, form_str(ERR_NOPRIVS
),
311 me
.name
, source_p
->name
, "unkline");
315 if (nick_is_valid(parv
[1]))
317 target_p
= find_named_person(parv
[1]);
318 if (target_p
== NULL
)
320 sendto_one_numeric(source_p
, ERR_NOSUCHNICK
,
321 form_str(ERR_NOSUCHNICK
), parv
[1]);
324 if (MyConnect(target_p
))
325 heal_nick(source_p
, target_p
);
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
));
332 else if (strchr(parv
[1], '.'))
334 if (hurt_find_exact(parv
[1]) == NULL
)
336 sendto_one_notice(source_p
, ":Mask [%s] is not HURT", parv
[1]);
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]);
347 sendto_one(source_p
, ":[%s] is not a valid IP address/nick", parv
[1]);
356 me_heal(struct Client
*client_p
, struct Client
*source_p
,
357 int parc
, const char **parv
)
359 struct Client
*target_p
;
361 /* as noted in me_hurt(), if we don't get sufficient arguments...
362 * *poof*, it's dropped...
367 if (nick_is_valid(parv
[1]))
369 target_p
= find_person(parv
[1]);
370 if (target_p
!= NULL
&& MyConnect(target_p
))
371 heal_nick(source_p
, target_p
);
373 else if (strchr(parv
[1], '.')) /* host or mask to remove ban for */
375 if (hurt_find_exact(parv
[1]) == NULL
)
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]);
392 /* {{{ static void hurt_check_event() */
394 hurt_check_event(void *arg
)
396 rb_dlink_node
*ptr
, *next_ptr
;
397 struct Client
*client_p
;
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
))
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 */
407 else if (client_p
->localClient
->receiveM
> hurt_state
.cutoff
)
408 exit_client(NULL
, client_p
, &me
, hurt_state
.exit_reason
);
413 /* {{{ static void hurt_expire_event() */
415 hurt_expire_event(void *unused
)
417 rb_dlink_node
*ptr
, *next_ptr
;
420 RB_DLINK_FOREACH_SAFE (ptr
, next_ptr
, hurt_confs
.head
)
422 hurt
= (hurt_t
*) ptr
->data
;
424 if (hurt
->expire
<= rb_current_time())
426 rb_dlinkFindDestroy(hurt
, &hurt_confs
);
437 /* {{{ static void client_exit_hook() */
439 client_exit_hook(hook_data_client_exit
*data
)
441 s_assert(data
!= NULL
);
442 s_assert(data
->target
!= NULL
);
444 rb_dlinkFindDestroy(data
->target
, &hurt_state
.hurt_clients
);
448 /* {{{ static void new_local_user_hook() */
450 new_local_user_hook(struct Client
*source_p
)
452 if (IsAnyDead(source_p
) || !EmptyString(source_p
->user
->suser
) ||
453 IsExemptKline(source_p
))
456 if (hurt_find(source_p
->sockhost
) || hurt_find(source_p
->orighost
))
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.");
466 /* {{{ static void doing_stats_hook() */
468 doing_stats_hook(hook_data_int
*hdata
)
472 struct Client
*source_p
;
475 s_assert(hdata
->client
);
477 source_p
= hdata
->client
;
478 if(hdata
->arg2
!= (int) 's')
480 if((ConfigFileEntry
.stats_k_oper_only
== 2) && !IsOper(source_p
))
482 if ((ConfigFileEntry
.stats_k_oper_only
== 1) && !IsOper(source_p
))
484 hurt
= hurt_find(source_p
->sockhost
);
487 sendto_one_numeric(source_p
, RPL_STATSKLINE
,
488 form_str(RPL_STATSKLINE
), 's',
489 "*", hurt
->ip
, hurt
->reason
, "", "");
493 hurt
= hurt_find(source_p
->orighost
);
496 sendto_one_numeric(source_p
, RPL_STATSKLINE
,
497 form_str(RPL_STATSKLINE
), 's',
498 "*", hurt
->ip
, hurt
->reason
, "", "");
504 RB_DLINK_FOREACH(ptr
, hurt_confs
.head
)
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
, "", "");
514 /* {{{ static void hurt_propagate()
516 * client_p - specific server to propagate HURT to, or NULL to propagate to all
518 * source_p - source (oper who added the HURT)
519 * hurt - HURT to be propagated
522 hurt_propagate(struct Client
*client_p
, struct Client
*source_p
, hurt_t
*hurt
)
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
);
531 sendto_server(&me
, NULL
, NOCAPS
, NOCAPS
,
532 ":%s ENCAP * HURT %ld %s :%s",
534 (long)(hurt
->expire
- rb_current_time()),
535 hurt
->ip
, hurt
->reason
);
539 /* {{{ static hurt_t *hurt_new() */
541 hurt_new(time_t expire
, const char *ip
, const char *reason
)
545 hurt
= rb_malloc(sizeof(hurt_t
));
547 hurt
->ip
= rb_strdup(ip
);
548 hurt
->reason
= rb_strdup(reason
);
549 hurt
->expire
= rb_current_time() + expire
;
555 /* {{{ static void hurt_destroy() */
557 hurt_destroy(void *hurt
)
572 hurt_add(hurt_t
*hurt
)
574 rb_dlinkAddAlloc(hurt
, &hurt_confs
);
578 hurt_find_exact(const char *ip
)
583 RB_DLINK_FOREACH(ptr
, hurt_confs
.head
)
585 hurt
= (hurt_t
*) ptr
->data
;
587 if (!strcasecmp(ip
, hurt
->ip
))
595 hurt_find(const char *ip
)
600 RB_DLINK_FOREACH(ptr
, hurt_confs
.head
)
602 hurt
= (hurt_t
*) ptr
->data
;
604 if (match(hurt
->ip
, ip
))
612 hurt_remove(const char *ip
)
614 hurt_t
*hurt
= hurt_find_exact(ip
);
616 rb_dlinkFindDestroy(hurt
, &hurt_confs
);
620 /* {{{ static int heal_nick() */
622 heal_nick(struct Client
*source_p
, struct Client
*target_p
)
624 if (rb_dlinkFindDestroy(target_p
, &hurt_state
.hurt_clients
))
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 */
635 sendto_one_notice(source_p
, ":%s was not hurt", target_p
->name
);
645 /* {{{ static int nick_is_valid() */
647 nick_is_valid(const char *nick
)
649 const char *s
= nick
;
651 for (; *s
!= '\0'; s
++) {
661 * vim: ts=8 sw=8 noet fdm=marker tw=80