]> jfr.im git - irc/quakenet/newserv.git/blame - glines/glines.c
Merge and clean up paul's glines code.
[irc/quakenet/newserv.git] / glines / glines.c
CommitLineData
8f128e0d 1#include <stdio.h>
a44fc5f7
GB
2#include <string.h>
3#include <assert.h>
a473a1be 4#include "../lib/irc_string.h"
c4610da5 5#include "../lib/version.h"
813c5b73 6#include "../irc/irc.h"
813c5b73 7#include "../trusts/trusts.h"
a473a1be 8#include "../control/control.h"
813c5b73
CP
9#include "glines.h"
10
c4610da5
GB
11MODULE_VERSION("");
12
a44fc5f7
GB
13void _init() {
14 /* If we're connected to IRC, force a disconnect. */
15 if (connected) {
16 irc_send("%s SQ %s 0 :Resync [adding gline support]",mynumeric->content,myserver->content); irc_disconnected();
17 }
18
19 registerserverhandler("GL", handleglinemsg, 6);
20 registerhook(HOOK_CORE_STATSREQUEST, handleglinestats);
21
22}
23
24void _fini() {
25 deregisterserverhandler("GL", handleglinemsg);
26 deregisterhook(HOOK_CORE_STATSREQUEST, handleglinestats);
27}
28
a473a1be
GB
29static int countmatchingusers(const char *identmask, struct irc_in_addr *ip, unsigned char bits) {
30 nick *np;
31 int i;
32 int count = 0;
33
34 for(i=0;i<NICKHASHSIZE;i++)
35 for (np=nicktable[i];np;np=np->next)
36 if (ipmask_check(&np->p_nodeaddr, ip, bits) && match2strings(identmask, np->ident))
37 count++;
38
39 return count;
40}
41
3c5c26a8 42void glinesetmask(const char *mask, int duration, const char *reason, const char *creator) {
a44fc5f7
GB
43 gline *gl;
44 time_t now = getnettime();
45
3057daed
GB
46 if(strcmp(creator, "trusts_policy")==0) {
47 controlwall(NO_OPER, NL_GLINES, "(NOT) Setting gline on '%s' lasting %s with reason '%s', created by: %s", mask, longtoduration(duration, 0), reason, creator);
48 return;
49 }
3c5c26a8 50
a44fc5f7
GB
51 if (!(gl = gline_find(mask))) {
52 /* new gline */
53 gl = gline_add(creator, mask, reason, now + duration, now, now + duration);
54 gline_propagate(gl);
55 } else {
56 /* existing gline
57 * in 1.4, can update expire, reason etc
58 * in 1.3 can only activate an existing inactive gline */
59 if(!(gl->flags & GLINE_ACTIVE))
60 gline_activate(gl,0,1);
61 }
813c5b73
CP
62}
63
a44fc5f7
GB
64void glineunsetmask(const char *mask) {
65 gline *gl = gline_find(mask);
66
67 if (gl && (gl->flags & GLINE_ACTIVE)) {
68 controlwall(NO_OPER, NL_GLINES, "Deactivating gline on '%s'.", mask);
69 gline_deactivate(gl, 0, 1);
70 }
8f128e0d 71}
ac3af088 72
8f128e0d 73static void glinesetmask_cb(const char *mask, int hits, void *arg) {
46b488b3 74 gline_params *gp = arg;
3c5c26a8 75 glinesetmask(mask, gp->duration, gp->reason, gp->creator);
8f128e0d 76}
a473a1be 77
8f128e0d
GB
78int glinesuggestbyip(const char *user, struct irc_in_addr *ip, unsigned char bits, int flags, gline_callback callback, void *uarg) {
79 trusthost *th, *oth;
80 int count;
81 unsigned char nodebits;
82
83 nodebits = getnodebits(ip);
a473a1be
GB
84
85 if (!(flags & GLINE_IGNORE_TRUST)) {
8f128e0d 86 th = th_getbyhost(ip);
ac3af088 87
3a8c35c9 88 if(th && (th->group->flags & TRUST_RELIABLE_USERNAME)) { /* Trust with reliable usernames */
8f128e0d
GB
89 count = 0;
90
91 for(oth=th->group->hosts;oth;oth=oth->next)
92 count += glinesuggestbyip(user, &oth->ip, oth->bits, flags | GLINE_ALWAYS_USER | GLINE_IGNORE_TRUST, callback, uarg);
93
94 return count;
ac3af088
GB
95 }
96 }
813c5b73 97
a473a1be 98 if (!(flags & GLINE_ALWAYS_USER))
8f128e0d 99 user = "*";
a473a1be 100
8f128e0d
GB
101 /* Widen gline to match the node mask. */
102 if(nodebits<bits)
103 bits = nodebits;
a473a1be 104
8f128e0d
GB
105 count = countmatchingusers(user, ip, bits);
106
107 if (!(flags & GLINE_SIMULATE)) {
108 char mask[512];
109 snprintf(mask, sizeof(mask), "%s@%s", user, trusts_cidr2str(ip, bits));
110 callback(mask, count, uarg);
a473a1be
GB
111 }
112
a473a1be 113 return count;
813c5b73
CP
114}
115
3c5c26a8 116int glinebyip(const char *user, struct irc_in_addr *ip, unsigned char bits, int duration, const char *reason, int flags, const char *creator) {
8f128e0d 117 gline_params gp;
8c45c18f
GB
118
119 if(!(flags & GLINE_SIMULATE)) {
120 int hits = glinesuggestbyip(user, ip, bits, flags | GLINE_SIMULATE, NULL, NULL);
121 if(hits>MAXGLINEUSERS) {
122 controlwall(NO_OPER, NL_GLINES, "Suggested gline(s) for '%s@%s' lasting %s with reason '%s' would hit %d users (limit: %d) - NOT SET.",
123 user, trusts_cidr2str(ip, bits), longtoduration(duration, 0), reason, hits, MAXGLINEUSERS);
124 return 0;
125 }
126 }
127
8f128e0d
GB
128 gp.duration = duration;
129 gp.reason = reason;
82a9536d 130 gp.creator = creator;
8f128e0d
GB
131 return glinesuggestbyip(user, ip, bits, flags, glinesetmask_cb, &gp);
132}
ac3af088 133
8f128e0d
GB
134int glinesuggestbynick(nick *np, int flags, void (*callback)(const char *, int, void *), void *uarg) {
135 if (flags & GLINE_ALWAYS_NICK) {
8c45c18f
GB
136 if(!(flags & GLINE_SIMULATE)) {
137 char mask[512];
138 snprintf(mask, sizeof(mask), "%s!*@*", np->nick);
139 callback(mask, 1, uarg);
140 }
141
142 return 1;
8f128e0d
GB
143 } else {
144 return glinesuggestbyip(np->ident, &np->p_ipaddr, 128, flags, callback, uarg);
ac3af088 145 }
8f128e0d 146}
813c5b73 147
3c5c26a8 148int glinebynick(nick *np, int duration, const char *reason, int flags, const char *creator) {
8f128e0d 149 gline_params gp;
8c45c18f
GB
150
151 if(!(flags & GLINE_SIMULATE)) {
152 int hits = glinesuggestbyip(np->ident, &np->p_ipaddr, 128, flags | GLINE_SIMULATE, NULL, NULL);
153 if(hits>MAXGLINEUSERS) {
154 controlwall(NO_OPER, NL_GLINES, "Suggested gline(s) for nick '%s!%s@%s' lasting %s with reason '%s' would hit %d users (limit: %d) - NOT SET.",
29527e36 155 np->nick, np->ident, IPtostr(np->p_ipaddr), longtoduration(duration, 0), reason, hits, MAXGLINEUSERS);
8c45c18f
GB
156 return 0;
157 }
158 }
159
8f128e0d
GB
160 gp.duration = duration;
161 gp.reason = reason;
3c5c26a8 162 gp.creator = creator;
8f128e0d 163 return glinesuggestbynick(np, flags, glinesetmask_cb, &gp);
813c5b73 164}
a44fc5f7
GB
165
166gline *gline_add(const char *creator, const char *mask, const char *reason, time_t expire, time_t lastmod, time_t lifetime) {
167 gline *gl;
168
169 if (!(gl=makegline(mask))) { /* sets up nick,user,host,node and flags */
170 /* couldn't process gline mask */
171 Error("gline", ERR_WARNING, "Tried to add malformed G-Line %s!", mask);
172 return 0;
173 }
174
175 gl->creator = getsstring(creator, 255);
176
177 /* it's not unreasonable to assume gline is active, if we're adding a deactivated gline, we can remove this later */
178 gl->flags |= GLINE_ACTIVE;
179
180 gl->reason = getsstring(reason, 255);
181 gl->expire = expire;
182 gl->lastmod = lastmod;
183 gl->lifetime = lifetime;
184
185 gl->next = glinelist;
186 glinelist = gl;
187
188 return gl;
189}
190
191gline *makegline(const char *mask) {
192 /* populate gl-> user,host,node,nick and set appropriate flags */
193 int len;
194 int foundat=-1,foundbang=-1;
195 int foundwild=0;
196 int i;
197 struct irc_in_addr sin;
198 unsigned char bits;
199 gline *gl;
200
201 Error("gline", ERR_FATAL,"processing: %s", mask);
202 if (!(gl = newgline())) {
203 Error("gline", ERR_ERROR, "Failed to allocate new gline");
204 return NULL;
205 }
206
207 len=strlen(mask);
208
209 switch (*mask ) {
210 case '#':
211 case '&':
212 gl->flags |= GLINE_BADCHAN;
213 gl->user = getsstring(mask, CHANNELLEN);
214 return gl;
215 case '$':
216 switch (mask[1]) {
217 case 'R':
218 gl->flags |= GLINE_REALNAME;
219 break;
220 default:
221 return 0;
222 }
223 gl->user = getsstring(mask,REALLEN);
224 return gl;
225 default:
226 /* Default case of some host/ip/cidr mask */
227 for (i=(len-1);i>=0;i--) {
228 if (mask[i]=='@') {
229 /* host */
230 if ((len-i)-1 > HOSTLEN) {
231 /* host too long */
232 gl->flags |= GLINE_BADMASK;
233 } else if (i==(len-1)) {
234 /* no host supplied aka gline ends @ */
235 gl->flags |= GLINE_BADMASK;
236 } else if (i==(len-2) && mask[i+1]=='*') {
237 /* Special case: "@*" */
238 gl->flags |= GLINE_HOSTANY;
239 } else {
240 if (ipmask_parse(&mask[i+1], &sin, &bits) == 0) {
241 /* we have some host string */
242 gl->host=getsstring(&mask[i+1],HOSTLEN);
243 if (foundwild) {
244 gl->flags |= GLINE_HOSTMASK;
245 } else {
246 gl->flags |= GLINE_HOSTEXACT;
247 }
248 } else {
249 /* we have a / so cidr gline */
250 gl->ip = sin;
251 gl->bits = bits;
252 gl->host=getsstring(&mask[i+1],HOSTLEN);
253 if (!is_normalized_ipmask(&sin, bits)) {
254 gl->flags |= GLINE_BADMASK;
255 }
256 gl->flags |= GLINE_IPMASK;
257 }
258 }
259 foundat=i;
260 break;
261 } else if (mask[i]=='?' || mask[i]=='*') {
262 if (!foundwild) /* Mark last wildcard in string */
263 foundwild=i;
264 }
265 }
266 }
267
268 if (foundat<0) {
269 /* If there wasn't an @, this ban matches any host */
270 gl->host=NULL;
271 gl->flags |= GLINE_HOSTANY;
272 }
273
274 foundwild=0;
275
276 for (i=0;i<foundat;i++) {
277 if (mask[i]=='!') {
278 if (i==0) {
279 /* Invalid mask: nick is empty */
280 gl->flags |= GLINE_NICKNULL;
281 gl->nick=NULL;
282 } else if (i==1 && mask[0]=='*') {
283 /* matches any nick */
284 gl->flags |= GLINE_NICKANY;
285 gl->nick=NULL;
286 } else {
287 if (i>NICKLEN) {
288 /* too long */
289 gl->flags |= GLINE_BADMASK;
290 }
291 gl->nick=getsstring(mask,i);
292 if (foundwild)
293 gl->flags |= GLINE_NICKMASK;
294 else
295 gl->flags |= GLINE_NICKEXACT;
296 }
297 foundbang=i;
298 break;
299 } else if (mask[i]=='?' || mask[i]=='*') {
300 if (i<NICKLEN) {
301 foundwild=1;
302 }
303 }
304 }
305
306 if (foundbang<0) {
307 /* We didn't find a !, what we do now depends on what happened
308 * with the @ */
309 if (foundat<0) {
310 /* A gline with no ! or @ is treated as a host gline only */
311 /* Note that we've special-cased "*" at the top, so we can only
312 * hit the MASK or EXACT case here. */
313 gl->host=getsstring(mask,len);
314
315 if (foundwild)
316 gl->flags |= GLINE_HOSTMASK;
317 else
318 gl->flags |= GLINE_HOSTEXACT;
319
320 gl->flags |= (GLINE_USERANY | GLINE_NICKANY);
321 gl->nick=NULL;
322 gl->user=NULL;
323 } else {
324 /* A gline with @ only is treated as user@host */
325 gl->nick=NULL;
326 gl->flags |= GLINE_NICKANY;
327 }
328 }
329
330 if (foundat>=0) {
331 /* We found an @, so everything between foundbang+1 and foundat-1 is supposed to be ident */
332 /* This is true even if there was no !.. */
333 if (foundat==(foundbang+1)) {
334 /* empty ident matches nothing */ /*@@@TODO: * for glines? */
335 gl->flags |= (GLINE_BADMASK | GLINE_USERNULL);
336 } else if (foundat - foundbang - 1 > USERLEN) {
337 /* It's too long.. */
338 gl->flags |= GLINE_BADMASK;
339 } else if ((foundat - foundbang - 1 == 1) && mask[foundbang+1]=='*') {
340 gl->flags |= GLINE_USERANY;
341 } else {
342 gl->user=getsstring(&mask[foundbang+1],(foundat-foundbang-1));
343 if (strchr(gl->user->content,'*') || strchr(gl->user->content,'?'))
344 gl->flags |= GLINE_USERMASK;
345 else
346 gl->flags |= GLINE_USEREXACT;
347 }
348 /* Username part can't contain an @ */
349 if (gl->user && strchr(gl->user->content,'@')) {
350 gl->flags |= GLINE_BADMASK;
351 }
352 }
353
354 assert(gl->flags & (GLINE_USEREXACT | GLINE_USERMASK | GLINE_USERANY | GLINE_USERNULL));
355 assert(gl->flags & (GLINE_NICKEXACT | GLINE_NICKMASK | GLINE_NICKANY | GLINE_NICKNULL));
356 assert(gl->flags & (GLINE_HOSTEXACT | GLINE_HOSTMASK | GLINE_HOSTANY | GLINE_HOSTNULL | GLINE_IPMASK));
357
358 if (gl->flags & GLINE_BADMASK) {
359 Error("gline", ERR_WARNING, "BADMASK: %s", mask);
360 return 0;
361 }
362
363 /* Start: Safety Check for now... */
364 if ( strcmp( glinetostring(gl), mask ) ) {
365 /* oper can specify * to glist, ircd by default converts * to *!*@* */
366 if ( ((gl->flags & ( GLINE_USERANY | GLINE_NICKANY | GLINE_HOSTANY )) == ( GLINE_USERANY | GLINE_NICKANY | GLINE_HOSTANY )) && !strcmp("*",mask)){
367 return gl;
368 }
369 /* oper can specifiy *@host, ircd by default converst *@host to *!*@host */
370 if ( ((gl->flags & ( GLINE_NICKANY )) == ( GLINE_NICKANY )) && (mask[1]!='!') ) {
371 Error("gline", ERR_WARNING, "different: %s %s", glinetostring(gl), mask);
372 return gl;
373 }
374
375 Error("gline", ERR_WARNING, "DIFFERENT: %s %s", glinetostring(gl), mask);
376 }
377 /* End: Safety Check for now... */
378
379 return gl;
380}
381
382gline *gline_find(const char *mask) {
383 gline *gl, *sgl;
384 gline *globalgline;
385 time_t curtime = time(0);
386
387 if(!(globalgline=makegline(mask))) {
388 /* gline mask couldn't be processed */
389 return 0;
390 }
391
392 for (gl = glinelist; gl; gl = sgl) {
393 sgl = gl->next;
394
395 if (gl->lifetime <= curtime) {
396 removegline(gl);
397 continue;
398 } else if (gl->expire <= curtime) {
399 gl->flags &= ~GLINE_ACTIVE;
400 }
401
402 if (glineequal(globalgline, gl)) {
403 freegline(globalgline);
404 return gl;
405 }
406 }
407
408 freegline(globalgline);
409 return 0;
410}
411
412char *glinetostring(gline *g) {
413 static char outstring[NICKLEN+USERLEN+HOSTLEN+5]; /* check */
414 int strpos=0;
415
416 if (g->flags & GLINE_REALNAME) {
417 if (g->user)
418 strpos += sprintf(outstring+strpos, "%s", g->user->content);
419 /* @@ todo $R*
420 * add $R (not check badchans need same?) */
421 return outstring;
422 }
423
424 if (g->flags & GLINE_BADCHAN) {
425 if (g->user)
426 strpos += sprintf(outstring+strpos, "%s", g->user->content);
427 return outstring;
428 }
429
430 if (g->flags & GLINE_NICKANY) {
431 strpos += sprintf(outstring+strpos,"*");
432 } else if (g->nick) {
433 strpos += sprintf(outstring+strpos,"%s", g->nick->content);
434 }
435
436 strpos += sprintf(outstring+strpos, "!");
437
438 if (g->flags & GLINE_USERANY) {
439 strpos += sprintf(outstring+strpos, "*");
440 } else if (g->user) {
441 strpos += sprintf(outstring+strpos, "%s", g->user->content);
442 }
443
444 strpos += sprintf(outstring+strpos, "@");
445
446 if (g->flags & GLINE_HOSTANY) {
447 strpos += sprintf(outstring+strpos, "*");
448 } else if (g->host) {
449 strpos += sprintf(outstring+strpos, "%s", g->host->content);
450 } else if (g->flags & GLINE_IPMASK) {
451 strpos += sprintf(outstring+strpos, "%s", g->host->content);
452 }
453 return outstring;
454}
455
456gline *gline_activate(gline *agline, time_t lastmod, int propagate) {
457 time_t now = getnettime();
458 agline->flags |= GLINE_ACTIVE;
459
460 if (lastmod) {
461 agline->lastmod = lastmod;
462 } else {
463 if(now<=agline->lastmod)
464 agline->lastmod++;
465 else
466 agline->lastmod=now;
467 }
468
469 if (propagate)
470 gline_propagate(agline);
471
472 return agline;
473}
474
475gline *gline_deactivate(gline *agline, time_t lastmod, int propagate) {
476 time_t now = getnettime();
477 agline->flags &= ~GLINE_ACTIVE;
478
479 if (lastmod) {
480 agline->lastmod = lastmod;
481 } else {
482 if(now<=agline->lastmod)
483 agline->lastmod++;
484 else
485 agline->lastmod=now;
486 }
487
488 if (propagate)
489 gline_propagate(agline);
490
491 return agline;
492}
493
494void gline_propagate(gline *agline) {
495 if (agline->flags & GLINE_ACTIVE) {
496 controlwall(NO_OPER, NL_GLINES, "Setting gline on '%s' lasting %s with reason '%s', created by: %s", glinetostring(agline), longtoduration(agline->expire-getnettime(),0), agline->reason->content, agline->creator->content);
497 irc_send("%s GL * +%s %lu %lu :%s\r\n", mynumeric->content, glinetostring(agline), agline->expire - getnettime(), agline->lastmod, agline->reason->content);
498 } else {
499 irc_send("%s GL * -%s %lu %lu :%s\r\n", mynumeric->content, glinetostring(agline), agline->expire - getnettime(), agline->lastmod, agline->reason->content);
500 }
501}
502
503/* returns non-zero if the glines are exactly the same */
504int glineequal(gline *gla, gline *glb) {
505 if ((!gla->nick && glb->nick) || (gla->nick && !glb->nick))
506 return 0;
507
508 if (gla->nick && ircd_strcmp(gla->nick->content,glb->nick->content))
509 return 0;
510
511 if ((!gla->user && glb->user) || (gla->user && !glb->user))
512 return 0;
513
514 if (gla->user && ircd_strcmp(gla->user->content,glb->user->content))
515 return 0;
516
517 if ((!gla->host && glb->host) || (gla->host && !glb->host))
518 return 0;
519
520 if (gla->host && ircd_strcmp(gla->host->content,glb->host->content))
521 return 0;
522
523 /* TODO @@@ match bits flags */
524 return 1;
525}
526
527/* returns non-zero on match */
528int gline_match_mask( gline *gla, gline *glb) {
529 if ((!gla->nick && glb->nick) || (gla->nick && !glb->nick))
530 return 0;
531
532 if (gla->nick && match(gla->nick->content,glb->nick->content))
533 return 0;
534
535 // TODO should check for ANY etc
536 if ((!gla->user && glb->user) || (gla->user && !glb->user))
537 return 0;
538
539 if (gla->user && match(gla->user->content,glb->user->content))
540 return 0;
541
542 if ((!gla->host && glb->host) || (gla->host && !glb->host))
543 return 0;
544
545 if (gla->host && match(gla->host->content,glb->host->content))
546 return 0;
547
548 /* TODO @@@ match bits flags */
549 return 1;
550}
551
552int gline_match_nick(gline *gl, nick *np) {
553 return 0;
554}
555
556int gline_match_channel(gline *gl, channel *cp) {
557 return 0;
558}
559