]> jfr.im git - irc/quakenet/newserv.git/blob - regexgline/regexgline.c
Merge default.
[irc/quakenet/newserv.git] / regexgline / regexgline.c
1 /* regexgline.c */
2
3 /* TODO:
4
5 FUTURE: natural (sort of) language parsing
6 ^^ CIDR
7
8 PPA: if multiple users match the same user@host or *@host it'll send multiple glines?!
9 */
10
11 #include "regexgline.h"
12 #include "../lib/version.h"
13 #include "../dbapi/dbapi.h"
14 #include "../lib/stringbuf.h"
15 #include "../core/hooks.h"
16 #include "../server/server.h"
17 #include "../lib/strlfunc.h"
18 #include "../glines/glines.h"
19 #include <stdint.h>
20
21 #define INSTANT_IDENT_GLINE 1
22 #define INSTANT_HOST_GLINE 2
23 #define INSTANT_KILL 3
24 #define DELAYED_IDENT_GLINE 4
25 #define DELAYED_HOST_GLINE 5
26 #define DELAYED_KILL 6
27
28 #define RESERVED_NICK_GLINE_DURATION 3600 /* 1h */
29
30 MODULE_VERSION("1.44");
31
32 typedef struct rg_glinenode {
33 nick *np;
34 struct rg_struct *reason;
35 short punish;
36 struct rg_glinenode *next;
37 } rg_glinenode;
38
39 typedef struct rg_glinelist {
40 struct rg_glinenode *start;
41 struct rg_glinenode *end;
42 } rg_glinelist;
43
44 typedef struct rg_delay {
45 schedule *sch;
46 nick *np;
47 struct rg_struct *reason;
48 short punish;
49 struct rg_delay *next;
50 } rg_delay;
51
52 #define GLINE_HEADER " ID Expires Set by Class Type Last seen (ago) Hits(p) Hits Reason"
53
54 rg_delay *rg_delays;
55
56 void rg_setdelay(nick *np, struct rg_struct *reason, short punish);
57 void rg_deletedelay(rg_delay *delay);
58 void rg_dodelay(void *arg);
59
60 void rg_dogline(struct rg_glinelist *gll, nick *np, struct rg_struct *rp, char *matched);
61 void rg_flush_schedule(void *arg);
62
63 static char *gvhost(nick *np);
64 typedef void (scannick_fn)(struct rg_struct *, nick *, char *, void *);
65 static void rg_scannick(nick *np, scannick_fn *fn, void *arg);
66 static void rg_gline_match(struct rg_struct *rp, nick *np, char *hostname, void *arg);
67
68 static DBModuleIdentifier dbid;
69 static unsigned long highestid = 0;
70 static int attached = 0, started = 0;
71
72 static unsigned int getrgmarker(void);
73
74 #define RESERVED_NICK_CLASS "reservednick"
75 /* shadowserver only reports classes[0] */
76 static const char *classes[] = { "drone", "proxy", "spam", "other", RESERVED_NICK_CLASS, (char *)0 };
77
78 void rg_initglinelist(struct rg_glinelist *gll);
79 void rg_flushglines(struct rg_glinelist *gll);
80 static void rg_rename(int hooknum, void *arg);
81
82 void _init(void) {
83 sstring *max_casualties, *max_spew, *expiry_time, *max_per_gline;
84
85 max_casualties = getcopyconfigitem("regexgline", "maxcasualties", RGStringise(RG_MAX_CASUALTIES_DEFAULT), 8);
86 if(!protectedatoi(max_casualties->content, &rg_max_casualties))
87 rg_max_casualties = RG_MAX_CASUALTIES_DEFAULT;
88
89 freesstring(max_casualties);
90
91 max_spew = getcopyconfigitem("regexgline", "maxspew", RGStringise(RG_MAX_SPEW_DEFAULT), 8);
92 if(!protectedatoi(max_spew->content, &rg_max_spew))
93 rg_max_spew = RG_MAX_SPEW_DEFAULT;
94
95 freesstring(max_spew);
96
97 expiry_time = getcopyconfigitem("regexgline", "expirytime", RGStringise(RG_EXPIRY_TIME_DEFAULT), 8);
98 if(!protectedatoi(expiry_time->content, &rg_expiry_time))
99 rg_expiry_time = RG_EXPIRY_TIME_DEFAULT;
100
101 freesstring(expiry_time);
102
103 max_per_gline = getcopyconfigitem("regexgline", "maxpergline", RGStringise(RG_MAX_PER_GLINE_DEFAULT), 8);
104 if(!protectedatoi(max_per_gline->content, &rg_max_per_gline))
105 rg_max_per_gline = RG_MAX_PER_GLINE_DEFAULT;
106
107 freesstring(max_per_gline);
108
109 rg_delays = NULL;
110
111 if(dbconnected()) {
112 attached = 1;
113 dbid = dbgetid();
114 rg_dbload();
115 } else {
116 Error("regexgline", ERR_STOP, "Could not connect to database.");
117 }
118 }
119
120 static void rg_count_match(struct rg_struct *rp, nick *np, char *hostname, void *arg) {
121 void **varg = (void **)arg;
122 int *count = (int *)varg[0];
123
124 (*count)++;
125 }
126
127 static void rg_gline_reply_match(struct rg_struct *rp, nick *np, char *hostname, void *arg) {
128 void **varg = (void **)arg;
129
130 rg_count_match(rp, np, hostname, arg);
131 rg_gline_match(rp, np, hostname, varg[1]);
132 }
133
134 int rg_rescan(void *source, int cargc, char **cargv) {
135 int gline = 0;
136 int j;
137 nick *np = (nick *)source, *tnp;
138 void *arg[2];
139 int count = 0;
140 struct rg_glinelist gll;
141 scannick_fn *fn;
142
143 if(cargc > 0)
144 gline = !strcmp(cargv[0], "-g");
145
146 arg[0] = &count;
147
148 if(gline == 0) {
149 fn = rg_count_match;
150 } else {
151 controlreply(np, "G-line mode activated.");
152
153 rg_initglinelist(&gll);
154 arg[1] = &gll;
155
156 fn = rg_gline_reply_match;
157 }
158
159 controlreply(np, "Beginning scan, this may take a while...");
160
161 for(j=0;j<NICKHASHSIZE;j++)
162 for(tnp=nicktable[j];tnp;tnp=tnp->next)
163 rg_scannick(tnp, fn, arg);
164
165 controlreply(np, "Scan completed, %d hits.", count);
166
167 if(gline)
168 rg_flushglines(&gll);
169
170 return CMD_OK;
171 }
172
173 void _fini(void) {
174 struct rg_struct *gp = rg_list, *oldgp;
175 rg_delay *delay, *delaynext;
176
177 if(started) {
178 deregisterhook(HOOK_NICK_NEWNICK, &rg_nick);
179 deregisterhook(HOOK_NICK_RENAME, &rg_rename);
180 deregisterhook(HOOK_NICK_LOSTNICK, &rg_lostnick);
181 deregistercontrolcmd("regexspew", rg_spew);
182 deregistercontrolcmd("regexglist", rg_glist);
183 deregistercontrolcmd("regexdelgline", rg_delgline);
184 deregistercontrolcmd("regexgline", rg_gline);
185 deregistercontrolcmd("regexidlookup", rg_idlist);
186 deregistercontrolcmd("regexrescan", rg_rescan);
187 }
188
189 if(rg_delays) {
190 for(delay=rg_delays;delay;delay=delaynext) {
191 delaynext=delay->next;
192 deleteschedule(delay->sch, rg_dodelay, delay);
193 free(delay);
194 }
195 }
196
197 if(rg_schedule) {
198 deleteschedule(rg_schedule, &rg_checkexpiry, NULL);
199 rg_schedule = NULL;
200 }
201
202 deleteallschedules(rg_flush_schedule);
203 rg_flush_schedule(NULL);
204
205 for(gp=rg_list;gp;) {
206 oldgp = gp;
207 gp = gp->next;
208 rg_freestruct(oldgp);
209 }
210
211 if(attached) {
212 dbdetach("regexgline");
213 dbfreeid(dbid);
214 }
215 }
216
217 static int ignorable_nick(nick *np) {
218 if(IsOper(np) || IsService(np) || IsXOper(np) || SIsService(&serverlist[homeserver(np->numeric)]))
219 return 1;
220 return 0;
221 }
222
223 void rg_checkexpiry(void *arg) {
224 struct rg_struct *rp = rg_list, *lp = NULL;
225 time_t current = time(NULL);
226
227 while(rp) {
228 if (current >= rp->expires) {
229 dbquery("DELETE FROM regexglines WHERE id = %d", rp->id);
230 if (lp) {
231 lp->next = rp->next;
232 rg_freestruct(rp);
233 rp = lp->next;
234 } else {
235 rg_list = rp->next;
236 rg_freestruct(rp);
237 rp = rg_list;
238 }
239 } else {
240 lp = rp;
241 rp = rp->next;
242 }
243 }
244 }
245
246 void rg_setdelay(nick *np, rg_struct *reason, short punish) {
247 rg_delay *delay;
248 delay = (rg_delay *)malloc(sizeof(rg_delay));
249
250 /* Just incase */
251 if(!delay) {
252 killuser(NULL, np, "%s (ID: %08lx)", reason->reason->content, reason->glineid);
253 return;
254 }
255
256 delay->np = np;
257 delay->reason = reason;
258 delay->punish = punish;
259 delay->next = rg_delays;
260 rg_delays = delay;
261
262 delay->sch = scheduleoneshot(time(NULL) + (RG_MINIMUM_DELAY_TIME + (rand() % RG_MAXIMUM_RAND_TIME)), rg_dodelay, delay);
263 }
264
265 static void rg_shadowserver(nick *np, struct rg_struct *reason, int type) {
266 char buf[1024];
267
268 if(reason->class != classes[0]) /* drone */
269 return;
270
271 snprintf(buf, sizeof(buf), "regex-ban %lu %s!%s@%s %s %s", time(NULL), np->nick, np->ident, np->host->name->content, reason->mask->content, serverlist[homeserver(np->numeric)].name->content);
272
273 triggerhook(HOOK_SHADOW_SERVER, (void *)buf);
274 }
275
276 void rg_deletedelay(rg_delay *delay) {
277 rg_delay *temp, *prev;
278 prev = NULL;
279 for (temp=rg_delays;temp;temp=temp->next) {
280 if (temp==delay) {
281 if (temp==rg_delays)
282 rg_delays = temp->next;
283 else
284 prev->next = temp->next;
285
286 free(temp);
287 return;
288 }
289
290 prev = temp;
291 }
292 }
293
294 void rg_dodelay(void *arg) {
295 rg_delay *delay = (rg_delay *)arg;
296 char hostname[RG_MASKLEN];
297 int hostlen, usercount = 0;
298 char reason[200];
299 int glineflags = 0;
300
301 /* User or regex gline no longer exists */
302 if((!delay->np) || (!delay->reason)) {
303 rg_deletedelay(delay);
304 return;
305 }
306
307 hostlen = RGBuildHostname(hostname, delay->np);
308
309 /* User has wisely changed nicknames */
310 if(pcre_exec(delay->reason->regex, delay->reason->hint, hostname, hostlen, 0, 0, NULL, 0) < 0) {
311 rg_deletedelay(delay);
312 return;
313 }
314
315 if (delay->reason->type == DELAYED_HOST_GLINE) {
316 usercount = delay->np->host->clonecount;
317 }
318
319 if((delay->reason->type == DELAYED_IDENT_GLINE) || (usercount > rg_max_per_gline)) {
320 nick *tnp;
321
322 for(usercount=0,tnp=delay->np->host->nicks;tnp;tnp=tnp->nextbyhost)
323 if(!ircd_strcmp(delay->np->ident, tnp->ident))
324 usercount++;
325
326 glineflags = GLINE_ALWAYS_USER;
327 }
328
329 if ((delay->reason->type == DELAYED_KILL) || (usercount > rg_max_per_gline)) {
330 controlwall(NO_OPER, NL_HITS, "%s matched delayed kill regex %08lx (class: %s)", gvhost(delay->np), delay->reason->glineid, delay->reason->class);
331
332 rg_shadowserver(delay->np, delay->reason, DELAYED_KILL);
333 killuser(NULL, delay->np, "%s (ID: %08lx)", delay->reason->reason->content, delay->reason->glineid);
334 return;
335 }
336
337 if (delay->reason->type == DELAYED_IDENT_GLINE) {
338 controlwall(NO_OPER, NL_HITS, "%s matched delayed user@host gline regex %08lx (class: %s, hit %d user%s)", gvhost(delay->np), delay->reason->glineid, delay->reason->class, usercount, (usercount!=1)?"s":"");
339 } else if (delay->reason->type == DELAYED_HOST_GLINE) {
340 controlwall(NO_OPER, NL_HITS, "%s matched delayed *@host gline regex %08lx (class: %s, hit %d user%s)", gvhost(delay->np), delay->reason->glineid, delay->reason->class, usercount, (usercount!=1)?"s":"");
341 } else {
342 return;
343 }
344
345 rg_shadowserver(delay->np, delay->reason, delay->reason->type);
346 snprintf(reason, sizeof(reason), "AUTO: %s (ID: %08lx)", delay->reason->reason->content, delay->reason->glineid);
347 glinebynick(delay->np, rg_expiry_time, reason, glineflags, "regexgline");
348 rg_deletedelay(delay);
349 }
350
351 void rg_initglinelist(struct rg_glinelist *gll) {
352 gll->start = NULL;
353 gll->end = NULL;
354 }
355
356 void rg_flushglines(struct rg_glinelist *gll) {
357 struct rg_glinenode *nn, *pn;
358 for(nn=gll->start;nn;nn=pn) {
359 pn = nn->next;
360 if(nn->punish == INSTANT_KILL) {
361 controlwall(NO_OPER, NL_HITS, "%s matched kill regex %08lx (class: %s)", gvhost(nn->np), nn->reason->glineid, nn->reason->class);
362
363 rg_shadowserver(nn->np, nn->reason, nn->punish);
364 killuser(NULL, nn->np, "%s (ID: %08lx)", nn->reason->reason->content, nn->reason->glineid);
365 } else if ((nn->punish == DELAYED_IDENT_GLINE) || (nn->punish == DELAYED_HOST_GLINE) || (nn->punish == DELAYED_KILL)) {
366 rg_setdelay(nn->np, nn->reason, nn->punish);
367 }
368 free(nn);
369 }
370
371 rg_initglinelist(gll);
372 }
373
374 static void dbloaddata(DBConn *dbconn, void *arg) {
375 DBResult *dbres = dbgetresult(dbconn);
376
377 if(!dbquerysuccessful(dbres)) {
378 Error("chanserv", ERR_ERROR, "Error loading DB");
379 return;
380 }
381
382 if (dbnumfields(dbres) != 9) {
383 Error("regexgline", ERR_ERROR, "DB format error");
384 return;
385 }
386
387 while(dbfetchrow(dbres)) {
388 unsigned long id, hitssaved;
389 time_t lastseen;
390 char *gline, *setby, *reason, *expires, *type, *class;
391
392 id = strtoul(dbgetvalue(dbres, 0), NULL, 10);
393 if(id > highestid)
394 highestid = id;
395
396 gline = dbgetvalue(dbres, 1);
397 setby = dbgetvalue(dbres, 2);
398 reason = dbgetvalue(dbres, 3);
399 expires = dbgetvalue(dbres, 4);
400 type = dbgetvalue(dbres, 5);
401 class = dbgetvalue(dbres, 6);
402
403 lastseen = strtoul(dbgetvalue(dbres, 7), NULL, 10);
404 hitssaved = strtoul(dbgetvalue(dbres, 8), NULL, 10);
405
406 if (!rg_newsstruct(id, gline, setby, reason, expires, type, 0, class, lastseen, hitssaved))
407 dbquery("DELETE FROM regexgline.glines WHERE id = %lu", id);
408 }
409
410 dbclear(dbres);
411 }
412
413 static void dbloadfini(DBConn *dbconn, void *arg) {
414 started = 1;
415 StringBuf b;
416 const char **p;
417 char helpbuf[8192 * 2], allclasses[8192];
418
419 sbinit(&b, (char *)allclasses, sizeof(allclasses));
420 for(p=classes;*p;p++) {
421 sbaddstr(&b, (char *)*p);
422 sbaddchar(&b, ' ');
423 }
424 sbterminate(&b);
425
426 snprintf(helpbuf, sizeof(helpbuf),
427 "Usage: regexgline <regex> <duration> <type> <class> <reason>\n"
428 "Adds a new regular expression pattern.\n"
429 "Duration is represented as 3d, 3M etc.\n"
430 "Class is one of the following: %s\n"
431 "Type is an integer which represents the following:\n"
432 "1 - Instant USER@IP GLINE (igu)\n"
433 "2 - Instant *@IP GLINE (igh)\n"
434 "3 - Instant KILL (ik)\n"
435 "4 - Delayed USER@IP GLINE (dgu)\n"
436 "5 - Delayed *@IP GLINE (dgh)\n"
437 "6 - Delayed KILL (dk)\n"
438 "Note that some classes may have additional side effects (e.g. 'reservednick' also sets nick style glines).",
439 allclasses);
440
441 registercontrolhelpcmd("regexgline", NO_OPER, 5, &rg_gline, helpbuf);
442 registercontrolhelpcmd("regexdelgline", NO_OPER, 1, &rg_delgline, "Usage: regexdelgline <pattern>\nDeletes a regular expression pattern.");
443 registercontrolhelpcmd("regexglist", NO_OPER, 1, &rg_glist, "Usage: regexglist <pattern>\nLists regular expression patterns.");
444 registercontrolhelpcmd("regexspew", NO_OPER, 1, &rg_spew, "Usage: regexspew <pattern>\nLists users currently on the network which match the given pattern.");
445 registercontrolhelpcmd("regexidlookup", NO_OPER, 1, &rg_idlist, "Usage: regexidlookup <id>\nFinds a regular expression pattern by it's ID number.");
446 registercontrolhelpcmd("regexrescan", NO_OPER, 1, &rg_rescan, "Usage: regexrescan ?-g?\nRescans the net for missed clients, optionally glining matches (used for debugging).");
447
448 registerhook(HOOK_NICK_NEWNICK, &rg_nick);
449 registerhook(HOOK_NICK_RENAME, &rg_rename);
450 registerhook(HOOK_NICK_LOSTNICK, &rg_lostnick);
451 rg_startup();
452
453 rg_schedule = schedulerecurring(time(NULL) + 1, 0, 1, rg_checkexpiry, NULL);
454 schedulerecurring(time(NULL) + 60, 0, 60, rg_flush_schedule, NULL);
455 }
456
457 void rg_dbload(void) {
458 dbattach("regexgline");
459 dbcreatequery("CREATE TABLE regexgline.glines (id INT NOT NULL PRIMARY KEY, gline TEXT NOT NULL, setby VARCHAR(%d) NOT NULL, reason VARCHAR(%d) NOT NULL, expires INT NOT NULL, type INT NOT NULL DEFAULT 1, class TEXT NOT NULL, lastseen INT DEFAULT 0, hits INT DEFAULT 0)", ACCOUNTLEN, RG_REASON_MAX);
460 dbcreatequery("CREATE TABLE regexgline.clog (host VARCHAR(%d) NOT NULL, account VARCHAR(%d) NOT NULL, event TEXT NOT NULL, arg TEXT NOT NULL, ts TIMESTAMP)", RG_MASKLEN - 1, ACCOUNTLEN);
461 dbcreatequery("CREATE TABLE regexgline.glog (glineid INT NOT NULL, ts TIMESTAMP, nickname VARCHAR(%d) NOT NULL, username VARCHAR(%d) NOT NULL, hostname VARCHAR(%d) NOT NULL, realname VARCHAR(%d))", NICKLEN, USERLEN, HOSTLEN, REALLEN);
462
463 dbloadtable("regexgline.glines", NULL, dbloaddata, dbloadfini);
464 }
465
466 static void rg_scannick(nick *np, scannick_fn *fn, void *arg) {
467 struct rg_struct *rp;
468 char hostname[RG_MASKLEN];
469 int hostlen;
470
471 if(ignorable_nick(np))
472 return;
473
474 hostlen = RGBuildHostname(hostname, np);
475
476 for(rp=rg_list;rp;rp=rp->next) {
477 if(pcre_exec(rp->regex, rp->hint, hostname, hostlen, 0, 0, NULL, 0) >= 0) {
478 fn(rp, np, hostname, arg);
479 break;
480 }
481 }
482 }
483
484 static void rg_gline_match(struct rg_struct *rp, nick *np, char *hostname, void *arg) {
485 struct rg_glinelist *gll = (struct rg_glinelist *)arg;
486
487 rg_dogline(gll, np, rp, hostname);
488 }
489
490 void rg_rename(int hooknum, void *arg) {
491 void **harg = (void **)arg;
492 rg_nick(hooknum, harg[0]);
493 }
494
495 void rg_nick(int hooknum, void *arg) {
496 nick *np = (nick *)arg;
497 struct rg_glinelist gll;
498
499 rg_initglinelist(&gll);
500
501 rg_scannick(np, rg_gline_match, &gll);
502
503 rg_flushglines(&gll);
504 }
505
506 void rg_lostnick(int hooknum, void *arg) {
507 nick *np = (nick *)arg;
508 rg_delay *delay;
509
510 /* Cleanup the delays */
511 for(delay=rg_delays;delay;delay=delay->next)
512 if(delay->np==np)
513 delay->np = NULL;
514 }
515
516 int rg_gline(void *source, int cargc, char **cargv) {
517 nick *np = (nick *)source, *tnp;
518 time_t realexpiry;
519 const char *expirybuf;
520 int expiry, count, j, hostlen;
521 struct rg_struct *rp;
522 struct rg_glinelist gll;
523 const char **p;
524
525 char eemask[RG_QUERY_BUF_SIZE], eesetby[RG_QUERY_BUF_SIZE], eereason[RG_QUERY_BUF_SIZE], eeclass[RG_QUERY_BUF_SIZE];
526 char hostname[RG_MASKLEN], *class, *reason, *regex, type;
527
528 if(cargc < 5)
529 return CMD_USAGE;
530
531 type = cargv[2][0];
532 if ((strlen(cargv[2]) != 1) || ((type != '1') && (type != '2') && (type != '3') && (type != '4') && (type != '5') && (type != '6'))) {
533 controlreply(np, "Invalid type specified!");
534 return CMD_USAGE;
535 }
536
537 regex = cargv[0];
538 class = cargv[3];
539 reason = cargv[4];
540
541 for(p=classes;*p;p++)
542 if(!strcasecmp(class, *p))
543 break;
544
545 if(!*p) {
546 controlreply(np, "Bad class supplied.");
547 return CMD_USAGE;
548 }
549
550 if (!(expiry = durationtolong(cargv[1]))) {
551 controlreply(np, "Invalid duration specified!");
552 return CMD_USAGE;
553 }
554
555 for(rp=rg_list;rp;rp=rp->next) {
556 if (RGMasksEqual(rp->mask->content, regex)) {
557 controlreply(np, "That regexgline already exists!");
558 return CMD_ERROR;
559 }
560 }
561
562 if (rg_sanitycheck(regex, &count)) {
563 controlreply(np, "Error in expression.");
564 return CMD_ERROR;
565 } else if (count < 0) {
566 controlreply(np, "That expression would hit too many users (%d)!", -count);
567 return CMD_ERROR;
568 }
569
570 realexpiry = expiry + time(NULL);
571
572 dbescapestring(eemask, regex, strlen(regex));
573 dbescapestring(eesetby, np->nick, strlen(np->nick));
574 dbescapestring(eeclass, class, strlen(class));
575 dbescapestring(eereason, reason, strlen(reason));
576
577 highestid = highestid + 1;
578 dbquery("INSERT INTO regexgline.glines (id, gline, setby, reason, expires, type, class, lastseen, hits) VALUES (%lu, '%s', '%s', '%s', %lu, %c, '%s', 0, 0)", highestid, eemask, eesetby, eereason, realexpiry, type, eeclass);
579 rp = rg_newsstruct(highestid, regex, np->nick, reason, "", cargv[2], realexpiry, class, 0, 0);
580
581 rg_initglinelist(&gll);
582
583 for(j=0;j<NICKHASHSIZE;j++) {
584 for(tnp=nicktable[j];tnp;tnp=tnp->next) {
585 if(ignorable_nick(tnp))
586 continue;
587
588 hostlen = RGBuildHostname(hostname, tnp);
589 if(pcre_exec(rp->regex, rp->hint, hostname, hostlen, 0, 0, NULL, 0) >= 0)
590 rg_dogline(&gll, tnp, rp, hostname);
591 }
592 }
593
594 rg_flushglines(&gll);
595
596 expirybuf = longtoduration(expiry, 0);
597
598 rg_logevent(np, "regexgline", "%s %d %d %s %s", regex, expiry, count, class, reason);
599 controlreply(np, "Added regexgline: %s (class: %s, expires in: %s, hit %d user%s): %s", regex, class, expirybuf, count, (count!=1)?"s":"", reason);
600 /* If we are using NO, can we safely assume the user is authed here and use ->authname? */
601 controlwall(NO_OPER, NL_GLINES, "%s!%s@%s/%s added regexgline: %s (class: %s, expires in: %s, hit %d user%s): %s", np->nick, np->ident, np->host->name->content, np->authname, regex, class, expirybuf, count, (count!=1)?"s":"", reason);
602
603 return CMD_OK;
604 }
605
606 int rg_sanitycheck(char *mask, int *count) {
607 const char *error;
608 char hostname[RG_MASKLEN];
609 int erroroffset, hostlen, j, masklen = strlen(mask);
610 pcre *regex;
611 pcre_extra *hint;
612 nick *np;
613
614 if((masklen < RG_MIN_MASK_LEN) || (masklen > RG_REGEXGLINE_MAX))
615 return 1;
616
617 if(!(regex = pcre_compile(mask, RG_PCREFLAGS, &error, &erroroffset, NULL))) {
618 Error("regexgline", ERR_WARNING, "Error compiling expression %s at offset %d: %s", mask, erroroffset, error);
619 return 2;
620 } else {
621 hint = pcre_study(regex, 0, &error);
622 if(error) {
623 Error("regexgline", ERR_WARNING, "Error studying expression %s: %s", mask, error);
624 pcre_free(regex);
625 return 3;
626 }
627 }
628
629 *count = 0;
630 for(j=0;j<NICKHASHSIZE;j++) {
631 for(np=nicktable[j];np;np=np->next) {
632 hostlen = RGBuildHostname(hostname, np);
633 if(pcre_exec(regex, hint, hostname, hostlen, 0, 0, NULL, 0) >= 0) {
634 (*count)++;
635 }
636 }
637 }
638
639 pcre_free(regex);
640 if(hint)
641 pcre_free(hint);
642
643 if(*count >= rg_max_casualties)
644 *count = -(*count);
645
646 return 0;
647 }
648
649 int rg_delgline(void *source, int cargc, char **cargv) {
650 nick *np = (nick *)source;
651 rg_delay *delay;
652 struct rg_struct *rp = rg_list, *last = NULL;
653 int count = 0;
654
655 if(cargc < 1)
656 return CMD_USAGE;
657
658 rg_logevent(np, "regexdelgline", "%s", cargv[0]);
659 while(rp) {
660 if(RGMasksEqual(rp->mask->content, cargv[0])) {
661 count++;
662
663 /* Cleanup the delays */
664 for(delay=rg_delays;delay;delay=delay->next)
665 if(delay->reason==rp)
666 delay->reason = NULL;
667
668 dbquery("DELETE FROM regexgline.glines WHERE id = %d", rp->id);
669 if(last) {
670 last->next = rp->next;
671 rg_freestruct(rp);
672 rp = last->next;
673 } else {
674 rg_list = rp->next;
675 rg_freestruct(rp);
676 rp = rg_list;
677 }
678 } else {
679 last = rp;
680 rp = rp->next;
681 }
682 }
683 if (count > 0) {
684 controlreply(np, "Deleted (matched: %d).", count);
685 /* If we are using NO, can we safely assume the user is authed here and use ->authname? */
686 controlwall(NO_OPER, NL_GLINES, "%s!%s@%s/%s removed regexgline: %s", np->nick, np->ident, np->host->name->content, np->authname, cargv[0]);
687 } else {
688 controlreply(np, "No glines matched: %s", cargv[0]);
689 }
690 return CMD_OK;
691 }
692
693 int rg_idlist(void *source, int cargc, char **cargv) {
694 nick *np = (nick *)source;
695
696 if(cargc < 1) {
697 return CMD_USAGE;
698 } else if (strlen(cargv[0]) != 8) {
699 controlreply(np, "Invalid gline id!");
700 return CMD_ERROR;
701 } else {
702 struct rg_struct *rp;
703 unsigned long id = 0;
704 int i, longest = 0;
705 unsigned int m;
706
707 for(i=0;i<8;i++) {
708 if(0xff == rc_hexlookup[(int)cargv[0][i]]) {
709 controlreply(np, "Invalid gline id!");
710 return CMD_ERROR;
711 } else {
712 id = (id << 4) | rc_hexlookup[(int)cargv[0][i]];
713 }
714 }
715
716 m = getrgmarker();
717 controlreply(np, GLINE_HEADER);
718 for(rp=rg_list;rp;rp=rp->next) {
719 if(id == rp->glineid) {
720 rp->marker = m;
721 if(rp->mask->length > longest)
722 longest = rp->mask->length;
723 }
724 }
725
726 for(rp=rg_list;rp;rp=rp->next)
727 if(rp->marker == m)
728 rg_displaygline(np, rp, longest);
729 controlreply(np, "Done.");
730
731 return CMD_OK;
732 }
733 }
734
735 int rg_glist(void *source, int cargc, char **cargv) {
736 nick *np = (nick *)source;
737 struct rg_struct *rp;
738 int longest = 0;
739
740 if(cargc) {
741 int erroroffset;
742 pcre *regex;
743 pcre_extra *hint;
744 const char *error;
745 unsigned int m;
746
747 if(!(regex = pcre_compile(cargv[0], RG_PCREFLAGS, &error, &erroroffset, NULL))) {
748 controlreply(np, "Error compiling expression %s at offset %d: %s", cargv[0], erroroffset, error);
749 return CMD_ERROR;
750 } else {
751 hint = pcre_study(regex, 0, &error);
752 if(error) {
753 controlreply(np, "Error studying expression %s: %s", cargv[0], error);
754 pcre_free(regex);
755 return CMD_ERROR;
756 }
757 }
758
759 m = getrgmarker();
760 rg_logevent(np, "regexglist", "%s", cargv[0]);
761 controlreply(np, GLINE_HEADER);
762 for(rp=rg_list;rp;rp=rp->next) {
763 if(pcre_exec(regex, hint, rp->mask->content, rp->mask->length, 0, 0, NULL, 0) >= 0) {
764 rp->marker = m;
765 if(rp->mask->length > longest)
766 longest = rp->mask->length;
767 }
768 }
769
770 for(rp=rg_list;rp;rp=rp->next)
771 if(rp->marker == m)
772 rg_displaygline(np, rp, longest);
773
774 pcre_free(regex);
775 if(hint)
776 pcre_free(hint);
777
778 } else {
779 rg_logevent(np, "regexglist", "%s", "");
780 controlreply(np, GLINE_HEADER);
781 for(rp=rg_list;rp;rp=rp->next)
782 if(rp->mask->length > longest)
783 longest = rp->mask->length;
784
785 for(rp=rg_list;rp;rp=rp->next)
786 rg_displaygline(np, rp, longest);
787 }
788
789 controlreply(np, "Done.");
790 return CMD_OK;
791 }
792
793 char *displaytype(int type) {
794 char *ctype;
795 static char ctypebuf[10];
796
797 switch(type) {
798 case 1:
799 ctype = "igu";
800 break;
801 case 2:
802 ctype = "igh";
803 break;
804 case 3:
805 ctype = "ik";
806 break;
807 case 4:
808 ctype = "dgu";
809 break;
810 case 5:
811 ctype = "dgh";
812 break;
813 case 6:
814 ctype = "dk";
815 break;
816 default:
817 ctype = "??";
818 }
819
820 snprintf(ctypebuf, sizeof(ctype), "%1d:%s", type, ctype);
821 return ctypebuf;
822 }
823
824 char *getsep(int longest) {
825 static int lastlongest = -1;
826 static char lenbuf[1024];
827
828 longest = 125;
829 /*
830 if(longest < 100)
831 longest = 100;
832
833 if(longest >= sizeof(lenbuf) - 20)
834 longest = sizeof(lenbuf) - 20;
835 */
836 longest+=4;
837 if(lastlongest == -1) {
838 int i;
839
840 for(i=0;i<sizeof(lenbuf)-1;i++)
841 lenbuf[i] = '-';
842 lenbuf[sizeof(lenbuf)-1] = '\0';
843 lastlongest = 0;
844 }
845
846 if(lastlongest != longest) {
847 lenbuf[lastlongest] = '-';
848 lenbuf[longest] = '\0';
849 lastlongest = longest;
850 }
851
852 return lenbuf;
853 }
854
855 void rg_displaygline(nick *np, struct rg_struct *rp, int longest) { /* could be a macro? I'll assume the C compiler inlines it */
856 char *sep = getsep(longest);
857 /* 12345678 12345678901234567890 123456789012345 12345678 12345 12345678901234567890 1234567 1234567 123456
858 ID Expires Set by Class Type Last seen (ago) Hits(s) Hits Reason
859 */
860
861 char d[512];
862 time_t t = time(NULL);
863
864 if(rp->lastseen == 0) {
865 strlcpy(d, "(never)", sizeof(d));
866 } else {
867 strlcpy(d, longtoduration(t - rp->lastseen, 2), sizeof(d));
868 }
869
870 controlreply(np, "%s", rp->mask->content);
871 controlreply(np, " %08lx %-20s %-15s %-8s %-5s %-20s %-7lu %-7lu %s", rp->glineid, longtoduration(rp->expires - t, 2), rp->setby->content, rp->class, displaytype(rp->type), d, rp->hitssaved, rp->hits, rp->reason->content);
872 controlreply(np, "%s", sep);
873 }
874
875 int rg_spew(void *source, int cargc, char **cargv) {
876 nick *np = (nick *)source, *tnp;
877 int counter = 0, erroroffset, hostlen, j;
878 pcre *regex;
879 pcre_extra *hint;
880 const char *error;
881 char hostname[RG_MASKLEN];
882 int ovector[30];
883 int pcreret;
884
885 if(cargc < 1)
886 return CMD_USAGE;
887
888 if(!(regex = pcre_compile(cargv[0], RG_PCREFLAGS, &error, &erroroffset, NULL))) {
889 controlreply(np, "Error compiling expression %s at offset %d: %s", cargv[0], erroroffset, error);
890 return CMD_ERROR;
891 } else {
892 hint = pcre_study(regex, 0, &error);
893 if(error) {
894 controlreply(np, "Error studying expression %s: %s", cargv[0], error);
895 pcre_free(regex);
896 return CMD_ERROR;
897 }
898 }
899
900 rg_logevent(np, "regexspew", "%s", cargv[0]);
901
902 for(j=0;j<NICKHASHSIZE;j++) {
903 for(tnp=nicktable[j];tnp;tnp=tnp->next) {
904 hostlen = RGBuildHostname(hostname, tnp);
905 pcreret = pcre_exec(regex, hint, hostname, hostlen, 0, 0, ovector, sizeof(ovector) / sizeof(int));
906 if(pcreret >= 0) {
907 if(counter == rg_max_spew) {
908 controlreply(np, "Reached maximum spew count (%d) - aborting display.", rg_max_spew);
909 } else if (counter < rg_max_spew) {
910 /* 15 should be number of bolds */
911 char boldbuf[RG_MASKLEN + 15], *tp, *fp, *realname = NULL;
912 int boldon = 0;
913 for(tp=hostname,fp=boldbuf;*tp;) {
914 if(tp - hostname == ovector[0]) {
915 *fp++ = '\002';
916 boldon = 1;
917 }
918 if(tp - hostname == ovector[1]) {
919 *fp++ = '\002';
920 boldon = 0;
921 }
922 if(*tp == '\r') {
923 if(boldon)
924 *fp++ = '\002';
925 *fp++ = '\0';
926 realname = fp;
927 if(boldon)
928 *fp++ = '\002';
929 tp++;
930 } else {
931 *fp++ = *tp++;
932 }
933 }
934 if(boldon)
935 *fp++ = '\002';
936 *fp++ = '\0';
937 controlreply(np, "%s (%s) (%dc)", boldbuf, realname, tnp->channels->cursi);
938 }
939 counter++;
940 }
941 }
942 }
943 controlreply(np, "Done - %d matches.", counter);
944
945 pcre_free(regex);
946 if(hint)
947 pcre_free(hint);
948
949 return CMD_OK;
950 }
951
952 void rg_startup(void) {
953 int j;
954 nick *np;
955 struct rg_glinelist gll;
956
957 rg_initglinelist(&gll);
958
959 for(j=0;j<NICKHASHSIZE;j++)
960 for(np=nicktable[j];np;np=np->next)
961 rg_scannick(np, rg_gline_match, &gll);
962
963 rg_flushglines(&gll);
964 }
965
966 void rg_freestruct(struct rg_struct *rp) {
967 freesstring(rp->mask);
968 freesstring(rp->setby);
969 freesstring(rp->reason);
970 pcre_free(rp->regex);
971 if(rp->hint)
972 pcre_free(rp->hint);
973 free(rp);
974 }
975
976 struct rg_struct *rg_newstruct(time_t expires) {
977 struct rg_struct *rp;
978
979 if (time(NULL) >= expires)
980 return NULL;
981
982 rp = (struct rg_struct*)malloc(sizeof(struct rg_struct));
983 if(rp) {
984 struct rg_struct *tp, *lp;
985
986 memset(rp, 0, sizeof(rg_struct));
987 rp->expires = expires;
988
989 for(lp=NULL,tp=rg_list;tp;lp=tp,tp=tp->next) {
990 if (expires <= tp->expires) { /* <= possible, slight speed increase */
991 rp->next = tp;
992 if (lp) {
993 lp->next = rp;
994 } else {
995 rg_list = rp;
996 }
997 break;
998 }
999 }
1000 if (!tp) {
1001 rp->next = NULL;
1002 if (lp) {
1003 lp->next = rp;
1004 } else {
1005 rg_list = rp;
1006 }
1007 }
1008
1009 }
1010 return rp;
1011 }
1012
1013 struct rg_struct *rg_newsstruct(unsigned long id, char *mask, char *setby, char *reason, char *expires, char *type, time_t iexpires, char *class, time_t lastseen, unsigned int hitssaved) {
1014 struct rg_struct *newrow, *lp, *cp;
1015 time_t rexpires;
1016 char glineiddata[1024];
1017 const char **p;
1018
1019 if (iexpires == 0) {
1020 int qexpires;
1021 if(!protectedatoi(expires, &qexpires))
1022 return NULL;
1023 rexpires = (time_t)qexpires;
1024 } else {
1025 rexpires = iexpires;
1026 }
1027
1028 newrow = rg_newstruct(rexpires);
1029
1030 if(newrow) {
1031 const char *error;
1032 int erroroffset;
1033
1034 for(p=classes;*p;p++) {
1035 if(!strcasecmp(class, *p)) {
1036 newrow->class = *p;
1037 break;
1038 }
1039 }
1040
1041 if(!*p)
1042 newrow->class = "unknown";
1043
1044 if(!(newrow->regex = pcre_compile(mask, RG_PCREFLAGS, &error, &erroroffset, NULL))) {
1045 Error("regexgline", ERR_WARNING, "Error compiling expression %s at offset %d: %s", mask, erroroffset, error);
1046 goto dispose;
1047 } else {
1048 newrow->hint = pcre_study(newrow->regex, 0, &error);
1049 if(error) {
1050 Error("regexgline", ERR_WARNING, "Error studying expression %s: %s", mask, error);
1051 pcre_free(newrow->regex);
1052 goto dispose;
1053 }
1054 }
1055
1056 newrow->id = id;
1057 newrow->hitssaved = hitssaved;
1058 newrow->lastseen = lastseen;
1059
1060 newrow->mask = getsstring(mask, RG_REGEXGLINE_MAX);
1061 if(!newrow->mask) {
1062 Error("regexgline", ERR_WARNING, "Error allocating memory for mask!");
1063 goto dispose2;
1064 }
1065
1066 newrow->setby = getsstring(setby, ACCOUNTLEN);
1067 if(!newrow->setby) {
1068 Error("regexgline", ERR_WARNING, "Error allocating memory for setby!");
1069 goto dispose2;
1070 }
1071
1072 newrow->reason = getsstring(reason, RG_REASON_MAX);
1073 if(!newrow->reason) {
1074 Error("regexgline", ERR_WARNING, "Error allocating memory for reason!");
1075 goto dispose2;
1076 }
1077
1078 if(!protectedatoi(type, &newrow->type))
1079 newrow->type = 0; /* just in case */
1080
1081 snprintf(glineiddata, sizeof(glineiddata), "%s regexgline %s %s %s %d %d", mynumeric->content, mask, setby, reason, (int)iexpires, newrow->type);
1082 newrow->glineid = crc32(glineiddata);
1083 }
1084
1085 return newrow;
1086
1087 dispose2:
1088 if(newrow->mask)
1089 freesstring(newrow->mask);
1090 if(newrow->setby)
1091 freesstring(newrow->setby);
1092 if(newrow->reason)
1093 freesstring(newrow->reason);
1094 pcre_free(newrow->regex);
1095 if(newrow->hint)
1096 pcre_free(newrow->hint);
1097
1098 dispose:
1099 for(lp=NULL,cp=rg_list;cp;lp=cp,cp=cp->next) {
1100 if(newrow == cp) {
1101 if(lp) {
1102 lp->next = cp->next;
1103 } else {
1104 rg_list = cp->next;
1105 }
1106 free(newrow);
1107 break;
1108 }
1109 }
1110 return NULL;
1111 }
1112
1113 int __rg_dogline(struct rg_glinelist *gll, nick *np, struct rg_struct *rp, char *matched) { /* PPA: if multiple users match the same user@host or *@host it'll send multiple glines?! */
1114 int usercount = 0;
1115 int validdelay;
1116 char reason[200];
1117 int glineflags = 0;
1118
1119 rg_loggline(rp, np);
1120
1121 if (rp->type == INSTANT_HOST_GLINE) {
1122 usercount = np->host->clonecount;
1123 }
1124
1125 if ((rp->type == INSTANT_IDENT_GLINE) || (usercount > rg_max_per_gline)) {
1126 nick *tnp;
1127
1128 for(usercount=0,tnp=np->host->nicks;tnp;tnp=tnp->nextbyhost)
1129 if(!ircd_strcmp(np->ident, tnp->ident))
1130 usercount++;
1131
1132 glineflags = GLINE_ALWAYS_USER;
1133 }
1134
1135 if(!strcmp(rp->class, RESERVED_NICK_CLASS)) {
1136 char reason[512];
1137 snprintf(reason, sizeof(reason), "AUTO %s (ID: %08lx)", rp->reason->content, rp->glineid);
1138 glinebynick(np, RESERVED_NICK_GLINE_DURATION, reason, GLINE_ALWAYS_NICK, "regexgline");
1139 }
1140
1141 validdelay = (rp->type == INSTANT_KILL) || (rp->type == DELAYED_IDENT_GLINE) || (rp->type == DELAYED_HOST_GLINE) || (rp->type == DELAYED_KILL);
1142 if (validdelay || (usercount > rg_max_per_gline)) {
1143 struct rg_glinenode *nn = (struct rg_glinenode *)malloc(sizeof(struct rg_glinenode));
1144 if(nn) {
1145 nn->next = NULL;
1146 if(gll->end) {
1147 gll->end->next = nn;
1148 gll->end = nn;
1149 } else {
1150 gll->start = nn;
1151 gll->end = nn;
1152 }
1153
1154 nn->np = np;
1155 nn->reason = rp;
1156 if(!validdelay) {
1157 nn->punish = INSTANT_KILL;
1158 } else {
1159 nn->punish = rp->type;
1160 }
1161 }
1162 return usercount;
1163 }
1164
1165 if (rp->type == INSTANT_IDENT_GLINE) {
1166 controlwall(NO_OPER, NL_HITS, "%s matched user@host gline regex %08lx (class: %s, hit %d user%s)", gvhost(np), rp->glineid, rp->class, usercount, (usercount!=1)?"s":"");
1167 } else if(rp->type == INSTANT_HOST_GLINE) {
1168 controlwall(NO_OPER, NL_HITS, "%s matched *@host gline regex %08lx (class: %s, hit %d user%s)", gvhost(np), rp->glineid, rp->class, usercount, (usercount!=1)?"s":"");
1169 } else {
1170 return 0;
1171 }
1172
1173 rg_shadowserver(np, rp, rp->type);
1174 snprintf(reason, sizeof(reason), "AUTO: %s (ID: %08lx)", rp->reason->content, rp->glineid);
1175 glinebynick(np, rg_expiry_time, reason, glineflags, "regexgline");
1176 return usercount;
1177 }
1178
1179 static char *gvhost(nick *np) {
1180 static char buf[NICKLEN+1+USERLEN+1+HOSTLEN+1+ACCOUNTLEN+4+REALLEN+1+10];
1181
1182 if(IsAccount(np)) {
1183 snprintf(buf, sizeof(buf), "%s!%s@%s/%s r(%s)", np->nick, np->ident, np->host->name->content, np->authname, np->realname->name->content);
1184 } else {
1185 snprintf(buf, sizeof(buf), "%s!%s@%s r(%s)", np->nick, np->ident, np->host->name->content, np->realname->name->content);
1186 }
1187
1188 return buf;
1189 }
1190
1191 static int floodprotection = 0;
1192 static int lastfloodspam = 0;
1193
1194 void rg_dogline(struct rg_glinelist *gll, nick *np, struct rg_struct *rp, char *matched) {
1195 int t = time(NULL);
1196
1197 if(t > floodprotection) {
1198 floodprotection = t;
1199 } else if((floodprotection - t) / 8 > RG_NETWORK_WIDE_MAX_GLINES_PER_8_SEC) {
1200 if(t > lastfloodspam + 3600) {
1201 channel *cp = findchannel("#twilightzone");
1202 if(cp)
1203 controlchanmsg(cp, "WARNING! REGEXGLINE DISABLED FOR AN HOUR DUE TO NETWORK WIDE LOOKING GLINE!: %d exceeded %d", (floodprotection - t) / 8, RG_NETWORK_WIDE_MAX_GLINES_PER_8_SEC);
1204 controlwall(NO_OPER, NL_MANAGEMENT, "WARNING! REGEXGLINE DISABLED FOR AN HOUR DUE TO NETWORK WIDE LOOKING GLINE!");
1205 lastfloodspam = t;
1206 floodprotection = t + RG_NETWORK_WIDE_MAX_GLINES_PER_8_SEC * 3600 * 8;
1207 }
1208 return;
1209 }
1210
1211 floodprotection+=__rg_dogline(gll, np, rp, matched);
1212 }
1213
1214 void rg_logevent(nick *np, char *event, char *details, ...) {
1215 char eeevent[RG_QUERY_BUF_SIZE], eedetails[RG_QUERY_BUF_SIZE], eemask[RG_QUERY_BUF_SIZE], eeaccount[RG_QUERY_BUF_SIZE];
1216 char buf[513], account[ACCOUNTLEN + 1], mask[RG_MASKLEN];
1217 int masklen;
1218
1219 va_list va;
1220
1221 if(details) {
1222 va_start(va, details);
1223 vsnprintf(buf, sizeof(buf), details, va);
1224 va_end(va);
1225 } else {
1226 buf[0] = '\0';
1227 }
1228
1229 if(np) {
1230 if (IsAccount(np)) {
1231 strncpy(account, np->authname, sizeof(account) - 1);
1232 account[sizeof(account) - 1] = '\0';
1233 } else {
1234 account[0] = '\0';
1235 }
1236 masklen = RGBuildHostname(mask, np);
1237 } else {
1238 mask[0] = '\0';
1239 masklen = 0;
1240 }
1241
1242 dbescapestring(eeevent, event, strlen(event));
1243 dbescapestring(eedetails, buf, strlen(buf));
1244 dbescapestring(eeaccount, account, strlen(account));
1245 dbescapestring(eemask, mask, masklen);
1246
1247 dbquery("INSERT INTO regexgline.clog (host, account, event, arg, ts) VALUES ('%s', '%s', '%s', '%s', NOW())", eemask, eeaccount, eeevent, eedetails);
1248 }
1249
1250 void rg_loggline(struct rg_struct *rg, nick *np) {
1251 char eenick[RG_QUERY_BUF_SIZE], eeuser[RG_QUERY_BUF_SIZE], eehost[RG_QUERY_BUF_SIZE], eereal[RG_QUERY_BUF_SIZE];
1252
1253 rg->hits++;
1254 rg->hitssaved++;
1255 rg->lastseen = time(NULL);
1256 rg->dirty = 1;
1257
1258 /* @paul: disabled */
1259
1260 return;
1261 dbescapestring(eenick, np->nick, strlen(np->nick));
1262 dbescapestring(eeuser, np->ident, strlen(np->ident));
1263 dbescapestring(eehost, np->host->name->content, strlen(np->host->name->content));
1264 dbescapestring(eereal, np->realname->name->content, strlen(np->realname->name->content));
1265
1266 dbquery("INSERT INTO regexgline.glog (glineid, nickname, username, hostname, realname, ts) VALUES (%d, '%s', '%s', '%s', '%s', NOW())", rg->id, eenick, eeuser, eehost, eereal);
1267 }
1268
1269 static unsigned int getrgmarker(void) {
1270 static unsigned int marker = 0;
1271
1272 marker++;
1273 if(!marker) {
1274 struct rg_struct *l;
1275
1276 /* If we wrapped to zero, zap the marker on all hosts */
1277 for(l=rg_list;l;l=l->next)
1278 l->marker=0;
1279 marker++;
1280 }
1281
1282 return marker;
1283 }
1284
1285 void rg_flush_schedule(void *arg) {
1286 struct rg_struct *l;
1287
1288 for(l=rg_list;l;l=l->next) {
1289 if(!l->dirty)
1290 continue;
1291
1292 dbquery("UPDATE regexgline.glines SET lastseen = %jd, hits = %lu WHERE id = %d", (intmax_t)l->lastseen, l->hitssaved, l->id);
1293
1294 l->dirty = 0;
1295 }
1296 }
1297