X-Git-Url: https://jfr.im/git/irc/quakenet/newserv.git/blobdiff_plain/d1a46768ca1f3507410fe4c4d8bb3a71351c632f..04b5f1160dbde3d7ee6059e778cbd6835727c723:/trusts/trusts_policy.c diff --git a/trusts/trusts_policy.c b/trusts/trusts_policy.c index 4db22fe0..b5c70d8e 100644 --- a/trusts/trusts_policy.c +++ b/trusts/trusts_policy.c @@ -5,6 +5,7 @@ #include #include #include +#include #ifndef __USE_MISC #define __USE_MISC /* inet_aton */ @@ -16,6 +17,7 @@ #include #include +#include "../lib/version.h" #include "../lib/hmac.h" #include "../core/events.h" #include "../core/schedule.h" @@ -26,30 +28,35 @@ #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:""); + + 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;inonce, 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:"", 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; + } }