X-Git-Url: https://jfr.im/git/irc/quakenet/newserv.git/blobdiff_plain/27ce0548659b866469bb01b639aab0682a4ea667..28aa186de14dc4fa7488866e70beb6ae933dde79:/channel/channelbans.c?ds=sidebyside diff --git a/channel/channelbans.c b/channel/channelbans.c index f4dc299c..fc86bc76 100644 --- a/channel/channelbans.c +++ b/channel/channelbans.c @@ -9,441 +9,41 @@ #include "../lib/irc_string.h" #include "../irc/irc_config.h" #include "../irc/irc.h" - -/* - * makeban: - * Converts the specified ban into a ban structure - */ - -chanban *makeban(const char *mask) { - int len; - int foundat=-1,foundbang=-1; - int dotcount=0; - int notip=0; - int foundwild=0; - int foundslash=0; - int i; - int checklen; - chanban *cbp; - - cbp=getchanban(); - len=strlen(mask); - - cbp->flags=0; - - /* Let's catch a silly case first */ - if (!strcmp(mask,"*")) { - cbp->flags=CHANBAN_HOSTANY | CHANBAN_USERANY | CHANBAN_NICKANY; - cbp->nick=NULL; - cbp->user=NULL; - cbp->host=NULL; - cbp->timeset=time(NULL); - return cbp; - } - - for (i=(len-1);i>=0;i--) { - if (mask[i]=='@') { - /* Got @ sign: everything after here is host */ - if ((len-i)-1 > HOSTLEN) { - /* This is too long, we need to truncate it */ - cbp->host=getsstring(&mask[len-HOSTLEN],HOSTLEN); - cbp->host->content[0]='*'; - cbp->flags |= CHANBAN_HOSTMASK; - } else if (i==(len-1)) { - /* Ban ending with @, just mark it invalid */ - /* Note that "moo@*" overrides "moo@", so mark it as having a host too */ - cbp->flags |= (CHANBAN_INVALID | CHANBAN_HOSTNULL); - cbp->host=NULL; - } else if (i==(len-2) && mask[i+1]=='*') { - /* Special case: "@*" */ - cbp->flags |= CHANBAN_HOSTANY; - cbp->host=NULL; - } else if (foundslash) { - /* If we found a slash (/), this can only be a CIDR ban */ - /* However, it might be broken, so we need to retain the exact string - * to track it accurately */ - cbp->host=getsstring(&mask[i+1],HOSTLEN); - if ((notip || dotcount!=3) && !foundwild) { - cbp->flags |= (CHANBAN_INVALID | CHANBAN_HOSTEXACT); - } - if (foundwild) { - cbp->flags |= (CHANBAN_INVALID | CHANBAN_HOSTMASK); - } - cbp->flags |= (CHANBAN_HOSTEXACT | CHANBAN_CIDR); - } else { - /* We have some string with between 1 and HOSTLEN characters.. */ - cbp->host=getsstring(&mask[i+1],HOSTLEN); - if (foundwild) { - /* We check all characters after the last wildcard (if any).. if they match - * the corresponding bits of the hidden host string we mark it accordingly */ - if (!(checklen=len-foundwild-1)) { /* Ban ends in *, mark it anyway.. */ - cbp->flags |= CHANBAN_HIDDENHOST; - } else { - if (checklen>=strlen(HIS_HIDDENHOST)) { - if (!ircd_strcmp(cbp->host->content+(cbp->host->length-strlen(HIS_HIDDENHOST)), HIS_HIDDENHOST)) - cbp->flags |= CHANBAN_HIDDENHOST; - } else { - if (!ircd_strcmp(cbp->host->content+(cbp->host->length-checklen), - (HIS_HIDDENHOST)+strlen(HIS_HIDDENHOST)-checklen)) - cbp->flags |= CHANBAN_HIDDENHOST; - } - } - cbp->flags |= CHANBAN_HOSTMASK; - if (!notip && dotcount<=3) - cbp->flags |= CHANBAN_IP; - } else { - /* Exact host: see if it ends with the "hidden host" string */ - cbp->flags |= CHANBAN_HOSTEXACT; - if ((cbp->host->length > (strlen(HIS_HIDDENHOST)+1)) && - !ircd_strcmp(cbp->host->content+(cbp->host->length-strlen(HIS_HIDDENHOST)), HIS_HIDDENHOST)) - cbp->flags |= CHANBAN_HIDDENHOST; - else if (!notip && dotcount==3) - cbp->flags |= CHANBAN_IP; - } - } - foundat=i; - break; - } else if (mask[i]=='/') { - foundslash=1; - } else if (mask[i]=='.') { - dotcount++; - } else if (mask[i]=='?' || mask[i]=='*') { - if (!foundwild) /* Mark last wildcard in string */ - foundwild=i; - } else if (mask[i]<'0' || mask[i]>'9') { - notip=1; - } - } - - if (foundat<0) { - /* If there wasn't an @, this ban matches any host */ - cbp->host=NULL; - cbp->flags |= CHANBAN_HOSTANY; - } - - foundwild=0; - - for (i=0;iflags |= CHANBAN_NICKNULL; - cbp->nick=NULL; - } else if (i==1 && mask[0]=='*') { - /* matches any nick */ - cbp->flags |= CHANBAN_NICKANY; - cbp->nick=NULL; - } else { - if (i>NICKLEN) { - /* too long: just take the first NICKLEN chars */ - cbp->nick=getsstring(mask,NICKLEN); - } else { - cbp->nick=getsstring(mask,i); - } - if (foundwild) - cbp->flags |= CHANBAN_NICKMASK; - else - cbp->flags |= CHANBAN_NICKEXACT; - } - foundbang=i; - break; - } else if (mask[i]=='?' || mask[i]=='*') { - if (iNICKLEN) - cbp->nick=getsstring(mask,NICKLEN); - else - cbp->nick=getsstring(mask,len); - - if (foundwild) - cbp->flags |= CHANBAN_NICKMASK; - else - cbp->flags |= CHANBAN_NICKEXACT; - - cbp->flags |= (CHANBAN_USERANY | CHANBAN_HOSTANY); - cbp->host=NULL; - cbp->user=NULL; - } else { - /* A ban with @ only is treated as user@host */ - cbp->nick=NULL; - cbp->flags |= CHANBAN_NICKANY; - } - } - - if (foundat>=0) { - /* We found an @, so everything between foundbang+1 and foundat-1 is supposed to be ident */ - /* This is true even if there was no !.. */ - if (foundat==(foundbang+1)) { - /* empty ident matches nothing */ - cbp->flags |= (CHANBAN_INVALID | CHANBAN_USERNULL); - cbp->user=NULL; - } else if (foundat - foundbang - 1 > USERLEN) { - /* It's too long.. */ - cbp->user=getsstring(&mask[foundat-USERLEN],USERLEN); - cbp->user->content[0]='*'; - cbp->flags |= CHANBAN_USERMASK; - } else if ((foundat - foundbang - 1 == 1) && mask[foundbang+1]=='*') { - cbp->user=NULL; - cbp->flags |= CHANBAN_USERANY; - } else { - cbp->user=getsstring(&mask[foundbang+1],(foundat-foundbang-1)); - if (strchr(cbp->user->content,'*') || strchr(cbp->user->content,'?')) - cbp->flags |= CHANBAN_USERMASK; - else - cbp->flags |= CHANBAN_USEREXACT; - } - } - - assert(cbp->flags & (CHANBAN_USEREXACT | CHANBAN_USERMASK | CHANBAN_USERANY | CHANBAN_USERNULL)); - assert(cbp->flags & (CHANBAN_NICKEXACT | CHANBAN_NICKMASK | CHANBAN_NICKANY | CHANBAN_NICKNULL)); - assert(cbp->flags & (CHANBAN_HOSTEXACT | CHANBAN_HOSTMASK | CHANBAN_HOSTANY | CHANBAN_HOSTNULL)); - - cbp->timeset=time(NULL); - - return cbp; -} - -/* banoverlap: - * Returns nonzero iff bana is a SUPERSET of banb - */ - -int banoverlap(const chanban *bana, const chanban *banb) { - /* This function works by looking for cases where bana DOESN'T overlap banb */ - - /* NICK section */ - /* If bana has CHANBAN_NICKANY then it clearly overlaps - * in the nick section so there are no checks */ - - if ((bana->flags & CHANBAN_NICKNULL) && !(banb->flags & CHANBAN_NICKNULL)) { - return 0; - } - - if (bana->flags & CHANBAN_NICKMASK) { - if (banb->flags & (CHANBAN_NICKANY | CHANBAN_NICKNULL)) { - return 0; - } - if ((banb->flags & CHANBAN_NICKMASK) && !match2patterns(bana->nick->content, banb->nick->content)) { - return 0; - } - if ((banb->flags & CHANBAN_NICKEXACT) && !match2strings(bana->nick->content, banb->nick->content)) { - return 0; - } - } - - if (bana->flags & CHANBAN_NICKEXACT) { - if (banb->flags & (CHANBAN_NICKANY | CHANBAN_NICKMASK | CHANBAN_NICKNULL)) { - return 0; - } - if ((!bana->nick && banb->nick) || (bana->nick && !banb->nick)) { - return 0; - } - if (bana->nick && ircd_strcmp(bana->nick->content,banb->nick->content)) { - return 0; - } - } - - /* USER section */ - /* If bana has CHANBAN_USERANY then it clearly overlaps - * in the user section so there are no checks */ - - if ((bana->flags & CHANBAN_USERNULL) && !(banb->flags & CHANBAN_USERNULL)) { - return 0; - } - - if (bana->flags & CHANBAN_USERMASK) { - if (banb->flags & (CHANBAN_USERANY | CHANBAN_USERNULL)) { - return 0; - } - if ((banb->flags & CHANBAN_USERMASK) && !match2patterns(bana->user->content, banb->user->content)) { - return 0; - } - if ((banb->flags & CHANBAN_USEREXACT) && !match2strings(bana->user->content, banb->user->content)) { - return 0; - } - } - - if (bana->flags & CHANBAN_USEREXACT) { - if (banb->flags & (CHANBAN_USERANY | CHANBAN_USERMASK | CHANBAN_USERNULL)) { - return 0; - } - if ((!bana->user && banb->user) || (bana->user && !banb->user)) { - return 0; - } - if (bana->user && ircd_strcmp(bana->user->content,banb->user->content)) { - return 0; - } - } - - /* HOST section */ - /* If bana has CHANBAN_HOSTANY then it clearly overlaps - * in the host section so there are no checks */ - - if ((bana->flags & CHANBAN_HOSTNULL) && !(banb->flags & CHANBAN_HOSTNULL)) { - return 0; - } - - if (bana->flags & CHANBAN_HOSTMASK) { - if (banb->flags & (CHANBAN_HOSTANY | CHANBAN_HOSTNULL)) { - return 0; - } - if ((banb->flags & CHANBAN_HOSTMASK) && !match2patterns(bana->host->content, banb->host->content)) { - return 0; - } - if ((banb->flags & CHANBAN_HOSTEXACT) && !match2strings(bana->host->content, banb->host->content)) { - return 0; - } - } - - if (bana->flags & CHANBAN_HOSTEXACT) { - if (banb->flags & (CHANBAN_HOSTANY | CHANBAN_HOSTMASK | CHANBAN_HOSTNULL)) { - return 0; - } - if ((!bana->host && banb->host) || (bana->host && !banb->host)) { - return 0; - } - if (bana->host && ircd_strcmp(bana->host->content,banb->host->content)) { - return 0; - } - } - - return 1; -} - -/* - * banequal: - * Returns nonzero iff the bans are EXACTLY the same - */ - -int banequal(chanban *bana, chanban *banb) { - if (bana->flags != banb->flags) - return 0; - - if ((!bana->nick && banb->nick) || (bana->nick && !banb->nick)) - return 0; - - if (bana->nick && ircd_strcmp(bana->nick->content,banb->nick->content)) - return 0; - - if ((!bana->user && banb->user) || (bana->user && !banb->user)) - return 0; - - if (bana->user && ircd_strcmp(bana->user->content,banb->user->content)) - return 0; - - if ((!bana->host && banb->host) || (bana->host && !banb->host)) - return 0; - - if (bana->host && ircd_strcmp(bana->host->content,banb->host->content)) - return 0; - - return 1; -} - -/* - * bantostring: - * Convert the specified ban to a string - */ - -char *bantostring(chanban *bp) { - static char outstring[NICKLEN+USERLEN+HOSTLEN+5]; - int strpos=0; - - if (bp->flags & CHANBAN_NICKANY) { - strpos += sprintf(outstring+strpos,"*"); - } else if (bp->nick) { - strpos += sprintf(outstring+strpos,"%s",bp->nick->content); - } - - strpos += sprintf(outstring+strpos,"!"); - - if (bp->flags & CHANBAN_USERANY) { - strpos += sprintf(outstring+strpos,"*"); - } else if (bp->user) { - strpos += sprintf(outstring+strpos,"%s",bp->user->content); - } - - strpos += sprintf(outstring+strpos,"@"); - - if (bp->flags & CHANBAN_HOSTANY) { - strpos += sprintf(outstring+strpos,"*"); - } else if (bp->host) { - strpos += sprintf(outstring+strpos,"%s",bp->host->content); - } - - return outstring; -} - -/* - * bantostringdebug: - * Convert the specified ban to a string (debugging version) - */ - -char *bantostringdebug(chanban *bp) { - static char outstring[NICKLEN+USERLEN+HOSTLEN+5]; - int strpos=0; - - strpos += sprintf(outstring+strpos, "flags=%04x ",bp->flags); - - if (bp->nick) { - strpos += sprintf(outstring+strpos, "nick=%s ",bp->nick->content); - } else { - strpos += sprintf(outstring+strpos, "nonick "); - } - - if (bp->user) { - strpos += sprintf(outstring+strpos, "user=%s ",bp->user->content); - } else { - strpos += sprintf(outstring+strpos, "nouser "); - } - - if (bp->host) { - strpos += sprintf(outstring+strpos, "host=%s ",bp->host->content); - } else { - strpos += sprintf(outstring+strpos, "nohost "); - } - - - return outstring; -} +#include "../lib/irc_ipv6.h" /* * nickmatchban: * Returns true iff the supplied nick* matches the supplied ban* + * + * "visibleonly" flag indicates that we shouldn't check against the real + * host if it's masked. */ -int nickmatchban(nick *np, chanban *bp) { - const char *ipstring; +int nickmatchban(nick *np, chanban *bp, int visibleonly) { char fakehost[HOSTLEN+1]; + char *ident; - /* nick/ident section: return 0 (no match) if they don't match */ - + /* If it's not valid, don't bother */ if (bp->flags & CHANBAN_INVALID) return 0; + + /* nick/ident section: return 0 (no match) if they don't match */ + + /* Pick up the visible username. If a sethost username is set, the real + * username is not checked. */ + ident=np->ident; + if (IsSetHost(np) && np->shident) + ident=np->shident->content; if (bp->flags & CHANBAN_USEREXACT && - ircd_strcmp(np->ident,bp->user->content) && - (!IsSetHost(np) || !np->shident || - ircd_strcmp(np->shident->content,bp->user->content))) + ircd_strcmp(ident,bp->user->content)) return 0; if (bp->flags & CHANBAN_NICKEXACT && ircd_strcmp(np->nick,bp->nick->content)) return 0; if (bp->flags & CHANBAN_USERMASK && - !match2strings(bp->user->content,np->ident) && - (!IsSetHost(np) || !np->shident || - !match2strings(bp->user->content, np->shident->content))) + !match2strings(bp->user->content,ident)) return 0; if (bp->flags & CHANBAN_NICKMASK && !match2strings(bp->nick->content,np->nick)) @@ -455,63 +55,72 @@ int nickmatchban(nick *np, chanban *bp) { if (bp->flags & CHANBAN_HOSTANY) return 1; + + /* "visibleonly" means don't check IP/realhost if they are sethosted/+x. + * We change this to mean "Don't check IP/realhost unconditionally" - by + * clearing it to 0 if the user isn't sethosted or +x. Simplifies logic + * later. */ + + if (!(IsSetHost(np) || (IsAccount(np) && IsHideHost(np)))) + visibleonly=0; - if (bp->flags & CHANBAN_IP) { - /* IP bans are checked against IP address only */ - ipstring=IPtostr(np->p_ipaddr); - - if (bp->flags & CHANBAN_HOSTEXACT && !ircd_strcmp(ipstring,bp->host->content)) + if ((bp->flags & CHANBAN_IP) && !visibleonly) { + if (ipmask_check(&(np->ipnode->prefix->sin), &(bp->ipaddr), bp->prefixlen)) return 1; + } + + /* Hostname bans need to be checked against +x host, +h host (if set) + * and actual host. Note that the +x host is only generated (and checked) if it's + * possible for the ban to match a hidden host. And it's checked regardless + * of whether the user is actually +x. */ + + if ((bp->flags & CHANBAN_HIDDENHOST) && IsAccount(np)) { + sprintf(fakehost,"%s.%s",np->authname, HIS_HIDDENHOST); - if (bp->flags & CHANBAN_HOSTMASK && match2strings(bp->host->content,ipstring)) + if ((bp->flags & CHANBAN_HOSTEXACT) && + !ircd_strcmp(fakehost, bp->host->content)) return 1; - } else { - /* Hostname bans need to be checked against +x host, +h host (if set) - * and actual host. Note that the +x host is only generated (and checked) if it's - * possible for the ban to match a hidden host.. */ - if ((bp->flags & CHANBAN_HIDDENHOST) && IsAccount(np)) { - sprintf(fakehost,"%s.%s",np->authname, HIS_HIDDENHOST); - - if ((bp->flags & CHANBAN_HOSTEXACT) && - !ircd_strcmp(fakehost, bp->host->content)) - return 1; - - if ((bp->flags & CHANBAN_HOSTMASK) && - match2strings(bp->host->content, fakehost)) - return 1; - } + if ((bp->flags & CHANBAN_HOSTMASK) && + match2strings(bp->host->content, fakehost)) + return 1; + } - if (IsSetHost(np)) { - if ((bp->flags & CHANBAN_HOSTEXACT) && + if (IsSetHost(np)) { + if ((bp->flags & CHANBAN_HOSTEXACT) && !ircd_strcmp(np->sethost->content, bp->host->content)) - return 1; + return 1; - if ((bp->flags & CHANBAN_HOSTMASK) && + if ((bp->flags & CHANBAN_HOSTMASK) && match2strings(bp->host->content, np->sethost->content)) - return 1; - } - - if (bp->flags & CHANBAN_HOSTEXACT && !ircd_strcmp(np->host->name->content,bp->host->content)) - return 1; - - if (bp->flags & CHANBAN_HOSTMASK && match2strings(bp->host->content,np->host->name->content)) return 1; } + /* If we are only checking visible host and the host is somehow masked, don't check + * against the real one. */ + if (visibleonly) + return 0; + + if (bp->flags & CHANBAN_HOSTEXACT && !ircd_strcmp(np->host->name->content,bp->host->content)) + return 1; + + if (bp->flags & CHANBAN_HOSTMASK && match2strings(bp->host->content,np->host->name->content)) + return 1; + return 0; } /* * nickbanned: * Returns true iff the supplied nick* is banned on the supplied chan* + * + * Pass the visibleonly flag on to nickbanned(). */ - -int nickbanned(nick *np, channel *cp) { +int nickbanned(nick *np, channel *cp, int visibleonly) { chanban *cbp; for (cbp=cp->bans;cbp;cbp=cbp->next) { - if (nickmatchban(np,cbp)) + if (nickmatchban(np,cbp,visibleonly)) return 1; } @@ -522,13 +131,25 @@ int nickbanned(nick *np, channel *cp) { * setban: * Set the specified ban; if it completely encloses any existing bans * then remove them. + * + * Returns 1 if the ban was set, or 0 if the ban was not set because an + * existing ban overlapped it. */ -void setban(channel *cp, const char *ban) { +int setban(channel *cp, const char *ban) { chanban **cbh,*cbp,*cbp2; cbp=makeban(ban); + /* Don't set our ban if something encloses it */ + for (cbp2=cp->bans;cbp2;cbp2=cbp2->next) { + if (banoverlap(cbp2, cbp)) { + /* This ban overlaps the one we are adding. Abort. */ + freechanban(cbp); + return 0; + } + } + /* Remove enclosed bans first */ for (cbh=&(cp->bans);*cbh;) { if (banoverlap(cbp,*cbh)) { @@ -547,6 +168,8 @@ void setban(channel *cp, const char *ban) { /* Now set the new ban */ cbp->next=(struct chanban *)cp->bans; cp->bans=cbp; + + return 1; } /*