]> git.netwichtig.de Git - user/henk/code/exim.git/blobdiff - src/src/rda.c
Docs: remove SUPPORT_TLS mentions
[user/henk/code/exim.git] / src / src / rda.c
index 07cbb96f114f2a88827308e6bd38ba63b1f6453a..42b7b14a5557ee972fa6b20dece577c05583f6dc 100644 (file)
@@ -1,10 +1,8 @@
-/* $Cambridge: exim/src/src/rda.c,v 1.16 2009/11/16 19:50:37 nm4 Exp $ */
-
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2009 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* This module contains code for extracting addresses from a forwarding list
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* This module contains code for extracting addresses from a forwarding list
@@ -44,14 +42,14 @@ static BOOL
 match_tag(const uschar *s, const uschar *tag)
 {
 for (; *tag != 0; s++, tag++)
 match_tag(const uschar *s, const uschar *tag)
 {
 for (; *tag != 0; s++, tag++)
-  {
   if (*tag == ' ')
     {
     while (*s == ' ' || *s == '\t') s++;
     s--;
     }
   if (*tag == ' ')
     {
     while (*s == ' ' || *s == '\t') s++;
     s--;
     }
-  else if (tolower(*s) != tolower(*tag)) break;
-  }
+  else
+   if (tolower(*s) != tolower(*tag)) break;
+
 return (*tag == 0);
 }
 
 return (*tag == 0);
 }
 
@@ -98,37 +96,37 @@ static int
 rda_exists(uschar *filename, uschar **error)
 {
 int rc, saved_errno;
 rda_exists(uschar *filename, uschar **error)
 {
 int rc, saved_errno;
-uschar *slash;
 struct stat statbuf;
 struct stat statbuf;
+uschar * s;
 
 if ((rc = Ustat(filename, &statbuf)) >= 0) return FILE_EXIST;
 saved_errno = errno;
 
 
 if ((rc = Ustat(filename, &statbuf)) >= 0) return FILE_EXIST;
 saved_errno = errno;
 
-Ustrncpy(big_buffer, filename, big_buffer_size - 3);
+s = string_copy(filename);
 sigalrm_seen = FALSE;
 
 if (saved_errno == ENOENT)
   {
 sigalrm_seen = FALSE;
 
 if (saved_errno == ENOENT)
   {
-  slash = Ustrrchr(big_buffer, '/');
-  Ustrcpy(slash+1, ".");
+  uschar * slash = Ustrrchr(s, '/');
+  Ustrcpy(slash+1, US".");
 
 
-  alarm(30);
-  rc = Ustat(big_buffer, &statbuf);
+  ALARM(30);
+  rc = Ustat(s, &statbuf);
   if (rc != 0 && errno == EACCES && !sigalrm_seen)
     {
     *slash = 0;
   if (rc != 0 && errno == EACCES && !sigalrm_seen)
     {
     *slash = 0;
-    rc = Ustat(big_buffer, &statbuf);
+    rc = Ustat(s, &statbuf);
     }
   saved_errno = errno;
     }
   saved_errno = errno;
-  alarm(0);
+  ALARM_CLR(0);
 
 
-  DEBUG(D_route) debug_printf("stat(%s)=%d\n", big_buffer, rc);
+  DEBUG(D_route) debug_printf("stat(%s)=%d\n", s, rc);
   }
 
 if (sigalrm_seen || rc != 0)
   {
   }
 
 if (sigalrm_seen || rc != 0)
   {
-  *error = string_sprintf("failed to stat %s (%s)", big_buffer,
-    sigalrm_seen? "timeout" : strerror(saved_errno));
+  *error = string_sprintf("failed to stat %s (%s)", s,
+    sigalrm_seen?  "timeout" : strerror(saved_errno));
   return FILE_EXIST_UNCLEAR;
   }
 
   return FILE_EXIST_UNCLEAR;
   }
 
@@ -252,11 +250,8 @@ if (!uid_ok)
   if (rdata->pw != NULL && statbuf.st_uid == rdata->pw->pw_uid)
     uid_ok = TRUE;
   else if (rdata->owners != NULL)
   if (rdata->pw != NULL && statbuf.st_uid == rdata->pw->pw_uid)
     uid_ok = TRUE;
   else if (rdata->owners != NULL)
-    {
-    int i;
-    for (i = 1; i <= (int)(rdata->owners[0]); i++)
+    for (int i = 1; i <= (int)(rdata->owners[0]); i++)
       if (rdata->owners[i] == statbuf.st_uid) { uid_ok = TRUE; break; }
       if (rdata->owners[i] == statbuf.st_uid) { uid_ok = TRUE; break; }
-    }
   }
 
 if (!gid_ok)
   }
 
 if (!gid_ok)
@@ -264,11 +259,8 @@ if (!gid_ok)
   if (rdata->pw != NULL && statbuf.st_gid == rdata->pw->pw_gid)
     gid_ok = TRUE;
   else if (rdata->owngroups != NULL)
   if (rdata->pw != NULL && statbuf.st_gid == rdata->pw->pw_gid)
     gid_ok = TRUE;
   else if (rdata->owngroups != NULL)
-    {
-    int i;
-    for (i = 1; i <= (int)(rdata->owngroups[0]); i++)
+    for (int i = 1; i <= (int)(rdata->owngroups[0]); i++)
       if (rdata->owngroups[i] == statbuf.st_gid) { gid_ok = TRUE; break; }
       if (rdata->owngroups[i] == statbuf.st_gid) { gid_ok = TRUE; break; }
-    }
   }
 
 if (!uid_ok || !gid_ok)
   }
 
 if (!uid_ok || !gid_ok)
@@ -289,7 +281,7 @@ if (statbuf.st_size > MAX_FILTER_SIZE)
 
 /* Read the file in one go in order to minimize the time we have it open. */
 
 
 /* Read the file in one go in order to minimize the time we have it open. */
 
-filebuf = store_get(statbuf.st_size + 1);
+filebuf = store_get(statbuf.st_size + 1, is_tainted(filename));
 
 if (fread(filebuf, 1, statbuf.st_size, fwd) != statbuf.st_size)
   {
 
 if (fread(filebuf, 1, statbuf.st_size, fwd) != statbuf.st_size)
   {
@@ -360,7 +352,7 @@ if (rdata->isfile)
   }
 else data = rdata->string;
 
   }
 else data = rdata->string;
 
-*filtertype = system_filtering? FILTER_EXIM : rda_is_filter(data);
+*filtertype = f.system_filtering ? FILTER_EXIM : rda_is_filter(data);
 
 /* Filter interpretation is done by a general function that is also called from
 the filter testing option (-bf). There are two versions: one for Exim filtering
 
 /* Filter interpretation is done by a general function that is also called from
 the filter testing option (-bf). There are two versions: one for Exim filtering
@@ -374,7 +366,7 @@ if (*filtertype != FILTER_FORWARD)
   int old_expand_forbid = expand_forbid;
 
   DEBUG(D_route) debug_printf("data is %s filter program\n",
   int old_expand_forbid = expand_forbid;
 
   DEBUG(D_route) debug_printf("data is %s filter program\n",
-    (*filtertype == FILTER_EXIM)? "an Exim" : "a Sieve");
+    *filtertype == FILTER_EXIM ? "an Exim" : "a Sieve");
 
   /* RDO_FILTER is an "allow" bit */
 
 
   /* RDO_FILTER is an "allow" bit */
 
@@ -385,8 +377,7 @@ if (*filtertype != FILTER_FORWARD)
     }
 
   expand_forbid =
     }
 
   expand_forbid =
-    (expand_forbid & ~RDO_FILTER_EXPANSIONS) |
-    (options & RDO_FILTER_EXPANSIONS);
+    expand_forbid & ~RDO_FILTER_EXPANSIONS  |  options & RDO_FILTER_EXPANSIONS;
 
   /* RDO_{EXIM,SIEVE}_FILTER are forbid bits */
 
 
   /* RDO_{EXIM,SIEVE}_FILTER are forbid bits */
 
@@ -435,22 +426,24 @@ return parse_forward_list(data,
 *         Write string down pipe                 *
 *************************************************/
 
 *         Write string down pipe                 *
 *************************************************/
 
-/* This function is used for tranferring a string down a pipe between
+/* This function is used for transferring a string down a pipe between
 processes. If the pointer is NULL, a length of zero is written.
 
 Arguments:
   fd         the pipe
   s          the string
 
 processes. If the pointer is NULL, a length of zero is written.
 
 Arguments:
   fd         the pipe
   s          the string
 
-Returns:     nothing
+Returns:     -1 on error, else 0
 */
 
 */
 
-static void
-rda_write_string(int fd, uschar *s)
+static int
+rda_write_string(int fd, const uschar *s)
 {
 int len = (s == NULL)? 0 : Ustrlen(s) + 1;
 {
 int len = (s == NULL)? 0 : Ustrlen(s) + 1;
-(void)write(fd, &len, sizeof(int));
-if (s != NULL) (void)write(fd, s, len);
+return (  write(fd, &len, sizeof(int)) != sizeof(int)
+       || (s != NULL  &&  write(fd, s, len) != len)
+       )
+       ? -1 : 0;
 }
 
 
 }
 
 
@@ -474,11 +467,13 @@ rda_read_string(int fd, uschar **sp)
 int len;
 
 if (read(fd, &len, sizeof(int)) != sizeof(int)) return FALSE;
 int len;
 
 if (read(fd, &len, sizeof(int)) != sizeof(int)) return FALSE;
-if (len == 0) *sp = NULL; else
-  {
-  *sp = store_get(len);
-  if (read(fd, *sp, len) != len) return FALSE;
-  }
+if (len == 0)
+  *sp = NULL;
+else
+  /* We know we have enough memory so disable the error on "len" */
+  /* coverity[tainted_data] */
+  /* We trust the data source, so untainted */
+  if (read(fd, *sp = store_get(len, FALSE), len) != len) return FALSE;
 return TRUE;
 }
 
 return TRUE;
 }
 
@@ -491,7 +486,7 @@ return TRUE;
 /* This function is passed a forward list string (unexpanded) or the name of a
 file (unexpanded) whose contents are the forwarding list. The list may in fact
 be a filter program if it starts with "#Exim filter" or "#Sieve filter". Other
 /* This function is passed a forward list string (unexpanded) or the name of a
 file (unexpanded) whose contents are the forwarding list. The list may in fact
 be a filter program if it starts with "#Exim filter" or "#Sieve filter". Other
-types of filter, with different inital tag strings, may be introduced in due
+types of filter, with different initial tag strings, may be introduced in due
 course.
 
 The job of the function is to process the forwarding list or filter. It is
 course.
 
 The job of the function is to process the forwarding list or filter. It is
@@ -557,22 +552,21 @@ uschar *data;
 uschar *readerror = US"";
 void (*oldsignal)(int);
 
 uschar *readerror = US"";
 void (*oldsignal)(int);
 
-DEBUG(D_route) debug_printf("rda_interpret (%s): %s\n",
-  (rdata->isfile)? "file" : "string", rdata->string);
+DEBUG(D_route) debug_printf("rda_interpret (%s): '%s'\n",
+  rdata->isfile ? "file" : "string", string_printing(rdata->string));
 
 /* Do the expansions of the file name or data first, while still privileged. */
 
 
 /* Do the expansions of the file name or data first, while still privileged. */
 
-data = expand_string(rdata->string);
-if (data == NULL)
+if (!(data = expand_string(rdata->string)))
   {
   {
-  if (expand_string_forcedfail) return FF_NOTDELIVERED;
+  if (f.expand_string_forcedfail) return FF_NOTDELIVERED;
   *error = string_sprintf("failed to expand \"%s\": %s", rdata->string,
     expand_string_message);
   return FF_ERROR;
   }
 rdata->string = data;
 
   *error = string_sprintf("failed to expand \"%s\": %s", rdata->string,
     expand_string_message);
   return FF_ERROR;
   }
 rdata->string = data;
 
-DEBUG(D_route) debug_printf("expanded: %s\n", data);
+DEBUG(D_route) debug_printf("expanded: '%s'\n", data);
 
 if (rdata->isfile && data[0] != '/')
   {
 
 if (rdata->isfile && data[0] != '/')
   {
@@ -635,7 +629,7 @@ if ((pid = fork()) == 0)
     {
     DEBUG(D_rewrite) debug_printf("turned off address rewrite logging (not "
       "root or exim in this process)\n");
     {
     DEBUG(D_rewrite) debug_printf("turned off address rewrite logging (not "
       "root or exim in this process)\n");
-    log_write_selector &= ~L_address_rewrite;
+    BIT_CLEAR(log_selector, log_selector_size, Li_address_rewrite);
     }
 
   /* Now do the business */
     }
 
   /* Now do the business */
@@ -647,53 +641,60 @@ if ((pid = fork()) == 0)
   /* Pass back whether it was a filter, and the return code and any overall
   error text via the pipe. */
 
   /* Pass back whether it was a filter, and the return code and any overall
   error text via the pipe. */
 
-  (void)write(fd, filtertype, sizeof(int));
-  (void)write(fd, &yield, sizeof(int));
-  rda_write_string(fd, *error);
+  if (  write(fd, filtertype, sizeof(int)) != sizeof(int)
+     || write(fd, &yield, sizeof(int)) != sizeof(int)
+     || rda_write_string(fd, *error) != 0
+     )
+    goto bad;
 
   /* Pass back the contents of any syntax error blocks if we have a pointer */
 
 
   /* Pass back the contents of any syntax error blocks if we have a pointer */
 
-  if (eblockp != NULL)
+  if (eblockp)
     {
     {
-    error_block *ep;
-    for (ep = *eblockp; ep != NULL; ep = ep->next)
-      {
-      rda_write_string(fd, ep->text1);
-      rda_write_string(fd, ep->text2);
-      }
-    rda_write_string(fd, NULL);    /* Indicates end of eblocks */
+    for (error_block * ep = *eblockp; ep; ep = ep->next)
+      if (  rda_write_string(fd, ep->text1) != 0
+         || rda_write_string(fd, ep->text2) != 0
+        )
+       goto bad;
+    if (rda_write_string(fd, NULL) != 0)    /* Indicates end of eblocks */
+      goto bad;
     }
 
   /* If this is a system filter, we have to pass back the numbers of any
   original header lines that were removed, and then any header lines that were
   added but not subsequently removed. */
 
     }
 
   /* If this is a system filter, we have to pass back the numbers of any
   original header lines that were removed, and then any header lines that were
   added but not subsequently removed. */
 
-  if (system_filtering)
+  if (f.system_filtering)
     {
     int i = 0;
     {
     int i = 0;
-    header_line *h;
-    for (h = header_list; h != waslast->next; i++, h = h->next)
-      {
-      if (h->type == htype_old) (void)write(fd, &i, sizeof(i));
-      }
+    for (header_line * h = header_list; h != waslast->next; i++, h = h->next)
+      if (  h->type == htype_old
+         && write(fd, &i, sizeof(i)) != sizeof(i)
+        )
+       goto bad;
+
     i = -1;
     i = -1;
-    (void)write(fd, &i, sizeof(i));
+    if (write(fd, &i, sizeof(i)) != sizeof(i))
+       goto bad;
 
     while (waslast != header_last)
       {
       waslast = waslast->next;
       if (waslast->type != htype_old)
 
     while (waslast != header_last)
       {
       waslast = waslast->next;
       if (waslast->type != htype_old)
-        {
-        rda_write_string(fd, waslast->text);
-        (void)write(fd, &(waslast->type), sizeof(waslast->type));
-        }
+       if (  rda_write_string(fd, waslast->text) != 0
+           || write(fd, &(waslast->type), sizeof(waslast->type))
+             != sizeof(waslast->type)
+          )
+         goto bad;
       }
       }
-    rda_write_string(fd, NULL);    /* Indicates end of added headers */
+    if (rda_write_string(fd, NULL) != 0)    /* Indicates end of added headers */
+      goto bad;
     }
 
   /* Write the contents of the $n variables */
 
     }
 
   /* Write the contents of the $n variables */
 
-  (void)write(fd, filter_n, sizeof(filter_n));
+  if (write(fd, filter_n, sizeof(filter_n)) != sizeof(filter_n))
+    goto bad;
 
   /* If the result was DELIVERED or NOTDELIVERED, we pass back the generated
   addresses, and their associated information, through the pipe. This is
 
   /* If the result was DELIVERED or NOTDELIVERED, we pass back the generated
   addresses, and their associated information, through the pipe. This is
@@ -704,57 +705,72 @@ if ((pid = fork()) == 0)
   if (yield == FF_DELIVERED || yield == FF_NOTDELIVERED ||
       yield == FF_FAIL || yield == FF_FREEZE)
     {
   if (yield == FF_DELIVERED || yield == FF_NOTDELIVERED ||
       yield == FF_FAIL || yield == FF_FREEZE)
     {
-    address_item *addr;
-    for (addr = *generated; addr != NULL; addr = addr->next)
+    for (address_item * addr = *generated; addr; addr = addr->next)
       {
       int reply_options = 0;
       {
       int reply_options = 0;
-
-      rda_write_string(fd, addr->address);
-      (void)write(fd, &(addr->mode), sizeof(addr->mode));
-      (void)write(fd, &(addr->flags), sizeof(addr->flags));
-      rda_write_string(fd, addr->p.errors_address);
-
-      if (addr->pipe_expandn != NULL)
-        {
-        uschar **pp;
-        for (pp = addr->pipe_expandn; *pp != NULL; pp++)
-          rda_write_string(fd, *pp);
-        }
-      rda_write_string(fd, NULL);
-
-      if (addr->reply == NULL)
-        (void)write(fd, &reply_options, sizeof(int));    /* 0 means no reply */
+      int ig_err = addr->prop.ignore_error ? 1 : 0;
+
+      if (  rda_write_string(fd, addr->address) != 0
+         || write(fd, &addr->mode, sizeof(addr->mode)) != sizeof(addr->mode)
+         || write(fd, &addr->flags, sizeof(addr->flags)) != sizeof(addr->flags)
+         || rda_write_string(fd, addr->prop.errors_address) != 0
+         || write(fd, &ig_err, sizeof(ig_err)) != sizeof(ig_err)
+        )
+       goto bad;
+
+      if (addr->pipe_expandn)
+        for (uschar ** pp = addr->pipe_expandn; *pp; pp++)
+          if (rda_write_string(fd, *pp) != 0)
+           goto bad;
+      if (rda_write_string(fd, NULL) != 0)
+        goto bad;
+
+      if (!addr->reply)
+       {
+        if (write(fd, &reply_options, sizeof(int)) != sizeof(int))    /* 0 means no reply */
+         goto bad;
+       }
       else
         {
         reply_options |= REPLY_EXISTS;
         if (addr->reply->file_expand) reply_options |= REPLY_EXPAND;
         if (addr->reply->return_message) reply_options |= REPLY_RETURN;
       else
         {
         reply_options |= REPLY_EXISTS;
         if (addr->reply->file_expand) reply_options |= REPLY_EXPAND;
         if (addr->reply->return_message) reply_options |= REPLY_RETURN;
-        (void)write(fd, &reply_options, sizeof(int));
-        (void)write(fd, &(addr->reply->expand_forbid), sizeof(int));
-        (void)write(fd, &(addr->reply->once_repeat), sizeof(time_t));
-        rda_write_string(fd, addr->reply->to);
-        rda_write_string(fd, addr->reply->cc);
-        rda_write_string(fd, addr->reply->bcc);
-        rda_write_string(fd, addr->reply->from);
-        rda_write_string(fd, addr->reply->reply_to);
-        rda_write_string(fd, addr->reply->subject);
-        rda_write_string(fd, addr->reply->headers);
-        rda_write_string(fd, addr->reply->text);
-        rda_write_string(fd, addr->reply->file);
-        rda_write_string(fd, addr->reply->logfile);
-        rda_write_string(fd, addr->reply->oncelog);
+        if (  write(fd, &reply_options, sizeof(int)) != sizeof(int)
+           || write(fd, &(addr->reply->expand_forbid), sizeof(int))
+             != sizeof(int)
+           || write(fd, &(addr->reply->once_repeat), sizeof(time_t))
+             != sizeof(time_t)
+           || rda_write_string(fd, addr->reply->to) != 0
+           || rda_write_string(fd, addr->reply->cc) != 0
+           || rda_write_string(fd, addr->reply->bcc) != 0
+           || rda_write_string(fd, addr->reply->from) != 0
+           || rda_write_string(fd, addr->reply->reply_to) != 0
+           || rda_write_string(fd, addr->reply->subject) != 0
+           || rda_write_string(fd, addr->reply->headers) != 0
+           || rda_write_string(fd, addr->reply->text) != 0
+           || rda_write_string(fd, addr->reply->file) != 0
+           || rda_write_string(fd, addr->reply->logfile) != 0
+           || rda_write_string(fd, addr->reply->oncelog) != 0
+          )
+         goto bad;
         }
       }
 
         }
       }
 
-    rda_write_string(fd, NULL);   /* Marks end of addresses */
+    if (rda_write_string(fd, NULL) != 0)   /* Marks end of addresses */
+      goto bad;
     }
 
   /* OK, this process is now done. Free any cached resources. Must use _exit()
   and not exit() !! */
 
     }
 
   /* OK, this process is now done. Free any cached resources. Must use _exit()
   and not exit() !! */
 
+out:
   (void)close(fd);
   search_tidyup();
   (void)close(fd);
   search_tidyup();
-  _exit(0);
+  exim_underbar_exit(0);
+
+bad:
+  DEBUG(D_rewrite) debug_printf("rda_interpret: failed write to pipe\n");
+  goto out;
   }
 
 /* Back in the main process: panic if the fork did not succeed. */
   }
 
 /* Back in the main process: panic if the fork did not succeed. */
@@ -777,29 +793,27 @@ if (read(fd, filtertype, sizeof(int)) != sizeof(int) ||
 
 /* Read the contents of any syntax error blocks if we have a pointer */
 
 
 /* Read the contents of any syntax error blocks if we have a pointer */
 
-if (eblockp != NULL)
+if (eblockp)
   {
   {
-  uschar *s;
   error_block *e;
   error_block *e;
-  error_block **p = eblockp;
-  for (;;)
+  for (error_block ** p = eblockp; ; p = &e->next)
     {
     {
+    uschar *s;
     if (!rda_read_string(fd, &s)) goto DISASTER;
     if (!rda_read_string(fd, &s)) goto DISASTER;
-    if (s == NULL) break;
-    e = store_get(sizeof(error_block));
+    if (!s) break;
+    e = store_get(sizeof(error_block), FALSE);
     e->next = NULL;
     e->text1 = s;
     if (!rda_read_string(fd, &s)) goto DISASTER;
     e->text2 = s;
     *p = e;
     e->next = NULL;
     e->text1 = s;
     if (!rda_read_string(fd, &s)) goto DISASTER;
     e->text2 = s;
     *p = e;
-    p = &(e->next);
     }
   }
 
 /* If this is a system filter, read the identify of any original header lines
 that were removed, and then read data for any new ones that were added. */
 
     }
   }
 
 /* If this is a system filter, read the identify of any original header lines
 that were removed, and then read data for any new ones that were added. */
 
-if (system_filtering)
+if (f.system_filtering)
   {
   int hn = 0;
   header_line *h = header_list;
   {
   int hn = 0;
   header_line *h = header_list;
@@ -812,8 +826,7 @@ if (system_filtering)
     while (hn < n)
       {
       hn++;
     while (hn < n)
       {
       hn++;
-      h = h->next;
-      if (h == NULL) goto DISASTER_NO_HEADER;
+      if (!(h = h->next)) goto DISASTER_NO_HEADER;
       }
     h->type = htype_old;
     }
       }
     h->type = htype_old;
     }
@@ -823,7 +836,7 @@ if (system_filtering)
     uschar *s;
     int type;
     if (!rda_read_string(fd, &s)) goto DISASTER;
     uschar *s;
     int type;
     if (!rda_read_string(fd, &s)) goto DISASTER;
-    if (s == NULL) break;
+    if (!s) break;
     if (read(fd, &type, sizeof(type)) != sizeof(type)) goto DISASTER;
     header_add(type, "%s", s);
     }
     if (read(fd, &type, sizeof(type)) != sizeof(type)) goto DISASTER;
     header_add(type, "%s", s);
     }
@@ -852,7 +865,7 @@ if (yield == FF_DELIVERED || yield == FF_NOTDELIVERED ||
     /* First string is the address; NULL => end of addresses */
 
     if (!rda_read_string(fd, &recipient)) goto DISASTER;
     /* First string is the address; NULL => end of addresses */
 
     if (!rda_read_string(fd, &recipient)) goto DISASTER;
-    if (recipient == NULL) break;
+    if (!recipient) break;
 
     /* Hang on the end of the chain */
 
 
     /* Hang on the end of the chain */
 
@@ -862,9 +875,13 @@ if (yield == FF_DELIVERED || yield == FF_NOTDELIVERED ||
 
     /* Next comes the mode and the flags fields */
 
 
     /* Next comes the mode and the flags fields */
 
-    if (read(fd, &(addr->mode), sizeof(addr->mode)) != sizeof(addr->mode) ||
-        read(fd, &(addr->flags), sizeof(addr->flags)) != sizeof(addr->flags) ||
-        !rda_read_string(fd, &(addr->p.errors_address))) goto DISASTER;
+    if (  read(fd, &addr->mode, sizeof(addr->mode)) != sizeof(addr->mode)
+       || read(fd, &addr->flags, sizeof(addr->flags)) != sizeof(addr->flags)
+       || !rda_read_string(fd, &addr->prop.errors_address)
+       || read(fd, &i, sizeof(i)) != sizeof(i)
+       )
+      goto DISASTER;
+    addr->prop.ignore_error = (i != 0);
 
     /* Next comes a possible setting for $thisaddress and any numerical
     variables for pipe expansion, terminated by a NULL string. The maximum
 
     /* Next comes a possible setting for $thisaddress and any numerical
     variables for pipe expansion, terminated by a NULL string. The maximum
@@ -883,7 +900,7 @@ if (yield == FF_DELIVERED || yield == FF_NOTDELIVERED ||
 
     if (i > 0)
       {
 
     if (i > 0)
       {
-      addr->pipe_expandn = store_get((i+1) * sizeof(uschar **));
+      addr->pipe_expandn = store_get((i+1) * sizeof(uschar *), FALSE);
       addr->pipe_expandn[i] = NULL;
       while (--i >= 0) addr->pipe_expandn[i] = expandn[i];
       }
       addr->pipe_expandn[i] = NULL;
       while (--i >= 0) addr->pipe_expandn[i] = expandn[i];
       }
@@ -893,7 +910,7 @@ if (yield == FF_DELIVERED || yield == FF_NOTDELIVERED ||
     if (read(fd, &reply_options, sizeof(int)) != sizeof(int)) goto DISASTER;
     if ((reply_options & REPLY_EXISTS) != 0)
       {
     if (read(fd, &reply_options, sizeof(int)) != sizeof(int)) goto DISASTER;
     if ((reply_options & REPLY_EXISTS) != 0)
       {
-      addr->reply = store_get(sizeof(reply_item));
+      addr->reply = store_get(sizeof(reply_item), FALSE);
 
       addr->reply->file_expand = (reply_options & REPLY_EXPAND) != 0;
       addr->reply->return_message = (reply_options & REPLY_RETURN) != 0;
 
       addr->reply->file_expand = (reply_options & REPLY_EXPAND) != 0;
       addr->reply->return_message = (reply_options & REPLY_RETURN) != 0;