X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/9f01e50d7efc5c625614e4e055790ca4a92a52a8..759502e5af0acfb310b8571f056d2dbf59adb1d3:/src/src/transport.c diff --git a/src/src/transport.c b/src/src/transport.c index df7fd1628..90789fd60 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -15,75 +15,76 @@ transports. */ data blocks and which therefore have the opt_public flag set. Note that there are other options living inside this structure which can be set only from certain transports. */ +#define LOFF(field) OPT_OFF(transport_instance, field) optionlist optionlist_transports[] = { /* name type value */ { "*expand_group", opt_stringptr|opt_hidden|opt_public, - (void *)offsetof(transport_instance, expand_gid) }, + LOFF(expand_gid) }, { "*expand_user", opt_stringptr|opt_hidden|opt_public, - (void *)offsetof(transport_instance, expand_uid) }, + LOFF(expand_uid) }, { "*headers_rewrite_flags", opt_int|opt_public|opt_hidden, - (void *)offsetof(transport_instance, rewrite_existflags) }, + LOFF(rewrite_existflags) }, { "*headers_rewrite_rules", opt_void|opt_public|opt_hidden, - (void *)offsetof(transport_instance, rewrite_rules) }, + LOFF(rewrite_rules) }, { "*set_group", opt_bool|opt_hidden|opt_public, - (void *)offsetof(transport_instance, gid_set) }, + LOFF(gid_set) }, { "*set_user", opt_bool|opt_hidden|opt_public, - (void *)offsetof(transport_instance, uid_set) }, + LOFF(uid_set) }, { "body_only", opt_bool|opt_public, - (void *)offsetof(transport_instance, body_only) }, + LOFF(body_only) }, { "current_directory", opt_stringptr|opt_public, - (void *)offsetof(transport_instance, current_dir) }, + LOFF(current_dir) }, { "debug_print", opt_stringptr | opt_public, - (void *)offsetof(transport_instance, debug_string) }, + LOFF(debug_string) }, { "delivery_date_add", opt_bool|opt_public, - (void *)(offsetof(transport_instance, delivery_date_add)) }, + LOFF(delivery_date_add) }, { "disable_logging", opt_bool|opt_public, - (void *)(offsetof(transport_instance, disable_logging)) }, + LOFF(disable_logging) }, { "driver", opt_stringptr|opt_public, - (void *)offsetof(transport_instance, driver_name) }, + LOFF(driver_name) }, { "envelope_to_add", opt_bool|opt_public, - (void *)(offsetof(transport_instance, envelope_to_add)) }, + LOFF(envelope_to_add) }, #ifndef DISABLE_EVENT { "event_action", opt_stringptr | opt_public, - (void *)offsetof(transport_instance, event_action) }, + LOFF(event_action) }, #endif { "group", opt_expand_gid|opt_public, - (void *)offsetof(transport_instance, gid) }, + LOFF(gid) }, { "headers_add", opt_stringptr|opt_public|opt_rep_str, - (void *)offsetof(transport_instance, add_headers) }, + LOFF(add_headers) }, { "headers_only", opt_bool|opt_public, - (void *)offsetof(transport_instance, headers_only) }, + LOFF(headers_only) }, { "headers_remove", opt_stringptr|opt_public|opt_rep_str, - (void *)offsetof(transport_instance, remove_headers) }, + LOFF(remove_headers) }, { "headers_rewrite", opt_rewrite|opt_public, - (void *)offsetof(transport_instance, headers_rewrite) }, + LOFF(headers_rewrite) }, { "home_directory", opt_stringptr|opt_public, - (void *)offsetof(transport_instance, home_dir) }, + LOFF(home_dir) }, { "initgroups", opt_bool|opt_public, - (void *)offsetof(transport_instance, initgroups) }, + LOFF(initgroups) }, { "max_parallel", opt_stringptr|opt_public, - (void *)offsetof(transport_instance, max_parallel) }, + LOFF(max_parallel) }, { "message_size_limit", opt_stringptr|opt_public, - (void *)offsetof(transport_instance, message_size_limit) }, + LOFF(message_size_limit) }, { "rcpt_include_affixes", opt_bool|opt_public, - (void *)offsetof(transport_instance, rcpt_include_affixes) }, + LOFF(rcpt_include_affixes) }, { "retry_use_local_part", opt_bool|opt_public, - (void *)offsetof(transport_instance, retry_use_local_part) }, + LOFF(retry_use_local_part) }, { "return_path", opt_stringptr|opt_public, - (void *)(offsetof(transport_instance, return_path)) }, + LOFF(return_path) }, { "return_path_add", opt_bool|opt_public, - (void *)(offsetof(transport_instance, return_path_add)) }, + LOFF(return_path_add) }, { "shadow_condition", opt_stringptr|opt_public, - (void *)offsetof(transport_instance, shadow_condition) }, + LOFF(shadow_condition) }, { "shadow_transport", opt_stringptr|opt_public, - (void *)offsetof(transport_instance, shadow) }, + LOFF(shadow) }, { "transport_filter", opt_stringptr|opt_public, - (void *)offsetof(transport_instance, filter_command) }, + LOFF(filter_command) }, { "transport_filter_timeout", opt_time|opt_public, - (void *)offsetof(transport_instance, filter_timeout) }, + LOFF(filter_timeout) }, { "user", opt_expand_uid|opt_public, - (void *)offsetof(transport_instance, uid) } + LOFF(uid) } }; int optionlist_transports_size = nelem(optionlist_transports); @@ -172,6 +173,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 +230,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 +246,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 = -#ifndef DISABLE_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 = -#ifndef DISABLE_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. */ @@ -581,14 +591,14 @@ if (include_affixes) return addr->address; } -if (addr->suffix == NULL) +if (!addr->suffix) { - if (addr->prefix == NULL) return addr->address; + if (!addr->prefix) return addr->address; return addr->address + Ustrlen(addr->prefix); } at = Ustrrchr(addr->address, '@'); -plen = (addr->prefix == NULL)? 0 : Ustrlen(addr->prefix); +plen = addr->prefix ? Ustrlen(addr->prefix) : 0; slen = Ustrlen(addr->suffix); return string_sprintf("%.*s@%s", (int)(at - addr->address - plen - slen), @@ -728,10 +738,17 @@ for (header_line * h = header_list; h; h = h->next) if (h->type != htype_old) return FALSE; } len = s ? Ustrlen(s) : 0; - if (strncmpic(h->text, s, len) != 0) continue; - ss = h->text + len; - while (*ss == ' ' || *ss == '\t') ss++; - if (*ss == ':') break; + if (len && s[len-1] == '*') /* trailing glob */ + { + if (strncmpic(h->text, s, len-1) == 0) break; + } + else + { + if (strncmpic(h->text, s, len) != 0) continue; + ss = h->text + len; + while (*ss == ' ' || *ss == '\t') ss++; + if (*ss == ':') break; + } } if (s) { include_header = FALSE; break; } } @@ -1251,8 +1268,8 @@ if ((write_pid = fork()) == 0) != 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) + || write(pfd[pipe_write], (void *)&tctx->addr->delivery_time, sizeof(struct timeval)) + != sizeof(struct timeval) ) rc = FALSE; /* compiler quietening */ exim_underbar_exit(0); @@ -1378,7 +1395,7 @@ if (write_pid > 0) { 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)); + dummy = read(pfd[pipe_read], (void *)&tctx->addr->delivery_time, sizeof(struct timeval)); dummy = dummy; /* compiler quietening */ yield = FALSE; } @@ -1543,12 +1560,17 @@ for (host_item * host = hostlist; host; host = host->next) /* If this record is full, write it out with a new name constructed from the sequence number, increase the sequence number, and empty - the record. */ + the record. If we're doing a two-phase queue run initial phase, ping the + daemon to consider running a delivery on this host. */ if (host_record->count >= WAIT_NAME_MAX) { sprintf(CS buffer, "%.200s:%d", host->name, host_record->sequence); dbfn_write(dbm_file, buffer, host_record, sizeof(dbdata_wait) + host_length); +#ifdef EXPERIMENTAL_QUEUE_RAMP + if (f.queue_2stage && queue_fast_ramp && !queue_run_in_order) + queue_notify_daemon(message_id); +#endif host_record->sequence++; host_record->count = 0; host_length = 0;