]> jfr.im git - irc/quakenet/newserv.git/blobdiff - trusts/trusts_policy.c
TRUSTS: turn on tcpkeepalive for trust policy sockets.
[irc/quakenet/newserv.git] / trusts / trusts_policy.c
index 4556112b93bbb73f88a859d8af72eee3ab3b5a00..b5c70d8e9642b0172d7342b0c53e8ef40280889b 100644 (file)
@@ -5,6 +5,7 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
+#include <netinet/tcp.h>
 
 #ifndef __USE_MISC
 #define __USE_MISC /* inet_aton */
@@ -27,6 +28,7 @@
 #include "../lib/irc_string.h"
 #include "../irc/irc.h"
 #include "../glines/glines.h"
+#include "../patricianick/patricianick.h"
 #include "trusts.h"
 
 MODULE_VERSION("");
@@ -48,6 +50,7 @@ typedef struct trustsocket {
   time_t timeout;
   int accepted;
   int rejected;
+  int unthrottled;
 
   struct trustsocket *next;
 } trustsocket;
@@ -64,13 +67,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 +96,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_CLONING, "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 https://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;
     }
 
@@ -105,16 +128,16 @@ static int checkconnectionth(const char *username, struct irc_in_addr *ip, trust
         tg->exts[countext] = (void *)(long)tg->count;
 
         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(*ip));
+        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, "Too many connections from your trust (%s) - see https://www.quakenet.org/help/trusts/connection-limit for details.", IPtostr(*ip));
+      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_CLONING, "Ident required: %s@%s (group: %s).", username, IPtostr(*ip), tg->name->content);
-      snprintf(message, messagelen, "IDENTD required from your host (%s) - see https://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 +154,8 @@ static int checkconnectionth(const char *username, struct irc_in_addr *ip, trust
       }
 
       if(identcount + usercountadjustment > tg->maxperident) {
-        controlwall(NO_OPER, NL_CLONING, "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 https://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;
       }
     }
@@ -144,14 +167,10 @@ static int checkconnectionth(const char *username, struct irc_in_addr *ip, trust
   if(tg->trustedfor > 0)
     snprintf(message, messagelen, "Trust has %d out of %d allowed connections.", tg->count + usercountadjustment, tg->trustedfor);
 
-  return POLICY_SUCCESS;
-}
-
-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;
 
-  return checkconnectionth(username, &ip_canonicalized, th_getbyhost(&ip_canonicalized), hooknum, cloneadjustment, message, messagelen);
+  return POLICY_SUCCESS;
 }
 
 static int trustdowrite(trustsocket *sock, char *format, ...) {
@@ -175,16 +194,16 @@ 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;
-  
-  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;
@@ -192,6 +211,11 @@ static int policycheck_auth(trustsocket *sock, const char *sequence_id, const ch
   if (verdict == POLICY_SUCCESS) {
     sock->accepted++;
 
+    if (unthrottle) {
+      sock->unthrottled++;
+      trustdowrite(sock, "UNTHROTTLE %s", sequence_id);
+    }
+
     if(message[0])
       return trustdowrite(sock, "PASS %s %s", sequence_id, message);
     else
@@ -220,9 +244,8 @@ static void trustfreeconnection(trustsocket *sock, int unlink) {
     return;
   }
 
-  pnext = &tslist;
-  
-  for(ts=*pnext;*pnext;pnext=&((*pnext)->next)) {
+  for(pnext=&tslist;*pnext;pnext=&((*pnext)->next)) {
+    ts=*pnext;
     if(ts == sock) {
       *pnext = sock->next;
       trustfreeconnection(sock, 0);
@@ -237,6 +260,7 @@ static int handletrustauth(trustsocket *sock, char *server_name, char *mac) {
   unsigned char digest[16];
   char noncehexbuf[NONCELEN * 2 + 1];
   char hexbuf[sizeof(digest) * 2 + 1];
+  trustsocket *ts, **pnext;
 
   for(i=0;i<MAXSERVERS;i++) {
     if(trustaccounts[i].used && strcmp(trustaccounts[i].server, server_name) == 0) {
@@ -259,6 +283,16 @@ static int handletrustauth(trustsocket *sock, char *server_name, char *mac) {
     return trustkillconnection(sock, "Bad MAC.");
   }
 
+  for(pnext=&tslist;*pnext;pnext=&((*pnext)->next)) {
+    ts = *pnext;
+    if(ts->authed && strcmp(ts->authuser, server_name) == 0) {
+      trustkillconnection(ts, "New connection with same server name.");
+      *pnext = ts->next;
+      trustfreeconnection(ts, 0);
+      break;
+    }
+  }
+
   sock->authed = 1;
   strncpy(sock->authuser, server_name, SERVERLEN);
 
@@ -356,7 +390,12 @@ 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, 1);
@@ -366,9 +405,8 @@ static void trustdotimeout(void *arg) {
   time_t t = time(NULL);
   trustsocket **pnext, *sock;
 
-  pnext = &tslist;
-    
-  for(sock=*pnext;*pnext;pnext=&((*pnext)->next)) {
+  for(pnext=&tslist;*pnext;pnext=&((*pnext)->next)) {
+    sock = *pnext;
     if(!sock->authed && t >= sock->timeout) {
       trustkillconnection(sock, "Auth timeout.");
       *pnext = sock->next;
@@ -381,6 +419,7 @@ static void processtrustlistener(int fd, short events) {
   if(events & POLLIN) {
     trustsocket *sock;
     char buf[NONCELEN * 2 + 1];
+    int optval;
 
     int newfd = accept(fd, NULL, NULL), flags;
     if(newfd == -1)
@@ -399,6 +438,15 @@ static void processtrustlistener(int fd, short events) {
       return;
     }
 
+    optval = 10;
+    setsockopt(newfd, SOL_SOCKET, TCP_KEEPIDLE, &optval, sizeof(optval));
+    optval = 3;
+    setsockopt(newfd, SOL_SOCKET, TCP_KEEPCNT, &optval, sizeof(optval));
+    optval = 10;
+    setsockopt(newfd, SOL_SOCKET, TCP_KEEPINTVL, &optval, sizeof(optval));
+    optval = 1;
+    setsockopt(newfd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
+
     registerhandler(newfd, POLLIN|POLLERR|POLLHUP, processtrustclient);
       
     sock = nsmalloc(POOL_TRUSTS, sizeof(trustsocket));
@@ -423,6 +471,7 @@ static void processtrustlistener(int fd, short events) {
       sock->timeout = time(NULL) + 30;
       sock->accepted = 0;
       sock->rejected = 0;
+      sock->unthrottled = 0;
       if(!trustdowrite(sock, "AUTH %s", hmac_printhex(sock->nonce, buf, NONCELEN))) {
         Error("trusts_policy", ERR_WARNING, "Error writing auth to fd %d.", newfd);
         deregisterhandler(newfd, 1);
@@ -475,12 +524,12 @@ 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;
 
   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;
@@ -490,12 +539,13 @@ 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;
   }
+
 }
 
 static int trusts_cmdtrustpolicyirc(void *source, int cargc, char **cargv) {
@@ -536,10 +586,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        Unthrottled");
 
   for(sock=tslist;sock;sock=sock->next)
-    controlreply(sender, "%-20s %20s %10d %15d", sock->authed?sock->authuser:"<unauthenticated connection>", longtoduration(now - sock->connected, 0), sock->accepted, sock->rejected);
+    controlreply(sender, "%-35s %-20s %-15d %-15d %-15d", sock->authed?sock->authuser:"<unauthenticated connection>", longtoduration(now - sock->connected, 0), sock->accepted, sock->rejected, sock->unthrottled);
 
   controlreply(sender, "-- End of list.");
   return CMD_OK;