2 * ircd-ratbox: A slightly useful ircd.
3 * m_gline.c: Votes towards globally banning a mask.
5 * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
6 * Copyright (C) 1996-2002 Hybrid Development Team
7 * Copyright (C) 2002-2005 ircd-ratbox development team
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
24 * $Id: m_gline.c 3161 2007-01-25 07:23:01Z nenolod $
34 #include "irc_string.h"
35 #include "sprintf_irc.h"
41 #include "s_newconf.h"
51 static int mo_gline(struct Client
*, struct Client
*, int, const char **);
52 static int mc_gline(struct Client
*, struct Client
*, int, const char **);
53 static int ms_gline(struct Client
*, struct Client
*, int, const char **);
54 static int mo_ungline(struct Client
*, struct Client
*, int, const char **);
56 struct Message gline_msgtab
= {
57 "GLINE", 0, 0, 0, MFLG_SLOW
,
58 {mg_unreg
, mg_not_oper
, {mc_gline
, 3}, {ms_gline
, 7}, mg_ignore
, {mo_gline
, 3}}
60 struct Message ungline_msgtab
= {
61 "UNGLINE", 0, 0, 0, MFLG_SLOW
,
62 {mg_unreg
, mg_not_oper
, mg_ignore
, mg_ignore
, mg_ignore
, {mo_ungline
, 2}}
65 mapi_clist_av1 gline_clist
[] = { &gline_msgtab
, &ungline_msgtab
, NULL
};
66 DECLARE_MODULE_AV1(gline
, NULL
, NULL
, gline_clist
, NULL
, NULL
, "$Revision: 3161 $");
68 static int majority_gline(struct Client
*source_p
, const char *user
,
69 const char *host
, const char *reason
);
70 static void set_local_gline(struct Client
*source_p
, const char *user
,
71 const char *host
, const char *reason
);
73 static int check_wild_gline(const char *, const char *);
74 static int invalid_gline(struct Client
*, const char *, const char *, char *);
76 static int remove_temp_gline(const char *, const char *);
81 * inputs - The usual for a m_ function
83 * side effects - place a gline if 3 opers agree
86 mo_gline(struct Client
*client_p
, struct Client
*source_p
, int parc
, const char *parv
[])
88 const char *user
= NULL
;
89 char *host
= NULL
; /* user and host of GLINE "victim" */
90 char *reason
= NULL
; /* reason for "victims" demise */
94 if(!ConfigFileEntry
.glines
)
96 sendto_one_notice(source_p
, ":GLINE disabled, perhaps you want a clustered or remote KLINE?");
100 if(!IsOperGline(source_p
))
102 sendto_one(source_p
, form_str(ERR_NOPRIVS
),
103 me
.name
, source_p
->name
, "gline");
107 host
= strchr(parv
[1], '@');
109 /* specific user@host */
115 /* gline for "@host", use *@host */
122 /* ok, its not a host.. abort */
123 if(strchr(parv
[1], '.') == NULL
)
125 sendto_one_notice(source_p
, ":Invalid parameters");
130 host
= LOCAL_COPY(parv
[1]);
133 reason
= LOCAL_COPY(parv
[2]);
135 if(invalid_gline(source_p
, user
, host
, reason
))
138 /* Not enough non-wild characters were found, assume they are trying to gline *@*. */
139 if(check_wild_gline(user
, host
))
141 if(MyClient(source_p
))
142 sendto_one_notice(source_p
,
143 ":Please include at least %d non-wildcard characters with the user@host",
144 ConfigFileEntry
.min_nonwildcard
);
148 if((ptr
= strchr(host
, '/')) != NULL
)
151 bitlen
= strtol(++ptr
, NULL
, 10);
154 if(strchr(host
, ':') == NULL
)
156 if(bitlen
< ConfigFileEntry
.gline_min_cidr
)
158 sendto_one_notice(source_p
, ":Cannot set G-Lines with cidr length < %d",
159 ConfigFileEntry
.gline_min_cidr
);
164 else if(bitlen
< ConfigFileEntry
.gline_min_cidr6
)
166 sendto_one_notice(source_p
, ":Cannot set G-Lines with cidr length < %d",
167 ConfigFileEntry
.gline_min_cidr6
);
172 /* inform users about the gline before we call majority_gline()
173 * so already voted comes below gline request --fl
175 sendto_realops_snomask(SNO_GENERAL
, L_ALL
,
176 "%s!%s@%s on %s is requesting gline for [%s@%s] [%s]",
177 source_p
->name
, source_p
->username
,
178 source_p
->host
, me
.name
, user
, host
, reason
);
179 ilog(L_GLINE
, "R %s %s %s %s %s %s %s",
180 source_p
->name
, source_p
->username
, source_p
->host
,
181 source_p
->user
->server
, user
, host
, reason
);
183 /* If at least 3 opers agree this user should be G lined then do it */
184 majority_gline(source_p
, user
, host
, reason
);
186 /* 4 param version for hyb-7 servers */
187 sendto_server(NULL
, NULL
, CAP_GLN
|CAP_TS6
, NOCAPS
,
188 ":%s GLINE %s %s :%s",
189 use_id(source_p
), user
, host
, reason
);
190 sendto_server(NULL
, NULL
, CAP_GLN
, CAP_TS6
,
191 ":%s GLINE %s %s :%s",
192 source_p
->name
, user
, host
, reason
);
194 /* 8 param for hyb-6 */
195 sendto_server(NULL
, NULL
, NOCAPS
, CAP_GLN
,
196 ":%s GLINE %s %s %s %s %s %s :%s",
197 me
.name
, source_p
->name
, source_p
->username
,
198 source_p
->host
, source_p
->user
->server
,
206 mc_gline(struct Client
*client_p
, struct Client
*source_p
,
207 int parc
, const char *parv
[])
209 struct Client
*acptr
;
215 /* hyb6 allows empty gline reasons */
216 if(parc
< 4 || EmptyString(parv
[3]))
223 reason
= LOCAL_COPY(parv
[3]);
225 if(invalid_gline(acptr
, user
, host
, reason
))
228 sendto_server(client_p
, NULL
, CAP_GLN
|CAP_TS6
, NOCAPS
,
229 ":%s GLINE %s %s :%s",
230 use_id(acptr
), user
, host
, reason
);
231 sendto_server(client_p
, NULL
, CAP_GLN
, CAP_TS6
,
232 ":%s GLINE %s %s :%s",
233 acptr
->name
, user
, host
, reason
);
234 sendto_server(client_p
, NULL
, NOCAPS
, CAP_GLN
,
235 ":%s GLINE %s %s %s %s %s %s :%s",
236 acptr
->user
->server
, acptr
->name
,
237 acptr
->username
, acptr
->host
,
238 acptr
->user
->server
, user
, host
, reason
);
240 if(!ConfigFileEntry
.glines
)
243 /* check theres enough non-wildcard chars */
244 if(check_wild_gline(user
, host
))
246 sendto_realops_snomask(SNO_GENERAL
, L_ALL
,
247 "%s!%s@%s on %s is requesting a gline without "
248 "%d non-wildcard characters for [%s@%s] [%s]",
249 acptr
->name
, acptr
->username
,
250 acptr
->host
, acptr
->user
->server
,
251 ConfigFileEntry
.min_nonwildcard
,
256 if((ptr
= strchr(host
, '/')) != NULL
)
259 bitlen
= strtol(++ptr
, NULL
, 10);
262 if(strchr(host
, ':') == NULL
)
264 if(bitlen
< ConfigFileEntry
.gline_min_cidr
)
266 sendto_realops_snomask(SNO_GENERAL
, L_ALL
, "%s!%s@%s on %s is requesting a "
267 "gline with a cidr mask < %d for [%s@%s] [%s]",
268 acptr
->name
, acptr
->username
, acptr
->host
,
270 ConfigFileEntry
.gline_min_cidr
,
276 else if(bitlen
< ConfigFileEntry
.gline_min_cidr6
)
278 sendto_realops_snomask(SNO_GENERAL
, L_ALL
, "%s!%s@%s on %s is requesting a "
279 "gline with a cidr mask < %d for [%s@%s] [%s]",
280 acptr
->name
, acptr
->username
, acptr
->host
,
282 ConfigFileEntry
.gline_min_cidr6
,
289 sendto_realops_snomask(SNO_GENERAL
, L_ALL
,
290 "%s!%s@%s on %s is requesting gline for [%s@%s] [%s]",
291 acptr
->name
, acptr
->username
, acptr
->host
,
292 acptr
->user
->server
, user
, host
, reason
);
294 ilog(L_GLINE
, "R %s %s %s %s %s %s %s",
295 source_p
->name
, source_p
->username
, source_p
->host
,
296 source_p
->user
->server
, user
, host
, reason
);
298 /* If at least 3 opers agree this user should be G lined then do it */
299 majority_gline(acptr
, user
, host
, reason
);
307 * inputs - The usual for a m_ function
309 * side effects - attempts to place a gline, if 3 opers agree
312 ms_gline(struct Client
*client_p
, struct Client
*source_p
, int parc
, const char *parv
[])
314 struct Client
*acptr
;
319 /* hyb6 allows empty gline reasons */
320 if(parc
< 8 || EmptyString(parv
[7]))
323 /* client doesnt exist.. someones messing */
324 if((acptr
= find_client(parv
[1])) == NULL
)
327 /* client that sent the gline, isnt on the server that sent
328 * the gline out. somethings fucked.
330 if(acptr
->servptr
!= source_p
)
335 reason
= LOCAL_COPY(parv
[7]);
337 if(invalid_gline(acptr
, user
, host
, reason
))
340 sendto_server(client_p
, NULL
, CAP_GLN
|CAP_TS6
, NOCAPS
,
341 ":%s GLINE %s %s :%s",
342 use_id(acptr
), user
, host
, reason
);
343 sendto_server(client_p
, NULL
, CAP_GLN
, CAP_TS6
,
344 ":%s GLINE %s %s :%s",
345 acptr
->name
, user
, host
, reason
);
346 sendto_server(client_p
, NULL
, NOCAPS
, CAP_GLN
,
347 ":%s GLINE %s %s %s %s %s %s :%s",
348 acptr
->user
->server
, acptr
->name
,
349 acptr
->username
, acptr
->host
,
350 acptr
->user
->server
, user
, host
, reason
);
352 if(!ConfigFileEntry
.glines
)
355 /* check theres enough non-wildcard chars */
356 if(check_wild_gline(user
, host
))
358 sendto_realops_snomask(SNO_GENERAL
, L_ALL
,
359 "%s!%s@%s on %s is requesting a gline without "
360 "%d non-wildcard characters for [%s@%s] [%s]",
361 acptr
->name
, acptr
->username
,
362 acptr
->host
, acptr
->user
->server
,
363 ConfigFileEntry
.min_nonwildcard
,
368 sendto_realops_snomask(SNO_GENERAL
, L_ALL
,
369 "%s!%s@%s on %s is requesting gline for [%s@%s] [%s]",
370 acptr
->name
, acptr
->username
, acptr
->host
,
371 acptr
->user
->server
, user
, host
, reason
);
373 ilog(L_GLINE
, "R %s %s %s %s %s %s %s",
374 acptr
->name
, acptr
->username
, acptr
->host
,
375 acptr
->user
->server
, user
, host
, reason
);
377 /* If at least 3 opers agree this user should be G lined then do it */
378 majority_gline(acptr
, user
, host
, reason
);
385 * parv[0] = sender nick
386 * parv[1] = gline to remove
389 mo_ungline(struct Client
*client_p
, struct Client
*source_p
, int parc
, const char *parv
[])
392 char *h
= LOCAL_COPY(parv
[1]);
396 if(!ConfigFileEntry
.glines
)
398 sendto_one_notice(source_p
, ":UNGLINE disabled, perhaps you want UNKLINE?");
402 if(!IsOperUnkline(source_p
) || !IsOperGline(source_p
))
404 sendto_one(source_p
, form_str(ERR_NOPRIVS
),
405 me
.name
, source_p
->name
, "unkline");
409 if((host
= strchr(h
, '@')) || *h
== '*')
411 /* Explicit user@host mask given */
417 /* check for @host */
434 sendto_one_notice(source_p
, ":Invalid parameters");
438 if(remove_temp_gline(user
, host
))
440 sendto_one_notice(source_p
, ":Un-glined [%s@%s]", user
, host
);
441 sendto_realops_snomask(SNO_GENERAL
, L_ALL
,
442 "%s has removed the G-Line for: [%s@%s]",
443 get_oper_name(source_p
), user
, host
);
444 ilog(L_GLINE
, "U %s %s %s %s %s %s",
445 source_p
->name
, source_p
->username
, source_p
->host
,
446 source_p
->user
->server
, user
, host
);
450 sendto_one_notice(source_p
, ":No G-Line for %s@%s", user
, host
);
459 * inputs - user, host of gline
460 * output - 1 if not enough non-wildchar char's, 0 if ok
461 * side effects - NONE
464 check_wild_gline(const char *user
, const char *host
)
473 while ((tmpch
= *p
++))
475 if(!IsKWildChar(tmpch
))
477 /* enough of them, break */
478 if(++nonwild
>= ConfigFileEntry
.min_nonwildcard
)
483 if(nonwild
< ConfigFileEntry
.min_nonwildcard
)
485 /* user doesnt, try host */
487 while ((tmpch
= *p
++))
489 if(!IsKWildChar(tmpch
))
490 if(++nonwild
>= ConfigFileEntry
.min_nonwildcard
)
495 if(nonwild
< ConfigFileEntry
.min_nonwildcard
)
503 * inputs - pointer to source client, ident, host and reason
504 * outputs - 1 if invalid, 0 if valid
508 invalid_gline(struct Client
*source_p
, const char *luser
,
509 const char *lhost
, char *lreason
)
511 if(strchr(luser
, '!'))
513 sendto_one_notice(source_p
, ":Invalid character '!' in gline");
517 if(strlen(lreason
) > REASONLEN
)
518 lreason
[REASONLEN
] = '\0';
526 * inputs - pointer to oper nick/username/host/server,
527 * victim user/host and reason
532 set_local_gline(struct Client
*source_p
, const char *user
,
533 const char *host
, const char *reason
)
535 char buffer
[IRCD_BUFSIZE
];
536 struct ConfItem
*aconf
;
537 const char *current_date
;
541 current_date
= smalldate();
543 my_reason
= LOCAL_COPY(reason
);
546 aconf
->status
= CONF_GLINE
;
547 aconf
->flags
|= CONF_FLAGS_TEMPORARY
;
549 if(strlen(my_reason
) > REASONLEN
)
550 my_reason
[REASONLEN
-1] = '\0';
552 if((oper_reason
= strchr(my_reason
, '|')) != NULL
)
557 if(!EmptyString(oper_reason
))
558 DupString(aconf
->spasswd
, oper_reason
);
561 ircsnprintf(buffer
, sizeof(buffer
), "%s (%s)", reason
, current_date
);
563 DupString(aconf
->passwd
, buffer
);
564 DupString(aconf
->user
, user
);
565 DupString(aconf
->host
, host
);
566 aconf
->hold
= CurrentTime
+ ConfigFileEntry
.gline_time
;
569 sendto_realops_snomask(SNO_GENERAL
, L_ALL
,
570 "%s!%s@%s on %s has triggered gline for [%s@%s] [%s]",
571 source_p
->name
, source_p
->username
,
572 source_p
->host
, source_p
->user
->server
,
574 ilog(L_GLINE
, "T %s %s %s %s %s %s %s",
575 source_p
->name
, source_p
->username
, source_p
->host
,
576 source_p
->user
->server
, user
, host
, reason
);
583 * input - client doing gline, user, host and reason of gline
584 * output - YES if there are 3 different opers/servers agree, else NO
588 majority_gline(struct Client
*source_p
, const char *user
,
589 const char *host
, const char *reason
)
591 dlink_node
*pending_node
;
592 struct gline_pending
*pending
;
594 /* to avoid desync.. --fl */
595 cleanup_glines(NULL
);
597 /* if its already glined, why bother? :) -- fl_ */
598 if(find_is_glined(host
, user
))
601 DLINK_FOREACH(pending_node
, pending_glines
.head
)
603 pending
= pending_node
->data
;
605 if((irccmp(pending
->user
, user
) == 0) &&
606 (irccmp(pending
->host
, host
) == 0))
608 /* check oper or server hasnt already voted */
609 if(((irccmp(pending
->oper_user1
, source_p
->username
) == 0) ||
610 (irccmp(pending
->oper_host1
, source_p
->host
) == 0)))
612 sendto_realops_snomask(SNO_GENERAL
, L_ALL
, "oper has already voted");
615 else if(irccmp(pending
->oper_server1
, source_p
->user
->server
) == 0)
617 sendto_realops_snomask(SNO_GENERAL
, L_ALL
, "server has already voted");
621 if(pending
->oper_user2
[0] != '\0')
623 /* if two other opers on two different servers have voted yes */
624 if(((irccmp(pending
->oper_user2
, source_p
->username
) == 0) ||
625 (irccmp(pending
->oper_host2
, source_p
->host
) == 0)))
627 sendto_realops_snomask(SNO_GENERAL
, L_ALL
,
628 "oper has already voted");
631 else if(irccmp(pending
->oper_server2
, source_p
->user
->server
) == 0)
633 sendto_realops_snomask(SNO_GENERAL
, L_ALL
,
634 "server has already voted");
638 /* trigger the gline using the original reason --fl */
639 set_local_gline(source_p
, user
, host
,
642 cleanup_glines(NULL
);
647 strlcpy(pending
->oper_nick2
, source_p
->name
,
648 sizeof(pending
->oper_nick2
));
649 strlcpy(pending
->oper_user2
, source_p
->username
,
650 sizeof(pending
->oper_user2
));
651 strlcpy(pending
->oper_host2
, source_p
->host
,
652 sizeof(pending
->oper_host2
));
653 DupString(pending
->reason2
, reason
);
654 pending
->oper_server2
= find_or_add(source_p
->user
->server
);
655 pending
->last_gline_time
= CurrentTime
;
656 pending
->time_request2
= CurrentTime
;
662 /* no pending gline, create a new one */
663 pending
= (struct gline_pending
*)
664 MyMalloc(sizeof(struct gline_pending
));
666 strlcpy(pending
->oper_nick1
, source_p
->name
,
667 sizeof(pending
->oper_nick1
));
668 strlcpy(pending
->oper_user1
, source_p
->username
,
669 sizeof(pending
->oper_user1
));
670 strlcpy(pending
->oper_host1
, source_p
->host
,
671 sizeof(pending
->oper_host1
));
673 pending
->oper_server1
= find_or_add(source_p
->user
->server
);
675 strlcpy(pending
->user
, user
, sizeof(pending
->user
));
676 strlcpy(pending
->host
, host
, sizeof(pending
->host
));
677 DupString(pending
->reason1
, reason
);
678 pending
->reason2
= NULL
;
680 pending
->last_gline_time
= CurrentTime
;
681 pending
->time_request1
= CurrentTime
;
683 dlinkAddAlloc(pending
, &pending_glines
);
688 /* remove_temp_gline()
690 * inputs - username, hostname to ungline
692 * side effects - tries to ungline anything that matches
695 remove_temp_gline(const char *user
, const char *host
)
697 struct ConfItem
*aconf
;
699 struct irc_sockaddr_storage addr
, caddr
;
703 mtype
= parse_netmask(host
, (struct sockaddr
*)&addr
, &bits
);
705 DLINK_FOREACH(ptr
, glines
.head
)
709 gtype
= parse_netmask(aconf
->host
, (struct sockaddr
*)&caddr
, &cbits
);
711 if(gtype
!= mtype
|| (user
&& irccmp(user
, aconf
->user
)))
716 if(irccmp(aconf
->host
, host
))
719 else if(bits
!= cbits
||
720 !comp_with_mask_sock((struct sockaddr
*)&addr
,
721 (struct sockaddr
*)&caddr
, bits
))
724 dlinkDestroy(ptr
, &glines
);
725 delete_one_address_conf(aconf
->host
, aconf
);