X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/4c2b250a893bbe2a78086f750318d79bfca1d56d..700d22f3fc0cc559170e8085a1b799b61dceb738:/src/src/transport.c diff --git a/src/src/transport.c b/src/src/transport.c index 7c5c98149..7dd1afb85 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -1,10 +1,8 @@ -/* $Cambridge: exim/src/src/transport.c,v 1.22 2008/09/30 09:20:31 tom Exp $ */ - /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2007 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* General functions concerned with transportation, and generic options for all @@ -70,11 +68,11 @@ optionlist optionlist_transports[] = { (void *)(offsetof(transport_instance, envelope_to_add)) }, { "group", opt_expand_gid|opt_public, (void *)offsetof(transport_instance, gid) }, - { "headers_add", opt_stringptr|opt_public, + { "headers_add", opt_stringptr|opt_public|opt_rep_str, (void *)offsetof(transport_instance, add_headers) }, { "headers_only", opt_bool|opt_public, (void *)offsetof(transport_instance, headers_only) }, - { "headers_remove", opt_stringptr|opt_public, + { "headers_remove", opt_stringptr|opt_public|opt_rep_str, (void *)offsetof(transport_instance, remove_headers) }, { "headers_rewrite", opt_rewrite|opt_public, (void *)offsetof(transport_instance, headers_rewrite) }, @@ -221,7 +219,7 @@ for (i = 0; i < 100; i++) if (transport_write_timeout <= 0) /* No timeout wanted */ { #ifdef SUPPORT_TLS - if (tls_active == fd) rc = tls_write(block, len); else + if (tls_out.active == fd) rc = tls_write(FALSE, block, len); else #endif rc = write(fd, block, len); save_errno = errno; @@ -233,7 +231,7 @@ for (i = 0; i < 100; i++) { alarm(local_timeout); #ifdef SUPPORT_TLS - if (tls_active == fd) rc = tls_write(block, len); else + if (tls_out.active == fd) rc = tls_write(FALSE, block, len); else #endif rc = write(fd, block, len); save_errno = errno; @@ -324,7 +322,7 @@ Returns: the yield of transport_write_block() */ BOOL -transport_write_string(int fd, char *format, ...) +transport_write_string(int fd, const char *format, ...) { va_list ap; va_start(ap, format); @@ -426,6 +424,7 @@ for (ptr = start; ptr < end; ptr++) if (use_crlf) *chunk_ptr++ = '\r'; *chunk_ptr++ = '\n'; + transport_newlines++; /* The check_string test (formerly "from hack") replaces the specific string at the start of a line with an escape string (e.g. "From " becomes @@ -920,19 +919,19 @@ if ((options & topt_no_body) == 0) } } - /* Finished with the check string */ - - nl_check_length = nl_escape_length = 0; - /* A read error on the body will have left len == -1 and errno set. */ if (len != 0) return FALSE; + } - /* If requested, add a terminating "." line (SMTP output). */ +/* Finished with the check string */ - if ((options & topt_end_dot) != 0 && !write_chunk(fd, US".\n", 2, use_crlf)) - return FALSE; - } +nl_check_length = nl_escape_length = 0; + +/* If requested, add a terminating "." line (SMTP output). */ + +if ((options & topt_end_dot) != 0 && !write_chunk(fd, US".\n", 2, use_crlf)) + return FALSE; /* Write out any remaining data in the buffer before returning. */ @@ -941,7 +940,7 @@ return (len = chunk_ptr - deliver_out_buffer) <= 0 || } -#if (defined EXPERIMENTAL_DOMAINKEYS) || (defined EXPERIMENTAL_DKIM) +#ifndef DISABLE_DKIM /*************************************************************************************************** * External interface to write the message, while signing it with DKIM and/or Domainkeys * @@ -965,14 +964,6 @@ Arguments: as for internal_transport_write_message() above, with additional 0/false => send anyway uschar *dkim_sign_headers DKIM: List of headers that should be included in signature generation - uschar *dk_private_key Domainkeys: The private key to use (filename or plain data) - uschar *dk_domain Domainkeys: Override domain (normally NULL) - uschar *dk_selector Domainkeys: The selector to use. - uschar *dk_canon Domainkeys: The canonalization scheme to use, "simple" or "nofws" - uschar *dk_headers Domainkeys: Colon-separated header list to include in the signing - process. - uschar *dk_strict Domainkeys: What to do if signing fails: 1/true => throw error - 0/false => send anyway Returns: TRUE on success; FALSE (with errno) for any failure */ @@ -982,9 +973,7 @@ 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, - uschar *dk_private_key, uschar *dk_domain, uschar *dk_selector, uschar *dk_canon, - uschar *dk_headers, uschar *dk_strict + uschar *dkim_selector, uschar *dkim_canon, uschar *dkim_strict, uschar *dkim_sign_headers ) { int dkim_fd; @@ -995,12 +984,10 @@ dkim_transport_write_message(address_item *addr, int fd, int options, int sread = 0; int wwritten = 0; uschar *dkim_signature = NULL; - uschar *dk_signature = NULL; off_t size = 0; - if ( !( ((dkim_private_key != NULL) && (dkim_domain != NULL) && (dkim_selector != NULL)) || - ((dk_private_key != NULL) && (dk_selector != NULL)) ) ) { - /* If we can sign with neither method, just call the original function. */ + if (!( ((dkim_private_key != NULL) && (dkim_domain != NULL) && (dkim_selector != NULL)) )) { + /* If we can't sign, just call the original function. */ return transport_write_message(addr, fd, options, size_limit, add_headers, remove_headers, check_string, escape_string, rewrite_rules, @@ -1031,8 +1018,6 @@ dkim_transport_write_message(address_item *addr, int fd, int options, goto CLEANUP; } - - #ifdef EXPERIMENTAL_DKIM if ( (dkim_private_key != NULL) && (dkim_domain != NULL) && (dkim_selector != NULL) ) { /* Rewind file and feed it to the goats^W DKIM lib */ lseek(dkim_fd, 0, SEEK_SET); @@ -1048,7 +1033,9 @@ dkim_transport_write_message(address_item *addr, int fd, int options, if (dkim_strict_result != NULL) { if ( (strcmpic(dkim_strict,US"1") == 0) || (strcmpic(dkim_strict,US"true") == 0) ) { - save_errno = errno; + /* Set errno to something halfway meaningful */ + save_errno = EACCES; + log_write(0, LOG_MAIN, "DKIM: message could not be signed, and dkim_strict is set. Deferring message delivery."); rc = FALSE; goto CLEANUP; } @@ -1059,7 +1046,7 @@ dkim_transport_write_message(address_item *addr, int fd, int options, int siglen = Ustrlen(dkim_signature); while(siglen > 0) { #ifdef SUPPORT_TLS - if (tls_active == fd) wwritten = tls_write(dkim_signature, siglen); else + if (tls_out.active == fd) wwritten = tls_write(FALSE, dkim_signature, siglen); else #endif wwritten = write(fd,dkim_signature,siglen); if (wwritten == -1) { @@ -1073,49 +1060,6 @@ dkim_transport_write_message(address_item *addr, int fd, int options, } } } - #endif - - #ifdef EXPERIMENTAL_DOMAINKEYS - if ( (dk_private_key != NULL) && (dk_selector != NULL) ) { - /* Rewind file and feed it to the goats^W DK lib */ - lseek(dkim_fd, 0, SEEK_SET); - dk_signature = dk_exim_sign(dkim_fd, - dk_private_key, - dk_domain, - dk_selector, - dk_canon); - if (dk_signature == NULL) { - if (dk_strict != NULL) { - uschar *dk_strict_result = expand_string(dk_strict); - if (dk_strict_result != NULL) { - if ( (strcmpic(dk_strict,US"1") == 0) || - (strcmpic(dk_strict,US"true") == 0) ) { - save_errno = errno; - rc = FALSE; - goto CLEANUP; - } - } - } - } - else { - int siglen = Ustrlen(dk_signature); - while(siglen > 0) { - #ifdef SUPPORT_TLS - if (tls_active == fd) wwritten = tls_write(dk_signature, siglen); else - #endif - wwritten = write(fd,dk_signature,siglen); - if (wwritten == -1) { - /* error, bail out */ - save_errno = errno; - rc = FALSE; - goto CLEANUP; - } - siglen -= wwritten; - dk_signature += wwritten; - } - } - } - #endif /* Fetch file positition (the size) */ size = lseek(dkim_fd,0,SEEK_CUR); @@ -1128,7 +1072,7 @@ dkim_transport_write_message(address_item *addr, int fd, int options, 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) + if (tls_out.active != fd) { ssize_t copied = 0; off_t offset = 0; @@ -1152,7 +1096,7 @@ dkim_transport_write_message(address_item *addr, int fd, int options, /* write the chunk */ DKIM_WRITE: #ifdef SUPPORT_TLS - if (tls_active == fd) wwritten = tls_write(US p, sread); else + if (tls_out.active == fd) wwritten = tls_write(FALSE, US p, sread); else #endif wwritten = write(fd,p,sread); if (wwritten == -1) @@ -1181,10 +1125,11 @@ dkim_transport_write_message(address_item *addr, int fd, int options, CLEANUP: /* unlink -K file */ (void)close(dkim_fd); - //Uunlink(dkim_spool_name); + Uunlink(dkim_spool_name); errno = save_errno; return rc; } + #endif @@ -1281,9 +1226,14 @@ if ((write_pid = fork()) == 0) size_limit, add_headers, remove_headers, NULL, NULL, rewrite_rules, rewrite_existflags); save_errno = errno; - (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)); + if ( write(pfd[pipe_write], (void *)&rc, sizeof(BOOL)) + != sizeof(BOOL) + || write(pfd[pipe_write], (void *)&save_errno, sizeof(int)) + != sizeof(int) + || write(pfd[pipe_write], (void *)&(addr->more_errno), sizeof(int)) + != sizeof(int) + ) + rc = FALSE; /* compiler quietening */ _exit(0); } save_errno = errno; @@ -1394,11 +1344,11 @@ if (write_pid > 0) if (rc == 0) { BOOL ok; - (void)read(pfd[pipe_read], (void *)&ok, sizeof(BOOL)); + int dummy = read(pfd[pipe_read], (void *)&ok, sizeof(BOOL)); if (!ok) { - (void)read(pfd[pipe_read], (void *)&save_errno, sizeof(int)); - (void)read(pfd[pipe_read], (void *)&(addr->more_errno), sizeof(int)); + dummy = read(pfd[pipe_read], (void *)&save_errno, sizeof(int)); + dummy = read(pfd[pipe_read], (void *)&(addr->more_errno), sizeof(int)); yield = FALSE; } } @@ -2047,7 +1997,122 @@ if (expand_arguments) memmove(argv + i + 1 + additional, argv + i + 1, (argcount - i)*sizeof(uschar *)); - for (ad = addr; ad != NULL; ad = ad->next) argv[i++] = ad->address; + for (ad = addr; ad != NULL; ad = ad->next) { + argv[i++] = ad->address; + argcount++; + } + + /* Subtract one since we replace $pipe_addresses */ + argcount--; + i--; + } + + /* Handle special case of $address_pipe when af_force_command is set */ + + else if (addr != NULL && testflag(addr,af_force_command) && + (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; + + /* We can never have more then the argv we will be loading into */ + address_pipe_max_args = max_args - argcount + 1; + + DEBUG(D_transport) + 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 *)); + + /* +1 because addr->local_part[0] == '|' since af_force_command is set */ + s = expand_string(addr->local_part + 1); + + if (s == NULL || *s == '\0') + { + addr->transport_return = FAIL; + addr->message = string_sprintf("Expansion of \"%s\" " + "from command \"%s\" in %s failed: %s", + (addr->local_part + 1), cmd, etext, expand_string_message); + return FALSE; + } + + while (isspace(*s)) s++; /* strip leading space */ + + while (*s != 0 && address_pipe_argcount < address_pipe_max_args) + { + if (*s == '\'') + { + ss = s + 1; + while (*ss != 0 && *ss != '\'') ss++; + address_pipe_argv[address_pipe_argcount++] = ss = store_get(ss - s++); + while (*s != 0 && *s != '\'') *ss++ = *s++; + if (*s != 0) s++; + *ss++ = 0; + } + else address_pipe_argv[address_pipe_argcount++] = string_dequote(&s); + while (isspace(*s)) s++; /* strip space after arg */ + } + + address_pipe_argv[address_pipe_argcount] = (uschar *)0; + + /* If *s != 0 we have run out of argument slots. */ + if (*s != 0) + { + uschar *msg = string_sprintf("Too many arguments in $address_pipe " + "\"%s\" in %s", addr->local_part + 1, etext); + if (addr != NULL) + { + addr->transport_return = FAIL; + addr->message = msg; + } + else *errptr = msg; + return FALSE; + } + + /* address_pipe_argcount - 1 + * because we are replacing $address_pipe in the argument list + * with the first thing it expands to */ + if (argcount + address_pipe_argcount - 1 > max_args) + { + addr->transport_return = FAIL; + addr->message = string_sprintf("Too many arguments to command " + "\"%s\" after expanding $address_pipe in %s", cmd, etext); + return FALSE; + } + + /* If we are not just able to replace the slot that contained + * $address_pipe (address_pipe_argcount == 1) + * We have to move the existing argv by address_pipe_argcount - 1 + * Visually if address_pipe_argcount == 2: + * [argv 0][argv 1][argv 2($address_pipe)][argv 3][0] + * [argv 0][argv 1][ap_arg0][ap_arg1][old argv 3][0] + */ + if (address_pipe_argcount > 1) + memmove( + /* current position + additonal args */ + argv + i + address_pipe_argcount, + /* current position + 1 (for the (uschar *)0 at the end) */ + argv + i + 1, + /* -1 for the (uschar *)0 at the end)*/ + (argcount - i)*sizeof(uschar *) + ); + + /* 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; + address_pipe_argv[address_pipe_i] != (uschar *)0; + address_pipe_i++) + { + argv[i++] = address_pipe_argv[address_pipe_i]; + argcount++; + } + + /* Subtract one since we replace $address_pipe */ + argcount--; i--; }