]> 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 4db22fe051fac072f95c6170efe02eef676b9176..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 */
@@ -16,6 +17,7 @@
 #include <errno.h>
 #include <fcntl.h>
 
+#include "../lib/version.h"
 #include "../lib/hmac.h"
 #include "../core/events.h"
 #include "../core/schedule.h"
 #include "../lib/irc_string.h"
 #include "../irc/irc.h"
 #include "../glines/glines.h"
+#include "../patricianick/patricianick.h"
 #include "trusts.h"
 
-static int countext, enforcepolicy;
+MODULE_VERSION("");
+
+static int countext, enforcepolicy_irc, enforcepolicy_auth;
 
 #define TRUSTBUFSIZE 8192
 #define TRUSTPASSLEN 128
 #define NONCELEN 16
 
-#define STATE_UNAUTHED 0
-#define STATE_AUTHED 1
-
 typedef struct trustsocket {
   int fd;
-  int state;
+  int authed;
+  char authuser[SERVERLEN+1];
   char buf[TRUSTBUFSIZE];
   unsigned char nonce[NONCELEN];
   int size;
+  time_t connected;
   time_t timeout;
+  int accepted;
+  int rejected;
+  int unthrottled;
 
   struct trustsocket *next;
 } trustsocket;
 
 static trustsocket *tslist;
-static int listenerfd;
+static int listenerfd = -1;
 static FILE *urandom;
 
 typedef struct trustaccount {
@@ -60,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, char *hint) {
+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)
+  if(!th || !trustsdbloaded || irc_in_addr_is_loopback(ipaddress))
     return POLICY_SUCCESS;
 
   tg = th->group;
@@ -80,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_TRUSTS, "Hard connection limit exceeded on subnet: %s (group: %s) %d connected, %d max - %s.", trusts_cidr2str(ip, th->nodebits), tg->name->content, nodecount, th->maxpernode, hint);
-      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;
     }
 
@@ -100,17 +127,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: '%s', %d connected, %d max. - %s.", tg->name->content, tg->count, tg->trustedfor, hint);
-        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) - %s.", username, IPtostr(*ip), tg->name->content, hint);
-      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;
     }
 
@@ -127,8 +154,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 - %s.", username, IPtostr(*ip), tg->name->content, identcount, tg->maxperident, hint);
-        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;
       }
     }
@@ -137,14 +164,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, char *hint) {
-  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), hooknum, cloneadjustment, message, messagelen, hint);
+  return POLICY_SUCCESS;
 }
 
 static int trustdowrite(trustsocket *sock, char *format, ...) {
@@ -168,21 +194,36 @@ 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), "enforcing with IAuth");
+  }
+
+  verdict = checkconnection(username, &ipaddress, HOOK_TRUSTS_NEWNICK, 1, message, sizeof(message), &unthrottle);
+
+  if(!enforcepolicy_auth)
+    verdict = POLICY_SUCCESS;
 
   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
       return trustdowrite(sock, "PASS %s", sequence_id);
   } else {
+    sock->rejected++;
+
+    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);
   }
 }
@@ -192,18 +233,24 @@ 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;
+  }
+
+  for(pnext=&tslist;*pnext;pnext=&((*pnext)->next)) {
+    ts=*pnext;
     if(ts == sock) {
-      deregisterhandler(sock->fd, 1);
       *pnext = sock->next;
-      nsfree(POOL_TRUSTS, sock);
+      trustfreeconnection(sock, 0);
       break;
     }
-    
-    pnext = &(sock->next);
   }
 }
 
@@ -213,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) {
@@ -221,17 +269,34 @@ static int handletrustauth(trustsocket *sock, char *server_name, char *mac) {
     }
   }
 
-  if (!password)
+  if (!password) {
+    controlwall(NO_OPER, NL_TRUSTS, "Invalid servername for policy socket: '%s'", server_name);
     return trustkillconnection(sock, "Invalid servername.");
+  }
 
   hmacmd5 h;
   hmacmd5_init(&h, (unsigned char *)password, strlen(password));
   hmacmd5_update(&h, (unsigned char *)hmac_printhex(sock->nonce, noncehexbuf, NONCELEN), NONCELEN * 2);
   hmacmd5_final(&h, digest);
-  if(hmac_strcmp(mac, hmac_printhex(digest, hexbuf, sizeof(digest))))
+  if(hmac_strcmp(mac, hmac_printhex(digest, hexbuf, sizeof(digest)))) {
+    controlwall(NO_OPER, NL_TRUSTS, "Invalid password for policy socket with servername '%s'.", server_name);
     return trustkillconnection(sock, "Bad MAC.");
+  }
 
-  sock->state = STATE_AUTHED;
+  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);
+
+  controlwall(NO_OPER, NL_TRUSTS, "Successful authentication for policy socket with servername '%s'.", server_name);
   return trustdowrite(sock, "AUTHOK");
 }
 
@@ -261,17 +326,20 @@ static int handletrustline(trustsocket *sock, char *line) {
     tokens[tokensfound++] = lastpos;
   }
 
-  if(sock->state == STATE_UNAUTHED && !strcmp("AUTH", command)) {
+  if(!sock->authed && !strcmp("AUTH", command)) {
     if(tokensfound != 2)
       return trustkillconnection(sock, "incorrect arg count for command.");
 
     return handletrustauth(sock, tokens[0], tokens[1]);
-  } else if(sock->state == STATE_AUTHED && !strcmp("CHECK", command)) {
+  } else if(sock->authed && !strcmp("CHECK", command)) {
     if(tokensfound != 3)
       return trustkillconnection(sock, "incorrect arg count for command.");
 
     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;
@@ -322,31 +390,28 @@ 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;
+  trustsocket **pnext, *sock;
 
-  pnext = &tslist;
-    
-  for(trustsocket *sock=tslist;sock;) {
-    next = sock->next;
-
-    if(sock->state == STATE_UNAUTHED && t >= sock->timeout) {
+  for(pnext=&tslist;*pnext;pnext=&((*pnext)->next)) {
+    sock = *pnext;
+    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;
   }
 }
 
@@ -354,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)
@@ -372,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));
@@ -390,9 +465,13 @@ static void processtrustlistener(int fd, short events) {
       tslist = sock->next;
       nsfree(POOL_TRUSTS, sock);
     } else {
-      sock->state = STATE_UNAUTHED;
+      sock->authed = 0;
       sock->size = 0;
+      sock->connected = time(NULL);
       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);
@@ -445,71 +524,81 @@ static void policycheck_irc(int hooknum, void *arg) {
   nick *np = args[0];
   long moving = (long)args[1];
   char message[512];
-  int verdict;
-  char *hint;
+  int verdict, unthrottle;
 
   if(moving)
     return;
 
-  if (enforcepolicy)
-    hint = "enforcing with glines";
-  else
-    hint = "not enforcing";
-
-  verdict = checkconnectionth(np->ident, &np->p_nodeaddr, gettrusthost(np), hooknum, 0, message, sizeof(message), hint);
-    
-  if(!enforcepolicy)
-    return;
+  verdict = checkconnection(np->ident, &np->ipaddress, hooknum, 0, message, sizeof(message), &unthrottle);
     
+  if(!enforcepolicy_irc)
+    verdict = POLICY_SUCCESS;
+
   switch (verdict) {
     case POLICY_FAILURE_NODECOUNT:
-      glinebynick(np, POLICY_GLINE_DURATION, message, GLINE_IGNORE_TRUST);
+      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);
+      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);
+      glinebynick(np, POLICY_GLINE_DURATION, message, GLINE_ALWAYS_USER|GLINE_IGNORE_TRUST, "trusts_policy");
       break;
   }
+
 }
 
-static int trusts_cmdtrustpolicy(void *source, int cargc, char **cargv) {
+static int trusts_cmdtrustpolicyirc(void *source, int cargc, char **cargv) {
   nick *sender = source;
 
   if(cargc < 1) {
-    controlreply(sender, "Trust policy enforcement is currently %s.", enforcepolicy?"enabled":"disabled");
+    controlreply(sender, "Use of glines for trust policy enforcement is currently %s.", enforcepolicy_irc?"enabled":"disabled");
     return CMD_OK;
   }
 
-  enforcepolicy = atoi(cargv[0]);
-  controlwall(NO_OPER, NL_TRUSTS, "%s %s trust policy enforcement.", controlid(sender), enforcepolicy?"enabled":"disabled");
-  controlreply(sender, "Trust policy enforcement is now %s.", enforcepolicy?"enabled":"disabled");
+  enforcepolicy_irc = atoi(cargv[0]);
+  controlwall(NO_OPER, NL_TRUSTS, "%s %s use of glines for trust policy enforcement.", controlid(sender), enforcepolicy_irc?"enabled":"disabled");
+  controlreply(sender, "Use of glines for trust policy enforcement is now %s.", enforcepolicy_irc?"enabled":"disabled");
 
   return CMD_OK;
 }
 
-void _init(void) {
-  sstring *m;
-  array *accts;
-  int trustport;
+static int trusts_cmdtrustpolicyauth(void *source, int cargc, char **cargv) {
+  nick *sender = source;
 
-  countext = registertgext("count");
-  if(countext == -1)
-    return;
+  if(cargc < 1) {
+    controlreply(sender, "Trust policy enforcement with IAuth is currently %s.", enforcepolicy_auth?"enabled":"disabled");
+    return CMD_OK;
+  }
 
-  m = getconfigitem("trusts_policy", "enforcepolicy");
-  if(m)
-    enforcepolicy = atoi(m->content);
+  enforcepolicy_auth = atoi(cargv[0]);
+  controlwall(NO_OPER, NL_TRUSTS, "%s %s trust policy enforcement with IAuth.", controlid(sender), enforcepolicy_auth?"enabled":"disabled");
+  controlreply(sender, "Trust policy enforcement with IAuth is now %s.", enforcepolicy_auth?"enabled":"disabled");
 
-  m = getconfigitem("trusts_policy", "trustport");
-  if(m)
-    trustport = atoi(m->content);
-  else
-    trustport = DEFAULT_TRUSTPORT;
+  return CMD_OK;
+}
 
-  if(trustport)
-    listenerfd = createlistenersock(trustport);
+
+static int trusts_cmdtrustsockets(void *source, int cargc, char **cargv) {
+  nick *sender = source;
+  time_t now;
+  trustsocket *sock;
+
+  time(&now);
+
+  controlreply(sender, "Server                              Connected for        Accepted        Rejected        Unthrottled");
+
+  for(sock=tslist;sock;sock=sock->next)
+    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;
+}
+
+void loadtrustaccounts(void) {
+  array *accts;
+
+  memset(trustaccounts, 0, sizeof(trustaccounts));
 
   accts = getconfigitems("trusts_policy", "server");
   if(!accts) {
@@ -525,28 +614,63 @@ void _init(void) {
         Error("trusts_policy", ERR_INFO, "Too many servers specified.");
         break;
       }
-      
+
       strncpy(server, servers[i]->content, sizeof(server));
-      
+
       pos = strchr(server, ',');
-      
+
       if(!pos) {
         Error("trusts_policy", ERR_INFO, "Server line is missing password: %s", server);
         continue;
       }
-      
+
       *pos = '\0';
-      
+
       trustaccounts[i].used = 1;
       strncpy(trustaccounts[i].server, server, SERVERLEN);
       strncpy(trustaccounts[i].password, pos+1, TRUSTPASSLEN);
     }
   }
-    
+}
+
+static void trustaccounts_rehash(int hooknum, void *arg) {
+  loadtrustaccounts();
+}
+
+void _init(void) {
+  sstring *m;
+  int trustport;
+
+  countext = registertgext("count");
+  if(countext == -1)
+    return;
+
+  m = getconfigitem("trusts_policy", "enforcepolicy_irc");
+  if(m)
+    enforcepolicy_irc = atoi(m->content);
+
+  m = getconfigitem("trusts_policy", "enforcepolicy_auth");
+  if(m)
+    enforcepolicy_auth = atoi(m->content);
+
+  m = getconfigitem("trusts_policy", "trustport");
+  if(m)
+    trustport = atoi(m->content);
+  else
+    trustport = DEFAULT_TRUSTPORT;
+
+  if(trustport)
+    listenerfd = createlistenersock(trustport);
+
+  loadtrustaccounts();
+
   registerhook(HOOK_TRUSTS_NEWNICK, policycheck_irc);
-  registerhook(HOOK_TRUSTS_LOSTNICK, policycheck_irc);
+  registerhook(HOOK_TRUSTS_LOSTNICK, &policycheck_irc);
+  registerhook(HOOK_CORE_REHASH, trustaccounts_rehash);
 
-  registercontrolhelpcmd("trustpolicy", NO_DEVELOPER, 1, trusts_cmdtrustpolicy, "Usage: trustpolicy ?1|0?\nEnables or disables policy enforcement. Shows current status when no parameter is specified.");
+  registercontrolhelpcmd("trustpolicyirc", NO_DEVELOPER, 1, trusts_cmdtrustpolicyirc, "Usage: trustpolicyirc ?1|0?\nEnables or disables policy enforcement (IRC). Shows current status when no parameter is specified.");
+  registercontrolhelpcmd("trustpolicyauth", NO_DEVELOPER, 1, trusts_cmdtrustpolicyauth, "Usage: trustpolicyauth ?1|0?\nEnables or disables policy enforcement (IAuth). Shows current status when no parameter is specified.");
+  registercontrolhelpcmd("trustsockets", NO_DEVELOPER, 0, trusts_cmdtrustsockets, "Usage: trustsockets\nLists all currently active TRUST sockets.");
 
   schedulerecurring(time(NULL)+1, 0, 5, trustdotimeout, NULL);
   
@@ -556,6 +680,8 @@ void _init(void) {
 }
 
 void _fini(void) {
+  trustsocket *sock, *next;
+
   if(countext == -1)
     return;
 
@@ -563,11 +689,26 @@ void _fini(void) {
 
   deregisterhook(HOOK_TRUSTS_NEWNICK, policycheck_irc);
   deregisterhook(HOOK_TRUSTS_LOSTNICK, policycheck_irc);
+  deregisterhook(HOOK_CORE_REHASH, trustaccounts_rehash);
 
-  deregistercontrolcmd("trustpolicy", trusts_cmdtrustpolicy);
-  
-  deleteallschedules(trustdotimeout);
+  deregistercontrolcmd("trustpolicyirc", trusts_cmdtrustpolicyirc);
+  deregistercontrolcmd("trustpolicyauth", trusts_cmdtrustpolicyauth);
+  deregistercontrolcmd("trustsockets", trusts_cmdtrustsockets);
   
+  deleteallschedules(trustdotimeout); 
   if (urandom)
     fclose(urandom);
+
+  if (listenerfd != -1)
+    deregisterhandler(listenerfd, 1);
+
+  for(sock=tslist;sock;) {
+    next = sock->next;
+
+    trustkillconnection(sock, "Unloading module.");
+    trustfreeconnection(sock, 0);
+
+    sock = next;
+  }
 }