]> jfr.im git - irc/quakenet/newserv.git/blobdiff - regexgline/regexgline.c
Fix ns-cidr and implement IPv6 support.
[irc/quakenet/newserv.git] / regexgline / regexgline.c
index 3d8f43164e783f5e0e52106db41131afe124a5cd..53ec4f6a921e90eddaf4133a87fb621adadc77d0 100644 (file)
@@ -15,6 +15,7 @@
 #include "../core/hooks.h"
 #include "../server/server.h"
 #include "../lib/strlfunc.h"
+#include <stdint.h>
 
 #define INSTANT_IDENT_GLINE  1
 #define INSTANT_HOST_GLINE   2
@@ -23,7 +24,9 @@
 #define DELAYED_HOST_GLINE   5
 #define DELAYED_KILL         6
 
-MODULE_VERSION("1.43");
+#define RESERVED_NICK_GLINE_DURATION 3600 /* 1h */
+
+MODULE_VERSION("1.44");
 
 typedef struct rg_glinenode {
   nick *np;
@@ -56,14 +59,24 @@ void rg_dodelay(void *arg);
 void rg_dogline(struct rg_glinelist *gll, nick *np, struct rg_struct *rp, char *matched);
 void rg_flush_schedule(void *arg);
 
+static char *gvhost(nick *np);
+typedef void (scannick_fn)(struct rg_struct *, nick *, char *, void *);
+static void rg_scannick(nick *np, scannick_fn *fn, void *arg);
+static void rg_gline_match(struct rg_struct *rp, nick *np, char *hostname, void *arg);
+
 static DBModuleIdentifier dbid;
 static unsigned long highestid = 0;
 static int attached = 0, started = 0;
 
 static unsigned int getrgmarker(void);
 
+#define RESERVED_NICK_CLASS "reservednick"
 /* shadowserver only reports classes[0] */
-static const char *classes[] = { "drone", "proxy", "spam", "fakeauth", "other", (char *)0 };
+static const char *classes[] = { "drone", "proxy", "spam", "other", RESERVED_NICK_CLASS, (char *)0 };
+
+void rg_initglinelist(struct rg_glinelist *gll);
+void rg_flushglines(struct rg_glinelist *gll);
+static void rg_rename(int hooknum, void *arg);
 
 void _init(void) {
   sstring *max_casualties, *max_spew, *expiry_time, *max_per_gline;
@@ -102,20 +115,74 @@ void _init(void) {
     Error("regexgline", ERR_STOP, "Could not connect to database.");
   }
 }
-    
+
+static void rg_count_match(struct rg_struct *rp, nick *np, char *hostname, void *arg) {
+  void **varg = (void **)arg;
+  int *count = (int *)varg[0];
+
+  (*count)++;
+}
+
+static void rg_gline_reply_match(struct rg_struct *rp, nick *np, char *hostname, void *arg) {
+  void **varg = (void **)arg;
+
+  rg_count_match(rp, np, hostname, arg);
+  rg_gline_match(rp, np, hostname, varg[1]);
+}
+
+int rg_rescan(void *source, int cargc, char **cargv) {
+  int gline = 0;
+  int j;
+  nick *np = (nick *)source, *tnp;
+  void *arg[2];
+  int count = 0;
+  struct rg_glinelist gll;
+  scannick_fn *fn;
+
+  if(cargc > 0)
+    gline = !strcmp(cargv[0], "-g");
+
+  arg[0] = &count;
+
+  if(gline == 0) {
+    fn = rg_count_match;
+  } else {
+    controlreply(np, "G-line mode activated.");
+
+    rg_initglinelist(&gll);
+    arg[1] = &gll;
+
+    fn = rg_gline_reply_match;
+  }
+
+  controlreply(np, "Beginning scan, this may take a while...");
+
+  for(j=0;j<NICKHASHSIZE;j++)
+    for(tnp=nicktable[j];tnp;tnp=tnp->next)
+      rg_scannick(tnp, fn, arg);
+
+  controlreply(np, "Scan completed, %d hits.", count);
+
+  if(gline)
+    rg_flushglines(&gll);
+
+  return CMD_OK;
+}
+
 void _fini(void) {
-  struct rg_struct *gp = rg_list, *oldgp;
+  struct rg_struct *gp, *oldgp;
   rg_delay *delay, *delaynext;
   
   if(started) {
     deregisterhook(HOOK_NICK_NEWNICK, &rg_nick);
-    deregisterhook(HOOK_NICK_RENAME, &rg_nick);
+    deregisterhook(HOOK_NICK_RENAME, &rg_rename);
     deregisterhook(HOOK_NICK_LOSTNICK, &rg_lostnick);
     deregistercontrolcmd("regexspew", rg_spew);
     deregistercontrolcmd("regexglist", rg_glist);
     deregistercontrolcmd("regexdelgline", rg_delgline);
     deregistercontrolcmd("regexgline", rg_gline);
     deregistercontrolcmd("regexidlookup", rg_idlist);
+    deregistercontrolcmd("regexrescan", rg_rescan);
   }
 
   if(rg_delays) {
@@ -146,12 +213,19 @@ void _fini(void) {
   }
 }
 
+static int ignorable_nick(nick *np) {
+  if(IsOper(np) || IsService(np) || IsXOper(np) || SIsService(&serverlist[homeserver(np->numeric)]))
+    return 1;
+  return 0;
+}
+
 void rg_checkexpiry(void *arg) {
   struct rg_struct *rp = rg_list, *lp = NULL;
   time_t current = time(NULL);
   
   while(rp) {
     if (current >= rp->expires) {
+      dbquery("DELETE FROM regexglines WHERE id = %d", rp->id);
       if (lp) {
         lp->next = rp->next;
         rg_freestruct(rp);
@@ -251,11 +325,7 @@ void rg_dodelay(void *arg) {
   }
   
   if ((delay->reason->type == DELAYED_KILL) || (usercount > rg_max_per_gline)) {
-    if (IsAccount(delay->np)) {
-      controlwall(NO_OPER, NL_HITS, "%s!%s@%s/%s matched delayed kill regex %08lx (class: %s)", delay->np->nick, delay->np->ident, delay->np->host->name->content, delay->np->authname, delay->reason->glineid, delay->reason->class);
-    } else {
-      controlwall(NO_OPER, NL_HITS, "%s!%s@%s matched delayed kill regex %08lx (class: %s)", delay->np->nick, delay->np->ident, delay->np->host->name->content, delay->reason->glineid, delay->reason->class);
-    }
+    controlwall(NO_OPER, NL_HITS, "%s matched delayed kill regex %08lx (class: %s)", gvhost(delay->np), delay->reason->glineid, delay->reason->class);
 
     rg_shadowserver(delay->np, delay->reason, DELAYED_KILL);
     killuser(NULL, delay->np, "%s (ID: %08lx)", delay->reason->reason->content, delay->reason->glineid);
@@ -263,23 +333,15 @@ void rg_dodelay(void *arg) {
   }
   
   if (delay->reason->type == DELAYED_IDENT_GLINE) {
-    if (IsAccount(delay->np)) {
-      controlwall(NO_OPER, NL_HITS, "%s!%s@%s/%s matched delayed user@host gline regex %08lx (class: %s, hit %d user%s)", delay->np->nick, delay->np->ident, delay->np->host->name->content, delay->np->authname, delay->reason->glineid, delay->reason->class, usercount, (usercount!=1)?"s":"");
-    } else {
-      controlwall(NO_OPER, NL_HITS, "%s!%s@%s matched delayed user@host gline regex %08lx (class: %s, hit %d user%s)", delay->np->nick, delay->np->ident, delay->np->host->name->content, delay->reason->glineid, delay->reason->class, usercount, (usercount!=1)?"s":"");
-    }
+    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":"");
   } else if (delay->reason->type == DELAYED_HOST_GLINE) {
-    if (IsAccount(delay->np)) {
-      controlwall(NO_OPER, NL_HITS, "%s!%s@%s/%s matched delayed *@host gline regex %08lx (class: %s, hit %d user%s)", delay->np->nick, delay->np->ident, delay->np->host->name->content, delay->np->authname, delay->reason->glineid, delay->reason->class, usercount, (usercount!=1)?"s":"");
-    } else {
-      controlwall(NO_OPER, NL_HITS, "%s!%s@%s matched delayed *@host gline regex %08lx (class: %s, hit %d user%s)", delay->np->nick, delay->np->ident, delay->np->host->name->content, delay->reason->glineid, delay->reason->class, usercount, (usercount!=1)?"s":"");
-    }
+    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":"");
   } else {
     return;
   }
   
   rg_shadowserver(delay->np, delay->reason, delay->reason->type);
-  irc_send("%s GL * +%s %d %zu :AUTO: %s (ID: %08lx)\r\n", mynumeric->content, hostname, rg_expiry_time, time(NULL), delay->reason->reason->content, delay->reason->glineid);
+  irc_send("%s GL * +%s %d %jd :AUTO: %s (ID: %08lx)\r\n", mynumeric->content, hostname, rg_expiry_time, (intmax_t)time(NULL), delay->reason->reason->content, delay->reason->glineid);
   rg_deletedelay(delay);
 }
 
@@ -293,11 +355,7 @@ void rg_flushglines(struct rg_glinelist *gll) {
   for(nn=gll->start;nn;nn=pn) {
     pn = nn->next;
     if(nn->punish == INSTANT_KILL) {
-      if ( IsAccount(nn->np) ) {
-        controlwall(NO_OPER, NL_HITS, "%s!%s@%s/%s matched kill regex %08lx (class: %s)", nn->np->nick, nn->np->ident, nn->np->host->name->content, nn->np->authname, nn->reason->glineid, nn->reason->class);
-      } else {
-        controlwall(NO_OPER, NL_HITS, "%s!%s@%s matched kill regex %08lx (class: %s)", nn->np->nick, nn->np->ident, nn->np->host->name->content, nn->reason->glineid, nn->reason->class);
-      }
+      controlwall(NO_OPER, NL_HITS, "%s matched kill regex %08lx (class: %s)", gvhost(nn->np), nn->reason->glineid, nn->reason->class);
 
       rg_shadowserver(nn->np, nn->reason, nn->punish);
       killuser(NULL, nn->np, "%s (ID: %08lx)", nn->reason->reason->content, nn->reason->glineid);
@@ -373,7 +431,8 @@ static void dbloadfini(DBConn *dbconn, void *arg) {
                          "3 - Instant KILL (ik)\n"
                          "4 - Delayed USER@IP GLINE (dgu)\n"
                          "5 - Delayed *@IP GLINE (dgh)\n"
-                         "6 - Delayed KILL (dk)",
+                         "6 - Delayed KILL (dk)\n"
+                         "Note that some classes may have additional side effects (e.g. 'reservednick' also sets nick style glines).",
                          allclasses);
 
   registercontrolhelpcmd("regexgline", NO_OPER, 5, &rg_gline, helpbuf);
@@ -381,9 +440,10 @@ static void dbloadfini(DBConn *dbconn, void *arg) {
   registercontrolhelpcmd("regexglist", NO_OPER, 1, &rg_glist, "Usage: regexglist <pattern>\nLists regular expression patterns.");
   registercontrolhelpcmd("regexspew", NO_OPER, 1, &rg_spew, "Usage: regexspew <pattern>\nLists users currently on the network which match the given pattern.");
   registercontrolhelpcmd("regexidlookup", NO_OPER, 1, &rg_idlist, "Usage: regexidlookup <id>\nFinds a regular expression pattern by it's ID number.");
+  registercontrolhelpcmd("regexrescan", NO_OPER, 1, &rg_rescan, "Usage: regexrescan ?-g?\nRescans the net for missed clients, optionally glining matches (used for debugging).");
 
   registerhook(HOOK_NICK_NEWNICK, &rg_nick);
-  registerhook(HOOK_NICK_RENAME, &rg_nick);
+  registerhook(HOOK_NICK_RENAME, &rg_rename);
   registerhook(HOOK_NICK_LOSTNICK, &rg_lostnick);
   rg_startup();
 
@@ -400,26 +460,42 @@ void rg_dbload(void) {
   dbloadtable("regexgline.glines", NULL, dbloaddata, dbloadfini);
 }
 
-void rg_nick(int hooknum, void *arg) {
-  nick *np = (nick *)arg;
+static void rg_scannick(nick *np, scannick_fn *fn, void *arg) {
   struct rg_struct *rp;
   char hostname[RG_MASKLEN];
   int hostlen;
-  struct rg_glinelist gll;
 
-  rg_initglinelist(&gll);
+  if(ignorable_nick(np))
+    return;
 
   hostlen = RGBuildHostname(hostname, np);
 
-  if(IsOper(np) || IsService(np) || IsXOper(np))
-    return;
-
   for(rp=rg_list;rp;rp=rp->next) {
     if(pcre_exec(rp->regex, rp->hint, hostname, hostlen, 0, 0, NULL, 0) >= 0) {
-      rg_dogline(&gll, np, rp, hostname);
+      fn(rp, np, hostname, arg);
       break;
     }
   }
+}
+
+static void rg_gline_match(struct rg_struct *rp, nick *np, char *hostname, void *arg) {
+  struct rg_glinelist *gll = (struct rg_glinelist *)arg;
+
+  rg_dogline(gll, np, rp, hostname);
+}
+
+void rg_rename(int hooknum, void *arg) {
+  void **harg = (void **)arg;
+  rg_nick(hooknum, harg[0]);
+}
+
+void rg_nick(int hooknum, void *arg) {
+  nick *np = (nick *)arg;
+  struct rg_glinelist gll;
+
+  rg_initglinelist(&gll);
+
+  rg_scannick(np, rg_gline_match, &gll);
 
   rg_flushglines(&gll);
 }
@@ -503,7 +579,7 @@ int rg_gline(void *source, int cargc, char **cargv) {
 
   for(j=0;j<NICKHASHSIZE;j++) {
     for(tnp=nicktable[j];tnp;tnp=tnp->next) {
-      if(IsOper(tnp) || IsService(tnp) || IsXOper(tnp))
+      if(ignorable_nick(tnp))
         continue;
 
       hostlen = RGBuildHostname(hostname, tnp);
@@ -871,27 +947,15 @@ int rg_spew(void *source, int cargc, char **cargv) {
 }
 
 void rg_startup(void) {
-  int j, hostlen;
+  int j;
   nick *np;
-  struct rg_struct *rp;
   struct rg_glinelist gll;
-  char hostname[RG_MASKLEN];
 
   rg_initglinelist(&gll);
 
-  for(j=0;j<NICKHASHSIZE;j++) {
-    for(np=nicktable[j];np;np=np->next) {
-      if(IsOper(np) || IsService(np) || IsXOper(np))
-        continue;
-      hostlen = RGBuildHostname(hostname, np);
-      for(rp=rg_list;rp;rp=rp->next) {
-        if(pcre_exec(rp->regex, rp->hint, hostname, hostlen, 0, 0, NULL, 0) >= 0) {
-          rg_dogline(&gll, np, rp, hostname);
-          break;
-        }
-      }
-    }
-  }
+  for(j=0;j<NICKHASHSIZE;j++)
+    for(np=nicktable[j];np;np=np->next)
+      rg_scannick(np, rg_gline_match, &gll);
   
   rg_flushglines(&gll);
 }
@@ -1065,6 +1129,9 @@ int __rg_dogline(struct rg_glinelist *gll, nick *np, struct rg_struct *rp, char
     snprintf(hostname, sizeof(hostname), "%s@%s", np->ident, IPtostr(np->p_ipaddr));
   }
 
+  if(!strcmp(rp->class, RESERVED_NICK_CLASS))
+    irc_send("%s GL * +%s!*@* %d %jd :AUTO: %s (ID: %08lx)\r\n", mynumeric->content, np->nick, RESERVED_NICK_GLINE_DURATION, (intmax_t)time(NULL), rp->reason->content, rp->glineid);
+
   validdelay = (rp->type == INSTANT_KILL) || (rp->type == DELAYED_IDENT_GLINE) || (rp->type == DELAYED_HOST_GLINE) || (rp->type == DELAYED_KILL);
   if (validdelay || (usercount > rg_max_per_gline)) {
     struct rg_glinenode *nn = (struct rg_glinenode *)malloc(sizeof(struct rg_glinenode));
@@ -1090,26 +1157,30 @@ int __rg_dogline(struct rg_glinelist *gll, nick *np, struct rg_struct *rp, char
   }
   
   if (rp->type == INSTANT_IDENT_GLINE) {
-    if (IsAccount(np)) {
-      controlwall(NO_OPER, NL_HITS, "%s!%s@%s/%s matched user@host gline regex %08lx (class: %s, hit %d user%s)", np->nick, np->ident, np->host->name->content, np->authname, rp->glineid, rp->class, usercount, (usercount!=1)?"s":"");
-    } else {
-      controlwall(NO_OPER, NL_HITS, "%s!%s@%s matched user@host gline regex %08lx (class: %s, hit %d user%s)", np->nick, np->ident, np->host->name->content, rp->glineid, rp->class, usercount, (usercount!=1)?"s":"");
-    }
+    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":"");
   } else if(rp->type == INSTANT_HOST_GLINE) {
-    if (IsAccount(np)) {
-      controlwall(NO_OPER, NL_HITS, "%s!%s@%s/%s matched *@host gline regex %08lx (class: %s, hit %d user%s)", np->nick, np->ident, np->host->name->content, np->authname, rp->glineid, rp->class, usercount, (usercount!=1)?"s":"");
-    } else {
-      controlwall(NO_OPER, NL_HITS, "%s!%s@%s matched *@host gline regex %08lx (class: %s, hit %d user%s)", np->nick, np->ident, np->host->name->content, rp->glineid, rp->class, usercount, (usercount!=1)?"s":"");
-    }
+    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":"");
   } else {
     return 0;
   }
   
   rg_shadowserver(np, rp, rp->type);
-  irc_send("%s GL * +%s %d %zu :AUTO: %s (ID: %08lx)\r\n", mynumeric->content, hostname, rg_expiry_time, time(NULL), rp->reason->content, rp->glineid);
+  irc_send("%s GL * +%s %d %jd :AUTO: %s (ID: %08lx)\r\n", mynumeric->content, hostname, rg_expiry_time, (intmax_t)time(NULL), rp->reason->content, rp->glineid);
   return usercount;
 }
 
+static char *gvhost(nick *np) {
+  static char buf[NICKLEN+1+USERLEN+1+HOSTLEN+1+ACCOUNTLEN+4+REALLEN+1+10];
+
+  if(IsAccount(np)) {
+    snprintf(buf, sizeof(buf), "%s!%s@%s/%s r(%s)", np->nick, np->ident, np->host->name->content, np->authname, np->realname->name->content);
+  } else {
+    snprintf(buf, sizeof(buf), "%s!%s@%s r(%s)", np->nick, np->ident, np->host->name->content, np->realname->name->content);
+  }
+
+  return buf;
+}
+
 static int floodprotection = 0;
 static int lastfloodspam = 0;
 
@@ -1211,7 +1282,7 @@ void rg_flush_schedule(void *arg) {
     if(!l->dirty)
       continue;
 
-    dbquery("UPDATE regexgline.glines SET lastseen = %zu, hits = %lu WHERE id = %d", l->lastseen, l->hitssaved, l->id);
+    dbquery("UPDATE regexgline.glines SET lastseen = %jd, hits = %lu WHERE id = %d", (intmax_t)l->lastseen, l->hitssaved, l->id);
 
     l->dirty = 0;
   }