REQUIRETLS: amplify docs discussion
[exim.git] / src / src / rda.c
index 443cbb1bdcd64103217a7d36b96ed9c59e0ea486..2d8bda8b1815ca51184170cac6f56c541ba71328 100644 (file)
@@ -1,10 +1,8 @@
-/* $Cambridge: exim/src/src/rda.c,v 1.2 2004/11/04 10:42:11 ph10 Exp $ */
-
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2004 */
+/* 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
@@ -299,23 +297,17 @@ if (fread(filebuf, 1, statbuf.st_size, fwd) != statbuf.st_size)
   }
 filebuf[statbuf.st_size] = 0;
 
   }
 filebuf[statbuf.st_size] = 0;
 
-/* Don't pass statbuf.st_size directly to debug_printf. On some systems it
-is a long, which may not be the same as an int. */
-
 DEBUG(D_route)
 DEBUG(D_route)
-  {
-  int size = (int)statbuf.st_size;
-  debug_printf("%d bytes read from %s\n", size, filename);
-  }
+  debug_printf(OFF_T_FMT " bytes read from %s\n", statbuf.st_size, filename);
 
 
-fclose(fwd);
+(void)fclose(fwd);
 return filebuf;
 
 /* Return an error: the string is already set up. */
 
 ERROR_RETURN:
 *yield = FF_ERROR;
 return filebuf;
 
 /* Return an error: the string is already set up. */
 
 ERROR_RETURN:
 *yield = FF_ERROR;
-fclose(fwd);
+(void)fclose(fwd);
 return NULL;
 }
 
 return NULL;
 }
 
@@ -333,6 +325,9 @@ Arguments:
   options                   the options bits
   include_directory         restrain to this directory
   sieve_vacation_directory  passed to sieve_interpret
   options                   the options bits
   include_directory         restrain to this directory
   sieve_vacation_directory  passed to sieve_interpret
+  sieve_enotify_mailto_owner passed to sieve_interpret
+  sieve_useraddress         passed to sieve_interpret
+  sieve_subaddress          passed to sieve_interpret
   generated                 where to hang generated addresses
   error                     for error messages
   eblockp                   for details of skipped syntax errors
   generated                 where to hang generated addresses
   error                     for error messages
   eblockp                   for details of skipped syntax errors
@@ -348,14 +343,16 @@ Returns:                    a suitable return for rda_interpret()
 
 static int
 rda_extract(redirect_block *rdata, int options, uschar *include_directory,
 
 static int
 rda_extract(redirect_block *rdata, int options, uschar *include_directory,
-  uschar *sieve_vacation_directory, address_item **generated, uschar **error,
-  error_block **eblockp, int *filtertype)
+  uschar *sieve_vacation_directory, uschar *sieve_enotify_mailto_owner,
+  uschar *sieve_useraddress, uschar *sieve_subaddress,
+  address_item **generated, uschar **error, error_block **eblockp,
+  int *filtertype)
 {
 uschar *data;
 
 if (rdata->isfile)
   {
 {
 uschar *data;
 
 if (rdata->isfile)
   {
-  int yield;
+  int yield = 0;
   data = rda_get_file_contents(rdata, options, error, &yield);
   if (data == NULL) return yield;
   }
   data = rda_get_file_contents(rdata, options, error, &yield);
   if (data == NULL) return yield;
   }
@@ -378,7 +375,7 @@ if (*filtertype != FILTER_FORWARD)
     (*filtertype == FILTER_EXIM)? "an Exim" : "a Sieve");
 
   /* RDO_FILTER is an "allow" bit */
     (*filtertype == FILTER_EXIM)? "an Exim" : "a Sieve");
 
   /* RDO_FILTER is an "allow" bit */
-   
+
   if ((options & RDO_FILTER) == 0)
     {
     *error = US"filtering not enabled";
   if ((options & RDO_FILTER) == 0)
     {
     *error = US"filtering not enabled";
@@ -388,18 +385,18 @@ if (*filtertype != FILTER_FORWARD)
   expand_forbid =
     (expand_forbid & ~RDO_FILTER_EXPANSIONS) |
     (options & RDO_FILTER_EXPANSIONS);
   expand_forbid =
     (expand_forbid & ~RDO_FILTER_EXPANSIONS) |
     (options & RDO_FILTER_EXPANSIONS);
-  
+
   /* RDO_{EXIM,SIEVE}_FILTER are forbid bits */
   /* RDO_{EXIM,SIEVE}_FILTER are forbid bits */
-   
+
   if (*filtertype == FILTER_EXIM)
     {
     if ((options & RDO_EXIM_FILTER) != 0)
       {
       *error = US"Exim filtering not enabled";
       return FF_ERROR;
   if (*filtertype == FILTER_EXIM)
     {
     if ((options & RDO_EXIM_FILTER) != 0)
       {
       *error = US"Exim filtering not enabled";
       return FF_ERROR;
-      }    
+      }
     frc = filter_interpret(data, options, generated, error);
     frc = filter_interpret(data, options, generated, error);
-    }  
+    }
   else
     {
     if ((options & RDO_SIEVE_FILTER) != 0)
   else
     {
     if ((options & RDO_SIEVE_FILTER) != 0)
@@ -407,9 +404,10 @@ if (*filtertype != FILTER_FORWARD)
       *error = US"Sieve filtering not enabled";
       return FF_ERROR;
       }
       *error = US"Sieve filtering not enabled";
       return FF_ERROR;
       }
-    frc = sieve_interpret(data, options, sieve_vacation_directory, generated, 
-      error);
-    }   
+    frc = sieve_interpret(data, options, sieve_vacation_directory,
+      sieve_enotify_mailto_owner, sieve_useraddress, sieve_subaddress,
+      generated, error);
+    }
 
   expand_forbid = old_expand_forbid;
   return frc;
 
   expand_forbid = old_expand_forbid;
   return frc;
@@ -435,22 +433,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;
-write(fd, &len, sizeof(int));
-if (s != NULL) write(fd, s, len);
+return (  write(fd, &len, sizeof(int)) != sizeof(int)
+       || (s != NULL  &&  write(fd, s, len) != len)
+       )
+       ? -1 : 0;
 }
 
 
 }
 
 
@@ -474,11 +474,12 @@ 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] */
+  if (read(fd, *sp = store_get(len), len) != len) return FALSE;
 return TRUE;
 }
 
 return TRUE;
 }
 
@@ -514,7 +515,10 @@ Arguments:
   options                   options to pass to the extraction functions,
                               plus ENOTDIR and EACCES handling bits
   include_directory         restrain :include: to this directory
   options                   options to pass to the extraction functions,
                               plus ENOTDIR and EACCES handling bits
   include_directory         restrain :include: to this directory
-  sieve_vacation_directory  directory passed to sieve_interpret()
+  sieve_vacation_directory  directory passed to sieve_interpret
+  sieve_enotify_mailto_owner passed to sieve_interpret
+  sieve_useraddress         passed to sieve_interpret
+  sieve_subaddress          passed to sieve_interpret
   ugid                      uid/gid to run under - if NULL, no change
   generated                 where to hang generated addresses, initially NULL
   error                     pointer for error message
   ugid                      uid/gid to run under - if NULL, no change
   generated                 where to hang generated addresses, initially NULL
   error                     pointer for error message
@@ -541,8 +545,10 @@ Returns:        values from extraction function, or FF_NONEXIST:
 
 int
 rda_interpret(redirect_block *rdata, int options, uschar *include_directory,
 
 int
 rda_interpret(redirect_block *rdata, int options, uschar *include_directory,
-  uschar *sieve_vacation_directory, ugid_block *ugid, address_item **generated,
-  uschar **error, error_block **eblockp, int *filtertype, uschar *rname)
+  uschar *sieve_vacation_directory, uschar *sieve_enotify_mailto_owner,
+  uschar *sieve_useraddress, uschar *sieve_subaddress, ugid_block *ugid,
+  address_item **generated, uschar **error, error_block **eblockp,
+  int *filtertype, uschar *rname)
 {
 int fd, rc, pfd[2];
 int yield, status;
 {
 int fd, rc, pfd[2];
 int yield, status;
@@ -586,7 +592,8 @@ if (!ugid->uid_set ||                         /* Either there's no uid, or */
      Ustrstr(data, ":include:") == NULL))     /* and there's no :include: */
   {
   return rda_extract(rdata, options, include_directory,
      Ustrstr(data, ":include:") == NULL))     /* and there's no :include: */
   {
   return rda_extract(rdata, options, include_directory,
-    sieve_vacation_directory, generated, error, eblockp, filtertype);
+    sieve_vacation_directory, sieve_enotify_mailto_owner, sieve_useraddress,
+    sieve_subaddress, generated, error, eblockp, filtertype);
   }
 
 /* We need to run the processing code in a sub-process. However, if we can
   }
 
 /* We need to run the processing code in a sub-process. However, if we can
@@ -606,15 +613,19 @@ if (pipe(pfd) != 0)
 
 /* Ensure that SIGCHLD is set to SIG_DFL before forking, so that the child
 process can be waited for. We sometimes get here with it set otherwise. Save
 
 /* Ensure that SIGCHLD is set to SIG_DFL before forking, so that the child
 process can be waited for. We sometimes get here with it set otherwise. Save
-the old state for resetting on the wait. */
+the old state for resetting on the wait. Ensure that all cached resources are
+freed so that the subprocess starts with a clean slate and doesn't interfere
+with the parent process. */
 
 oldsignal = signal(SIGCHLD, SIG_DFL);
 
 oldsignal = signal(SIGCHLD, SIG_DFL);
+search_tidyup();
+
 if ((pid = fork()) == 0)
   {
   header_line *waslast = header_last;   /* Save last header */
 
   fd = pfd[pipe_write];
 if ((pid = fork()) == 0)
   {
   header_line *waslast = header_last;   /* Save last header */
 
   fd = pfd[pipe_write];
-  close(pfd[pipe_read]);
+  (void)close(pfd[pipe_read]);
   exim_setugid(ugid->uid, ugid->gid, FALSE, rname);
 
   /* Addresses can get rewritten in filters; if we are not root or the exim
   exim_setugid(ugid->uid, ugid->gid, FALSE, rname);
 
   /* Addresses can get rewritten in filters; if we are not root or the exim
@@ -625,20 +636,23 @@ 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 */
 
   yield = rda_extract(rdata, options, include_directory,
     }
 
   /* Now do the business */
 
   yield = rda_extract(rdata, options, include_directory,
-    sieve_vacation_directory, generated, error, eblockp, filtertype);
+    sieve_vacation_directory, sieve_enotify_mailto_owner, sieve_useraddress,
+    sieve_subaddress, generated, error, eblockp, filtertype);
 
   /* 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. */
 
-  write(fd, filtertype, sizeof(int));
-  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 */
 
@@ -646,11 +660,12 @@ if ((pid = fork()) == 0)
     {
     error_block *ep;
     for (ep = *eblockp; ep != NULL; ep = ep->next)
     {
     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 */
+      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
     }
 
   /* If this is a system filter, we have to pass back the numbers of any
@@ -662,27 +677,33 @@ if ((pid = fork()) == 0)
     int i = 0;
     header_line *h;
     for (h = header_list; h != waslast->next; i++, h = h->next)
     int i = 0;
     header_line *h;
     for (h = header_list; h != waslast->next; i++, h = h->next)
-      {
-      if (h->type == htype_old) write(fd, &i, sizeof(i));
-      }
+      if (  h->type == htype_old
+         && write(fd, &i, sizeof(i)) != sizeof(i)
+        )
+       goto bad;
+
     i = -1;
     i = -1;
-    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);
-        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 */
 
-  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
@@ -694,54 +715,73 @@ if ((pid = fork()) == 0)
       yield == FF_FAIL || yield == FF_FREEZE)
     {
     address_item *addr;
       yield == FF_FAIL || yield == FF_FREEZE)
     {
     address_item *addr;
-    for (addr = *generated; addr != NULL; addr = addr->next)
+    for (addr = *generated; addr; addr = addr->next)
       {
       int reply_options = 0;
 
       {
       int reply_options = 0;
 
-      rda_write_string(fd, addr->address);
-      write(fd, &(addr->mode), sizeof(addr->mode));
-      write(fd, &(addr->flags), sizeof(addr->flags));
-      rda_write_string(fd, addr->p.errors_address);
+      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
+        )
+       goto bad;
 
 
-      if (addr->pipe_expandn != NULL)
+      if (addr->pipe_expandn)
         {
         uschar **pp;
         {
         uschar **pp;
-        for (pp = addr->pipe_expandn; *pp != NULL; pp++)
-          rda_write_string(fd, *pp);
+        for (pp = addr->pipe_expandn; *pp; pp++)
+          if (rda_write_string(fd, *pp) != 0)
+           goto bad;
         }
         }
-      rda_write_string(fd, NULL);
-
-      if (addr->reply == NULL)
-        write(fd, &reply_options, sizeof(int));    /* 0 means no reply */
+      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;
-        write(fd, &reply_options, sizeof(int));
-        write(fd, &(addr->reply->expand_forbid), sizeof(int));
-        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. Must use _exit() and not exit() !! */
+  /* OK, this process is now done. Free any cached resources. Must use _exit()
+  and not exit() !! */
 
 
-  close(fd);
+out:
+  (void)close(fd);
+  search_tidyup();
   _exit(0);
   _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. */
@@ -753,7 +793,7 @@ if (pid < 0)
 writing end must be closed first, as otherwise read() won't return zero on an
 empty pipe. Afterwards, close the reading end. */
 
 writing end must be closed first, as otherwise read() won't return zero on an
 empty pipe. Afterwards, close the reading end. */
 
-close(pfd[pipe_write]);
+(void)close(pfd[pipe_write]);
 
 /* Read initial data, including yield and contents of *error */
 
 
 /* Read initial data, including yield and contents of *error */
 
@@ -762,27 +802,23 @@ if (read(fd, filtertype, sizeof(int)) != sizeof(int) ||
     read(fd, &yield, sizeof(int)) != sizeof(int) ||
     !rda_read_string(fd, error)) goto DISASTER;
 
     read(fd, &yield, sizeof(int)) != sizeof(int) ||
     !rda_read_string(fd, error)) goto DISASTER;
 
-DEBUG(D_route)
-  debug_printf("rda_interpret: subprocess yield=%d error=%s\n", yield, *error);
-
 /* 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 (;;)
+  error_block **p;
+  for (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;
+    if (!s) break;
     e = store_get(sizeof(error_block));
     e->next = NULL;
     e->text1 = s;
     if (!rda_read_string(fd, &s)) goto DISASTER;
     e->text2 = s;
     *p = e;
     e = store_get(sizeof(error_block));
     e->next = NULL;
     e->text1 = s;
     if (!rda_read_string(fd, &s)) goto DISASTER;
     e->text2 = s;
     *p = e;
-    p = &(e->next);
     }
   }
 
     }
   }
 
@@ -802,8 +838,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;
     }
@@ -813,7 +848,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,9 +887,9 @@ 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)) goto DISASTER;
 
     /* 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
@@ -873,7 +908,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 *));
       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];
       }
@@ -921,6 +956,9 @@ while ((rc = wait(&status)) != pid)
     }
   }
 
     }
   }
 
+DEBUG(D_route)
+  debug_printf("rda_interpret: subprocess yield=%d error=%s\n", yield, *error);
+
 if (had_disaster)
   {
   *error = string_sprintf("internal problem in %s: failure to transfer "
 if (had_disaster)
   {
   *error = string_sprintf("internal problem in %s: failure to transfer "
@@ -938,7 +976,7 @@ else if (status != 0)
   }
 
 FINAL_EXIT:
   }
 
 FINAL_EXIT:
-close(fd);
+(void)close(fd);
 signal(SIGCHLD, oldsignal);   /* restore */
 return yield;
 
 signal(SIGCHLD, oldsignal);   /* restore */
 return yield;