* Write block of data *
*************************************************/
+static int
+tpt_write(int fd, uschar * block, int len, BOOL more, int options)
+{
+return
+#ifndef DISABLE_TLS
+ tls_out.active.sock == fd
+ ? tls_write(tls_out.active.tls_ctx, block, len, more) :
+#endif
+#ifdef MSG_MORE
+ more && !(options & topt_not_socket) ? send(fd, block, len, MSG_MORE) :
+#endif
+ write(fd, block, len);
+}
+
/* Subroutine called by write_chunk() and at the end of the message actually
to write a data block. Also called directly by some transports to write
additional data to the file descriptor (e.g. prefix, suffix).
*/
static BOOL
-transport_write_block_fd(transport_ctx * tctx, uschar *block, int len, BOOL more)
+transport_write_block_fd(transport_ctx * tctx, uschar * block, int len, BOOL more)
{
int rc, save_errno;
int local_timeout = transport_write_timeout;
+int connretry = 1;
int fd = tctx->u.fd;
/* This loop is for handling incomplete writes and other retries. In most
debug_printf("writing data block fd=%d size=%d timeout=%d%s\n",
fd, len, local_timeout, more ? " (more expected)" : "");
- /* This code makes use of alarm() in order to implement the timeout. This
- isn't a very tidy way of doing things. Using non-blocking I/O with select()
- provides a neater approach. However, I don't know how to do this when TLS is
- in use. */
+ /* When doing TCP Fast Open we may get this far before the 3-way handshake
+ is complete, and write returns ENOTCONN. Detect that, wait for the socket
+ to become writable, and retry once only. */
- if (transport_write_timeout <= 0) /* No timeout wanted */
+ for(;;)
{
- rc =
-#ifdef SUPPORT_TLS
- tls_out.active.sock == fd ? tls_write(tls_out.active.tls_ctx, block, len, more) :
-#endif
-#ifdef MSG_MORE
- more && !(tctx->options & topt_not_socket)
- ? send(fd, block, len, MSG_MORE) :
-#endif
- write(fd, block, len);
- save_errno = errno;
- }
+ fd_set fds;
+ /* This code makes use of alarm() in order to implement the timeout. This
+ isn't a very tidy way of doing things. Using non-blocking I/O with select()
+ provides a neater approach. However, I don't know how to do this when TLS is
+ in use. */
- /* Timeout wanted. */
-
- else
- {
- ALARM(local_timeout);
-
- rc =
-#ifdef SUPPORT_TLS
- tls_out.active.sock == fd ? tls_write(tls_out.active.tls_ctx, block, len, more) :
-#endif
-#ifdef MSG_MORE
- more && !(tctx->options & topt_not_socket)
- ? send(fd, block, len, MSG_MORE) :
-#endif
- write(fd, block, len);
-
- save_errno = errno;
- local_timeout = ALARM_CLR(0);
- if (sigalrm_seen)
+ if (transport_write_timeout <= 0) /* No timeout wanted */
{
- errno = ETIMEDOUT;
- return FALSE;
+ rc = tpt_write(fd, block, len, more, tctx->options);
+ save_errno = errno;
}
+ else /* Timeout wanted. */
+ {
+ ALARM(local_timeout);
+ rc = tpt_write(fd, block, len, more, tctx->options);
+ save_errno = errno;
+ local_timeout = ALARM_CLR(0);
+ if (sigalrm_seen)
+ {
+ errno = ETIMEDOUT;
+ return FALSE;
+ }
+ }
+
+ if (rc >= 0 || errno != ENOTCONN || connretry <= 0)
+ break;
+
+ FD_ZERO(&fds); FD_SET(fd, &fds);
+ select(fd+1, NULL, &fds, NULL, NULL); /* could set timout? */
+ connretry--;
}
/* Hopefully, the most common case is success, so test that first. */
gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer };
va_list ap;
+/* Use taint-unchecked routines for writing into big_buffer, trusting
+that the result will never be expanded. */
+
va_start(ap, format);
-if (!string_vformat(&gs, FALSE, format, ap))
+if (!string_vformat(&gs, SVFMT_TAINT_NOCHK, format, ap))
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong formatted string in transport");
va_end(ap);
tctx.u.fd = fd;
for (ppp = *pdlist; ppp; ppp = ppp->next) if (p == ppp->ptr) return TRUE;
-ppp = store_get(sizeof(struct aci));
+ppp = store_get(sizeof(struct aci), FALSE);
ppp->next = *pdlist;
*pdlist = ppp;
ppp->ptr = p;
/* Remember what we have output, and output it. */
-ppp = store_get(sizeof(struct aci));
+ppp = store_get(sizeof(struct aci), FALSE);
ppp->next = *pplist;
*pplist = ppp;
ppp->ptr = pp;
{
if (tblock && tblock->rewrite_rules)
{
- void *reset_point = store_get(0);
+ rmark reset_point = store_mark();
header_line *hh;
if ((hh = rewrite_header(h, NULL, NULL, tblock->rewrite_rules,
BOOL first = TRUE;
struct aci *plist = NULL;
struct aci *dlist = NULL;
- void *reset_point = store_get(0);
+ rmark reset_point = store_mark();
if (!write_chunk(tctx, US"Envelope-to: ", 13)) goto bad;
!= sizeof(int)
|| write(pfd[pipe_write], (void *)&tctx->addr->more_errno, sizeof(int))
!= sizeof(int)
- || write(pfd[pipe_write], (void *)&tctx->addr->delivery_usec, sizeof(int))
- != sizeof(int)
+ || write(pfd[pipe_write], (void *)&tctx->addr->delivery_time, sizeof(struct timeval))
+ != sizeof(struct timeval)
)
rc = FALSE; /* compiler quietening */
- _exit(0);
+ exim_underbar_exit(0);
}
save_errno = errno;
/* When testing, let the subprocess get going */
-if (f.running_in_test_harness) millisleep(250);
+testharness_pause_ms(250);
DEBUG(D_transport)
debug_printf("process %d writing to transport filter\n", (int)write_pid);
{
int dummy = read(pfd[pipe_read], (void *)&save_errno, sizeof(int));
dummy = read(pfd[pipe_read], (void *)&tctx->addr->more_errno, sizeof(int));
- dummy = read(pfd[pipe_read], (void *)&tctx->addr->delivery_usec, sizeof(int));
+ dummy = read(pfd[pipe_read], (void *)&tctx->addr->delivery_time, sizeof(struct timeval));
dummy = dummy; /* compiler quietening */
yield = FALSE;
}
/* Open the database for this transport */
if (!(dbm_file = dbfn_open(string_sprintf("wait-%.200s", tpname),
- O_RDWR, &dbblock, TRUE)))
+ O_RDWR, &dbblock, TRUE, TRUE)))
return;
/* Scan the list of hosts for which this message is waiting, and ensure
if (!(host_record = dbfn_read(dbm_file, host->name)))
{
- host_record = store_get(sizeof(dbdata_wait) + MESSAGE_ID_LENGTH);
+ host_record = store_get(sizeof(dbdata_wait) + MESSAGE_ID_LENGTH, FALSE);
host_record->count = host_record->sequence = 0;
}
else
{
dbdata_wait *newr =
- store_get(sizeof(dbdata_wait) + host_length + MESSAGE_ID_LENGTH);
+ store_get(sizeof(dbdata_wait) + host_length + MESSAGE_ID_LENGTH, FALSE);
memcpy(newr, host_record, sizeof(dbdata_wait) + host_length);
host_record = newr;
}
/* Open the waiting information database. */
if (!(dbm_file = dbfn_open(string_sprintf("wait-%.200s", transport_name),
- O_RDWR, &dbblock, TRUE)))
+ O_RDWR, &dbblock, TRUE, TRUE)))
return FALSE;
/* See if there is a record for this host; if not, there's nothing to do. */
/* create an array to read entire message queue into memory for processing */
- msgq = store_malloc(sizeof(msgq_t) * host_record->count);
+ msgq = store_get(sizeof(msgq_t) * host_record->count, FALSE);
msgq_count = host_record->count;
msgq_actual = msgq_count;
{
msgq[i].bKeep = TRUE;
- Ustrncpy(msgq[i].message_id, host_record->text + (i * MESSAGE_ID_LENGTH),
+ Ustrncpy_nt(msgq[i].message_id, host_record->text + (i * MESSAGE_ID_LENGTH),
MESSAGE_ID_LENGTH);
msgq[i].message_id[MESSAGE_ID_LENGTH] = 0;
}
for (i = msgq_count - 1; i >= 0; --i) if (msgq[i].bKeep)
{
uschar subdir[2];
+ uschar * mid = msgq[i].message_id;
- subdir[0] = split_spool_directory ? msgq[i].message_id[5] : 0;
- subdir[1] = 0;
-
- if (Ustat(spool_fname(US"input", subdir, msgq[i].message_id, US"-D"),
- &statbuf) != 0)
+ set_subdir_str(subdir, mid, 0);
+ if (Ustat(spool_fname(US"input", subdir, mid, US"-D"), &statbuf) != 0)
msgq[i].bKeep = FALSE;
- else if (!oicf_func || oicf_func(msgq[i].message_id, oicf_data))
+ else if (!oicf_func || oicf_func(mid, oicf_data))
{
- Ustrcpy(new_message_id, msgq[i].message_id);
+ Ustrcpy_nt(new_message_id, mid);
msgq[i].bKeep = FALSE;
bFound = TRUE;
break;
}
if (bFound) /* Usual exit from main loop */
- {
- store_free (msgq);
break;
- }
/* If host_length <= 0 we have emptied a record and not found a good message,
and there are no continuation records. Otherwise there is a continuation
dbfn_close(dbm_file);
return FALSE;
}
-
- store_free(msgq);
} /* we need to process a continuation record */
/* Control gets here when an existing message has been encountered; its
if (smtp_peer_options & OPTION_DSN) argv[i++] = US"-MCD";
if (smtp_peer_options & OPTION_PIPE) argv[i++] = US"-MCP";
if (smtp_peer_options & OPTION_SIZE) argv[i++] = US"-MCS";
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
if (smtp_peer_options & OPTION_TLS)
if (tls_out.active.sock >= 0 || continue_proxy_cipher)
{
DEBUG(D_transport) debug_printf("transport_pass_socket succeeded (final-pid %d)\n", pid);
_exit(EXIT_SUCCESS);
}
- if (f.running_in_test_harness) sleep(1);
+ testharness_pause_ms(1000);
transport_do_pass_socket(transport_name, hostname, hostaddress,
id, socket_fd);
for (address_item * ad = addr; ad; ad = ad->next) address_count++;
max_args = address_count + 60;
-*argvptr = argv = store_get((max_args+1)*sizeof(uschar *));
+*argvptr = argv = store_get((max_args+1)*sizeof(uschar *), FALSE);
/* Split the command up into arguments terminated by white space. Lose
trailing space at the start and end. Double-quoted arguments can contain \\ and
s = cmd;
while (isspace(*s)) s++;
-while (*s != 0 && argcount < max_args)
+for (; *s != 0 && argcount < max_args; argcount++)
{
if (*s == '\'')
{
ss = s + 1;
while (*ss != 0 && *ss != '\'') ss++;
- argv[argcount++] = ss = store_get(ss - s++);
+ argv[argcount] = ss = store_get(ss - s++, is_tainted(cmd));
while (*s != 0 && *s != '\'') *ss++ = *s++;
if (*s != 0) s++;
*ss++ = 0;
}
- else argv[argcount++] = string_copy(string_dequote(CUSS &s));
+ else
+ argv[argcount] = string_dequote(CUSS &s);
while (isspace(*s)) s++;
}
DEBUG(D_transport)
{
debug_printf("direct command:\n");
- for (int i = 0; argv[i] != US 0; i++)
- debug_printf(" argv[%d] = %s\n", i, string_printing(argv[i]));
+ for (int i = 0; argv[i]; i++)
+ debug_printf(" argv[%d] = '%s'\n", i, string_printing(argv[i]));
}
if (expand_arguments)
int address_pipe_argcount = 0;
int address_pipe_max_args;
uschar **address_pipe_argv;
+ BOOL tainted;
/* We can never have more then the argv we will be loading into */
address_pipe_max_args = max_args - argcount + 1;
debug_printf("address_pipe_max_args=%d\n", address_pipe_max_args);
/* We allocate an additional for (uschar *)0 */
- address_pipe_argv = store_get((address_pipe_max_args+1)*sizeof(uschar *));
+ address_pipe_argv = store_get((address_pipe_max_args+1)*sizeof(uschar *), FALSE);
/* +1 because addr->local_part[0] == '|' since af_force_command is set */
s = expand_string(addr->local_part + 1);
+ tainted = is_tainted(s);
if (s == NULL || *s == '\0')
{
{
ss = s + 1;
while (*ss != 0 && *ss != '\'') ss++;
- address_pipe_argv[address_pipe_argcount++] = ss = store_get(ss - s++);
+ address_pipe_argv[address_pipe_argcount++] = ss = store_get(ss - s++, tainted);
while (*s != 0 && *s != '\'') *ss++ = *s++;
if (*s != 0) s++;
*ss++ = 0;
expanded_arg = expand_cstring(argv[i]);
f.enable_dollar_recipients = FALSE;
- if (expanded_arg == NULL)
+ if (!expanded_arg)
{
uschar *msg = string_sprintf("Expansion of \"%s\" "
"from command \"%s\" in %s failed: %s",
argv[i], cmd, etext, expand_string_message);
- if (addr != NULL)
+ if (addr)
{
addr->transport_return = expand_failed;
addr->message = msg;