]> jfr.im git - irc/quakenet/newserv.git/blame - glines/glines_buf.c
glines: Only use CIDR masks when necessary.
[irc/quakenet/newserv.git] / glines / glines_buf.c
CommitLineData
ce11a200 1#include <stdarg.h>
cb581522
GB
2#include <stdio.h>
3#include <string.h>
31c690b7 4#include <assert.h>
a887af59 5#include "../lib/array.h"
a86fc0c4
GB
6#include "../lib/irc_string.h"
7#include "../irc/irc.h"
cb581522
GB
8#include "../control/control.h"
9#include "../trusts/trusts.h"
10#include "glines.h"
11
ce11a200
GB
12static int nextglinebufid = 1;
13glinebuf *glinebuflog[MAXGLINELOG] = {};
14int glinebuflogoffset = 0;
15
324b4e11
GB
16void glinebufinit(glinebuf *gbuf, int id) {
17 gbuf->id = id;
ce11a200 18 gbuf->comment = NULL;
33e09c2c 19 gbuf->commit = 0;
9f47116c 20 gbuf->amend = 0;
ce11a200 21 gbuf->glines = NULL;
fe262e64 22 gbuf->hitsvalid = 1;
bbb80250
GB
23 gbuf->userhits = 0;
24 gbuf->channelhits = 0;
a887af59 25 array_init(&gbuf->hits, sizeof(sstring *));
cb581522
GB
26}
27
28gline *glinebufadd(glinebuf *gbuf, const char *mask, const char *creator, const char *reason, time_t expire, time_t lastmod, time_t lifetime) {
324b4e11 29 gline *gl;
cb581522
GB
30
31 gl = makegline(mask); /* sets up nick,user,host,node and flags */
32
33 if (!gl) {
34 /* couldn't process gline mask */
35 Error("gline", ERR_WARNING, "Tried to add malformed G-Line %s!", mask);
36 return 0;
37 }
38
cb581522
GB
39 gl->creator = getsstring(creator, 255);
40
41 /* it's not unreasonable to assume gline is active, if we're adding a deactivated gline, we can remove this later */
42 gl->flags |= GLINE_ACTIVE;
43
44 gl->reason = getsstring(reason, 255);
45 gl->expire = expire;
46 gl->lastmod = lastmod;
47 gl->lifetime = lifetime;
48
ce11a200
GB
49 gl->next = gbuf->glines;
50 gbuf->glines = gl;
cb581522 51
33e09c2c
GB
52 gbuf->hitsvalid = 0;
53
cb581522
GB
54 return gl;
55}
56
57void glinebufaddbyip(glinebuf *gbuf, const char *user, struct irc_in_addr *ip, unsigned char bits, int flags, const char *creator, const char *reason, time_t expire, time_t lastmod, time_t lifetime) {
58 trusthost *th, *oth;
59 char mask[512];
60 unsigned char nodebits;
61
62 nodebits = getnodebits(ip);
63
64 if (!(flags & GLINE_IGNORE_TRUST)) {
65 th = th_getbyhost(ip);
66
67 if (th && (th->group->flags & TRUST_RELIABLE_USERNAME)) { /* Trust with reliable usernames */
a86fc0c4 68 for (oth = th->group->hosts; oth; oth = oth->next)
cb581522 69 glinebufaddbyip(gbuf, user, &oth->ip, oth->bits, flags | GLINE_ALWAYS_USER | GLINE_IGNORE_TRUST, creator, reason, expire, lastmod, lifetime);
a86fc0c4
GB
70
71 return;
cb581522
GB
72 }
73 }
74
75 if (!(flags & GLINE_ALWAYS_USER))
76 user = "*";
77
78 /* Widen gline to match the node mask. */
79 if (nodebits < bits)
80 bits = nodebits;
81
440cd7e6 82 snprintf(mask, sizeof(mask), "%s@%s", user, (bits == 128) ? IPtostr(*ip) : CIDRtostr(*ip, bits));
cb581522
GB
83
84 glinebufadd(gbuf, mask, creator, reason, expire, lastmod, lifetime);
85}
86
87void glinebufaddbynick(glinebuf *gbuf, nick *np, int flags, const char *creator, const char *reason, time_t expire, time_t lastmod, time_t lifetime) {
88 if (flags & GLINE_ALWAYS_NICK) {
89 char mask[512];
90 snprintf(mask, sizeof(mask), "%s!*@*", np->nick);
91 glinebufadd(gbuf, mask, creator, reason, expire, lastmod, lifetime);
92 } else {
fdcb5d66 93 glinebufaddbyip(gbuf, np->ident, &np->ipaddress, 128, flags, creator, reason, expire, lastmod, lifetime);
cb581522
GB
94 }
95}
96
accce086 97void glinebufaddbywhowas(glinebuf *gbuf, whowas *ww, int flags, const char *creator, const char *reason, time_t expire, time_t lastmod, time_t lifetime) {
0495c1d1 98 nick *np = &ww->nick;
0eb4cbd3 99
accce086
GB
100 if (flags & GLINE_ALWAYS_NICK) {
101 char mask[512];
0eb4cbd3 102 snprintf(mask, sizeof(mask), "%s!*@*", np->nick);
accce086
GB
103 glinebufadd(gbuf, mask, creator, reason, expire, lastmod, lifetime);
104 } else {
fdcb5d66 105 glinebufaddbyip(gbuf, np->ident, &np->ipaddress, 128, flags, creator, reason, expire, lastmod, lifetime);
accce086
GB
106 }
107}
108
33e09c2c 109void glinebufcounthits(glinebuf *gbuf, int *users, int *channels) {
cb581522 110 gline *gl;
a887af59 111 int i, hit, slot;
cb581522
GB
112 chanindex *cip;
113 channel *cp;
114 nick *np;
a887af59 115 char uhmask[512];
cb581522 116
33e09c2c
GB
117#if 0 /* Let's just do a new hit check anyway. */
118 if (gbuf->hitsvalid)
119 return;
120#endif
121
bbb80250
GB
122 gbuf->userhits = 0;
123 gbuf->channelhits = 0;
cb581522 124
a887af59
GB
125 for (i = 0; i < gbuf->hits.cursi; i++)
126 freesstring(((sstring **)gbuf->hits.content)[i]);
127
128 array_free(&gbuf->hits);
129 array_init(&gbuf->hits, sizeof(sstring *));
130
bbb80250
GB
131 for (i = 0; i<CHANNELHASHSIZE; i++) {
132 for (cip = chantable[i]; cip; cip = cip->next) {
133 cp = cip->channel;
cb581522 134
bbb80250
GB
135 if (!cp)
136 continue;
cb581522 137
bbb80250 138 hit = 0;
cb581522 139
bbb80250
GB
140 for (gl = gbuf->glines; gl; gl = gl->next) {
141 if (gline_match_channel(gl, cp)) {
142 hit = 1;
143 break;
cb581522 144 }
bbb80250 145 }
cb581522 146
bbb80250 147 if (hit) {
a887af59
GB
148 snprintf(uhmask, sizeof(uhmask), "channel: %s", cip->name->content);
149
bbb80250 150 gbuf->channelhits++;
a887af59
GB
151
152 slot = array_getfreeslot(&gbuf->hits);
153 ((sstring **)gbuf->hits.content)[slot] = getsstring(uhmask, 512);
cb581522
GB
154 }
155 }
156 }
157
bbb80250
GB
158 for (i = 0; i < NICKHASHSIZE; i++) {
159 for (np = nicktable[i]; np; np = np->next) {
160 hit = 0;
cb581522 161
bbb80250
GB
162 for (gl = gbuf->glines; gl; gl = gl->next) {
163 if (gline_match_nick(gl, np)) {
164 hit = 1;
165 break;
cb581522 166 }
bbb80250 167 }
cb581522 168
bbb80250 169 if (hit) {
33e09c2c
GB
170 snprintf(uhmask, sizeof(uhmask), "user: %s!%s@%s%s%s r(%s)", np->nick, np->ident, np->host->name->content,
171 (np->auth) ? "/" : "", (np->auth) ? np->authname : "", np->realname->name->content);
18845894 172
bbb80250 173 gbuf->userhits++;
a887af59
GB
174
175 slot = array_getfreeslot(&gbuf->hits);
176 ((sstring **)gbuf->hits.content)[slot] = getsstring(uhmask, 512);
cb581522
GB
177 }
178 }
179 }
33e09c2c
GB
180
181 gbuf->hitsvalid = 1;
182
bbb80250
GB
183 if (users)
184 *users = gbuf->userhits;
185
186 if (channels)
187 *channels = gbuf->channelhits;
cb581522
GB
188}
189
33e09c2c 190int glinebufchecksane(glinebuf *gbuf, nick *spewto, int overridesanity, int overridelimit) {
18845894
GB
191 gline *gl;
192 int users, channels, good;
a86fc0c4
GB
193 const char *hint;
194
33e09c2c 195 glinebufcounthits(gbuf, &users, &channels);
a86fc0c4 196
18845894
GB
197 if (!overridelimit) {
198 if (channels > MAXUSERGLINECHANNELHITS) {
199 controlreply(spewto, "G-Lines would hit %d channels. Limit is %d. Not setting G-Lines.", channels, MAXUSERGLINECHANNELHITS);
200 return 0;
201 } else if (users > MAXUSERGLINEUSERHITS) {
202 controlreply(spewto, "G-Lines would hit %d users. Limit is %d. Not setting G-Lines.", users, MAXUSERGLINEUSERHITS);
203 return 0;
204 }
205 }
206
207 good = 1;
a86fc0c4 208
18845894
GB
209 if (!overridesanity) {
210 /* Remove glines that fail the sanity check */
ce11a200 211 for (gl = gbuf->glines; gl; gl = gl->next) {
18845894 212 if (!isglinesane(gl, &hint)) {
56af56be
GB
213 controlreply(spewto, "Sanity check failed for G-Line on '%s' - Skipping: %s",
214 glinetostring(gl), hint);
18845894 215 good = 0;
56af56be 216 }
a86fc0c4
GB
217 }
218 }
219
18845894 220 return good;
a86fc0c4
GB
221}
222
56af56be 223void glinebufspew(glinebuf *gbuf, nick *spewto) {
a86fc0c4 224 gline *gl;
a887af59 225 int i;
dda7fd90 226 char timebuf[30], lastmod[30];
33e09c2c
GB
227
228 if (!gbuf->hitsvalid)
229 glinebufcounthits(gbuf, NULL, NULL);
230
231 if (gbuf->id == 0)
232 controlreply(spewto, "Uncommitted G-Line transaction.");
233 else
234 controlreply(spewto, "G-Line transaction ID %d", gbuf->id);
235
236 controlreply(spewto, "Comment: %s", (gbuf->comment) ? gbuf->comment->content : "(no comment)");
237
238 if (gbuf->commit) {
239 strftime(timebuf, sizeof(timebuf), "%d/%m/%y %H:%M:%S", localtime(&gbuf->commit));
240 controlreply(spewto, "Committed at: %s", timebuf);
241 }
242
9f47116c
GB
243 if (gbuf->amend) {
244 strftime(timebuf, sizeof(timebuf), "%d/%m/%y %H:%M:%S", localtime(&gbuf->amend));
eb8f36ca 245 controlreply(spewto, "Amended at: %s", timebuf);
33e09c2c
GB
246 }
247
dda7fd90 248 controlreply(spewto, "Mask Expiry Last modified Creator Reason");
ce11a200 249
324b4e11 250 for (gl = gbuf->glines; gl; gl = gl->next) {
dda7fd90
GB
251 strftime(timebuf, sizeof(timebuf), "%d/%m/%y %H:%M:%S", localtime(&gl->expire));
252
253 if (gl->lastmod == 0)
254 strncpy(lastmod, "<ulined>", sizeof(lastmod));
255 else
256 strftime(lastmod, sizeof(lastmod), "%d/%m/%y %H:%M:%S", localtime(&gl->lastmod));
257
d7191799 258 controlreply(spewto, "%-40s %-20s %-20s %-25s %s", glinetostring(gl), timebuf, lastmod, gl->creator->content, gl->reason ? gl->reason->content : "");
324b4e11 259 }
a887af59 260
33e09c2c 261 controlreply(spewto, "Hits");
a887af59 262
33e09c2c 263 for (i = 0; i < gbuf->hits.cursi; i++) {
a887af59 264 controlreply(spewto, "%s", ((sstring **)gbuf->hits.content)[i]->content);
33e09c2c
GB
265
266 if (i >= 500) {
267 controlreply(spewto, "More than 500 hits, list truncated.");
268 break;
269 }
270 }
31c690b7
GB
271
272 if (i == 0)
273 controlreply(spewto, "(no hits)");
a86fc0c4
GB
274}
275
324b4e11
GB
276void glinebufmerge(glinebuf *gbuf) {
277 /* TODO: reimplement */
278
279/*
280 if (gbuf->merge) {
281 /-* Check if an existing gline supercedes this mask *-/
282 for (sgl = gbuf->glines; sgl; sgl = sgl->next) {
283 if (gline_match_mask(sgl, gl)) {
284 freegline(gl);
285 return sgl;
286 }
287 }
288
289 /-* Remove older glines this gline matches *-/
290 for (pnext = &gbuf->glines; *pnext; pnext = &((*pnext)->next)) {
291 sgl = *pnext;
292
293 if (gline_match_mask(gl, sgl)) {
294 *pnext = sgl->next;
295 freegline(sgl);
296
297 if (!*pnext)
298 break;
299 }
300 }
301 }
302*/
303}
304
0c925dc0 305int glinebufcommit(glinebuf *gbuf, int propagate) {
cb581522 306 gline *gl, *next, *sgl;
fe262e64 307 int users, channels, id;
cb581522
GB
308
309 /* Sanity check */
33e09c2c 310 glinebufcounthits(gbuf, &users, &channels);
cb581522
GB
311
312 if (propagate && (users > MAXGLINEUSERHITS || channels > MAXGLINECHANNELHITS)) {
4e52c61d 313 controlwall(NO_OPER, NL_GLINES_AUTO, "G-Line buffer would hit %d users/%d channels. Not setting G-Lines.");
0b2e8a55 314 glinebufabort(gbuf);
0c925dc0 315 return 0;
cb581522
GB
316 }
317
31c690b7 318 /* Record the commit time */
324b4e11 319 time(&gbuf->commit);
ce11a200 320
fe262e64 321 id = glinebufwritelog(gbuf, propagate);
ce11a200 322
0c925dc0 323 /* Move glines to the global gline list */
ce11a200 324 for (gl = gbuf->glines; gl; gl = next) {
cb581522
GB
325 next = gl->next;
326
327 sgl = findgline(glinetostring(gl));
328
329 if (sgl) {
330 /* existing gline
331 * in 1.4, can update expire, reason etc
332 * in 1.3 can only activate/deactivate an existing gline */
333
334 if (gl->flags & GLINE_ACTIVE && !(sgl->flags & GLINE_ACTIVE))
335 gline_activate(sgl, 0, 0);
336 else if (!(gl->flags & GLINE_ACTIVE) && sgl->flags & GLINE_ACTIVE)
337 gline_deactivate(sgl, 0, 0);
338
ea0acfb3
GB
339#if SNIRCD_VERSION >= 140
340 sgl->expire = gl->expire;
341
342 if (gl->lifetime > sgl->lifetime)
343 sgl->lifetime = gl->lifetime;
344
345 freesstring(sgl->reason);
346 sgl->reason = getsstring(gl->reason, 512);
347#endif
348
cb581522
GB
349 freegline(gl);
350 gl = sgl;
351 } else {
352 gl->next = glinelist;
353 glinelist = gl;
354 }
355
31c690b7
GB
356 gl->glinebufid = id;
357
fe262e64 358 if (propagate)
cb581522 359 gline_propagate(gl);
ce11a200
GB
360 }
361
324b4e11 362 /* We've moved all glines to the global gline list. Clear glines link in the glinebuf. */
6eba1a37
GB
363 gbuf->glines = NULL;
364
0b2e8a55 365 glinebufabort(gbuf);
0c925dc0
GB
366
367 return id;
cb581522
GB
368}
369
0b2e8a55 370void glinebufabort(glinebuf *gbuf) {
cb581522 371 gline *gl, *next;
a887af59 372 int i;
cb581522 373
ce11a200 374 for (gl = gbuf->glines; gl; gl = next) {
cb581522
GB
375 next = gl->next;
376
377 freegline(gl);
378 }
0b2e8a55
GB
379
380 freesstring(gbuf->comment);
a887af59
GB
381
382 for (i = 0; i < gbuf->hits.cursi; i++)
383 freesstring(((sstring **)gbuf->hits.content)[i]);
384
385 array_free(&gbuf->hits);
0b2e8a55
GB
386}
387
388int glinebufundo(int id) {
389 glinebuf *gbl;
390 gline *gl, *sgl;
391 int i;
392
393 for (i = 0; i < MAXGLINELOG; i++) {
394 gbl = glinebuflog[i];
395
396 if (!gbl || gbl->id != id)
397 continue;
398
399 for (gl = gbl->glines; gl; gl = gl->next) {
400 sgl = findgline(glinetostring(gl));
401
402 if (!sgl)
403 continue;
31c690b7
GB
404
405 sgl->glinebufid = 0;
406
0b2e8a55
GB
407 gline_deactivate(sgl, 0, 1);
408 }
409
410 glinebufabort(gbl);
411 glinebuflog[i] = NULL;
412
413 return 1;
414 }
415
416 return 0;
cb581522 417}
ce11a200
GB
418
419void glinebufcommentf(glinebuf *gbuf, const char *format, ...) {
420 char comment[512];
421 va_list va;
422
423 va_start(va, format);
424 vsnprintf(comment, 511, format, va);
425 comment[511] = '\0';
426 va_end(va);
427
428 gbuf->comment = getsstring(comment, 512);
429}
430
431void glinebufcommentv(glinebuf *gbuf, const char *prefix, int cargc, char **cargv) {
432 char comment[512];
433 int i;
434
435 if (prefix)
436 strncpy(comment, prefix, sizeof(comment));
437 else
438 comment[0] = '\0';
439
440 for (i = 0; i < cargc; i++) {
441 if (comment[0])
442 strncat(comment, " ", sizeof(comment));
443
444 strncat(comment, cargv[i], sizeof(comment));
445 }
446
447 comment[511] = '\0';
448
449 gbuf->comment = getsstring(comment, 512);
450}
fe262e64
GB
451
452int glinebufwritelog(glinebuf *gbuf, int propagating) {
453 int i, slot;
454 gline *gl, *sgl;
455 glinebuf *gbl;
456
457 if (!gbuf->glines)
458 return 0; /* Don't waste log IDs on empty gline buffers */
459
460 gbl = NULL;
461
462 /* Find an existing log buffer with the same id */
463 if (gbuf->id != 0) {
464 for (i = 0; i < MAXGLINELOG; i++) {
465 if (!glinebuflog[i])
466 continue;
467
468 if (glinebuflog[i]->id == gbuf->id) {
469 gbl = glinebuflog[i];
470 gbl->amend = gbuf->commit;
471
472 /* We're going to re-insert this glinebuf, so remove it for now */
473 glinebuflog[i] = NULL;
474
475 break;
476 }
477 }
478 }
479
480 /* Find a recent glinebuf that's a close match */
481 if (!gbl && !propagating) {
482 for (i = 0; i < MAXGLINELOG; i++) {
483 if (!glinebuflog[i])
484 continue;
485
486 if (glinebuflog[i]->commit < getnettime() - 5 && glinebuflog[i]->amend < getnettime() - 5)
487 continue;
488
489 assert(glinebuflog[i]->glines);
490
491 if (strcmp(glinebuflog[i]->glines->creator->content, gbuf->glines->creator->content) != 0)
492 continue;
493
494 gbl = glinebuflog[i];
495 gbl->amend = gbuf->commit;
496
497 /* We're going to re-insert this glinebuf, so remove it for now */
498 glinebuflog[i] = NULL;
499
500 break;
501 }
502 }
503
504 /* Make a new glinebuf for the log */
505 if (!gbl && gbuf->id == 0) {
506 gbl = malloc(sizeof(glinebuf));
507 glinebufinit(gbl, (gbuf->id == 0) ? nextglinebufid++ : gbuf->id);
508
509 assert(gbl->hitsvalid);
510
511 if (gbuf->comment)
512 glinebufcommentf(gbl, "%s", gbuf->comment->content);
513 else if (!propagating)
ac034e04 514 glinebufcommentf(gbl, "G-Lines set by %s", gbuf->glines->creator->content);
fe262e64
GB
515
516 gbl->commit = gbuf->commit;
517 }
518
519 /* Save a duplicate of the glines in the log buffer */
520 for (gl = gbuf->glines; gl; gl = gl->next) {
521 sgl = glinedup(gl);
522 sgl->next = gbl->glines;
523 gbl->glines = sgl;
524 }
525
526 gbl->userhits += gbuf->userhits;
527 gbl->channelhits += gbuf->channelhits;
528
529 assert(gbuf->userhits + gbuf->channelhits == gbuf->hits.cursi);
530
531 for (i = 0; i < gbuf->hits.cursi; i++) {
532 slot = array_getfreeslot(&gbl->hits);
533 ((sstring **)gbl->hits.content)[slot] = getsstring(((sstring **)gbuf->hits.content)[i]->content, 512);
534 }
535
536 /* Log the transaction */
537 glinebuflogoffset++;
538
539 if (glinebuflogoffset >= MAXGLINELOG)
540 glinebuflogoffset = 0;
541
542 if (glinebuflog[glinebuflogoffset])
543 glinebufabort(glinebuflog[glinebuflogoffset]);
544
ac034e04 545 glinebuflog[glinebuflogoffset] = gbl;
fe262e64
GB
546
547 return gbl->id;
eb8f36ca 548}