Follow CNAME chains only one step. Bug 2264
[exim.git] / src / src / readconf.c
index acf64570a6d56cc75f2e56d8ed90506236ea1123..3f307fd5cc5ff37a961c58b155e732b522664768 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2017 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions for reading the configuration file, and for displaying
@@ -123,6 +123,7 @@ static optionlist optionlist_config[] = {
 #endif
   { "dns_again_means_nonexist", opt_stringptr,   &dns_again_means_nonexist },
   { "dns_check_names_pattern",  opt_stringptr,   &check_dns_names_pattern },
+  { "dns_cname_loops",         opt_int,         &dns_cname_loops },
   { "dns_csa_search_limit",     opt_int,         &dns_csa_search_limit },
   { "dns_csa_use_reverse",      opt_bool,        &dns_csa_use_reverse },
   { "dns_dnssec_ok",            opt_int,         &dns_dnssec_ok },
@@ -195,7 +196,9 @@ static optionlist optionlist_config[] = {
   { "local_from_prefix",        opt_stringptr,   &local_from_prefix },
   { "local_from_suffix",        opt_stringptr,   &local_from_suffix },
   { "local_interfaces",         opt_stringptr,   &local_interfaces },
+#ifdef HAVE_LOCAL_SCAN
   { "local_scan_timeout",       opt_time,        &local_scan_timeout },
+#endif
   { "local_sender_retain",      opt_bool,        &local_sender_retain },
   { "localhost_number",         opt_stringptr,   &host_number_string },
   { "log_file_path",            opt_stringptr,   &log_file_path },
@@ -592,6 +595,40 @@ return US"";
 
 
 
+/*************************************************
+*       Deal with an assignment to a macro       *
+*************************************************/
+
+/* We have a new definition; append to the list.
+
+Args:
+ name  Name of the macro; will be copied
+ val   Expansion result for the macro; will be copied
+*/
+
+macro_item *
+macro_create(const uschar * name, const uschar * val, BOOL command_line)
+{
+macro_item * m = store_get(sizeof(macro_item));
+
+/* fprintf(stderr, "%s: '%s' '%s'\n", __FUNCTION__, name, val); */
+m->next = NULL;
+m->command_line = command_line;
+m->namelen = Ustrlen(name);
+m->replen = Ustrlen(val);
+m->name = string_copy(name);
+m->replacement = string_copy(val);
+if (mlast)
+  mlast->next = m;
+else
+  macros = m;
+mlast = m;
+if (!macros_user)
+  macros_user = m;
+return m;
+}
+
+
 /* This function is called when a line that starts with an upper case letter is
 encountered. The argument "line" should contain a complete logical line, and
 start with the first letter of the macro name. The macro name and the
@@ -642,37 +679,53 @@ while (isspace(*s)) s++;
 just skip this definition. It's an error to attempt to redefine a macro without
 redef set to TRUE, or to redefine a macro when it hasn't been defined earlier.
 It is also an error to define a macro whose name begins with the name of a
-previously defined macro.
+previously defined macro.  This is the requirement that make using a tree
+for macros hard; we must check all macros for the substring.  Perhaps a
+sorted list, and a bsearch, would work?
 Note: it is documented that the other way round works. */
 
-if ((m = macro_search_prefix(name)))
+for (m = macros; m; m = m->next)
   {
-  if (m->namelen < namelen)    /* substring match */
+  if (Ustrcmp(m->name, name) == 0)
     {
-    log_write(0, LOG_CONFIG|LOG_PANIC, "\"%s\" cannot be defined as "
-      "a macro because previously defined macro \"%s\" is a substring",
-      name, m->tnode.name);
-    return FALSE;
+    if (!m->command_line && !redef)
+      {
+      log_write(0, LOG_CONFIG|LOG_PANIC, "macro \"%s\" is already "
+       "defined (use \"==\" if you want to redefine it)", name);
+      return FALSE;
+      }
+    break;
     }
-                               /* exact match */
-  if (!m->command_line && !redef)
+
+  if (m->namelen < namelen && Ustrstr(name, m->name) != NULL)
     {
-    log_write(0, LOG_CONFIG|LOG_PANIC, "macro \"%s\" is already "
-       "defined (use \"==\" if you want to redefine it", name);
+    log_write(0, LOG_CONFIG|LOG_PANIC, "\"%s\" cannot be defined as "
+      "a macro because previously defined macro \"%s\" is a substring",
+      name, m->name);
     return FALSE;
     }
 
-  if (m->command_line)         /* overriding cmdline definition */
-    return TRUE;
+  /* We cannot have this test, because it is documented that a substring
+  macro is permitted (there is even an example).
+  *
+  * if (m->namelen > namelen && Ustrstr(m->name, name) != NULL)
+  *   log_write(0, LOG_CONFIG|LOG_PANIC_DIE, "\"%s\" cannot be defined as "
+  *     "a macro because it is a substring of previously defined macro \"%s\"",
+  *     name, m->name);
+  */
   }
 
+/* Check for an overriding command-line definition. */
+
+if (m && m->command_line) return TRUE;
+
 /* Redefinition must refer to an existing macro. */
 
 if (redef)
   if (m)
     {
     m->replen = Ustrlen(s);
-    m->tnode.data.ptr = string_copy(s);
+    m->replacement = string_copy(s);
     }
   else
     {
@@ -693,7 +746,7 @@ return TRUE;
 
 /* Process line for macros. The line is in big_buffer starting at offset len.
 Expand big_buffer if needed.  Handle definitions of new macros, and
-imacro expansions, rewriting the line in thw buffer.
+macro expansions, rewriting the line in the buffer.
 
 Arguments:
  len           Offset in buffer of start of line
@@ -708,6 +761,7 @@ macros_expand(int len, int * newlen, BOOL * macro_found)
 {
 uschar * ss = big_buffer + len;
 uschar * s;
+macro_item * m;
 
 /* Find the true start of the physical line - leading spaces are always
 ignored. */
@@ -728,52 +782,60 @@ if (len == 0 && isupper(*s))
   if (*s != '=') s = ss;          /* Not a macro definition */
   }
 
-/* Scan the line (from after XXX= if present), replacing any macros. Rescan
-after replacement for any later-defined macros. */
+/* Skip leading chars which cannot start a macro name, to avoid multiple
+pointless rescans in Ustrstr calls. */
+
+while (*s && !isupper(*s) && !(*s == '_' && isupper(s[1]))) s++;
+
+/* For each defined macro, scan the line (from after XXX= if present),
+replacing all occurrences of the macro. */
 
 *macro_found = FALSE;
-while (*s)
+if (*s) for (m = *s == '_' ? macros : macros_user; m; m = m->next)
   {
-  if (isupper(*s) || *s == '_' && isupper(s[1]))
-    {
-    macro_item * m;
-    unsigned mnum = 0;
+  uschar * p, *pp;
+  uschar * t;
 
-    while ((m = macro_search_largest_prefix(s)) && m->m_number > mnum)
-      {
-      uschar * pp;
-      int moveby;
+  while (*s && !isupper(*s) && !(*s == '_' && isupper(s[1]))) s++;
+  if (!*s) break;
 
-      /* Expand the buffer if necessary */
+  t = s;
+  while ((p = Ustrstr(t, m->name)) != NULL)
+    {
+    int moveby;
 
-      while (*newlen - m->namelen + m->replen + 1 > big_buffer_size)
-       {
-       int newsize = big_buffer_size + BIG_BUFFER_SIZE;
-       uschar *newbuffer = store_malloc(newsize);
-       memcpy(newbuffer, big_buffer, *newlen + 1);
-       s = newbuffer  + (s - big_buffer);
-       ss = newbuffer + (ss - big_buffer);
-       big_buffer_size = newsize;
-       store_free(big_buffer);
-       big_buffer = newbuffer;
-       }
+/* fprintf(stderr, "%s: matched '%s' in '%s'\n", __FUNCTION__, m->name, ss); */
+    /* Expand the buffer if necessary */
 
-      /* Shuffle the remaining characters up or down in the buffer before
-      copying in the replacement text. Don't rescan the replacement for this
-      same macro. */
+    while (*newlen - m->namelen + m->replen + 1 > big_buffer_size)
+      {
+      int newsize = big_buffer_size + BIG_BUFFER_SIZE;
+      uschar *newbuffer = store_malloc(newsize);
+      memcpy(newbuffer, big_buffer, *newlen + 1);
+      p = newbuffer  + (p - big_buffer);
+      s = newbuffer  + (s - big_buffer);
+      ss = newbuffer + (ss - big_buffer);
+      t = newbuffer  + (t - big_buffer);
+      big_buffer_size = newsize;
+      store_free(big_buffer);
+      big_buffer = newbuffer;
+      }
 
-      pp = s + m->namelen;
-      if ((moveby = m->replen - m->namelen) != 0)
-       {
-       memmove(s + m->replen, pp, (big_buffer + *newlen) - pp + 1);
-       *newlen += moveby;
-       }
-      Ustrncpy(s, m->tnode.data.ptr, m->replen);
-      *macro_found = TRUE;
-      mnum = m->m_number;
+    /* Shuffle the remaining characters up or down in the buffer before
+    copying in the replacement text. Don't rescan the replacement for this
+    same macro. */
+
+    pp = p + m->namelen;
+    if ((moveby = m->replen - m->namelen) != 0)
+      {
+      memmove(p + m->replen, pp, (big_buffer + *newlen) - pp + 1);
+      *newlen += moveby;
       }
+    Ustrncpy(p, m->replacement, m->replen);
+    t = p + m->replen;
+    while (*t && !isupper(*t) && !(*t == '_' && isupper(t[1]))) t++;
+    *macro_found = TRUE;
     }
-  s++;
   }
 
 /* An empty macro replacement at the start of a line could mean that ss no
@@ -2810,25 +2872,26 @@ else if (Ustrcmp(type, "macro") == 0)
     fprintf(stderr, "exim: permission denied\n");
     return FALSE;
     }
-
-  if (name)
-    if ((m = macro_search(name)))
-      macro_print(m->tnode.name, m->tnode.data.ptr, (void *)(long)names_only);
-    else
+  for (m = macros; m; m = m->next)
+    if (!name || Ustrcmp(name, m->name) == 0)
       {
-      printf("%s %s not found\n", type, name);
-      return FALSE;
+      if (names_only)
+        printf("%s\n", CS m->name);
+      else
+        printf("%s=%s\n", CS m->name, CS m->replacement);
+      if (name)
+        return TRUE;
       }
-  else
-    tree_walk(tree_macros, macro_print, (void *)(long)names_only);
+  if (!name) return TRUE;
 
-  return TRUE;
+  printf("%s %s not found\n", type, name);
+  return FALSE;
   }
 
 if (names_only)
   {
   for (; d; d = d->next) printf("%s\n", CS d->name);
-  return TRUE;;
+  return TRUE;
   }
 
 /* Either search for a given driver, or print all of them */
@@ -2838,8 +2901,7 @@ for (; d; d = d->next)
   BOOL rc = FALSE;
   if (!name)
     printf("\n%s %s:\n", d->name, type);
-  else if (Ustrcmp(d->name, name) != 0)
-    continue;
+  else if (Ustrcmp(d->name, name) != 0) continue;
 
   for (ol = ol2; ol < ol2 + size; ol++)
     if (!(ol->type & opt_hidden))