Fix an error in backtracking (apparently exacerbated by escapes).
[ircu2.10.12-pk.git] / ircd / match.c
index 1424007c0e37987826a393374d609926706c6734..b585053c795348900039553fae8c29f037fea525 100644 (file)
@@ -138,7 +138,7 @@ int mmatch(const char *old_mask, const char *new_mask)
  * Here `any' also includes \* and \? !
  *
  * After reworking the boolean expressions, we get:
- * (Optimized to use boolean shortcircuits, with most frequently occuring
+ * (Optimized to use boolean short-circuits, with most frequently occurring
  *  cases upfront (which took 2 hours!)).
  */
     if ((*m == '*' && !mq) ||
@@ -190,59 +190,58 @@ int match(const char *mask, const char *name)
 {
   const char *m = mask, *n = name;
   const char *m_tmp = mask, *n_tmp = name;
-  int wild = 0;
-
-  for (;;)
-  {
-    if (*m == '*') {
-      while (*m == '*')  /* clean up any additional wildcards */
-        m++;
-
-      m_tmp = m;
-      n_tmp = n;
-      wild = 1;
-    }
-    if (*m == '\\')  /* next wildcard is disregarded */
-      m++;
+  int star_p;
 
-    if (!*m) {
-      if (!*n)
-        return 0;  /* match */
-
-      for (m--; (m > mask) && (*m == '?'); m--);
-        ;
-
-      if (*m == '*' && (m > mask))
-        return 0;  /* match */
-
-      if (!wild)
-        return 1;
-
-      m = m_tmp;
-      n = ++n_tmp;
-    }
-    else if (!*n) {
-      while (*m == '*')  /* clean up any additional wildcards */
-        m++;
-
-      return (*m != 0);
-    }
-    if (ToLower(*m) != ToLower(*n) && *m != '?') {
-      if (!wild)
-        return 1;  /* failure! */
-
-      m = m_tmp;
-      n = ++n_tmp;
+  for (;;) switch (*m) {
+  case '\0':
+    if (!*n)
+      return 0;
+  backtrack:
+    if (m_tmp == mask)
+      return 1;
+    m = m_tmp;
+    n = ++n_tmp;
+    if (*n == '\0')
+      return 1;
+    break;
+  case '\\':
+    m++;
+    /* allow escaping to force capitalization */
+    if (*m++ != *n++)
+      goto backtrack;
+    break;
+  case '*': case '?':
+    for (star_p = 0; ; m++) {
+      if (*m == '*')
+        star_p = 1;
+      else if (*m == '?') {
+        if (!*n++)
+          goto backtrack;
+      } else break;
     }
-    else {
-      if (*m)
-        m++;
-      if (*n)
-        n++;
+    if (star_p) {
+      if (!*m)
+        return 0;
+      else if (*m == '\\') {
+        m_tmp = ++m;
+        if (!*m)
+          return 1;
+        for (n_tmp = n; *n && *n != *m; n++) ;
+      } else {
+        m_tmp = m;
+        for (n_tmp = n; *n && ToLower(*n) != ToLower(*m); n++) ;
+      }
     }
+    /* and fall through */
+  default:
+    if (!*n)
+      return *m != '\0';
+    if (ToLower(*m) != ToLower(*n))
+      goto backtrack;
+    m++;
+    n++;
+    break;
   }
-
-  return 1;  /* no match! */
 }
 
 /*
@@ -253,7 +252,7 @@ int match(const char *mask, const char *name)
  *
  * (C) Carlo Wood - 6 Oct 1998
  * Speedup rewrite by Andrea Cocito, December 1998.
- * Note that this new optimized alghoritm can *only* work in place.
+ * Note that this new optimized algorithm can *only* work in place.
  */
 
 /** Collapse a mask string to remove redundancies.
@@ -315,11 +314,11 @@ char *collapse(char *mask)
  * by means of matchcomp() that gets the plain text mask as input and writes
  * its result in the memory locations addressed by the 3 parameters:
  * - *cmask will contain the text of the compiled mask
- * - *minlen will contain the lenght of the shortest string that can match 
+ * - *minlen will contain the length of the shortest string that can match 
  *   the mask
  * - *charset will contain the minimal set of chars needed to match the mask
  * You can pass NULL as *charset and it will be simply not returned, but you
- * MUST pass valid pointers for *minlen and *cmask (wich must be big enough 
+ * MUST pass valid pointers for *minlen and *cmask (which must be big enough 
  * to contain the compiled mask text that is in the worst case as long as the 
  * text of the mask itself in plaintext format) and the return value of 
  * matchcomp() will be the number of chars actually written there (excluded 
@@ -335,7 +334,7 @@ char *collapse(char *mask)
  * of mmexec() that will tell if it completely overrides that mask (a lot like
  * what mmatch() does for plain text masks).
  * You can gain a lot of speed in many situations avoiding to matchexec() when:
- * - The maximum lenght of the field you are about to match() the mask to is
+ * - The maximum length of the field you are about to match() the mask to is
  *   shorter than minlen, in example when matching abc*def*ghil with a nick:
  *   It just cannot match since a nick is at most 9 chars long and the mask
  *   needs at least 10 chars (10 will be the value returned in minlen).
@@ -558,9 +557,9 @@ trychunk:
  * cmask).
  * The area pointed by *mask MUST be big enough (the mask might be up to
  * twice the size of its compiled form if it's made all of \? or \*, and
- * this function can NOT work in place since it might enflate the mask)
+ * this function can NOT work in place since it might inflate the mask)
  * The printed mask is not identical to the one that was compiled to cmask,
- * infact it is 1) forced to all lowercase, 2) collapsed, both things
+ * in fact it is 1) forced to all lowercase, 2) collapsed, both things
  * are supposed to NOT change it's meaning.
  * It returns the number of chars actually written to *mask;
  */
@@ -635,9 +634,9 @@ int matchdecomp(char *mask, const char *cmask)
  * "the wider overrides the restrict" means that any string that matches
  * the restrict one _will_ also match the wider one, always. 
  * In this we behave differently from mmatch() because in example we return 
- * true for " a?*cd overrides a*bcd " for wich the override happens for how 
+ * true for " a?*cd overrides a*bcd " for which the override happens for how 
  * we literally defined it, here mmatch() would have returned false.
- * The original concepts and the base alghoritm are copied from mmatch() 
+ * The original concepts and the base algorithm are copied from mmatch() 
  * written by Run (Carlo Wood), this function is written by
  * Nemesi (Andrea Cocito)
  */
@@ -703,7 +702,7 @@ int mmexec(const char *wcm, int wminlen, const char *rcm, int rminlen)
       if (!*w)                  /* Did last loop match the rest of chunk ? */
         return 0;               /* ... Yes, end of wm, matched !           */
       if (*w != 'Z')
-      {                         /* ... No, hitted non-star                 */
+      {                         /* ... No, hit non-star                    */
         w = bw;                 /* Rollback at beginning of chunk          */
         if (--trash < 0)        /* Trashed the char where this try started */
           return 1;             /* if we can't trash more chars fail       */
@@ -751,7 +750,7 @@ int mmexec(const char *wcm, int wminlen, const char *rcm, int rminlen)
       if (!(br < rz))
       {                         /* If we failed because we got the end of head */
         trash -= (br - rx);     /* it makes no sense to rollback, just trash   */
-        if (--trash < 0)        /* all the rest of the head wich isn't long    */
+        if (--trash < 0)        /* all the rest of the head which isn't long   */
           return 1;             /* enough for this chunk and go out of this    */
         break;                  /* loop, then we try with the chunks of rm     */
       };
@@ -774,8 +773,8 @@ int mmexec(const char *wcm, int wminlen, const char *rcm, int rminlen)
     while (*r)
     {
       bw = w;
-      while (eat && *r)         /* the '?' we had eated make us skip as many chars */
-        if (*r++ != 'Z')        /* here, but can't skip stars or trailing zero     */
+      while (eat && *r)         /* the '?' we ate makes us skip as many chars  */
+        if (*r++ != 'Z')        /* here, but can't skip stars or trailing zero */
           eat--;
       for (bw++; (*r) && (*bw != *r); r++)
         if ((*r != 'Z') && (--trash < 0))
@@ -813,7 +812,7 @@ int mmexec(const char *wcm, int wminlen, const char *rcm, int rminlen)
   }
 
   /* match the remaining chunks of wm against what remains of the tail of rm */
-  r = rz - eat - 1;             /* can't have <nul> or 'Z'within the tail, so just move r */
+  r = rz - eat - 1;             /* can't have <nul> or 'Z' within the tail, so just move r */
   while (r >= rx)
   {
     bw = w;
@@ -842,93 +841,6 @@ int mmexec(const char *wcm, int wminlen, const char *rcm, int rminlen)
   return 1;                     /* Auch... something left out ? Fail */
 }
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-/** Parse an input string as an IPv4 address.
- * @param[in] in Text form of address.
- * @param[out] out IPv4 address in network representation.
- * @return Number of address bits specified by \a in.
- */
-static int ipmask_parse_ipv4(const char *in, struct in_addr *out)
-{
-  int class;
-  char ipname[16];
-  int ad[4] = { 0 };
-  int bits = 0;
-
-  class = sscanf(in, "%d.%d.%d.%d/%d", &ad[0], &ad[1], &ad[2], &ad[3], &bits);
-  if (class != 5)
-    bits = class * 8;
-  ircd_snprintf(0, ipname, sizeof(ipname), "%d.%d.%d.%d", ad[0], ad[1], ad[2], ad[3]);
-  out->s_addr = inet_addr(ipname);
-  return bits;
-}
-
-/** Test whether a string looks like it matches only IPv4 addresses.
- * @param[in] mask Hostname matching mask.
- * @return Non-zero if \a mask can only match IPv4 addresses, zero otherwise.
- */
-int check_if_ipmask(const char *mask)
-{
-  int has_digit = 0;
-  const char *p;
-
-  for (p = mask; *p; ++p)
-    if (*p != '*' && *p != '?' && *p != '.' && *p != '/')
-    {
-      if (!IsDigit(*p))
-        return 0;
-      has_digit = -1;
-    }
-
-  return has_digit;
-}
-
-/** Try to parse an IPv4 or IPv6 address mask.
- * @param[in] in Address matching mask.
- * @param[out] mask Fixed bits of address mask.
- * @param[out] bits_ptr If non-NULL, receives number of bits specified in address mask.
- * @return Non-zero on successful parse; zero on error.
- */
-int ipmask_parse(const char *in, struct irc_in_addr *mask, unsigned char *bits_ptr)
-{
-  struct in_addr ipv4;
-  char *p;
-  int bits = 0;
-
-  if (check_if_ipmask(in)) {
-    bits = ipmask_parse_ipv4(in, &ipv4);
-    mask->in6_16[0] = mask->in6_16[1] = mask->in6_16[2] = 0;
-    mask->in6_16[3] = mask->in6_16[4] = 0;
-    mask->in6_16[5] = 0xffff;
-    memcpy(&mask->in6_16[6], &ipv4.s_addr, sizeof(ipv4.s_addr));
-    bits += 96;
-  } else {
-    if (!(p = strchr(in, '/')))
-      bits = 128;
-    else
-      *p = 0;
-    if (!ircd_aton(mask, in)) {
-      if (p)
-        *p = '/';
-      return 0;
-    }
-    if (p) {
-      bits = atoi(p + 1);
-      *p = '/';
-    }
-  }
-
-  if (bits_ptr)
-    *bits_ptr = bits;
-  return 1;
-}
-
 /** Test whether an address matches the most significant bits of a mask.
  * @param[in] addr Address to test.
  * @param[in] mask Address to test against.
@@ -942,7 +854,7 @@ int ipmask_check(const struct irc_in_addr *addr, const struct irc_in_addr *mask,
 
   for (k = 0; k < 8; k++) {
     if (bits < 16)
-      return (addr->in6_16[k] & htons(0xffff << (16-bits))) == mask->in6_16[k];
+      return !(htons(addr->in6_16[k] ^ mask->in6_16[k]) >> (16-bits));
     if (addr->in6_16[k] != mask->in6_16[k])
       return 0;
     if (!(bits -= 16))