]> jfr.im git - irc/quakenet/newserv.git/blobdiff - trusts/trusts_policy.c
CHANSERV: tell user when they can't attempts to auth any more, and drop max attempts...
[irc/quakenet/newserv.git] / trusts / trusts_policy.c
index 2f318af3ecf24f6fcd762ca6d40a5e4de768a5df..663007518c67ae2ed6146179215dd031e9cbff99 100644 (file)
@@ -27,6 +27,7 @@
 #include "../lib/irc_string.h"
 #include "../irc/irc.h"
 #include "../glines/glines.h"
+#include "../patricianick/patricianick.h"
 #include "trusts.h"
 
 MODULE_VERSION("");
@@ -64,13 +65,22 @@ typedef struct trustaccount {
 
 trustaccount trustaccounts[MAXSERVERS];
 
-static int checkconnectionth(const char *username, struct irc_in_addr *ip, trusthost *th, int hooknum, int usercountadjustment, char *message, size_t messagelen) {
+static int checkconnection(const char *username, struct irc_in_addr *ipaddress, int hooknum, int usercountadjustment, char *message, size_t messagelen, int *unthrottle) {
+  trusthost *th;
   trustgroup *tg;
+  struct irc_in_addr ipaddress_canonical;
+
+  ip_canonicalize_tunnel(&ipaddress_canonical, ipaddress);
+
+  th = th_getbyhost(&ipaddress_canonical);
+
+  if (unthrottle)
+    *unthrottle = 0;
 
   if(messagelen>0)
     message[0] = '\0';
   
-  if(!th || !trustsdbloaded)
+  if(!th || !trustsdbloaded || irc_in_addr_is_loopback(ipaddress))
     return POLICY_SUCCESS;
 
   tg = th->group;
@@ -84,19 +94,30 @@ static int checkconnectionth(const char *username, struct irc_in_addr *ip, trust
 
   if(hooknum == HOOK_TRUSTS_NEWNICK) {
     patricia_node_t *head, *node;
-    int nodecount = 0;
-
-    head = refnode(iptree, ip, th->nodebits);
-    PATRICIA_WALK(head, node)
-    {
-      nodecount += node->usercount;
+    int i, nodecount = 0;
+    patricianick_t *pnp;
+    nick *npp;
+
+    head = refnode(iptree, &ipaddress_canonical, th->nodebits);
+    nodecount = head->usercount;
+
+    /* Account for borrowed IP addresses. */
+    PATRICIA_WALK(head, node) {
+      pnp = node->exts[pnode_ext];
+
+      if (pnp)
+        for (i = 0; i < PATRICIANICK_HASHSIZE; i++)
+          for (npp = pnp->identhash[i]; npp; npp=npp->exts[pnick_ext])
+            if (NickOnServiceServer(npp))
+              usercountadjustment--;
     }
     PATRICIA_WALK_END;
+
     derefnode(iptree, head);
 
     if(th->maxpernode && nodecount + usercountadjustment > th->maxpernode) {
-      controlwall(NO_OPER, NL_TRUSTS, "Hard connection limit exceeded on subnet: %s (group: %s): %d connected, %d max.", trusts_cidr2str(ip, th->nodebits), tg->name->content, nodecount + usercountadjustment, th->maxpernode);
-      snprintf(message, messagelen, "Too many connections from your host (%s) - see http://www.quakenet.org/help/trusts/connection-limit for details.", IPtostr(*ip));
+      controlwall(NO_OPER, NL_CLONING, "Hard connection limit exceeded on subnet: %s (group: %s): %d connected, %d max.", CIDRtostr(*ipaddress, th->nodebits), tg->name->content, nodecount + usercountadjustment, th->maxpernode);
+      snprintf(message, messagelen, "Too many connections from your host (%s) - see https://www.quakenet.org/help/trusts/connection-limit for details.", IPtostr(*ipaddress));
       return POLICY_FAILURE_NODECOUNT;
     }
 
@@ -104,17 +125,17 @@ static int checkconnectionth(const char *username, struct irc_in_addr *ip, trust
       if(tg->count > (long)tg->exts[countext]) {
         tg->exts[countext] = (void *)(long)tg->count;
 
-        controlwall(NO_OPER, NL_TRUSTS, "Hard connection limit exceeded (group %s): %d connected, %d max.", tg->name->content, tg->count + usercountadjustment, tg->trustedfor);
-        snprintf(message, messagelen, "Too many connections from your trust (%s) - see http://www.quakenet.org/help/trusts/connection-limit for details.", IPtostr(*ip));
-        return POLICY_FAILURE_GROUPCOUNT;
+        controlwall(NO_OPER, NL_CLONING, "Hard connection limit exceeded (group %s): %d connected, %d max.", tg->name->content, tg->count + usercountadjustment, tg->trustedfor);
+        snprintf(message, messagelen, "Too many connections from your trust (%s) - see https://www.quakenet.org/help/trusts/connection-limit for details.", IPtostr(*ipaddress));
       }
-      
-      snprintf(message, messagelen, "Trust has %d out of %d allowed connections.", tg->count + usercountadjustment, tg->trustedfor);
+
+      snprintf(message, messagelen, "Too many connections from your trust (%s) - see https://www.quakenet.org/help/trusts/connection-limit for details.", IPtostr(*ipaddress));
+      return POLICY_FAILURE_GROUPCOUNT;
     }
 
     if((tg->flags & TRUST_ENFORCE_IDENT) && (username[0] == '~')) {
-      controlwall(NO_OPER, NL_TRUSTS, "Ident required: %s@%s (group: %s).", username, IPtostr(*ip), tg->name->content);
-      snprintf(message, messagelen, "IDENTD required from your host (%s) - see http://www.quakenet.org/help/trusts/connection-limit for details.", IPtostr(*ip));
+      controlwall(NO_OPER, NL_CLONING, "Ident required: %s@%s (group: %s).", username, IPtostr(*ipaddress), tg->name->content);
+      snprintf(message, messagelen, "IDENTD required from your host (%s) - see https://www.quakenet.org/help/trusts/connection-limit for details.", IPtostr(*ipaddress));
       return POLICY_FAILURE_IDENTD;
     }
 
@@ -131,8 +152,8 @@ static int checkconnectionth(const char *username, struct irc_in_addr *ip, trust
       }
 
       if(identcount + usercountadjustment > tg->maxperident) {
-        controlwall(NO_OPER, NL_TRUSTS, "Hard ident limit exceeded: %s@%s (group: %s): %d connected, %d max.", username, IPtostr(*ip), tg->name->content, identcount + usercountadjustment, tg->maxperident);
-        snprintf(message, messagelen, "Too many connections from your username (%s@%s) - see http://www.quakenet.org/help/trusts/connection-limit for details.", username, IPtostr(*ip));
+        controlwall(NO_OPER, NL_CLONING, "Hard ident limit exceeded: %s@%s (group: %s): %d connected, %d max.", username, IPtostr(*ipaddress), tg->name->content, identcount + usercountadjustment, tg->maxperident);
+        snprintf(message, messagelen, "Too many connections from your username (%s@%s) - see https://www.quakenet.org/help/trusts/connection-limit for details.", username, IPtostr(*ipaddress));
         return POLICY_FAILURE_IDENTCOUNT;
       }
     }
@@ -141,14 +162,13 @@ static int checkconnectionth(const char *username, struct irc_in_addr *ip, trust
       tg->exts[countext] = (void *)(long)tg->count;
   }
 
-  return POLICY_SUCCESS;
-}
+  if(tg->trustedfor > 0)
+    snprintf(message, messagelen, "Trust has %d out of %d allowed connections.", tg->count + usercountadjustment, tg->trustedfor);
 
-static int checkconnection(const char *username, struct irc_in_addr *ip, int hooknum, int cloneadjustment, char *message, size_t messagelen) {
-  struct irc_in_addr ip_canonicalized;
-  ip_canonicalize_tunnel(&ip_canonicalized, ip);
+  if(unthrottle && (tg->flags & TRUST_UNTHROTTLE))
+    *unthrottle = 1; /* TODO: Do _some_ kind of rate-limiting */
 
-  return checkconnectionth(username, &ip_canonicalized, th_getbyhost(&ip_canonicalized), hooknum, cloneadjustment, message, messagelen);
+  return POLICY_SUCCESS;
 }
 
 static int trustdowrite(trustsocket *sock, char *format, ...) {
@@ -172,16 +192,17 @@ static int trustdowrite(trustsocket *sock, char *format, ...) {
 
 static int policycheck_auth(trustsocket *sock, const char *sequence_id, const char *username, const char *host) {
   char message[512];
-  int verdict;
-  struct irc_in_addr ip;
+  int verdict, unthrottle;
+  struct irc_in_addr ipaddress;
   unsigned char bits;
+  trustsocket *ts;
   
-  if(!ipmask_parse(host, &ip, &bits)) {
+  if(!ipmask_parse(host, &ipaddress, &bits)) {
     sock->accepted++;
     return trustdowrite(sock, "PASS %s", sequence_id);
   }
-  
-  verdict = checkconnection(username, &ip, HOOK_TRUSTS_NEWNICK, 1, message, sizeof(message));
+
+  verdict = checkconnection(username, &ipaddress, HOOK_TRUSTS_NEWNICK, 1, message, sizeof(message), &unthrottle);
 
   if(!enforcepolicy_auth)
     verdict = POLICY_SUCCESS;
@@ -189,6 +210,11 @@ static int policycheck_auth(trustsocket *sock, const char *sequence_id, const ch
   if (verdict == POLICY_SUCCESS) {
     sock->accepted++;
 
+    if (unthrottle) {
+      for (ts = tslist; ts; ts = ts->next)
+        trustdowrite(ts, "UNTHROTTLE %s", IPtostr(ipaddress));
+    }
+
     if(message[0])
       return trustdowrite(sock, "PASS %s %s", sequence_id, message);
     else
@@ -196,7 +222,7 @@ static int policycheck_auth(trustsocket *sock, const char *sequence_id, const ch
   } else {
     sock->rejected++;
 
-    controlwall(NO_OPER, NL_TRUSTS, "Rejected connection from %s@%s using IAuth: %s", username, host, message);
+    controlwall(NO_OPER, NL_CLONING, "Rejected connection from %s@%s using IAuth: %s", username, host, message);
     return trustdowrite(sock, "KILL %s %s", sequence_id, message);
   }
 }
@@ -206,18 +232,25 @@ static int trustkillconnection(trustsocket *sock, char *reason) {
   return 0;
 }
 
-static void trustfreeconnection(trustsocket *sock) {
-  trustsocket **pnext = &tslist;
-    
-  for(trustsocket *ts=tslist;ts;ts=ts->next) {
+static void trustfreeconnection(trustsocket *sock, int unlink) {
+  trustsocket **pnext, *ts;
+
+  if(!unlink) {
+    controlwall(NO_OPER, NL_TRUSTS, "Lost connection on policy socket for '%s'.", sock->authed?sock->authuser:"<unauthenticated connection>");
+
+    deregisterhandler(sock->fd, 1);
+    nsfree(POOL_TRUSTS, sock);
+    return;
+  }
+
+  pnext = &tslist;
+  
+  for(ts=*pnext;*pnext;pnext=&((*pnext)->next)) {
     if(ts == sock) {
-      deregisterhandler(sock->fd, 1);
       *pnext = sock->next;
-      nsfree(POOL_TRUSTS, sock);
+      trustfreeconnection(sock, 0);
       break;
     }
-    
-    pnext = &(sock->next);
   }
 }
 
@@ -293,6 +326,9 @@ static int handletrustline(trustsocket *sock, char *line) {
 
     policycheck_auth(sock, tokens[0], tokens[1], tokens[2]);
     return 1;
+  } else if(!strcmp("VERSION", command)) {
+    /* Ignore this command for now. */
+    return 1;
   } else {
     Error("trusts_policy", ERR_WARNING, "Bad command: %s", command);
     return 0;
@@ -343,31 +379,29 @@ static void processtrustclient(int fd, short events) {
 
   if(!sock)
     return;
-  
+
+  if (events & (POLLPRI | POLLERR | POLLHUP | POLLNVAL)) {
+    trustfreeconnection(sock, 1);
+    return;
+  }
+
   if(events & POLLIN)
     if(!handletrustclient(sock))
-      trustfreeconnection(sock);
+      trustfreeconnection(sock, 1);
 }
 
 static void trustdotimeout(void *arg) {
   time_t t = time(NULL);
-  trustsocket **pnext, *next, *sock;
+  trustsocket **pnext, *sock;
 
   pnext = &tslist;
     
-  for(sock=tslist;sock;) {
-    next = sock->next;
-
+  for(sock=*pnext;*pnext;pnext=&((*pnext)->next)) {
     if(!sock->authed && t >= sock->timeout) {
       trustkillconnection(sock, "Auth timeout.");
-      deregisterhandler(sock->fd, 1);
       *pnext = sock->next;
-      nsfree(POOL_TRUSTS, sock);
-    } else {
-      pnext = &(sock->next);
+      trustfreeconnection(sock, 0);
     }
-    
-    sock=next;
   }
 }
 
@@ -469,12 +503,13 @@ static void policycheck_irc(int hooknum, void *arg) {
   nick *np = args[0];
   long moving = (long)args[1];
   char message[512];
-  int verdict;
+  int verdict, unthrottle;
+  trustsocket *ts;
 
   if(moving)
     return;
 
-  verdict = checkconnectionth(np->ident, &np->p_nodeaddr, gettrusthost(np), hooknum, 0, message, sizeof(message));
+  verdict = checkconnection(np->ident, &np->ipaddress, hooknum, 0, message, sizeof(message), &unthrottle);
     
   if(!enforcepolicy_irc)
     verdict = POLICY_SUCCESS;
@@ -484,12 +519,17 @@ static void policycheck_irc(int hooknum, void *arg) {
       glinebynick(np, POLICY_GLINE_DURATION, message, GLINE_IGNORE_TRUST, "trusts_policy");
       break;
     case POLICY_FAILURE_IDENTD:
-      glinebyip("~*", &np->p_ipaddr, 128, POLICY_GLINE_DURATION, message, GLINE_ALWAYS_USER|GLINE_IGNORE_TRUST, "trusts_policy");
+      glinebyip("~*", &np->ipaddress, 128, POLICY_GLINE_DURATION, message, GLINE_ALWAYS_USER|GLINE_IGNORE_TRUST, "trusts_policy");
       break;
     case POLICY_FAILURE_IDENTCOUNT:
       glinebynick(np, POLICY_GLINE_DURATION, message, GLINE_ALWAYS_USER|GLINE_IGNORE_TRUST, "trusts_policy");
       break;
   }
+
+  if (unthrottle && hooknum == HOOK_NICK_LOSTNICK && np->timestamp > getnettime() - TRUST_MIN_TIME_RETHROTTLE) {
+    for (ts = tslist; ts; ts = ts->next)
+      trustdowrite(ts, "THROTTLE %s", IPtostr(np->ipaddress));
+  }
 }
 
 static int trusts_cmdtrustpolicyirc(void *source, int cargc, char **cargv) {
@@ -530,10 +570,10 @@ static int trusts_cmdtrustsockets(void *source, int cargc, char **cargv) {
 
   time(&now);
 
-  controlreply(sender, "Server                   Connected for             Accepted        Rejected");
+  controlreply(sender, "Server                              Connected for        Accepted        Rejected");
 
   for(sock=tslist;sock;sock=sock->next)
-    controlreply(sender, "%-20s %20s %10d %15d", sock->authed?sock->authuser:"<not authenticated>", longtoduration(now - sock->connected, 0), sock->accepted, sock->rejected);
+    controlreply(sender, "%-35s %-20s %-15d %-15d", sock->authed?sock->authuser:"<unauthenticated connection>", longtoduration(now - sock->connected, 0), sock->accepted, sock->rejected);
 
   controlreply(sender, "-- End of list.");
   return CMD_OK;
@@ -651,7 +691,7 @@ void _fini(void) {
     next = sock->next;
 
     trustkillconnection(sock, "Unloading module.");
-    trustfreeconnection(sock);
+    trustfreeconnection(sock, 0);
 
     sock = next;
   }