]> jfr.im git - irc/quakenet/newserv.git/blob - trusts/trusts_policy.c
8160a8c870f3ba2ec3d4b9555ecd64cf6141b6cf
[irc/quakenet/newserv.git] / trusts / trusts_policy.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdarg.h>
4 #include <unistd.h>
5 #include <sys/types.h>
6 #include <sys/socket.h>
7 #include <netinet/in.h>
8
9 #ifndef __USE_MISC
10 #define __USE_MISC /* inet_aton */
11 #endif
12
13 #include <arpa/inet.h>
14 #include <sys/un.h>
15 #include <sys/poll.h>
16 #include <errno.h>
17 #include <fcntl.h>
18
19 #include "../lib/version.h"
20 #include "../lib/hmac.h"
21 #include "../core/events.h"
22 #include "../core/schedule.h"
23 #include "../core/nsmalloc.h"
24 #include "../core/hooks.h"
25 #include "../core/config.h"
26 #include "../control/control.h"
27 #include "../lib/irc_string.h"
28 #include "../irc/irc.h"
29 #include "../glines/glines.h"
30 #include "trusts.h"
31
32 MODULE_VERSION("");
33
34 static int countext, enforcepolicy_irc, enforcepolicy_auth;
35
36 #define TRUSTBUFSIZE 8192
37 #define TRUSTPASSLEN 128
38 #define NONCELEN 16
39
40 typedef struct trustsocket {
41 int fd;
42 int authed;
43 char authuser[SERVERLEN+1];
44 char buf[TRUSTBUFSIZE];
45 unsigned char nonce[NONCELEN];
46 int size;
47 time_t connected;
48 time_t timeout;
49 int accepted;
50 int rejected;
51
52 struct trustsocket *next;
53 } trustsocket;
54
55 static trustsocket *tslist;
56 static int listenerfd = -1;
57 static FILE *urandom;
58
59 typedef struct trustaccount {
60 int used;
61 char server[SERVERLEN+1];
62 char password[TRUSTPASSLEN+1];
63 } trustaccount;
64
65 trustaccount trustaccounts[MAXSERVERS];
66
67 static int checkconnectionth(const char *username, struct irc_in_addr *ip, trusthost *th, int hooknum, int usercountadjustment, char *message, size_t messagelen) {
68 trustgroup *tg;
69
70 if(messagelen>0)
71 message[0] = '\0';
72
73 if(!th || !trustsdbloaded)
74 return POLICY_SUCCESS;
75
76 tg = th->group;
77
78 /*
79 * the purpose of this logic is to avoid spam like this:
80 * WARNING: tgX exceeded limit: 11 connected vs 10 max
81 * (goes back down to 10)
82 * WARNING: tgX exceeded limit: 11 connected vs 10 max
83 */
84
85 if(hooknum == HOOK_TRUSTS_NEWNICK) {
86 patricia_node_t *head, *node;
87 int nodecount = 0;
88
89 head = refnode(iptree, ip, th->nodebits);
90 PATRICIA_WALK(head, node)
91 {
92 nodecount += node->usercount;
93 }
94 PATRICIA_WALK_END;
95 derefnode(iptree, head);
96
97 if(th->maxpernode && nodecount + usercountadjustment > th->maxpernode) {
98 controlwall(NO_OPER, NL_CLONING, "Hard connection limit exceeded on subnet: %s (group: %s): %d connected, %d max.", CIDRtostr(*ip, th->nodebits), tg->name->content, nodecount + usercountadjustment, th->maxpernode);
99 snprintf(message, messagelen, "Too many connections from your host (%s) - see https://www.quakenet.org/help/trusts/connection-limit for details.", IPtostr(*ip));
100 return POLICY_FAILURE_NODECOUNT;
101 }
102
103 if(tg->trustedfor && tg->count + usercountadjustment > tg->trustedfor) {
104 if(tg->count > (long)tg->exts[countext]) {
105 tg->exts[countext] = (void *)(long)tg->count;
106
107 controlwall(NO_OPER, NL_CLONING, "Hard connection limit exceeded (group %s): %d connected, %d max.", tg->name->content, tg->count + usercountadjustment, tg->trustedfor);
108 snprintf(message, messagelen, "Too many connections from your trust (%s) - see https://www.quakenet.org/help/trusts/connection-limit for details.", IPtostr(*ip));
109 }
110
111 snprintf(message, messagelen, "Too many connections from your trust (%s) - see https://www.quakenet.org/help/trusts/connection-limit for details.", IPtostr(*ip));
112 return POLICY_FAILURE_GROUPCOUNT;
113 }
114
115 if((tg->flags & TRUST_ENFORCE_IDENT) && (username[0] == '~')) {
116 controlwall(NO_OPER, NL_CLONING, "Ident required: %s@%s (group: %s).", username, IPtostr(*ip), tg->name->content);
117 snprintf(message, messagelen, "IDENTD required from your host (%s) - see https://www.quakenet.org/help/trusts/connection-limit for details.", IPtostr(*ip));
118 return POLICY_FAILURE_IDENTD;
119 }
120
121 if(tg->maxperident > 0) {
122 int identcount = 0;
123 trusthost *th2;
124 nick *tnp;
125
126 for(th2=tg->hosts;th2;th2=th2->next) {
127 for(tnp=th2->users;tnp;tnp=nextbytrust(tnp)) {
128 if(!ircd_strcmp(tnp->ident, username))
129 identcount++;
130 }
131 }
132
133 if(identcount + usercountadjustment > tg->maxperident) {
134 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);
135 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));
136 return POLICY_FAILURE_IDENTCOUNT;
137 }
138 }
139 } else {
140 if(tg->count < tg->maxusage)
141 tg->exts[countext] = (void *)(long)tg->count;
142 }
143
144 if(tg->trustedfor > 0)
145 snprintf(message, messagelen, "Trust has %d out of %d allowed connections.", tg->count + usercountadjustment, tg->trustedfor);
146
147 return POLICY_SUCCESS;
148 }
149
150 static int checkconnection(const char *username, struct irc_in_addr *ip, int hooknum, int cloneadjustment, char *message, size_t messagelen) {
151 struct irc_in_addr ip_canonicalized;
152 ip_canonicalize_tunnel(&ip_canonicalized, ip);
153
154 return checkconnectionth(username, &ip_canonicalized, th_getbyhost(&ip_canonicalized), hooknum, cloneadjustment, message, messagelen);
155 }
156
157 static int trustdowrite(trustsocket *sock, char *format, ...) {
158 char buf[1024];
159 va_list va;
160 int r;
161
162 va_start(va, format);
163 r = vsnprintf(buf, sizeof(buf), format, va);
164 va_end(va);
165
166 if(r >= sizeof(buf))
167 r = sizeof(buf) - 1;
168
169 buf[r] = '\n';
170
171 if(write(sock->fd, buf, r + 1) != r + 1)
172 return 0;
173 return 1;
174 }
175
176 static int policycheck_auth(trustsocket *sock, const char *sequence_id, const char *username, const char *host) {
177 char message[512];
178 int verdict;
179 struct irc_in_addr ip;
180 unsigned char bits;
181
182 if(!ipmask_parse(host, &ip, &bits)) {
183 sock->accepted++;
184 return trustdowrite(sock, "PASS %s", sequence_id);
185 }
186
187 verdict = checkconnection(username, &ip, HOOK_TRUSTS_NEWNICK, 1, message, sizeof(message));
188
189 if(!enforcepolicy_auth)
190 verdict = POLICY_SUCCESS;
191
192 if (verdict == POLICY_SUCCESS) {
193 sock->accepted++;
194
195 if(message[0])
196 return trustdowrite(sock, "PASS %s %s", sequence_id, message);
197 else
198 return trustdowrite(sock, "PASS %s", sequence_id);
199 } else {
200 sock->rejected++;
201
202 controlwall(NO_OPER, NL_CLONING, "Rejected connection from %s@%s using IAuth: %s", username, host, message);
203 return trustdowrite(sock, "KILL %s %s", sequence_id, message);
204 }
205 }
206
207 static int trustkillconnection(trustsocket *sock, char *reason) {
208 trustdowrite(sock, "QUIT %s", reason);
209 return 0;
210 }
211
212 static void trustfreeconnection(trustsocket *sock, int unlink) {
213 trustsocket **pnext, *ts;
214
215 if(!unlink) {
216 controlwall(NO_OPER, NL_TRUSTS, "Lost connection on policy socket for '%s'.", sock->authed?sock->authuser:"<unauthenticated connection>");
217
218 deregisterhandler(sock->fd, 1);
219 nsfree(POOL_TRUSTS, sock);
220 return;
221 }
222
223 pnext = &tslist;
224
225 for(ts=*pnext;*pnext;pnext=&((*pnext)->next)) {
226 if(ts == sock) {
227 *pnext = sock->next;
228 trustfreeconnection(sock, 0);
229 break;
230 }
231 }
232 }
233
234 static int handletrustauth(trustsocket *sock, char *server_name, char *mac) {
235 int i;
236 char *password = NULL;
237 unsigned char digest[16];
238 char noncehexbuf[NONCELEN * 2 + 1];
239 char hexbuf[sizeof(digest) * 2 + 1];
240
241 for(i=0;i<MAXSERVERS;i++) {
242 if(trustaccounts[i].used && strcmp(trustaccounts[i].server, server_name) == 0) {
243 password = trustaccounts[i].password;
244 break;
245 }
246 }
247
248 if (!password) {
249 controlwall(NO_OPER, NL_TRUSTS, "Invalid servername for policy socket: '%s'", server_name);
250 return trustkillconnection(sock, "Invalid servername.");
251 }
252
253 hmacmd5 h;
254 hmacmd5_init(&h, (unsigned char *)password, strlen(password));
255 hmacmd5_update(&h, (unsigned char *)hmac_printhex(sock->nonce, noncehexbuf, NONCELEN), NONCELEN * 2);
256 hmacmd5_final(&h, digest);
257 if(hmac_strcmp(mac, hmac_printhex(digest, hexbuf, sizeof(digest)))) {
258 controlwall(NO_OPER, NL_TRUSTS, "Invalid password for policy socket with servername '%s'.", server_name);
259 return trustkillconnection(sock, "Bad MAC.");
260 }
261
262 sock->authed = 1;
263 strncpy(sock->authuser, server_name, SERVERLEN);
264
265 controlwall(NO_OPER, NL_TRUSTS, "Successful authentication for policy socket with servername '%s'.", server_name);
266 return trustdowrite(sock, "AUTHOK");
267 }
268
269 #define MAXTOKENS 10
270 static int handletrustline(trustsocket *sock, char *line) {
271 char *command, *p, *lastpos;
272 char *tokens[MAXTOKENS];
273 int tokensfound = -1;
274
275 for(command=lastpos=p=line;*p;p++) {
276 if(*p == ' ') {
277 *p = '\0';
278 if(tokensfound == MAXTOKENS)
279 return trustkillconnection(sock, "too many tokens");
280
281 if(tokensfound >= 0) {
282 tokens[tokensfound++] = lastpos;
283 } else {
284 tokensfound++;
285 }
286 lastpos = p + 1;
287 }
288 }
289 if(lastpos != p) {
290 if(tokensfound == MAXTOKENS)
291 return trustkillconnection(sock, "too many tokens");
292 tokens[tokensfound++] = lastpos;
293 }
294
295 if(!sock->authed && !strcmp("AUTH", command)) {
296 if(tokensfound != 2)
297 return trustkillconnection(sock, "incorrect arg count for command.");
298
299 return handletrustauth(sock, tokens[0], tokens[1]);
300 } else if(sock->authed && !strcmp("CHECK", command)) {
301 if(tokensfound != 3)
302 return trustkillconnection(sock, "incorrect arg count for command.");
303
304 policycheck_auth(sock, tokens[0], tokens[1], tokens[2]);
305 return 1;
306 } else if(!strcmp("VERSION", command)) {
307 /* Ignore this command for now. */
308 return 1;
309 } else {
310 Error("trusts_policy", ERR_WARNING, "Bad command: %s", command);
311 return 0;
312 }
313 }
314
315 static trustsocket *findtrustsocketbyfd(int fd) {
316 for(trustsocket *ts=tslist;ts;ts=ts->next)
317 if(ts->fd==fd)
318 return ts;
319
320 return NULL;
321 }
322
323 static int handletrustclient(trustsocket *sock) {
324 int r, remaining = TRUSTBUFSIZE - sock->size, i;
325 char *lastpos, *c;
326
327 if(!remaining) {
328 trustkillconnection(sock, "Buffer overflow.");
329 return 0;
330 }
331
332 r = read(sock->fd, sock->buf + sock->size, remaining);
333 if(r <= 0)
334 return 0;
335
336 sock->size+=r;
337 lastpos = sock->buf;
338
339 for(c=sock->buf,i=0;i<sock->size;i++,c++) {
340 if(*c != '\n')
341 continue;
342 *c = '\0';
343 if(!handletrustline(sock, lastpos))
344 return 0;
345
346 lastpos = c + 1; /* is this ok? */
347 }
348 sock->size-=lastpos - sock->buf;
349 memmove(sock->buf, lastpos, sock->size);
350
351 return 1;
352 }
353
354 static void processtrustclient(int fd, short events) {
355 trustsocket *sock = findtrustsocketbyfd(fd);
356
357 if(!sock)
358 return;
359
360 if(events & POLLIN)
361 if(!handletrustclient(sock))
362 trustfreeconnection(sock, 1);
363 }
364
365 static void trustdotimeout(void *arg) {
366 time_t t = time(NULL);
367 trustsocket **pnext, *sock;
368
369 pnext = &tslist;
370
371 for(sock=*pnext;*pnext;pnext=&((*pnext)->next)) {
372 if(!sock->authed && t >= sock->timeout) {
373 trustkillconnection(sock, "Auth timeout.");
374 *pnext = sock->next;
375 trustfreeconnection(sock, 0);
376 }
377 }
378 }
379
380 static void processtrustlistener(int fd, short events) {
381 if(events & POLLIN) {
382 trustsocket *sock;
383 char buf[NONCELEN * 2 + 1];
384
385 int newfd = accept(fd, NULL, NULL), flags;
386 if(newfd == -1)
387 return;
388
389 flags = fcntl(newfd, F_GETFL, 0);
390 if(flags < 0) {
391 Error("trusts_policy", ERR_WARNING, "Unable to set socket non-blocking.");
392 close(newfd);
393 return;
394 }
395
396 if(fcntl(fd, F_SETFL, flags|O_NONBLOCK) < 0) {
397 Error("trusts_policy", ERR_WARNING, "Unable to set socket non-blocking.");
398 close(newfd);
399 return;
400 }
401
402 registerhandler(newfd, POLLIN|POLLERR|POLLHUP, processtrustclient);
403
404 sock = nsmalloc(POOL_TRUSTS, sizeof(trustsocket));
405 if(!sock) {
406 deregisterhandler(newfd, 1);
407 return;
408 }
409
410 sock->fd = newfd;
411 sock->next = tslist;
412 tslist = sock;
413
414 if(fread((char *)sock->nonce, 1, NONCELEN, urandom) != NONCELEN) {
415 Error("trusts_policy", ERR_WARNING, "Error getting random bytes.");
416 deregisterhandler(newfd, 1);
417 tslist = sock->next;
418 nsfree(POOL_TRUSTS, sock);
419 } else {
420 sock->authed = 0;
421 sock->size = 0;
422 sock->connected = time(NULL);
423 sock->timeout = time(NULL) + 30;
424 sock->accepted = 0;
425 sock->rejected = 0;
426 if(!trustdowrite(sock, "AUTH %s", hmac_printhex(sock->nonce, buf, NONCELEN))) {
427 Error("trusts_policy", ERR_WARNING, "Error writing auth to fd %d.", newfd);
428 deregisterhandler(newfd, 1);
429 tslist = sock->next;
430 nsfree(POOL_TRUSTS, sock);
431 return;
432 }
433 }
434 }
435 }
436
437 static int createlistenersock(int port) {
438 struct sockaddr_in s;
439 int fd;
440 int optval;
441
442 memset(&s, 0, sizeof(struct sockaddr_in));
443 s.sin_family = AF_INET;
444 s.sin_addr.s_addr = INADDR_ANY;
445 s.sin_port = htons(port);
446
447 fd = socket(PF_INET, SOCK_STREAM, 0);
448 if(fd < 0) {
449 Error("trusts_policy", ERR_WARNING, "Unable to get socket for trustfd.");
450 return -1;
451 }
452
453 optval = 1;
454 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
455
456 if(bind(fd, (struct sockaddr *)&s, sizeof(struct sockaddr_in)) < 0) {
457 Error("trusts_policy", ERR_WARNING, "Unable to bind trustfd.");
458 close(fd);
459 return -1;
460 }
461
462 if(listen(fd, 5) < 0) {
463 Error("trusts_policy", ERR_WARNING, "Unable to listen on trustfd.");
464 close(fd);
465 return -1;
466 }
467
468 registerhandler(fd, POLLIN, processtrustlistener);
469
470 return fd;
471 }
472
473 static void policycheck_irc(int hooknum, void *arg) {
474 void **args = arg;
475 nick *np = args[0];
476 long moving = (long)args[1];
477 char message[512];
478 int verdict;
479
480 if(moving)
481 return;
482
483 verdict = checkconnectionth(np->ident, &np->p_nodeaddr, gettrusthost(np), hooknum, 0, message, sizeof(message));
484
485 if(!enforcepolicy_irc)
486 verdict = POLICY_SUCCESS;
487
488 switch (verdict) {
489 case POLICY_FAILURE_NODECOUNT:
490 glinebynick(np, POLICY_GLINE_DURATION, message, GLINE_IGNORE_TRUST, "trusts_policy");
491 break;
492 case POLICY_FAILURE_IDENTD:
493 glinebyip("~*", &np->p_ipaddr, 128, POLICY_GLINE_DURATION, message, GLINE_ALWAYS_USER|GLINE_IGNORE_TRUST, "trusts_policy");
494 break;
495 case POLICY_FAILURE_IDENTCOUNT:
496 glinebynick(np, POLICY_GLINE_DURATION, message, GLINE_ALWAYS_USER|GLINE_IGNORE_TRUST, "trusts_policy");
497 break;
498 }
499 }
500
501 static int trusts_cmdtrustpolicyirc(void *source, int cargc, char **cargv) {
502 nick *sender = source;
503
504 if(cargc < 1) {
505 controlreply(sender, "Use of glines for trust policy enforcement is currently %s.", enforcepolicy_irc?"enabled":"disabled");
506 return CMD_OK;
507 }
508
509 enforcepolicy_irc = atoi(cargv[0]);
510 controlwall(NO_OPER, NL_TRUSTS, "%s %s use of glines for trust policy enforcement.", controlid(sender), enforcepolicy_irc?"enabled":"disabled");
511 controlreply(sender, "Use of glines for trust policy enforcement is now %s.", enforcepolicy_irc?"enabled":"disabled");
512
513 return CMD_OK;
514 }
515
516 static int trusts_cmdtrustpolicyauth(void *source, int cargc, char **cargv) {
517 nick *sender = source;
518
519 if(cargc < 1) {
520 controlreply(sender, "Trust policy enforcement with IAuth is currently %s.", enforcepolicy_auth?"enabled":"disabled");
521 return CMD_OK;
522 }
523
524 enforcepolicy_auth = atoi(cargv[0]);
525 controlwall(NO_OPER, NL_TRUSTS, "%s %s trust policy enforcement with IAuth.", controlid(sender), enforcepolicy_auth?"enabled":"disabled");
526 controlreply(sender, "Trust policy enforcement with IAuth is now %s.", enforcepolicy_auth?"enabled":"disabled");
527
528 return CMD_OK;
529 }
530
531
532 static int trusts_cmdtrustsockets(void *source, int cargc, char **cargv) {
533 nick *sender = source;
534 time_t now;
535 trustsocket *sock;
536
537 time(&now);
538
539 controlreply(sender, "Server Connected for Accepted Rejected");
540
541 for(sock=tslist;sock;sock=sock->next)
542 controlreply(sender, "%-20s %20s %10d %15d", sock->authed?sock->authuser:"<unauthenticated connection>", longtoduration(now - sock->connected, 0), sock->accepted, sock->rejected);
543
544 controlreply(sender, "-- End of list.");
545 return CMD_OK;
546 }
547
548 void loadtrustaccounts(void) {
549 array *accts;
550
551 memset(trustaccounts, 0, sizeof(trustaccounts));
552
553 accts = getconfigitems("trusts_policy", "server");
554 if(!accts) {
555 Error("trusts_policy", ERR_INFO, "No servers added.");
556 } else {
557 sstring **servers = (sstring **)(accts->content);
558 int i;
559 for(i=0;i<accts->cursi;i++) {
560 char server[512];
561 char *pos;
562
563 if(i>=MAXSERVERS) {
564 Error("trusts_policy", ERR_INFO, "Too many servers specified.");
565 break;
566 }
567
568 strncpy(server, servers[i]->content, sizeof(server));
569
570 pos = strchr(server, ',');
571
572 if(!pos) {
573 Error("trusts_policy", ERR_INFO, "Server line is missing password: %s", server);
574 continue;
575 }
576
577 *pos = '\0';
578
579 trustaccounts[i].used = 1;
580 strncpy(trustaccounts[i].server, server, SERVERLEN);
581 strncpy(trustaccounts[i].password, pos+1, TRUSTPASSLEN);
582 }
583 }
584 }
585
586 static void trustaccounts_rehash(int hooknum, void *arg) {
587 loadtrustaccounts();
588 }
589
590 void _init(void) {
591 sstring *m;
592 int trustport;
593
594 countext = registertgext("count");
595 if(countext == -1)
596 return;
597
598 m = getconfigitem("trusts_policy", "enforcepolicy_irc");
599 if(m)
600 enforcepolicy_irc = atoi(m->content);
601
602 m = getconfigitem("trusts_policy", "enforcepolicy_auth");
603 if(m)
604 enforcepolicy_auth = atoi(m->content);
605
606 m = getconfigitem("trusts_policy", "trustport");
607 if(m)
608 trustport = atoi(m->content);
609 else
610 trustport = DEFAULT_TRUSTPORT;
611
612 if(trustport)
613 listenerfd = createlistenersock(trustport);
614
615 loadtrustaccounts();
616
617 registerhook(HOOK_TRUSTS_NEWNICK, policycheck_irc);
618 registerhook(HOOK_TRUSTS_LOSTNICK, &policycheck_irc);
619 registerhook(HOOK_CORE_REHASH, trustaccounts_rehash);
620
621 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.");
622 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.");
623 registercontrolhelpcmd("trustsockets", NO_DEVELOPER, 0, trusts_cmdtrustsockets, "Usage: trustsockets\nLists all currently active TRUST sockets.");
624
625 schedulerecurring(time(NULL)+1, 0, 5, trustdotimeout, NULL);
626
627 urandom = fopen("/dev/urandom", "rb");
628 if(!urandom)
629 Error("trusts_policy", ERR_ERROR, "Couldn't open /dev/urandom.");
630 }
631
632 void _fini(void) {
633 trustsocket *sock, *next;
634
635 if(countext == -1)
636 return;
637
638 releasetgext(countext);
639
640 deregisterhook(HOOK_TRUSTS_NEWNICK, policycheck_irc);
641 deregisterhook(HOOK_TRUSTS_LOSTNICK, policycheck_irc);
642 deregisterhook(HOOK_CORE_REHASH, trustaccounts_rehash);
643
644 deregistercontrolcmd("trustpolicyirc", trusts_cmdtrustpolicyirc);
645 deregistercontrolcmd("trustpolicyauth", trusts_cmdtrustpolicyauth);
646 deregistercontrolcmd("trustsockets", trusts_cmdtrustsockets);
647
648 deleteallschedules(trustdotimeout);
649
650 if (urandom)
651 fclose(urandom);
652
653 if (listenerfd != -1)
654 deregisterhandler(listenerfd, 1);
655
656 for(sock=tslist;sock;) {
657 next = sock->next;
658
659 trustkillconnection(sock, "Unloading module.");
660 trustfreeconnection(sock, 0);
661
662 sock = next;
663 }
664 }