]> jfr.im git - irc/quakenet/newserv.git/blobdiff - channel/channelbans.c
gline playground
[irc/quakenet/newserv.git] / channel / channelbans.c
index f4dc299ce67d063f3e9da2efe6a5d7c9e7845966..57af8bbf17a3c53098ebb89f0fb435253214e1ef 100644 (file)
 #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;i<foundat;i++) {
-    if (mask[i]=='!') {
-      if (i==0) {
-        /* Invalid mask: nick is empty */
-        cbp->flags |= 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 (i<NICKLEN) {
-        foundwild=1;
-      }
-    }
-  }
-  
-  if (foundbang<0) {
-    /* We didn't find a !, what we do now depends on what happened
-     * with the @ */
-    if (foundat<0) {
-      /* A ban with no ! or @ is treated as a nick ban only */
-      /* Note that we've special-cased "*" at the top, so we can only 
-       * hit the MASK or EXACT case here. */
-      if (len>NICKLEN) 
-        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
+ * nickmatchban_visible:
+ *  Returns true iff the supplied nick* matches the supplied ban* 
+ *  Doesn't check "invisible" things like true hosts and IPs for 
+ *  +x/+h users.
+ *
+ * copy & pasted this, touch and go whether this was a good idea.
  */
 
-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 */
+int nickmatchban_visible(nick *np, chanban *bp) {
+  const char *ipstring;
+  char fakehost[HOSTLEN+1];
+  char *visibleident;
 
-  if ((bana->flags & CHANBAN_NICKNULL) && !(banb->flags & CHANBAN_NICKNULL)) {
+  /* Don't waste time on invalid bans */
+  if (bp->flags & CHANBAN_INVALID)
     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;
-  }
+  /* nick/ident section: return 0 (no match) if they don't match */
 
-  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;
-    }
-  }
+  /* Determine the visible ident for sethost users.  Don't test the real one. */  
+  if (IsSetHost(np) && np->shident && *np->shident->content)
+    visibleident=np->shident->content;
+  else
+    visibleident=np->ident;
   
-  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)) {
+  if (bp->flags & CHANBAN_USEREXACT && ircd_strcmp(visibleident,bp->user->content))
     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))
+  if (bp->flags & CHANBAN_NICKEXACT && ircd_strcmp(np->nick,bp->nick->content))
     return 0;
-
-  if ((!bana->host && banb->host) || (bana->host && !banb->host))
+  
+  if (bp->flags & CHANBAN_USERMASK && !match2strings(bp->user->content,visibleident))
     return 0;
-  if (bana->host && ircd_strcmp(bana->host->content,banb->host->content))
+  
+  if (bp->flags & CHANBAN_NICKMASK && !match2strings(bp->nick->content,np->nick))
     return 0;
+  
+  /* host section.  Return 1 (match) if they do match
+   * Note that if user or ident was specified, they've already been checked
+   */
 
-  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);
-  }
+  if (bp->flags & CHANBAN_HOSTANY)
+    return 1;
 
-  strpos += sprintf(outstring+strpos,"!");
+  if ((bp->flags & CHANBAN_CIDR) && (bp->flags & CHANBAN_HOSTEXACT)) {
+    unsigned int cip;
+    unsigned char *ch;
 
-  if (bp->flags & CHANBAN_USERANY) {
-    strpos += sprintf(outstring+strpos,"*");
-  } else if (bp->user) {
-    strpos += sprintf(outstring+strpos,"%s",bp->user->content);
-  }
+    /* CIDR bans don't visibly match sethosted users */
+    if (IsSetHost(np) || (IsAccount(np) && IsHideHost(np)))
+      return 0;
 
-  strpos += sprintf(outstring+strpos,"@");
+    /* CIDR bans don't match IPv6 hosts */
+    if (!irc_in_addr_is_ipv4(&(np->p_ipaddr)))
+      return 0;
 
-  if (bp->flags & CHANBAN_HOSTANY) {
-    strpos += sprintf(outstring+strpos,"*");
-  } else if (bp->host) {
-    strpos += sprintf(outstring+strpos,"%s",bp->host->content);
+    /* Extract the client's IP address into a usable format */
+    ch=(unsigned char *)&(np->p_ipaddr.in6_16[6]);
+    cip=(ch[0]<<24) | (ch[1]<<16) | (ch[2]<<8) | (ch[3]);
+    
+    if ((cip & bp->mask) == bp->ipaddr)
+      return 1;
+    
+    return 0; /* A CIDR ban won't match any other way */
   }
+  
+  if (bp->flags & CHANBAN_IP) {
+    /* IP bans don't match sethosted users */
+    if (IsSetHost(np) || (IsAccount(np) && IsHideHost(np)))
+      return 0;
+      
+    if (bp->flags & CHANBAN_HOSTEXACT) {
+      /* If it's an exact IP ban we can compare it numerically */
+      unsigned int cip;
+      unsigned char *ch;
 
-  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;
+      /* Well, it won't match if it's not an IPv4 host */
+      if (!irc_in_addr_is_ipv4(&(np->p_ipaddr)))
+        return 0;
 
-  strpos += sprintf(outstring+strpos, "flags=%04x ",bp->flags);
+      /* Extract the client's IP address into a usable format */
+      ch=(unsigned char *)&(np->p_ipaddr.in6_16[6]);
+      cip=(ch[0]<<24) | (ch[1]<<16) | (ch[2]<<8) | (ch[3]);
 
-  if (bp->nick) {
-    strpos += sprintf(outstring+strpos, "nick=%s ",bp->nick->content);
+      if (cip==bp->ipaddr) 
+        return 1;
+    } else {
+      /* It's not an exact IP ban so let's generate the string */
+      ipstring=IPtostr(np->p_ipaddr);
+      if (bp->flags & CHANBAN_HOSTMASK && match2strings(bp->host->content,ipstring))
+        return 1;
+    }
   } else {
-    strpos += sprintf(outstring+strpos, "nonick ");
-  }
+    /* 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->user) {
-    strpos += sprintf(outstring+strpos, "user=%s ",bp->user->content);
-  } else {
-    strpos += sprintf(outstring+strpos, "nouser ");
-  }
+    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 (IsSetHost(np)) {
+      if ((bp->flags & CHANBAN_HOSTEXACT) &&
+         !ircd_strcmp(np->sethost->content, bp->host->content))
+       return 1;
+      
+      if ((bp->flags & CHANBAN_HOSTMASK) &&
+         match2strings(bp->host->content, np->sethost->content))
+       return 1;
+    }
 
-  if (bp->host) {
-    strpos += sprintf(outstring+strpos, "host=%s ",bp->host->content);
-  } else {
-    strpos += sprintf(outstring+strpos, "nohost ");
+    /* If the user is +h or +rx don't check their real host */
+    if (IsSetHost(np) || (IsHideHost(np) && IsAccount(np)))
+      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 outstring;
-} 
+  
+  return 0;
+}
 
 /*
  * nickmatchban:
@@ -456,15 +184,47 @@ int nickmatchban(nick *np, chanban *bp) {
   if (bp->flags & CHANBAN_HOSTANY)
     return 1;
 
-  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_CIDR) && (bp->flags & CHANBAN_HOSTEXACT)) {
+    unsigned int cip;
+    unsigned char *ch;
+
+    /* CIDR bans don't match IPv6 hosts */
+    if (!irc_in_addr_is_ipv4(&(np->p_ipaddr)))
+      return 0;
+
+    /* Extract the client's IP address into a usable format */
+    ch=(unsigned char *)&(np->p_ipaddr.in6_16[6]);
+    cip=(ch[0]<<24) | (ch[1]<<16) | (ch[2]<<8) | (ch[3]);
+
+    if ((cip & bp->mask) == bp->ipaddr)
       return 1;
     
-    if (bp->flags & CHANBAN_HOSTMASK && match2strings(bp->host->content,ipstring))
-      return 1;
+    return 0; /* A CIDR ban won't match any other way */
+  }
+  
+  if (bp->flags & CHANBAN_IP) {
+    if (bp->flags & CHANBAN_HOSTEXACT) {
+      /* If it's an exact IP ban we can compare it numerically */
+      unsigned int cip;
+      unsigned char *ch;
+
+      /* Well, it won't match if it's not an IPv4 host */
+      if (!irc_in_addr_is_ipv4(&(np->p_ipaddr)))
+        return 0;
+
+      /* Extract the client's IP address into a usable format */
+      ch=(unsigned char *)&(np->p_ipaddr.in6_16[6]);
+      cip=(ch[0]<<24) | (ch[1]<<16) | (ch[2]<<8) | (ch[3]);
+
+      if (cip==bp->ipaddr) 
+        return 1;
+    } else {
+      /* It's not an exact IP ban so let's generate the string */
+      ipstring=IPtostr(np->p_ipaddr);
+      if (bp->flags & CHANBAN_HOSTMASK && match2strings(bp->host->content,ipstring))
+        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
@@ -505,30 +265,52 @@ int nickmatchban(nick *np, chanban *bp) {
 /*
  * nickbanned:
  *  Returns true iff the supplied nick* is banned on the supplied chan*
+ * 
+ * Also nickbanned_visible - doesn't violate privacy by checking hidden
+ * hosts and idents.  Factored into one function to reduce copy&paste.
  */
-
-int nickbanned(nick *np, channel *cp) {
+static int nickbanned_real(nick *np, channel *cp, int (*cmpfunc)(nick *, chanban *)) {
   chanban *cbp;
 
   for (cbp=cp->bans;cbp;cbp=cbp->next) {
-    if (nickmatchban(np,cbp))
+    if (cmpfunc(np,cbp))
       return 1; 
   }
 
   return 0;
 }
+
+int nickbanned(nick *np, channel *cp) {
+  return nickbanned_real(np,cp,nickmatchban);
+}
+
+int nickbanned_visible(nick *np, channel *cp) {
+  return nickbanned_real(np,cp,nickmatchban_visible);
+}
               
 /*
  * 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 +329,8 @@ void setban(channel *cp, const char *ban) {
   /* Now set the new ban */
   cbp->next=(struct chanban *)cp->bans;
   cp->bans=cbp;
+  
+  return 1;
 }
 
 /*