X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/05bf16f6217e93594929c8bbbbbc852caf3ed374..2791749f220602482c2cce772e6520c54218c0dd:/src/src/transport.c diff --git a/src/src/transport.c b/src/src/transport.c index f34db0914..c4f374509 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -172,6 +172,20 @@ for (transport_instance * t = transports; t; t = t->next) * Write block of data * *************************************************/ +static int +tpt_write(int fd, uschar * block, int len, BOOL more, int options) +{ +return +#ifndef DISABLE_TLS + tls_out.active.sock == fd + ? tls_write(tls_out.active.tls_ctx, block, len, more) : +#endif +#ifdef MSG_MORE + more && !(options & topt_not_socket) ? send(fd, block, len, MSG_MORE) : +#endif + write(fd, block, len); +} + /* Subroutine called by write_chunk() and at the end of the message actually to write a data block. Also called directly by some transports to write additional data to the file descriptor (e.g. prefix, suffix). @@ -215,10 +229,11 @@ Returns: TRUE on success, FALSE on failure (with errno preserved); */ static BOOL -transport_write_block_fd(transport_ctx * tctx, uschar *block, int len, BOOL more) +transport_write_block_fd(transport_ctx * tctx, uschar * block, int len, BOOL more) { int rc, save_errno; int local_timeout = transport_write_timeout; +int connretry = 1; int fd = tctx->u.fd; /* This loop is for handling incomplete writes and other retries. In most @@ -230,48 +245,42 @@ for (int i = 0; i < 100; i++) debug_printf("writing data block fd=%d size=%d timeout=%d%s\n", fd, len, local_timeout, more ? " (more expected)" : ""); - /* This code makes use of alarm() in order to implement the timeout. This - isn't a very tidy way of doing things. Using non-blocking I/O with select() - provides a neater approach. However, I don't know how to do this when TLS is - in use. */ + /* When doing TCP Fast Open we may get this far before the 3-way handshake + is complete, and write returns ENOTCONN. Detect that, wait for the socket + to become writable, and retry once only. */ - if (transport_write_timeout <= 0) /* No timeout wanted */ + for(;;) { - rc = -#ifdef SUPPORT_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) - ? send(fd, block, len, MSG_MORE) : -#endif - write(fd, block, len); - save_errno = errno; - } - - /* Timeout wanted. */ + fd_set fds; + /* This code makes use of alarm() in order to implement the timeout. This + isn't a very tidy way of doing things. Using non-blocking I/O with select() + provides a neater approach. However, I don't know how to do this when TLS is + in use. */ - else - { - ALARM(local_timeout); - - rc = -#ifdef SUPPORT_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) - ? send(fd, block, len, MSG_MORE) : -#endif - write(fd, block, len); - - save_errno = errno; - local_timeout = ALARM_CLR(0); - if (sigalrm_seen) + if (transport_write_timeout <= 0) /* No timeout wanted */ { - errno = ETIMEDOUT; - return FALSE; + rc = tpt_write(fd, block, len, more, tctx->options); + save_errno = errno; } + else /* Timeout wanted. */ + { + ALARM(local_timeout); + rc = tpt_write(fd, block, len, more, tctx->options); + save_errno = errno; + local_timeout = ALARM_CLR(0); + if (sigalrm_seen) + { + errno = ETIMEDOUT; + return FALSE; + } + } + + if (rc >= 0 || errno != ENOTCONN || connretry <= 0) + break; + + FD_ZERO(&fds); FD_SET(fd, &fds); + select(fd+1, NULL, &fds, NULL, NULL); /* could set timout? */ + connretry--; } /* Hopefully, the most common case is success, so test that first. */ @@ -375,8 +384,11 @@ 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(&gs, FALSE, 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; @@ -638,7 +650,7 @@ so that we don't handle it again. */ 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; @@ -662,7 +674,7 @@ if (ppp) return TRUE; /* 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; @@ -742,7 +754,7 @@ for (header_line * h = header_list; h; h = h->next) if (h->type != htype_old) { 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, @@ -949,7 +961,7 @@ if (!(tctx->options & topt_no_headers)) BOOL first = TRUE; 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; @@ -1242,17 +1254,25 @@ if ((write_pid = fork()) == 0) rc = internal_transport_write_message(tctx, size_limit); save_errno = errno; - 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 *)&tctx->addr->more_errno, sizeof(int)) - != sizeof(int) - || write(pfd[pipe_write], (void *)&tctx->addr->delivery_usec, sizeof(int)) - != sizeof(int) - ) - rc = FALSE; /* compiler quietening */ - _exit(0); + errno = 0; + for (int retries = 10;;) + { + if ( os_pipe_write(pfd[pipe_write], (void *)&rc, sizeof(BOOL)) + != sizeof(BOOL) + || os_pipe_write(pfd[pipe_write], (void *)&save_errno, sizeof(int)) + != sizeof(int) + || os_pipe_write(pfd[pipe_write], (void *)&tctx->addr->more_errno, sizeof(int)) + != sizeof(int) + || os_pipe_write(pfd[pipe_write], (void *)&tctx->addr->delivery_usec, sizeof(int)) + != sizeof(int) + ) + if (errno == EINTR && --retries > 0) + continue; + else + rc = FALSE; /* compiler quietening */ + break; + } + exim_underbar_exit(0); } save_errno = errno; @@ -1272,7 +1292,7 @@ if (write_pid < 0) /* When testing, let the subprocess get going */ -if (f.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); @@ -1364,7 +1384,7 @@ if (write_pid > 0) if (rc == 0) { BOOL ok; - if (read(pfd[pipe_read], (void *)&ok, sizeof(BOOL)) != sizeof(BOOL)) + if (os_pipe_read(pfd[pipe_read], (void *)&ok, sizeof(BOOL)) != sizeof(BOOL)) { DEBUG(D_transport) debug_printf("pipe read from writing process: %s\n", strerror(errno)); @@ -1373,9 +1393,9 @@ if (write_pid > 0) } else if (!ok) { - int dummy = read(pfd[pipe_read], (void *)&save_errno, sizeof(int)); - dummy = read(pfd[pipe_read], (void *)&tctx->addr->more_errno, sizeof(int)); - dummy = read(pfd[pipe_read], (void *)&tctx->addr->delivery_usec, sizeof(int)); + int dummy = os_pipe_read(pfd[pipe_read], (void *)&save_errno, sizeof(int)); + dummy = os_pipe_read(pfd[pipe_read], (void *)&tctx->addr->more_errno, sizeof(int)); + dummy = os_pipe_read(pfd[pipe_read], (void *)&tctx->addr->delivery_usec, sizeof(int)); dummy = dummy; /* compiler quietening */ yield = FALSE; } @@ -1475,7 +1495,7 @@ DEBUG(D_transport) debug_printf("updating wait-%s database\n", tpname); /* 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 @@ -1498,7 +1518,7 @@ for (host_item * host = hostlist; host; host = host->next) 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; } @@ -1557,7 +1577,7 @@ for (host_item * host = hostlist; host; host = host->next) 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; } @@ -1648,7 +1668,7 @@ if (local_message_max > 0 && continue_sequence >= local_message_max) /* 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. */ @@ -1692,7 +1712,7 @@ while (1) /* 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; @@ -1700,7 +1720,7 @@ while (1) { 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; } @@ -1719,16 +1739,14 @@ while (1) 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; @@ -1798,10 +1816,7 @@ while (1) } 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 @@ -1824,8 +1839,6 @@ while (1) 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 @@ -1867,7 +1880,7 @@ 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.sock >= 0 || continue_proxy_cipher) { @@ -1948,7 +1961,7 @@ if ((pid = fork()) == 0) DEBUG(D_transport) debug_printf("transport_pass_socket succeeded (final-pid %d)\n", pid); _exit(EXIT_SUCCESS); } - if (f.running_in_test_harness) sleep(1); + testharness_pause_ms(1000); transport_do_pass_socket(transport_name, hostname, hostaddress, id, socket_fd); @@ -2018,7 +2031,7 @@ delivery batch option is set. */ 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 @@ -2028,18 +2041,19 @@ arguments are verbatim. Copy each argument into a new string. */ 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++; } @@ -2079,8 +2093,8 @@ $recipients. */ DEBUG(D_transport) { debug_printf("direct command:\n"); - for (int 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) @@ -2133,6 +2147,7 @@ if (expand_arguments) 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; @@ -2141,10 +2156,11 @@ if (expand_arguments) 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') { @@ -2163,7 +2179,7 @@ if (expand_arguments) { 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; @@ -2242,12 +2258,12 @@ if (expand_arguments) expanded_arg = expand_cstring(argv[i]); 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;