#include "../lib/irc_string.h"
#include "../irc/irc.h"
#include "../glines/glines.h"
+#include "../patricianick/patricianick.h"
#include "trusts.h"
MODULE_VERSION("");
time_t timeout;
int accepted;
int rejected;
+ int unthrottled;
struct trustsocket *next;
} trustsocket;
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;
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;
}
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;
}
}
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;
}
}
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, ...) {
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;
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;
}
- 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);
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) {
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);
if(!sock)
return;
-
+
+ if (events & (POLLPRI | POLLERR | POLLHUP | POLLNVAL)) {
+ trustfreeconnection(sock, 1);
+ return;
+ }
+
if(events & POLLIN)
if(!handletrustclient(sock))
trustfreeconnection(sock, 1);
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;
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);
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;
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) {
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;