Copyright updates:
[exim.git] / src / src / match.c
index bf8cb3b98f8331700c187a9430ad8ebd9034b568..2e4bff078eacfe6195b26f0c63c02938c1932e95 100644 (file)
@@ -2,8 +2,8 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
+/* Copyright (c) The Exim Maintainers 2020 - 2022 */
 /* Copyright (c) University of Cambridge 1995 - 2018 */
-/* Copyright (c) The Exim Maintainers 2020 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions for matching strings */
@@ -128,9 +128,9 @@ required. */
 
 if (pattern[0] == '^')
   {
-  const pcre * re = regex_must_compile(pattern, cb->caseless, FALSE);
+  const pcre2_code * re = regex_must_compile(pattern, cb->caseless, FALSE);
   if (expand_setup < 0
-      ? pcre_exec(re, NULL, CCS s, Ustrlen(s), 0, PCRE_EOPT, NULL, 0) < 0
+      ? !regex_match(re, s, -1, NULL)
       : !regex_match_and_setup(re, s, 0, expand_setup)
      )
     return FAIL;
@@ -432,11 +432,9 @@ match_check_list(const uschar **listptr, int sep, tree_node **anchorptr,
   void *arg, int type, const uschar *name, const uschar **valueptr)
 {
 int yield = OK;
-unsigned int *original_cache_bits = *cache_ptr;
-BOOL include_unknown = FALSE;
-BOOL ignore_unknown = FALSE;
-BOOL include_defer = FALSE;
-BOOL ignore_defer = FALSE;
+unsigned int * original_cache_bits = *cache_ptr;
+BOOL include_unknown = FALSE, ignore_unknown = FALSE,
+      include_defer = FALSE, ignore_defer = FALSE;
 const uschar *list;
 uschar *sss;
 uschar *ot = NULL;
@@ -445,8 +443,8 @@ uschar *ot = NULL;
 
 HDEBUG(D_any)
   {
-  uschar *listname = readconf_find_option(listptr);
-  if (listname[0] != 0) ot = string_sprintf("%s in %s?", name, listname);
+  uschar * listname = readconf_find_option(listptr);
+  if (*listname) ot = string_sprintf("%s in %s?", name, listname);
   }
 
 /* If the list is empty, the answer is no. Skip the debugging output for
@@ -454,7 +452,7 @@ an unnamed list. */
 
 if (!*listptr)
   {
-  HDEBUG(D_lists) if (ot) debug_printf("%s no (option unset)\n", ot);
+  HDEBUG(D_lists) if (ot) debug_printf_indent("%s no (option unset)\n", ot);
   return FAIL;
   }
 
@@ -487,7 +485,7 @@ else
     {
     if (f.expand_string_forcedfail)
       {
-      HDEBUG(D_lists) debug_printf("expansion of \"%s\" forced failure: "
+      HDEBUG(D_lists) debug_printf_indent("expansion of \"%s\" forced failure: "
         "assume not in this list\n", *listptr);
       return FAIL;
       }
@@ -498,8 +496,18 @@ else
   }
 
 /* For an unnamed list, use the expanded version in comments */
+#define LIST_LIMIT_PR 2048
 
-HDEBUG(D_any) if (!ot) ot = string_sprintf("%s in \"%s\"?", name, list);
+HDEBUG(D_any) if (!ot)
+  {
+  int n, m;
+  gstring * g = string_fmt_append(NULL, "%s in \"%n%.*s%n\"",
+    name, &n, LIST_LIMIT_PR, list, &m);
+  if (m - n >= LIST_LIMIT_PR) g = string_catn(g, US"...", 3);
+  g = string_catn(g, US"?", 1);
+  gstring_release_unused(g);
+  ot = string_from_gstring(g);
+  }
 
 /* Now scan the list and process each item in turn, until one of them matches,
 or we hit an error. */
@@ -659,7 +667,7 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0)))
             so we use the permanent store pool */
 
             store_pool = POOL_PERM;
-            p = store_get(sizeof(namedlist_cacheblock), FALSE);
+            p = store_get(sizeof(namedlist_cacheblock), GET_UNTAINTED);
             p->key = string_copy(get_check_key(arg, type));
 
 
@@ -669,7 +677,7 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0)))
             p->next = nb->cache_data;
             nb->cache_data = p;
             if (*valueptr)
-              DEBUG(D_lists) debug_printf("data from lookup saved for "
+              DEBUG(D_lists) debug_printf_indent("data from lookup saved for "
                 "cache for %s: key '%s' value '%s'\n", ss, p->key, *valueptr);
             }
           }
@@ -681,7 +689,7 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0)))
 
       else
         {
-        DEBUG(D_lists) debug_printf("cached %s match for %s\n",
+        DEBUG(D_lists) debug_printf_indent("cached %s match for %s\n",
           (bits & (-bits)) == bits ? "yes" : "no", ss);
 
         cached = US" - cached";
@@ -695,7 +703,7 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0)))
               *valueptr = p->data;
               break;
               }
-          DEBUG(D_lists) debug_printf("cached lookup data = %s\n", *valueptr);
+          DEBUG(D_lists) debug_printf_indent("cached lookup data = %s\n", *valueptr);
           }
         }
 
@@ -704,8 +712,8 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0)))
 
       if ((bits & (-bits)) == bits)    /* Only one of the two bits is set */
         {
-        HDEBUG(D_lists) debug_printf("%s %s (matched \"%s\"%s)\n", ot,
-          (yield == OK)? "yes" : "no", sss, cached);
+        HDEBUG(D_lists) debug_printf_indent("%s %s (matched \"%s\"%s)\n", ot,
+          yield == OK ? "yes" : "no", sss, cached);
         return yield;
         }
       }
@@ -718,7 +726,7 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0)))
       switch ((func)(arg, ss, valueptr, &error))
         {
         case OK:
-         HDEBUG(D_lists) debug_printf("%s %s (matched \"%s\")\n", ot,
+         HDEBUG(D_lists) debug_printf_indent("%s %s (matched \"%s\")\n", ot,
            (yield == OK)? "yes" : "no", sss);
          return yield;
 
@@ -727,7 +735,7 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0)))
            error = string_sprintf("DNS lookup of \"%s\" deferred", ss);
          if (ignore_defer)
            {
-           HDEBUG(D_lists) debug_printf("%s: item ignored by +ignore_defer\n",
+           HDEBUG(D_lists) debug_printf_indent("%s: item ignored by +ignore_defer\n",
              error);
            break;
            }
@@ -747,12 +755,12 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0)))
         case ERROR:
          if (ignore_unknown)
            {
-           HDEBUG(D_lists) debug_printf("%s: item ignored by +ignore_unknown\n",
+           HDEBUG(D_lists) debug_printf_indent("%s: item ignored by +ignore_unknown\n",
              error);
            }
          else
            {
-           HDEBUG(D_lists) debug_printf("%s %s (%s)\n", ot,
+           HDEBUG(D_lists) debug_printf_indent("%s %s (%s)\n", ot,
              include_unknown? "yes":"no", error);
            if (!include_unknown)
              {
@@ -809,19 +817,19 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0)))
         sss = ss + 1;
         }
 
-      ss = filebuffer + Ustrlen(filebuffer);             /* trailing space */
+      ss = filebuffer + Ustrlen(filebuffer);           /* trailing space */
       while (ss > filebuffer && isspace(ss[-1])) ss--;
       *ss = 0;
 
       ss = filebuffer;
-      while (isspace(*ss)) ss++;                         /* leading space */
+      while (isspace(*ss)) ss++;                       /* leading space */
 
-      if (*ss == 0) continue;                            /* ignore empty */
+      if (!*ss) continue;                              /* ignore empty */
 
-      file_yield = yield;                                /* positive yield */
-      sss = ss;                                          /* for debugging */
+      file_yield = yield;                              /* positive yield */
+      sss = ss;                                                /* for debugging */
 
-      if (*ss == '!')                                    /* negation */
+      if (*ss == '!')                                  /* negation */
         {
         file_yield = (file_yield == OK)? FAIL : OK;
         while (isspace((*(++ss))));
@@ -831,8 +839,13 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0)))
         {
         case OK:
          (void)fclose(f);
-         HDEBUG(D_lists) debug_printf("%s %s (matched \"%s\" in %s)\n", ot,
+         HDEBUG(D_lists) debug_printf_indent("%s %s (matched \"%s\" in %s)\n", ot,
            yield == OK ? "yes" : "no", sss, filename);
+
+         /* The "pattern" being matched came from the file; we use a stack-local.
+         Copy it to allocated memory now we know it matched. */
+
+         if (valueptr) *valueptr = string_copy(ss);
          return file_yield;
 
         case DEFER:
@@ -840,7 +853,7 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0)))
            error = string_sprintf("DNS lookup of %s deferred", ss);
          if (ignore_defer)
            {
-           HDEBUG(D_lists) debug_printf("%s: item ignored by +ignore_defer\n",
+           HDEBUG(D_lists) debug_printf_indent("%s: item ignored by +ignore_defer\n",
              error);
            break;
            }
@@ -855,12 +868,12 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0)))
         case ERROR:            /* host name lookup failed - this can only */
          if (ignore_unknown)   /* be for an incoming host (not outgoing) */
            {
-           HDEBUG(D_lists) debug_printf("%s: item ignored by +ignore_unknown\n",
+           HDEBUG(D_lists) debug_printf_indent("%s: item ignored by +ignore_unknown\n",
              error);
            }
          else
           {
-           HDEBUG(D_lists) debug_printf("%s %s (%s)\n", ot,
+           HDEBUG(D_lists) debug_printf_indent("%s %s (%s)\n", ot,
              include_unknown? "yes":"no", error);
            (void)fclose(f);
            if (!include_unknown)
@@ -886,7 +899,7 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0)))
 /* End of list reached: if the last item was negated yield OK, else FAIL. */
 
 HDEBUG(D_lists)
-  debug_printf("%s %s (end of list)\n", ot, yield == OK ? "no":"yes");
+  debug_printf_indent("%s %s (end of list)\n", ot, yield == OK ? "no":"yes");
 return yield == OK ? FAIL : OK;
 
 /* Something deferred */
@@ -992,17 +1005,18 @@ Returns:         OK     for a match
 static int
 check_address(void *arg, const uschar *pattern, const uschar **valueptr, uschar **error)
 {
-check_address_block *cb = (check_address_block *)arg;
+check_address_block * cb = (check_address_block *)arg;
 check_string_block csb;
 int rc;
 int expand_inc = 0;
-unsigned int *null = NULL;
-const uschar *listptr;
-uschar *subject = cb->address;
-const uschar *s;
-uschar *pdomain, *sdomain;
-
-DEBUG(D_lists) debug_printf("address match test: subject=%s pattern=%s\n",
+unsigned int * null = NULL;
+const uschar * listptr;
+uschar * subject = cb->address;
+const uschar * s;
+uschar * pdomain, * sdomain;
+uschar * value = NULL;
+
+DEBUG(D_lists) debug_printf_indent("address match test: subject=%s pattern=%s\n",
   subject, pattern);
 
 /* Find the subject's domain */
@@ -1051,7 +1065,7 @@ if (*s == ';')
 because other patterns expect to have a local part and a domain to match
 against. */
 
-if (*subject == 0) return (*pattern == 0)? OK : FAIL;
+if (!*subject) return *pattern ? FAIL : OK;
 
 /* If the pattern starts with "@@" we have a split lookup, where the domain is
 looked up to obtain a list of local parts. If the subject's local part is just
@@ -1061,7 +1075,6 @@ if (pattern[0] == '@' && pattern[1] == '@')
   {
   int watchdog = 50;
   uschar *list, *ss;
-  uschar buffer[1024];
 
   if (sdomain == subject + 1 && *subject == '*') return FAIL;
 
@@ -1092,7 +1105,7 @@ if (pattern[0] == '@' && pattern[1] == '@')
     /* Look up the local parts provided by the list; negation is permitted.
     If a local part has to begin with !, a regex can be used. */
 
-    while ((ss = string_nextinlist(CUSS &list, &sep, buffer, sizeof(buffer))))
+    while ((ss = string_nextinlist(CUSS &list, &sep, NULL, 0)))
       {
       int local_yield;
 
@@ -1174,6 +1187,7 @@ if (pdomain != NULL)
       expand_nlength[cb->expand_setup] = sllen - cllen;
       expand_inc = 1;
       }
+    value = string_copyn(pattern + 1, cllen);
     }
   else
     {
@@ -1182,6 +1196,7 @@ if (pdomain != NULL)
         ? strncmpic(subject, pattern, sllen) != 0
        : Ustrncmp(subject, pattern, sllen) != 0) return FAIL;
     }
+    value = string_copyn(pattern, sllen);
   }
 
 /* If the local part matched, or was not being checked, check the domain using
@@ -1206,16 +1221,23 @@ csb.at_is_special = TRUE;
 listptr = pdomain ? pdomain + 1 : pattern;
 if (valueptr) *valueptr = NULL;
 
-return match_check_list(
-  &listptr,                  /* list of one item */
-  UCHAR_MAX+1,               /* impossible separator; single item */
-  &domainlist_anchor,        /* it's a domain list */
-  &null,                     /* ptr to NULL means no caching */
-  check_string,              /* the function to do one test */
-  &csb,                      /* its data */
-  MCL_DOMAIN + MCL_NOEXPAND, /* domain list; don't expand */
-  csb.subject,               /* string for messages */
-  valueptr);                 /* where to pass back lookup data */
+  {
+  const uschar * dvalue = NULL;
+  rc = match_check_list(
+    &listptr,                  /* list of one item */
+    UCHAR_MAX+1,               /* impossible separator; single item */
+    &domainlist_anchor,        /* it's a domain list */
+    &null,                     /* ptr to NULL means no caching */
+    check_string,              /* the function to do one test */
+    &csb,                      /* its data */
+    MCL_DOMAIN + MCL_NOEXPAND, /* domain list; don't expand */
+    csb.subject,               /* string for messages */
+    &dvalue);                       /* where to pass back lookup data */
+  if (valueptr && (value || dvalue))
+    *valueptr = string_sprintf("%s@%s",
+                 value ? value : US"", dvalue ? dvalue : US"");
+  }
+return rc;
 }
 
 
@@ -1270,8 +1292,8 @@ provided that "caseless" is set. (It is FALSE for calls for matching rewriting
 patterns.) Otherwise just the domain is lower cases. A magic item "+caseful" in
 the list can be used to restore a caseful copy of the local part from the
 original address.
-Limit the subject address size to avoid mem-exhastion attacks.  The size chosen
-is historical (we used to use big_buffer her). */
+Limit the subject address size to avoid mem-exhaustion attacks.  The size chosen
+is historical (we used to use big_buffer here). */
 
 if ((len = Ustrlen(address)) > BIG_BUFFER_SIZE) len = BIG_BUFFER_SIZE;
 ab.address = string_copyn(address, len);