void
options_transports(void)
{
-struct transport_info * ti;
uschar buf[64];
options_from_list(optionlist_transports, nelem(optionlist_transports), US"TRANSPORTS", NULL);
-for (ti = transports_available; ti->driver_name[0]; ti++)
+for (transport_info * ti = transports_available; ti->driver_name[0]; ti++)
{
spf(buf, sizeof(buf), US"_DRIVER_TRANSPORT_%T", ti->driver_name);
builtin_macro_create(buf);
void
transport_init(void)
{
-transport_instance *t;
-
readconf_driver_init(US"transport",
(driver_instance **)(&transports), /* chain anchor */
(driver_info *)transports_available, /* available drivers */
/* Now scan the configured transports and check inconsistencies. A shadow
transport is permitted only for local transports. */
-for (t = transports; t; t = t->next)
+for (transport_instance * t = transports; t; t = t->next)
{
if (!t->info->local && t->shadow)
log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
static BOOL
transport_write_block_fd(transport_ctx * tctx, uschar *block, int len, BOOL more)
{
-int i, rc, save_errno;
+int rc, save_errno;
int local_timeout = transport_write_timeout;
int fd = tctx->u.fd;
/* This loop is for handling incomplete writes and other retries. In most
normal cases, it is only ever executed once. */
-for (i = 0; i < 100; i++)
+for (int i = 0; i < 100; i++)
{
DEBUG(D_transport)
debug_printf("writing data block fd=%d size=%d timeout=%d%s\n",
if (transport_write_timeout <= 0) /* No timeout wanted */
{
rc =
-#ifdef SUPPORT_TLS
- tls_out.active == fd ? tls_write(FALSE, block, len, more) :
+#ifndef DISABLE_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)
else
{
- alarm(local_timeout);
+ ALARM(local_timeout);
rc =
-#ifdef SUPPORT_TLS
- tls_out.active == fd ? tls_write(FALSE, block, len, more) :
+#ifndef DISABLE_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)
write(fd, block, len);
save_errno = errno;
- local_timeout = alarm(0);
+ local_timeout = ALARM_CLR(0);
if (sigalrm_seen)
{
errno = ETIMEDOUT;
transport_write_string(int fd, const char *format, ...)
{
transport_ctx tctx = {{0}};
+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(big_buffer, big_buffer_size, 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;
-return transport_write_block(&tctx, big_buffer, Ustrlen(big_buffer), FALSE);
+return transport_write_block(&tctx, gs.s, gs.ptr, FALSE);
}
{
uschar *start = chunk;
uschar *end = chunk + len;
-uschar *ptr;
int mlen = DELIVER_OUT_BUFFER_SIZE - nl_escape_length - 2;
/* The assumption is made that the check string will never stretch over move
for possible escaping. The code for the non-NL route should be as fast as
possible. */
-for (ptr = start; ptr < end; ptr++)
+for (uschar * ptr = start; ptr < end; ptr++)
{
int ch, len;
if ( *ptr == '\r' && ptr[1] == '\n'
&& !(tctx->options & topt_use_crlf)
- && spool_file_wireformat
+ && f.spool_file_wireformat
)
ptr++;
/* Insert CR before NL if required */
- if (tctx->options & topt_use_crlf && !spool_file_wireformat)
+ if (tctx->options & topt_use_crlf && !f.spool_file_wireformat)
*chunk_ptr++ = '\r';
*chunk_ptr++ = '\n';
transport_newlines++;
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;
transport_headers_send(transport_ctx * tctx,
BOOL (*sendfn)(transport_ctx * tctx, uschar * s, int len))
{
-header_line *h;
const uschar *list;
transport_instance * tblock = tctx ? tctx->tblock : NULL;
address_item * addr = tctx ? tctx->addr : NULL;
separately and squash any empty ones.
Then check addr->prop.remove_headers too, provided that addr is not NULL. */
-for (h = header_list; h; h = h->next) if (h->type != htype_old)
+for (header_line * h = header_list; h; h = h->next) if (h->type != htype_old)
{
- int i;
BOOL include_header = TRUE;
list = tblock ? tblock->remove_headers : NULL;
- for (i = 0; i < 2; i++) /* For remove_headers && addr->prop.remove_headers */
+ for (int i = 0; i < 2; i++) /* For remove_headers && addr->prop.remove_headers */
{
if (list)
{
int len;
if (i == 0)
- if (!(s = expand_string(s)) && !expand_string_forcedfail)
+ if (!(s = expand_string(s)) && !f.expand_string_forcedfail)
{
errno = ERRNO_CHHEADER_FAIL;
return FALSE;
{
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,
if (addr)
{
- int i;
header_line *hprev = addr->prop.extra_headers;
- header_line *hnext;
- for (i = 0; i < 2; i++)
+ header_line *hnext, * h;
+ for (int i = 0; i < 2; i++)
for (h = hprev, hprev = NULL; h; h = hnext)
{
hnext = h->next;
}
}
}
- else if (!expand_string_forcedfail)
+ else if (!f.expand_string_forcedfail)
{ errno = ERRNO_CHHEADER_FAIL; return FALSE; }
}
if (!(tctx->options & topt_no_headers))
{
- BOOL save_wireformat = spool_file_wireformat;
- spool_file_wireformat = FALSE;
+ BOOL save_wireformat = f.spool_file_wireformat;
+ f.spool_file_wireformat = FALSE;
/* Add return-path: if requested. */
if (tctx->options & topt_add_envelope_to)
{
BOOL first = TRUE;
- address_item *p;
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;
anchors for lists of addresses already handled; they have to be defined at
this level because write_env_to() calls itself recursively. */
- for (p = tctx->addr; p; p = p->next)
- if (!write_env_to(p, &plist, &dlist, &first, tctx)) goto bad;
+ for (address_item * p = tctx->addr; p; p = p->next)
+ if (!write_env_to(p, &plist, &dlist, &first, tctx))
+ goto bad;
/* Add a final newline and reset the store used for tracking duplicates */
if (!transport_headers_send(tctx, &write_chunk))
{
bad:
- spool_file_wireformat = save_wireformat;
+ f.spool_file_wireformat = save_wireformat;
return FALSE;
}
- spool_file_wireformat = save_wireformat;
+ f.spool_file_wireformat = save_wireformat;
}
/* When doing RFC3030 CHUNKING output, work out how much data would be in a
if (size_limit > 0 && fsize > size_limit)
fsize = size_limit;
size = hsize + fsize;
- if (tctx->options & topt_use_crlf && !spool_file_wireformat)
+ if (tctx->options & topt_use_crlf && !f.spool_file_wireformat)
size += body_linecount; /* account for CRLF-expansion */
/* With topt_use_bdat we never do dot-stuffing; no need to
dkim signing, when we had CHUNKING input. */
#ifdef OS_SENDFILE
-if ( spool_file_wireformat
+if ( f.spool_file_wireformat
&& !(tctx->options & (topt_no_body | topt_end_dot))
&& !nl_check_length
- && tls_out.active != tctx->u.fd
+ && tls_out.active.sock != tctx->u.fd
)
{
ssize_t copied = 0;
DEBUG(D_transport)
if (!(tctx->options & topt_no_body))
debug_printf("cannot use sendfile for body: %s\n",
- !spool_file_wireformat ? "spoolfile not wireformat"
+ !f.spool_file_wireformat ? "spoolfile not wireformat"
: tctx->options & topt_end_dot ? "terminating dot wanted"
: nl_check_length ? "dot- or From-stuffing wanted"
: "TLS output wanted");
if (!(tctx->options & topt_no_body))
{
- int size = size_limit;
+ unsigned long size = size_limit > 0 ? size_limit : ULONG_MAX;
nl_check_length = abs(nl_check_length);
nl_partial_match = 0;
if (lseek(deliver_datafile, SPOOL_DATA_START_OFFSET, SEEK_SET) < 0)
return FALSE;
- while ( (len = MAX(DELIVER_IN_BUFFER_SIZE, size)) > 0
+ while ( (len = MIN(DELIVER_IN_BUFFER_SIZE, size)) > 0
&& (len = read(deliver_datafile, deliver_in_buffer, len)) > 0)
{
if (!write_chunk(tctx, deliver_in_buffer, len))
if (len != 0) return FALSE;
}
-/* Finished with the check string */
+/* Finished with the check string, and spool-format consideration */
nl_check_length = nl_escape_length = 0;
+f.spool_file_wireformat = FALSE;
/* If requested, add a terminating "." line (SMTP output). */
transport_write_message(transport_ctx * tctx, int size_limit)
{
BOOL last_filter_was_NL = TRUE;
-BOOL save_spool_file_wireformat = spool_file_wireformat;
+BOOL save_spool_file_wireformat = f.spool_file_wireformat;
int rc, len, yield, fd_read, fd_write, save_errno;
int pfd[2] = {-1, -1};
pid_t filter_pid, write_pid;
-transport_filter_timed_out = FALSE;
+f.transport_filter_timed_out = FALSE;
/* If there is no filter command set up, call the internal function that does
the actual work, passing it the incoming fd, and return its result. */
!= sizeof(int)
)
rc = FALSE; /* compiler quietening */
- _exit(0);
+ exim_underbar_exit(0);
}
save_errno = errno;
/* When testing, let the subprocess get going */
-if (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);
variable is TRUE). The output should always be unix-format as we converted
any wireformat source on writing input to the filter. */
-spool_file_wireformat = FALSE;
+f.spool_file_wireformat = FALSE;
chunk_ptr = deliver_out_buffer;
for (;;)
{
sigalrm_seen = FALSE;
- alarm(transport_filter_timeout);
+ ALARM(transport_filter_timeout);
len = read(fd_read, deliver_in_buffer, DELIVER_IN_BUFFER_SIZE);
- alarm(0);
+ ALARM_CLR(0);
if (sigalrm_seen)
{
errno = ETIMEDOUT;
- transport_filter_timed_out = TRUE;
+ f.transport_filter_timed_out = TRUE;
goto TIDY_UP;
}
sure. Also apply a paranoia timeout. */
TIDY_UP:
-spool_file_wireformat = save_spool_file_wireformat;
+f.spool_file_wireformat = save_spool_file_wireformat;
save_errno = errno;
(void)close(fd_read);
if (yield)
{
nl_check_length = nl_escape_length = 0;
+ f.spool_file_wireformat = FALSE;
if ( tctx->options & topt_end_dot
&& ( last_filter_was_NL
? !write_chunk(tctx, US".\n", 2)
transport_update_waiting(host_item *hostlist, uschar *tpname)
{
const uschar *prevname = US"";
-host_item *host;
open_db dbblock;
open_db *dbm_file;
/* 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
that the message id is in each host record. */
-for (host = hostlist; host; host = host->next)
+for (host_item * host = hostlist; host; host = host->next)
{
BOOL already = FALSE;
dbdata_wait *host_record;
- uschar *s;
- int i, host_length;
+ int host_length;
uschar buffer[256];
/* Skip if this is the same host as we just processed; otherwise remember
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;
}
/* Search the record to see if the current message is already in it. */
- for (s = host_record->text; s < host_record->text + host_length;
+ for (uschar * s = host_record->text; s < host_record->text + host_length;
s += MESSAGE_ID_LENGTH)
if (Ustrncmp(s, message_id, MESSAGE_ID_LENGTH) == 0)
{ already = TRUE; break; }
/* If we haven't found this message in the main record, search any
continuation records that exist. */
- for (i = host_record->sequence - 1; i >= 0 && !already; i--)
+ for (int i = host_record->sequence - 1; i >= 0 && !already; i--)
{
dbdata_wait *cont;
sprintf(CS buffer, "%.200s:%d", host->name, i);
if ((cont = dbfn_read(dbm_file, buffer)))
{
int clen = cont->count * MESSAGE_ID_LENGTH;
- for (s = cont->text; s < cont->text + clen; s += MESSAGE_ID_LENGTH)
+ for (uschar * s = cont->text; s < cont->text + clen; s += MESSAGE_ID_LENGTH)
if (Ustrncmp(s, message_id, MESSAGE_ID_LENGTH) == 0)
{ already = TRUE; break; }
}
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;
while (host_length <= 0)
{
- int i;
dbdata_wait * newr = NULL;
uschar buffer[256];
/* Search for a continuation */
- for (i = host_record->sequence - 1; i >= 0 && !newr; i--)
+ for (int i = host_record->sequence - 1; i >= 0 && !newr; i--)
{
sprintf(CS buffer, "%.200s:%d", hostname, i);
newr = dbfn_read(dbm_file, buffer);
}
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
argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, &i, FALSE, 0);
-if (smtp_authenticated) argv[i++] = US"-MCA";
+if (f.smtp_authenticated) argv[i++] = US"-MCA";
if (smtp_peer_options & OPTION_CHUNKING) argv[i++] = US"-MCK";
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 >= 0 || continue_proxy_cipher)
+ if (tls_out.active.sock >= 0 || continue_proxy_cipher)
{
argv[i++] = US"-MCt";
argv[i++] = sending_ip_address;
argv[i++] = string_sprintf("%d", sending_port);
- argv[i++] = tls_out.active >= 0 ? tls_out.cipher : continue_proxy_cipher;
+ argv[i++] = tls_out.active.sock >= 0 ? tls_out.cipher : continue_proxy_cipher;
}
else
argv[i++] = US"-MCT";
DEBUG(D_transport) debug_printf("transport_pass_socket succeeded (final-pid %d)\n", pid);
_exit(EXIT_SUCCESS);
}
- if (running_in_test_harness) sleep(1);
+ testharness_pause_ms(1000);
transport_do_pass_socket(transport_name, hostname, hostaddress,
id, socket_fd);
BOOL expand_arguments, int expand_failed, address_item *addr,
uschar *etext, uschar **errptr)
{
-address_item *ad;
const uschar **argv;
uschar *s, *ss;
int address_count = 0;
int argcount = 0;
-int i, max_args;
+int max_args;
/* Get store in which to build an argument list. Count the number of addresses
supplied, and allow for that many arguments, plus an additional 60, which
should be enough for anybody. Multiple addresses happen only when the local
delivery batch option is set. */
-for (ad = addr; ad != NULL; ad = ad->next) address_count++;
+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 (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)
addr->parent != NULL &&
Ustrcmp(addr->parent->address, "system-filter") == 0;
- for (i = 0; argv[i] != US 0; i++)
+ for (int i = 0; argv[i] != US 0; i++)
{
/* Handle special fudge for passing an address list */
memmove(argv + i + 1 + additional, argv + i + 1,
(argcount - i)*sizeof(uschar *));
- for (ad = addr; ad != NULL; ad = ad->next) {
- argv[i++] = ad->address;
- argcount++;
- }
+ for (address_item * ad = addr; ad; ad = ad->next)
+ {
+ argv[i++] = ad->address;
+ argcount++;
+ }
/* Subtract one since we replace $pipe_addresses */
argcount--;
(Ustrcmp(argv[i], "$address_pipe") == 0 ||
Ustrcmp(argv[i], "${address_pipe}") == 0))
{
- int address_pipe_i;
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;
/* Now we fill in the slots we just moved argv out of
* [argv 0][argv 1][argv 2=pipeargv[0]][argv 3=pipeargv[1]][old argv 3][0]
*/
- for (address_pipe_i = 0;
+ for (int address_pipe_i = 0;
address_pipe_argv[address_pipe_i] != US 0;
address_pipe_i++)
{
else
{
const uschar *expanded_arg;
- enable_dollar_recipients = allow_dollar_recipients;
+ f.enable_dollar_recipients = allow_dollar_recipients;
expanded_arg = expand_cstring(argv[i]);
- enable_dollar_recipients = FALSE;
+ 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;
DEBUG(D_transport)
{
debug_printf("direct command after expansion:\n");
- for (i = 0; argv[i] != US 0; i++)
+ for (int i = 0; argv[i] != US 0; i++)
debug_printf(" argv[%d] = %s\n", i, string_printing(argv[i]));
}
}