-/* $Cambridge: exim/src/src/transport.c,v 1.11 2005/06/22 15:44:38 ph10 Exp $ */
+/* $Cambridge: exim/src/src/transport.c,v 1.20 2007/09/28 12:21:57 tom Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2005 */
+/* Copyright (c) University of Cambridge 1995 - 2007 */
/* See the file NOTICE for conditions of use and distribution. */
/* General functions concerned with transportation, and generic options for all
#include "exim.h"
+#ifdef HAVE_LINUX_SENDFILE
+#include <sys/sendfile.h>
+#endif
/* Structure for keeping list of addresses that have been added to
Envelope-To:, in order to avoid duplication. */
int sread = 0;
int wwritten = 0;
uschar *dk_signature = NULL;
+ off_t size = 0;
- snprintf(CS dk_spool_name, 256, "%s/input/%s/%s-K",
- spool_directory, message_subdir, message_id);
- dk_fd = Uopen(dk_spool_name, O_RDWR|O_CREAT|O_EXCL, SPOOL_MODE);
+ (void)string_format(dk_spool_name, 256, "%s/input/%s/%s-%d-K",
+ spool_directory, message_subdir, message_id, (int)getpid());
+ dk_fd = Uopen(dk_spool_name, O_RDWR|O_CREAT|O_TRUNC, SPOOL_MODE);
if (dk_fd < 0)
{
/* Can't create spool file. Ugh. */
}
}
- /* Rewind file and send it down the original fd. */
+ /* Fetch file positition (the size) */
+ size = lseek(dk_fd,0,SEEK_CUR);
+
+ /* Rewind file */
lseek(dk_fd, 0, SEEK_SET);
+#ifdef HAVE_LINUX_SENDFILE
+ /* We can use sendfile() to shove the file contents
+ to the socket. However only if we don't use TLS,
+ in which case theres another layer of indirection
+ before the data finally hits the socket. */
+ if (tls_active != fd)
+ {
+ ssize_t copied = 0;
+ off_t offset = 0;
+ while((copied >= 0) && (offset<size))
+ {
+ copied = sendfile(fd, dk_fd, &offset, (size - offset));
+ }
+ if (copied < 0)
+ {
+ save_errno = errno;
+ rc = FALSE;
+ }
+ goto CLEANUP;
+ }
+#endif
+
+ /* Send file down the original fd */
while((sread = read(dk_fd,sbuf,2048)) > 0)
{
char *p = sbuf;
goto CLEANUP;
}
-
CLEANUP:
/* unlink -K file */
- close(dk_fd);
+ (void)close(dk_fd);
Uunlink(dk_spool_name);
errno = save_errno;
return rc;
#endif
+
+#ifdef EXPERIMENTAL_DKIM
+
+/**********************************************************************************
+* External interface to write the message, while signing it with DKIM *
+**********************************************************************************/
+
+/* This function is a wrapper around transport_write_message(). It is only called
+ from the smtp transport if
+ (1) DKIM support is compiled in.
+ (2) The dkim_private_key and dkim_domain option on the smtp transport is set.
+ The function sets up a replacement fd into a -K file, then calls the normal
+ function. This way, the exact bits that exim would have put "on the wire" will
+ end up in the file (except for TLS encapsulation, which is the very
+ very last thing). When we are done signing the file, send the
+ signed message down the original fd (or TLS fd).
+
+Arguments: as for internal_transport_write_message() above, with additional
+ arguments:
+ uschar *dkim_private_key The private key to use (filename or plain data)
+ uschar *dkim_domain The domain to use
+ uschar *dkim_selector The selector to use.
+ uschar *dkim_canon The canonalization scheme to use, "simple" or "relaxed"
+ uschar *dkim_strict What to do if signing fails: 1/true => throw error
+ 0/false => send anyway
+
+Returns: TRUE on success; FALSE (with errno) for any failure
+*/
+
+BOOL
+dkim_transport_write_message(address_item *addr, int fd, int options,
+ int size_limit, uschar *add_headers, uschar *remove_headers,
+ uschar *check_string, uschar *escape_string, rewrite_rule *rewrite_rules,
+ int rewrite_existflags, uschar *dkim_private_key, uschar *dkim_domain,
+ uschar *dkim_selector, uschar *dkim_canon, uschar *dkim_strict, uschar *dkim_sign_headers)
+{
+ int dkim_fd;
+ int save_errno = 0;
+ BOOL rc;
+ uschar dkim_spool_name[256];
+ char sbuf[2048];
+ int sread = 0;
+ int wwritten = 0;
+ uschar *dkim_signature = NULL;
+ off_t size = 0;
+
+ (void)string_format(dkim_spool_name, 256, "%s/input/%s/%s-%d-K",
+ spool_directory, message_subdir, message_id, (int)getpid());
+ dkim_fd = Uopen(dkim_spool_name, O_RDWR|O_CREAT|O_TRUNC, SPOOL_MODE);
+ if (dkim_fd < 0)
+ {
+ /* Can't create spool file. Ugh. */
+ rc = FALSE;
+ save_errno = errno;
+ goto CLEANUP;
+ }
+
+ /* Call original function */
+ rc = transport_write_message(addr, dkim_fd, options,
+ size_limit, add_headers, remove_headers,
+ check_string, escape_string, rewrite_rules,
+ rewrite_existflags);
+
+ /* Save error state. We must clean up before returning. */
+ if (!rc)
+ {
+ save_errno = errno;
+ goto CLEANUP;
+ }
+
+ /* Rewind file and feed it to the goats^W DKIM lib */
+ lseek(dkim_fd, 0, SEEK_SET);
+ dkim_signature = dkim_exim_sign(dkim_fd,
+ dkim_private_key,
+ dkim_domain,
+ dkim_selector,
+ dkim_canon,
+ dkim_sign_headers);
+
+ if (dkim_signature != NULL)
+ {
+ /* Send the signature first */
+ int siglen = Ustrlen(dkim_signature);
+ while(siglen > 0)
+ {
+ #ifdef SUPPORT_TLS
+ if (tls_active == fd) wwritten = tls_write(dkim_signature, siglen); else
+ #endif
+ wwritten = write(fd,dkim_signature,siglen);
+ if (wwritten == -1)
+ {
+ /* error, bail out */
+ save_errno = errno;
+ rc = FALSE;
+ goto CLEANUP;
+ }
+ siglen -= wwritten;
+ dkim_signature += wwritten;
+ }
+ }
+ else if (dkim_strict != NULL)
+ {
+ uschar *dkim_strict_result = expand_string(dkim_strict);
+ if (dkim_strict_result != NULL)
+ {
+ if ( (strcmpic(dkim_strict,US"1") == 0) ||
+ (strcmpic(dkim_strict,US"true") == 0) )
+ {
+ save_errno = errno;
+ rc = FALSE;
+ goto CLEANUP;
+ }
+ }
+ }
+
+ /* Fetch file positition (the size) */
+ size = lseek(dkim_fd,0,SEEK_CUR);
+
+ /* Rewind file */
+ lseek(dkim_fd, 0, SEEK_SET);
+
+#ifdef HAVE_LINUX_SENDFILE
+ /* We can use sendfile() to shove the file contents
+ to the socket. However only if we don't use TLS,
+ in which case theres another layer of indirection
+ before the data finally hits the socket. */
+ if (tls_active != fd)
+ {
+ ssize_t copied = 0;
+ off_t offset = 0;
+ while((copied >= 0) && (offset<size))
+ {
+ copied = sendfile(fd, dkim_fd, &offset, (size - offset));
+ }
+ if (copied < 0)
+ {
+ save_errno = errno;
+ rc = FALSE;
+ }
+ goto CLEANUP;
+ }
+#endif
+
+ /* Send file down the original fd */
+ while((sread = read(dkim_fd,sbuf,2048)) > 0)
+ {
+ char *p = sbuf;
+ /* write the chunk */
+ DKIM_WRITE:
+ #ifdef SUPPORT_TLS
+ if (tls_active == fd) wwritten = tls_write(US p, sread); else
+ #endif
+ wwritten = write(fd,p,sread);
+ if (wwritten == -1)
+ {
+ /* error, bail out */
+ save_errno = errno;
+ rc = FALSE;
+ goto CLEANUP;
+ }
+ if (wwritten < sread)
+ {
+ /* short write, try again */
+ p += wwritten;
+ sread -= wwritten;
+ goto DKIM_WRITE;
+ }
+ }
+
+ if (sread == -1)
+ {
+ save_errno = errno;
+ rc = FALSE;
+ goto CLEANUP;
+ }
+
+ CLEANUP:
+ /* unlink -K file */
+ (void)close(dkim_fd);
+ Uunlink(dkim_spool_name);
+ errno = save_errno;
+ return rc;
+}
+#endif
+
+
+
/*************************************************
* External interface to write the message *
*************************************************/
if ((write_pid = fork()) == 0)
{
BOOL rc;
- close(fd_read);
- close(pfd[pipe_read]);
+ (void)close(fd_read);
+ (void)close(pfd[pipe_read]);
nl_check_length = nl_escape_length = 0;
rc = internal_transport_write_message(addr, fd_write,
(options & ~(topt_use_crlf | topt_end_dot)),
size_limit, add_headers, remove_headers, NULL, NULL,
rewrite_rules, rewrite_existflags);
save_errno = errno;
- write(pfd[pipe_write], (void *)&rc, sizeof(BOOL));
- write(pfd[pipe_write], (void *)&save_errno, sizeof(int));
- write(pfd[pipe_write], (void *)&(addr->more_errno), sizeof(int));
+ (void)write(pfd[pipe_write], (void *)&rc, sizeof(BOOL));
+ (void)write(pfd[pipe_write], (void *)&save_errno, sizeof(int));
+ (void)write(pfd[pipe_write], (void *)&(addr->more_errno), sizeof(int));
_exit(0);
}
save_errno = errno;
/* Parent process: close our copy of the writing subprocess' pipes. */
-close(pfd[pipe_write]);
-close(fd_write);
+(void)close(pfd[pipe_write]);
+(void)close(fd_write);
fd_write = -1;
/* Writing process creation failed */
TIDY_UP:
save_errno = errno;
-close(fd_read);
-if (fd_write > 0) close(fd_write);
+(void)close(fd_read);
+if (fd_write > 0) (void)close(fd_write);
if (!yield)
{
if (rc == 0)
{
BOOL ok;
- read(pfd[pipe_read], (void *)&ok, sizeof(BOOL));
+ (void)read(pfd[pipe_read], (void *)&ok, sizeof(BOOL));
if (!ok)
{
- read(pfd[pipe_read], (void *)&save_errno, sizeof(int));
- read(pfd[pipe_read], (void *)&(addr->more_errno), sizeof(int));
+ (void)read(pfd[pipe_read], (void *)&save_errno, sizeof(int));
+ (void)read(pfd[pipe_read], (void *)&(addr->more_errno), sizeof(int));
yield = FALSE;
}
}
}
}
}
-close(pfd[pipe_read]);
+(void)close(pfd[pipe_read]);
/* If there have been no problems we can now add the terminating "." if this is
SMTP output, turning off escaping beforehand. If the last character from the
Old records should eventually get swept up by the exim_tidydb utility.
Arguments:
- hostlist list of hosts that this message could be sent to;
- the update_waiting flag is set if a host is to be noted
+ hostlist list of hosts that this message could be sent to
tpname name of the transport
Returns: nothing
open_db dbblock;
open_db *dbm_file;
+DEBUG(D_transport) debug_printf("updating wait-%s database\n", tpname);
+
/* Open the database for this transport */
sprintf(CS buffer, "wait-%.200s", tpname);
if (dbm_file == NULL) return;
/* Scan the list of hosts for which this message is waiting, and ensure
-that the message id is in each host record for those that have the
-update_waiting flag set. */
+that the message id is in each host record. */
for (host = hostlist; host!= NULL; host = host->next)
{
uschar *s;
int i, host_length;
- /* Skip if the update_waiting flag is not set. */
-
- if (!host->update_waiting) continue;
-
/* Skip if this is the same host as we just processed; otherwise remember
the name for next time. */
/* If this message is already in a record, no need to update. */
- if (already) continue;
+ if (already)
+ {
+ DEBUG(D_transport) debug_printf("already listed for %s\n", host->name);
+ continue;
+ }
/* If this record is full, write it out with a new name constructed
/* Update the database */
dbfn_write(dbm_file, host->name, host_record, sizeof(dbdata_wait) + host_length);
+ DEBUG(D_transport) debug_printf("added to list for %s\n", host->name);
}
/* All now done */
automatic comparison. */
if ((pid = fork()) != 0) _exit(EXIT_SUCCESS);
- if (running_in_test_harness) millisleep(500);
+ 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. */
if (socket_fd != 0)
{
- dup2(socket_fd, 0);
- close(socket_fd);
+ (void)dup2(socket_fd, 0);
+ (void)close(socket_fd);
}
DEBUG(D_exec) debug_print_argv(argv);