X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/958541e992051a5afcf573985eb5d4e772dc6979..f7572e5a358cd3d9581140b87e590d58b6c278f0:/src/src/transport.c diff --git a/src/src/transport.c b/src/src/transport.c index a104f51b4..4843d5250 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -1,10 +1,10 @@ -/* $Cambridge: exim/src/src/transport.c,v 1.9 2005/05/24 14:56:27 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 @@ -13,6 +13,9 @@ transports. */ #include "exim.h" +#ifdef HAVE_LINUX_SENDFILE +#include +#endif /* Structure for keeping list of addresses that have been added to Envelope-To:, in order to avoid duplication. */ @@ -983,10 +986,11 @@ dk_transport_write_message(address_item *addr, int fd, int options, 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. */ @@ -1042,8 +1046,8 @@ dk_transport_write_message(address_item *addr, int fd, int options, uschar *dk_strict_result = expand_string(dk_strict); if (dk_strict_result != NULL) { - if ( (strcmpic(dk_strict,"1") == 0) || - (strcmpic(dk_strict,"true") == 0) ) + if ( (strcmpic(dk_strict,US"1") == 0) || + (strcmpic(dk_strict,US"true") == 0) ) { save_errno = errno; rc = FALSE; @@ -1052,16 +1056,42 @@ dk_transport_write_message(address_item *addr, int fd, int options, } } - /* 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 0) { char *p = sbuf; /* write the chunk */ DK_WRITE: #ifdef SUPPORT_TLS - if (tls_active == fd) wwritten = tls_write(p, sread); else + if (tls_active == fd) wwritten = tls_write(US p, sread); else #endif wwritten = write(fd,p,sread); if (wwritten == -1) @@ -1087,10 +1117,9 @@ dk_transport_write_message(address_item *addr, int fd, int options, goto CLEANUP; } - CLEANUP: /* unlink -K file */ - close(dk_fd); + (void)close(dk_fd); Uunlink(dk_spool_name); errno = save_errno; return rc; @@ -1098,6 +1127,193 @@ dk_transport_write_message(address_item *addr, int fd, int options, #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 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 * *************************************************/ @@ -1164,10 +1380,10 @@ save_errno = 0; yield = FALSE; write_pid = (pid_t)(-1); -fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); +(void)fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); filter_pid = child_open(transport_filter_argv, NULL, 077, &fd_write, &fd_read, FALSE); -fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) & ~FD_CLOEXEC); +(void)fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) & ~FD_CLOEXEC); if (filter_pid < 0) goto TIDY_UP; /* errno set */ DEBUG(D_transport) @@ -1182,25 +1398,25 @@ if (pipe(pfd) != 0) goto TIDY_UP; /* errno set */ 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 */ @@ -1270,8 +1486,8 @@ sure. Also apply a paranoia timeout. */ 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) { @@ -1303,11 +1519,11 @@ if (write_pid > 0) 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; } } @@ -1320,7 +1536,7 @@ if (write_pid > 0) } } } -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 @@ -1389,8 +1605,7 @@ better. 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 @@ -1405,6 +1620,8 @@ host_item *host; 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); @@ -1412,8 +1629,7 @@ dbm_file = dbfn_open(buffer, O_RDWR, &dbblock, TRUE); 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) { @@ -1422,10 +1638,6 @@ 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. */ @@ -1475,7 +1687,11 @@ for (host = hostlist; host!= NULL; host = host->next) /* 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 @@ -1511,6 +1727,7 @@ for (host = hostlist; host!= NULL; host = host->next) /* 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 */ @@ -1749,7 +1966,7 @@ if ((pid = fork()) == 0) 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. */ @@ -1784,8 +2001,8 @@ if ((pid = fork()) == 0) 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);