(void *)offsetof(transport_instance, uid) }
};
-int optionlist_transports_size =
- sizeof(optionlist_transports)/sizeof(optionlist);
+int optionlist_transports_size = nelem(optionlist_transports);
+void
+readconf_options_transports(void)
+{
+struct transport_info * ti;
+
+readconf_options_from_list(optionlist_transports, nelem(optionlist_transports), US"TRANSPORTS", NULL);
+
+for (ti = transports_available; ti->driver_name[0]; ti++)
+ {
+ macro_create(string_sprintf("_DRIVER_TRANSPORT_%T", ti->driver_name), US"y", FALSE, TRUE);
+ readconf_options_from_list(ti->options, (unsigned)*ti->options_count, US"TRANSPORT", ti->driver_name);
+ }
+}
+
/*************************************************
* Initialize transport list *
*************************************************/
if (tctx && tctx->options & topt_use_bdat && tctx->chunk_cb)
{
- if ( tctx->chunk_cb(fd, tctx, (unsigned)len, 0) != OK
+ if ( tctx->chunk_cb(tctx, (unsigned)len, 0) != OK
|| !transport_write_block(fd, deliver_out_buffer, len)
- || tctx->chunk_cb(fd, tctx, 0, tc_reap_prev) != OK
+ || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
)
return FALSE;
}
-/* Add/remove/rewwrite headers, and send them plus the empty-line sparator.
+/* Add/remove/rewrite headers, and send them plus the empty-line separator.
Globals:
header_list
/* Pick up from all the addresses. The plist and dlist variables are
anchors for lists of addresses already handled; they have to be defined at
- this level becuase write_env_to() calls itself recursively. */
+ 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, fd, tctx))
if (size > DELIVER_OUT_BUFFER_SIZE && hsize > 0)
{
DEBUG(D_transport)
- debug_printf("sending small initial BDAT; hssize=%d\n", hsize);
- if ( tctx->chunk_cb(fd, tctx, hsize, 0) != OK
+ debug_printf("sending small initial BDAT; hsize=%d\n", hsize);
+ if ( tctx->chunk_cb(tctx, hsize, 0) != OK
|| !transport_write_block(fd, deliver_out_buffer, hsize)
- || tctx->chunk_cb(fd, tctx, 0, tc_reap_prev) != OK
+ || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
)
return FALSE;
chunk_ptr = deliver_out_buffer;
size -= hsize;
}
- /* Emit a LAST datachunk command. */
+ /* Emit a LAST datachunk command, and unmark the context for further
+ BDAT commands. */
- if (tctx->chunk_cb(fd, tctx, size, tc_chunk_last) != OK)
+ if (tctx->chunk_cb(tctx, size, tc_chunk_last) != OK)
return FALSE;
-
tctx->options &= ~topt_use_bdat;
}
BOOL
dkim_transport_write_message(int out_fd, transport_ctx * tctx,
- struct ob_dkim * dkim)
+ struct ob_dkim * dkim, const uschar ** err)
{
int dkim_fd;
int save_errno = 0;
BOOL rc;
uschar * dkim_spool_name;
-int sread = 0;
-int wwritten = 0;
-uschar *dkim_signature = NULL;
-int siglen = 0;
+uschar * dkim_signature = NULL;
+int sread = 0, wwritten = 0, siglen = 0, options;
off_t k_file_size;
-int options;
+const uschar * errstr;
/* If we can't sign, just call the original function. */
/* Can't create spool file. Ugh. */
rc = FALSE;
save_errno = errno;
+ *err = string_sprintf("dkim spoolfile create: %s", strerror(errno));
goto CLEANUP;
}
/* Rewind file and feed it to the goats^W DKIM lib */
dkim->dot_stuffed = !!(options & topt_end_dot);
lseek(dkim_fd, 0, SEEK_SET);
-if ((dkim_signature = dkim_exim_sign(dkim_fd, dkim)))
+if ((dkim_signature = dkim_exim_sign(dkim_fd, dkim, &errstr)))
siglen = Ustrlen(dkim_signature);
else if (dkim->dkim_strict)
{
save_errno = EACCES;
log_write(0, LOG_MAIN, "DKIM: message could not be signed,"
" and dkim_strict is set. Deferring message delivery.");
+ *err = errstr;
rc = FALSE;
goto CLEANUP;
}
if (siglen + k_file_size > DELIVER_OUT_BUFFER_SIZE && siglen > 0)
{
- if ( tctx->chunk_cb(out_fd, tctx, siglen, 0) != OK
+ if ( tctx->chunk_cb(tctx, siglen, 0) != OK
|| !transport_write_block(out_fd, dkim_signature, siglen)
- || tctx->chunk_cb(out_fd, tctx, 0, tc_reap_prev) != OK
+ || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
)
goto err;
siglen = 0;
}
- if (tctx->chunk_cb(out_fd, tctx, siglen + k_file_size, tc_chunk_last) != OK)
+ /* Send the BDAT command for the entire message, as a single LAST-marked
+ chunk. */
+
+ if (tctx->chunk_cb(tctx, siglen + k_file_size, tc_chunk_last) != OK)
goto err;
}
to write to the filter, and in this process just suck from the filter and write
down the given fd. At the end, tidy up the pipes and the processes.
-XXX
Arguments: as for internal_transport_write_message() above
Returns: TRUE on success; FALSE (with errno) for any failure
yield = FALSE;
write_pid = (pid_t)(-1);
-(void)fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
-filter_pid = child_open(USS transport_filter_argv, NULL, 077,
- &fd_write, &fd_read, FALSE);
-(void)fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) & ~FD_CLOEXEC);
+ {
+ int bits = fcntl(fd, F_GETFD);
+ (void)fcntl(fd, F_SETFD, bits | FD_CLOEXEC);
+ filter_pid = child_open(USS transport_filter_argv, NULL, 077,
+ &fd_write, &fd_read, FALSE);
+ (void)fcntl(fd, F_SETFD, bits & ~FD_CLOEXEC);
+ }
if (filter_pid < 0) goto TIDY_UP; /* errno set */
DEBUG(D_transport)
void
transport_update_waiting(host_item *hostlist, uschar *tpname)
{
-uschar buffer[256];
const uschar *prevname = US"";
host_item *host;
open_db dbblock;
/* Open the database for this transport */
-sprintf(CS buffer, "wait-%.200s", tpname);
-dbm_file = dbfn_open(buffer, O_RDWR, &dbblock, TRUE);
-if (dbm_file == NULL) return;
+if (!(dbm_file = dbfn_open(string_sprintf("wait-%.200s", tpname),
+ O_RDWR, &dbblock, 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!= NULL; host = host->next)
+for (host = hostlist; host; host = host->next)
{
BOOL already = FALSE;
dbdata_wait *host_record;
uschar *s;
int i, host_length;
+ uschar buffer[256];
/* Skip if this is the same host as we just processed; otherwise remember
the name for next time. */
/* Look up the host record; if there isn't one, make an empty one. */
- host_record = dbfn_read(dbm_file, host->name);
- if (host_record == NULL)
+ if (!(host_record = dbfn_read(dbm_file, host->name)))
{
host_record = store_get(sizeof(dbdata_wait) + MESSAGE_ID_LENGTH);
host_record->count = host_record->sequence = 0;
for (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. */
{
dbdata_wait *cont;
sprintf(CS buffer, "%.200s:%d", host->name, i);
- cont = dbfn_read(dbm_file, buffer);
- if (cont != NULL)
+ 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)
- {
if (Ustrncmp(s, message_id, MESSAGE_ID_LENGTH) == 0)
{ already = TRUE; break; }
- }
}
}
int host_length;
open_db dbblock;
open_db *dbm_file;
-uschar buffer[256];
int i;
struct stat statbuf;
/* Open the waiting information database. */
-sprintf(CS buffer, "wait-%.200s", transport_name);
-dbm_file = dbfn_open(buffer, O_RDWR, &dbblock, TRUE);
-if (dbm_file == NULL) return FALSE;
+if (!(dbm_file = dbfn_open(string_sprintf("wait-%.200s", transport_name),
+ O_RDWR, &dbblock, TRUE)))
+ return FALSE;
/* See if there is a record for this host; if not, there's nothing to do. */
}
}
-/* Jeremy: check for a continuation record, this code I do not know how to
-test but the code should work */
+ /* Check for a continuation record. */
while (host_length <= 0)
{
int i;
dbdata_wait * newr = NULL;
+ uschar buffer[256];
/* Search for a continuation */
* Deliver waiting message down same socket *
*************************************************/
+/* Just the regain-root-privilege exec portion */
+void
+transport_do_pass_socket(const uschar *transport_name, const uschar *hostname,
+ const uschar *hostaddress, uschar *id, int socket_fd)
+{
+int i = 20;
+const uschar **argv;
+
+/* Set up the calling arguments; use the standard function for the basics,
+but we have a number of extras that may be added. */
+
+argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, &i, FALSE, 0);
+
+if (smtp_authenticated) argv[i++] = US"-MCA";
+if (smtp_peer_options & PEER_OFFERED_CHUNKING) argv[i++] = US"-MCK";
+if (smtp_peer_options & PEER_OFFERED_DSN) argv[i++] = US"-MCD";
+if (smtp_peer_options & PEER_OFFERED_PIPE) argv[i++] = US"-MCP";
+if (smtp_peer_options & PEER_OFFERED_SIZE) argv[i++] = US"-MCS";
+#ifdef SUPPORT_TLS
+if (smtp_peer_options & PEER_OFFERED_TLS)
+ if (tls_out.active >= 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;
+ }
+ else
+ argv[i++] = US"-MCT";
+#endif
+
+if (queue_run_pid != (pid_t)0)
+ {
+ argv[i++] = US"-MCQ";
+ argv[i++] = string_sprintf("%d", queue_run_pid);
+ argv[i++] = string_sprintf("%d", queue_run_pipe);
+ }
+
+argv[i++] = US"-MC";
+argv[i++] = US transport_name;
+argv[i++] = US hostname;
+argv[i++] = US hostaddress;
+argv[i++] = string_sprintf("%d", continue_sequence + 1);
+argv[i++] = id;
+argv[i++] = NULL;
+
+/* Arrange for the channel to be on stdin. */
+
+if (socket_fd != 0)
+ {
+ (void)dup2(socket_fd, 0);
+ (void)close(socket_fd);
+ }
+
+DEBUG(D_exec) debug_print_argv(argv);
+exim_nullstd(); /* Ensure std{out,err} exist */
+execv(CS argv[0], (char *const *)argv);
+
+DEBUG(D_any) debug_printf("execv failed: %s\n", strerror(errno));
+_exit(errno); /* Note: must be _exit(), NOT exit() */
+}
+
+
+
/* Fork a new exim process to deliver the message, and do a re-exec, both to
get a clean delivery process, and to regain root privilege in cases where it
has been given away.
if ((pid = fork()) == 0)
{
- int i = 17;
- const uschar **argv;
-
/* Disconnect entirely from the parent process. If we are running in the
test harness, wait for a bit to allow the previous process time to finish,
write the log, etc., so that the output is always in the same order for
if ((pid = fork()) != 0) _exit(EXIT_SUCCESS);
if (running_in_test_harness) sleep(1);
- /* Set up the calling arguments; use the standard function for the basics,
- but we have a number of extras that may be added. */
-
- argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, &i, FALSE, 0);
-
- if (smtp_authenticated) argv[i++] = US"-MCA";
-
- if (smtp_peer_options & PEER_OFFERED_CHUNKING) argv[i++] = US"-MCK";
- if (smtp_peer_options & PEER_OFFERED_DSN) argv[i++] = US"-MCD";
- if (smtp_peer_options & PEER_OFFERED_PIPE) argv[i++] = US"-MCP";
- if (smtp_peer_options & PEER_OFFERED_SIZE) argv[i++] = US"-MCS";
-#ifdef SUPPORT_TLS
- if (smtp_peer_options & PEER_OFFERED_TLS) argv[i++] = US"-MCT";
-#endif
-
- if (queue_run_pid != (pid_t)0)
- {
- argv[i++] = US"-MCQ";
- argv[i++] = string_sprintf("%d", queue_run_pid);
- argv[i++] = string_sprintf("%d", queue_run_pipe);
- }
-
- argv[i++] = US"-MC";
- argv[i++] = US transport_name;
- argv[i++] = US hostname;
- argv[i++] = US hostaddress;
- argv[i++] = string_sprintf("%d", continue_sequence + 1);
- argv[i++] = id;
- argv[i++] = NULL;
-
- /* Arrange for the channel to be on stdin. */
-
- if (socket_fd != 0)
- {
- (void)dup2(socket_fd, 0);
- (void)close(socket_fd);
- }
-
- DEBUG(D_exec) debug_print_argv(argv);
- exim_nullstd(); /* Ensure std{out,err} exist */
- execv(CS argv[0], (char *const *)argv);
-
- DEBUG(D_any) debug_printf("execv failed: %s\n", strerror(errno));
- _exit(errno); /* Note: must be _exit(), NOT exit() */
+ transport_do_pass_socket(transport_name, hostname, hostaddress,
+ id, socket_fd);
}
/* If the process creation succeeded, wait for the first-level child, which
*/
if (address_pipe_argcount > 1)
memmove(
- /* current position + additonal args */
+ /* current position + additional args */
argv + i + address_pipe_argcount,
/* current position + 1 (for the (uschar *)0 at the end) */
argv + i + 1,