X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/2c0f3ea1858d9dafc8600f9d97bc6e57594c22d0..31323b30449a59713ed7a99f86e356cb1b9d0fb7:/src/src/deliver.c diff --git a/src/src/deliver.c b/src/src/deliver.c index c416ebf3f..e5c951560 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -9,6 +9,7 @@ #include "exim.h" +#include "transports/smtp.h" #include @@ -671,7 +672,7 @@ address_item *aa; while (addr->parent) { addr = addr->parent; - if ((addr->child_count -= 1) > 0) return; /* Incomplete parent */ + if (--addr->child_count > 0) return; /* Incomplete parent */ address_done(addr, now); /* Log the completion of all descendents only when there is no ancestor with @@ -2975,13 +2976,13 @@ while (addr_local) addr3 = store_get(sizeof(address_item)); *addr3 = *addr2; addr3->next = NULL; - addr3->shadow_message = (uschar *) &(addr2->shadow_message); + addr3->shadow_message = US &addr2->shadow_message; addr3->transport = stp; addr3->transport_return = DEFER; addr3->return_filename = NULL; addr3->return_file = -1; *last = addr3; - last = &(addr3->next); + last = &addr3->next; } /* If we found any addresses to shadow, run the delivery, and stick any @@ -3366,15 +3367,15 @@ while (!done) up by checking the IP address. */ case 'H': - for (h = addrlist->host_list; h; h = h->next) - { - if (!h->address || Ustrcmp(h->address, ptr+2) != 0) continue; - h->status = ptr[0]; - h->why = ptr[1]; - } - ptr += 2; - while (*ptr++); - break; + for (h = addrlist->host_list; h; h = h->next) + { + if (!h->address || Ustrcmp(h->address, ptr+2) != 0) continue; + h->status = ptr[0]; + h->why = ptr[1]; + } + ptr += 2; + while (*ptr++); + break; /* Retry items are sent in a preceding R item for each address. This is kept separate to keep each message short enough to guarantee it won't @@ -3388,62 +3389,61 @@ while (!done) that a "delete" item is dropped in favour of an "add" item. */ case 'R': - if (!addr) goto ADDR_MISMATCH; + if (!addr) goto ADDR_MISMATCH; - DEBUG(D_deliver|D_retry) - debug_printf("reading retry information for %s from subprocess\n", - ptr+1); + DEBUG(D_deliver|D_retry) + debug_printf("reading retry information for %s from subprocess\n", + ptr+1); - /* Cut out any "delete" items on the list. */ + /* Cut out any "delete" items on the list. */ - for (rp = &(addr->retries); (r = *rp); rp = &r->next) - if (Ustrcmp(r->key, ptr+1) == 0) /* Found item with same key */ - { - if ((r->flags & rf_delete) == 0) break; /* It was not "delete" */ - *rp = r->next; /* Excise a delete item */ - DEBUG(D_deliver|D_retry) - debug_printf(" existing delete item dropped\n"); - } + for (rp = &addr->retries; (r = *rp); rp = &r->next) + if (Ustrcmp(r->key, ptr+1) == 0) /* Found item with same key */ + { + if (!(r->flags & rf_delete)) break; /* It was not "delete" */ + *rp = r->next; /* Excise a delete item */ + DEBUG(D_deliver|D_retry) + debug_printf(" existing delete item dropped\n"); + } - /* We want to add a delete item only if there is no non-delete item; - however we still have to step ptr through the data. */ + /* We want to add a delete item only if there is no non-delete item; + however we still have to step ptr through the data. */ - if (!r || (*ptr & rf_delete) == 0) - { - r = store_get(sizeof(retry_item)); - r->next = addr->retries; - addr->retries = r; - r->flags = *ptr++; - r->key = string_copy(ptr); - while (*ptr++); - memcpy(&(r->basic_errno), ptr, sizeof(r->basic_errno)); - ptr += sizeof(r->basic_errno); - memcpy(&(r->more_errno), ptr, sizeof(r->more_errno)); - ptr += sizeof(r->more_errno); - r->message = (*ptr)? string_copy(ptr) : NULL; - DEBUG(D_deliver|D_retry) - debug_printf(" added %s item\n", - ((r->flags & rf_delete) == 0)? "retry" : "delete"); - } + if (!r || !(*ptr & rf_delete)) + { + r = store_get(sizeof(retry_item)); + r->next = addr->retries; + addr->retries = r; + r->flags = *ptr++; + r->key = string_copy(ptr); + while (*ptr++); + memcpy(&(r->basic_errno), ptr, sizeof(r->basic_errno)); + ptr += sizeof(r->basic_errno); + memcpy(&(r->more_errno), ptr, sizeof(r->more_errno)); + ptr += sizeof(r->more_errno); + r->message = *ptr ? string_copy(ptr) : NULL; + DEBUG(D_deliver|D_retry) debug_printf(" added %s item\n", + r->flags & rf_delete ? "delete" : "retry"); + } - else - { - DEBUG(D_deliver|D_retry) - debug_printf(" delete item not added: non-delete item exists\n"); - ptr++; - while(*ptr++); - ptr += sizeof(r->basic_errno) + sizeof(r->more_errno); - } + else + { + DEBUG(D_deliver|D_retry) + debug_printf(" delete item not added: non-delete item exists\n"); + ptr++; + while(*ptr++); + ptr += sizeof(r->basic_errno) + sizeof(r->more_errno); + } - while(*ptr++); - break; + while(*ptr++); + break; /* Put the amount of data written into the parlist block */ case 'S': - memcpy(&(p->transport_count), ptr, sizeof(transport_count)); - ptr += sizeof(transport_count); - break; + memcpy(&(p->transport_count), ptr, sizeof(transport_count)); + ptr += sizeof(transport_count); + break; /* Address items are in the order of items on the address chain. We remember the current address value in case this function is called @@ -3454,164 +3454,157 @@ while (!done) #ifdef SUPPORT_TLS case 'X': - if (!addr) goto ADDR_MISMATCH; /* Below, in 'A' handler */ - switch (subid) - { - case '1': - addr->cipher = NULL; - addr->peerdn = NULL; - - if (*ptr) - addr->cipher = string_copy(ptr); - while (*ptr++); - if (*ptr) - addr->peerdn = string_copy(ptr); - break; - - case '2': - if (*ptr) - (void) tls_import_cert(ptr, &addr->peercert); - else - addr->peercert = NULL; - break; + if (!addr) goto ADDR_MISMATCH; /* Below, in 'A' handler */ + switch (subid) + { + case '1': + addr->cipher = NULL; + addr->peerdn = NULL; - case '3': - if (*ptr) - (void) tls_import_cert(ptr, &addr->ourcert); - else - addr->ourcert = NULL; - break; + if (*ptr) + addr->cipher = string_copy(ptr); + while (*ptr++); + if (*ptr) + addr->peerdn = string_copy(ptr); + break; + + case '2': + if (*ptr) + (void) tls_import_cert(ptr, &addr->peercert); + else + addr->peercert = NULL; + break; + + case '3': + if (*ptr) + (void) tls_import_cert(ptr, &addr->ourcert); + else + addr->ourcert = NULL; + break; # ifndef DISABLE_OCSP - case '4': - addr->ocsp = OCSP_NOT_REQ; - if (*ptr) - addr->ocsp = *ptr - '0'; - break; + case '4': + addr->ocsp = *ptr ? *ptr - '0' : OCSP_NOT_REQ; + break; # endif - } - while (*ptr++); - break; + } + while (*ptr++); + break; #endif /*SUPPORT_TLS*/ case 'C': /* client authenticator information */ - switch (subid) - { - case '1': - addr->authenticator = (*ptr)? string_copy(ptr) : NULL; - break; - case '2': - addr->auth_id = (*ptr)? string_copy(ptr) : NULL; - break; - case '3': - addr->auth_sndr = (*ptr)? string_copy(ptr) : NULL; - break; - } - while (*ptr++); - break; + switch (subid) + { + case '1': addr->authenticator = *ptr ? string_copy(ptr) : NULL; break; + case '2': addr->auth_id = *ptr ? string_copy(ptr) : NULL; break; + case '3': addr->auth_sndr = *ptr ? string_copy(ptr) : NULL; break; + } + while (*ptr++); + break; #ifndef DISABLE_PRDR case 'P': - addr->flags |= af_prdr_used; - break; + addr->flags |= af_prdr_used; + break; #endif case 'K': - addr->flags |= af_chunking_used; - break; + addr->flags |= af_chunking_used; + break; case 'D': - if (!addr) goto ADDR_MISMATCH; - memcpy(&(addr->dsn_aware), ptr, sizeof(addr->dsn_aware)); - ptr += sizeof(addr->dsn_aware); - DEBUG(D_deliver) debug_printf("DSN read: addr->dsn_aware = %d\n", addr->dsn_aware); - break; - - case 'A': - if (!addr) - { - ADDR_MISMATCH: - msg = string_sprintf("address count mismatch for data read from pipe " - "for transport process %d for transport %s", pid, - addrlist->transport->driver_name); - done = TRUE; + if (!addr) goto ADDR_MISMATCH; + memcpy(&(addr->dsn_aware), ptr, sizeof(addr->dsn_aware)); + ptr += sizeof(addr->dsn_aware); + DEBUG(D_deliver) debug_printf("DSN read: addr->dsn_aware = %d\n", addr->dsn_aware); break; - } - switch (subid) - { -#ifdef SUPPORT_SOCKS - case '2': /* proxy information; must arrive before A0 and applies to that addr XXX oops*/ - proxy_session = TRUE; /*XXX should this be cleared somewhere? */ - if (*ptr == 0) - ptr++; - else - { - proxy_local_address = string_copy(ptr); - while(*ptr++); - memcpy(&proxy_local_port, ptr, sizeof(proxy_local_port)); - ptr += sizeof(proxy_local_port); - } + case 'A': + if (!addr) + { + ADDR_MISMATCH: + msg = string_sprintf("address count mismatch for data read from pipe " + "for transport process %d for transport %s", pid, + addrlist->transport->driver_name); + done = TRUE; break; -#endif + } -#ifdef EXPERIMENTAL_DSN_INFO - case '1': /* must arrive before A0, and applies to that addr */ - /* Two strings: smtp_greeting and helo_response */ - addr->smtp_greeting = string_copy(ptr); - while(*ptr++); - addr->helo_response = string_copy(ptr); - while(*ptr++); - break; -#endif + switch (subid) + { + #ifdef SUPPORT_SOCKS + case '2': /* proxy information; must arrive before A0 and applies to that addr XXX oops*/ + proxy_session = TRUE; /*XXX should this be cleared somewhere? */ + if (*ptr == 0) + ptr++; + else + { + proxy_local_address = string_copy(ptr); + while(*ptr++); + memcpy(&proxy_local_port, ptr, sizeof(proxy_local_port)); + ptr += sizeof(proxy_local_port); + } + break; + #endif - case '0': - addr->transport_return = *ptr++; - addr->special_action = *ptr++; - memcpy(&(addr->basic_errno), ptr, sizeof(addr->basic_errno)); - ptr += sizeof(addr->basic_errno); - memcpy(&(addr->more_errno), ptr, sizeof(addr->more_errno)); - ptr += sizeof(addr->more_errno); - memcpy(&(addr->flags), ptr, sizeof(addr->flags)); - ptr += sizeof(addr->flags); - addr->message = (*ptr)? string_copy(ptr) : NULL; - while(*ptr++); - addr->user_message = (*ptr)? string_copy(ptr) : NULL; - while(*ptr++); + #ifdef EXPERIMENTAL_DSN_INFO + case '1': /* must arrive before A0, and applies to that addr */ + /* Two strings: smtp_greeting and helo_response */ + addr->smtp_greeting = string_copy(ptr); + while(*ptr++); + addr->helo_response = string_copy(ptr); + while(*ptr++); + break; + #endif + + case '0': + DEBUG(D_deliver) debug_printf("A0 %s tret %d\n", addr->address, *ptr); + addr->transport_return = *ptr++; + addr->special_action = *ptr++; + memcpy(&(addr->basic_errno), ptr, sizeof(addr->basic_errno)); + ptr += sizeof(addr->basic_errno); + memcpy(&(addr->more_errno), ptr, sizeof(addr->more_errno)); + ptr += sizeof(addr->more_errno); + memcpy(&(addr->flags), ptr, sizeof(addr->flags)); + ptr += sizeof(addr->flags); + addr->message = *ptr ? string_copy(ptr) : NULL; + while(*ptr++); + addr->user_message = *ptr ? string_copy(ptr) : NULL; + while(*ptr++); - /* Always two strings for host information, followed by the port number and DNSSEC mark */ + /* Always two strings for host information, followed by the port number and DNSSEC mark */ - if (*ptr != 0) - { - h = store_get(sizeof(host_item)); - h->name = string_copy(ptr); - while (*ptr++); - h->address = string_copy(ptr); - while(*ptr++); - memcpy(&(h->port), ptr, sizeof(h->port)); - ptr += sizeof(h->port); - h->dnssec = *ptr == '2' ? DS_YES - : *ptr == '1' ? DS_NO - : DS_UNK; - ptr++; - addr->host_used = h; - } - else ptr++; + if (*ptr) + { + h = store_get(sizeof(host_item)); + h->name = string_copy(ptr); + while (*ptr++); + h->address = string_copy(ptr); + while(*ptr++); + memcpy(&h->port, ptr, sizeof(h->port)); + ptr += sizeof(h->port); + h->dnssec = *ptr == '2' ? DS_YES + : *ptr == '1' ? DS_NO + : DS_UNK; + ptr++; + addr->host_used = h; + } + else ptr++; - /* Finished with this address */ + /* Finished with this address */ - addr = addr->next; - break; - } - break; + addr = addr->next; + break; + } + break; /* Local interface address/port */ case 'I': - if (*ptr) sending_ip_address = string_copy(ptr); - while (*ptr++) ; - if (*ptr) sending_port = atoi(CS ptr); - while (*ptr++) ; - break; + if (*ptr) sending_ip_address = string_copy(ptr); + while (*ptr++) ; + if (*ptr) sending_port = atoi(CS ptr); + while (*ptr++) ; + break; /* Z marks the logical end of the data. It is followed by '0' if continue_transport was NULL at the end of transporting, otherwise '1'. @@ -3620,23 +3613,23 @@ while (!done) most normal messages it will remain NULL all the time. */ case 'Z': - if (*ptr == '0') - { - continue_transport = NULL; - continue_hostname = NULL; - } - done = TRUE; - DEBUG(D_deliver) debug_printf("Z0%c item read\n", *ptr); - break; + if (*ptr == '0') + { + continue_transport = NULL; + continue_hostname = NULL; + } + done = TRUE; + DEBUG(D_deliver) debug_printf("Z0%c item read\n", *ptr); + break; /* Anything else is a disaster. */ default: - msg = string_sprintf("malformed data (%d) read from pipe for transport " - "process %d for transport %s", ptr[-1], pid, - addr->transport->driver_name); - done = TRUE; - break; + msg = string_sprintf("malformed data (%d) read from pipe for transport " + "process %d for transport %s", ptr[-1], pid, + addr->transport->driver_name); + done = TRUE; + break; } } @@ -3898,14 +3891,12 @@ for (;;) /* Normally we do not repeat this loop */ maxpipe = 0; FD_ZERO(&select_pipes); for (poffset = 0; poffset < remote_max_parallel; poffset++) - { if (parlist[poffset].pid != 0) { int fd = parlist[poffset].fd; FD_SET(fd, &select_pipes); if (fd > maxpipe) maxpipe = fd; } - } /* Stick in a 60-second timeout, just in case. */ @@ -4436,6 +4427,23 @@ for (delivery_count = 0; addr_remote; delivery_count++) if (tp->setup) (void)((tp->setup)(addr->transport, addr, NULL, uid, gid, NULL)); + /* If we have a connection still open from a verify stage (lazy-close) + treat it as if it is a continued connection (apart from the counter used + for the log line mark). */ + + if (cutthrough.fd >= 0 && cutthrough.callout_hold_only) + { + DEBUG(D_deliver) + debug_printf("lazy-callout-close: have conn still open from verification\n"); + continue_transport = cutthrough.transport; + continue_hostname = string_copy(cutthrough.host.name); + continue_host_address = string_copy(cutthrough.host.address); + continue_sequence = 1; + sending_ip_address = cutthrough.snd_ip; + sending_port = cutthrough.snd_port; + smtp_peer_options = cutthrough.peer_options; + } + /* If this is a run to continue delivery down an already-established channel, check that this set of addresses matches the transport and the channel. If it does not, defer the addresses. If a host list exists, @@ -4446,14 +4454,31 @@ for (delivery_count = 0; addr_remote; delivery_count++) if (continue_transport) { BOOL ok = Ustrcmp(continue_transport, tp->name) == 0; - if (ok && addr->host_list) + + /* If the transport is about to override the host list do not check + it here but take the cost of running the transport process to discover + if the continued_hostname connection is suitable. This is a layering + violation which is unfortunate as it requires we haul in the smtp + include file. */ + + if (ok) { - host_item *h; - ok = FALSE; - for (h = addr->host_list; h; h = h->next) - if (Ustrcmp(h->name, continue_hostname) == 0) -/*XXX should also check port here */ - { ok = TRUE; break; } + smtp_transport_options_block * ob; + + if ( !( tp->info->driver_name == US"smtp" + && (ob = (smtp_transport_options_block *)tp->options_block) + && ob->hosts_override && ob->hosts + ) + && addr->host_list + ) + { + host_item * h; + ok = FALSE; + for (h = addr->host_list; h; h = h->next) + if (Ustrcmp(h->name, continue_hostname) == 0) + /*XXX should also check port here */ + { ok = TRUE; break; } + } } /* Addresses not suitable; defer or queue for fallback hosts (which @@ -4461,7 +4486,10 @@ for (delivery_count = 0; addr_remote; delivery_count++) if (!ok) { - DEBUG(D_deliver) debug_printf("not suitable for continue_transport\n"); + DEBUG(D_deliver) debug_printf("not suitable for continue_transport (%s)\n", + Ustrcmp(continue_transport, tp->name) != 0 + ? string_sprintf("tpt %s vs %s", continue_transport, tp->name) + : string_sprintf("no host matching %s", continue_hostname)); if (serialize_key) enq_end(serialize_key); if (addr->fallback_hosts && !fallback) @@ -4492,9 +4520,12 @@ for (delivery_count = 0; addr_remote; delivery_count++) /* Set a flag indicating whether there are further addresses that list the continued host. This tells the transport to leave the channel open, - but not to pass it to another delivery process. */ + but not to pass it to another delivery process. We'd like to do that + for non-continue_transport cases too but the knowlege of which host is + connected to is too hard to manage. Perhaps we need a finer-grain + interface to the transport. */ - for (next = addr_remote; next; next = next->next) + for (next = addr_remote; next && !continue_more; next = next->next) { host_item *h; for (h = next->host_list; h; h = h->next) @@ -4598,7 +4629,7 @@ for (delivery_count = 0; addr_remote; delivery_count++) predictable settings for each delivery process, so do something explicit here rather they rely on the fixed reset in the random number function. */ - random_seed = running_in_test_harness? 42 + 2*delivery_count : 0; + random_seed = running_in_test_harness ? 42 + 2*delivery_count : 0; /* Set close-on-exec on the pipe so that it doesn't get passed on to a new process that may be forked to do another delivery down the same @@ -4713,13 +4744,17 @@ for (delivery_count = 0; addr_remote; delivery_count++) if (!addr->peerdn) *ptr++ = 0; else - { - ptr += sprintf(CS ptr, "%.512s", addr->peerdn); - ptr++; - } + ptr += sprintf(CS ptr, "%.512s", addr->peerdn) + 1; rmt_dlv_checked_write(fd, 'X', '1', big_buffer, ptr - big_buffer); } + else if (continue_proxy_cipher) + { + ptr = big_buffer + sprintf(CS big_buffer, "%.128s", continue_proxy_cipher) + 1; + *ptr++ = 0; + rmt_dlv_checked_write(fd, 'X', '1', big_buffer, ptr - big_buffer); + } + if (addr->peercert) { ptr = big_buffer; @@ -4773,7 +4808,6 @@ for (delivery_count = 0; addr_remote; delivery_count++) memcpy(big_buffer, &addr->dsn_aware, sizeof(addr->dsn_aware)); rmt_dlv_checked_write(fd, 'D', '0', big_buffer, sizeof(addr->dsn_aware)); - DEBUG(D_deliver) debug_printf("DSN write: addr->dsn_aware = %d\n", addr->dsn_aware); /* Retry information: for most success cases this will be null. */ @@ -4888,6 +4922,19 @@ for (delivery_count = 0; addr_remote; delivery_count++) (void)close(pfd[pipe_write]); + /* If we have a connection still open from a verify stage (lazy-close) + release its TLS library context (if any) as responsibility was passed to + the delivery child process. */ + + if (cutthrough.fd >= 0 && cutthrough.callout_hold_only) + { +#ifdef SUPPORT_TLS + tls_close(FALSE, FALSE); +#endif + (void) close(cutthrough.fd); + release_cutthrough_connection(US"passed to transport proc"); + } + /* Fork failed; defer with error message */ if (pid < 0) @@ -5031,6 +5078,7 @@ if (percent_hack_domains) address_item *new_parent = store_get(sizeof(address_item)); *new_parent = *addr; addr->parent = new_parent; + new_parent->child_count = 1; addr->address = new_address; addr->unique = string_copy(new_address); addr->domain = deliver_domain; @@ -5901,9 +5949,9 @@ else if (system_filter && process_recipients != RECIP_FAIL_TIMEOUT) while (p) { - if (parent->child_count == SHRT_MAX) + if (parent->child_count == USHRT_MAX) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "system filter generated more " - "than %d delivery addresses", SHRT_MAX); + "than %d delivery addresses", USHRT_MAX); parent->child_count++; p->parent = parent; @@ -6019,9 +6067,7 @@ spool if the message is deferred, and in any case there are casing complications for local addresses. */ if (process_recipients != RECIP_IGNORE) - { for (i = 0; i < recipients_count; i++) - { if (!tree_search(tree_nonrecipients, recipients_list[i].address)) { recipient_item *r = recipients_list + i; @@ -6137,8 +6183,6 @@ if (process_recipients != RECIP_IGNORE) } #endif } - } - } DEBUG(D_deliver) { @@ -6203,10 +6247,8 @@ while (addr_new) /* Loop until all addresses dealt with */ not exist. In both cases, dbm_file is NULL. */ if (!(dbm_file = dbfn_open(US"retry", O_RDONLY, &dbblock, FALSE))) - { DEBUG(D_deliver|D_retry|D_route|D_hints_lookup) debug_printf("no retry data available\n"); - } /* Scan the current batch of new addresses, to handle pipes, files and autoreplies, and determine which others are ready for routing. */ @@ -6598,7 +6640,6 @@ while (addr_new) /* Loop until all addresses dealt with */ if ((rc = match_isinlist(addr->domain, (const uschar **)&queue_domains, 0, &domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE, NULL)) != OK) - { if (rc == DEFER) { addr->basic_errno = ERRNO_LISTDEFER; @@ -6610,7 +6651,6 @@ while (addr_new) /* Loop until all addresses dealt with */ addr->next = okaddr; okaddr = addr; } - } else { addr->basic_errno = ERRNO_QUEUE_DOMAIN; @@ -6645,7 +6685,7 @@ while (addr_new) /* Loop until all addresses dealt with */ &addr_succeed, v_none)) == DEFER) retry_add_item(addr, addr->router->retry_use_local_part - ? string_sprintf("R:%s@%s", addr->local_part, addr->domain) + ? string_sprintf("R:%s@%s", addr->local_part, addr->domain) : string_sprintf("R:%s", addr->domain), 0); @@ -7037,6 +7077,7 @@ phase, to minimize cases of half-done things. */ DEBUG(D_deliver) debug_printf(">>>>>>>>>>>>>>>> deliveries are done >>>>>>>>>>>>>>>>\n"); +cancel_cutthrough_connection(TRUE, "deliveries are done"); /* Root privilege is no longer needed */ @@ -7141,10 +7182,9 @@ for (addr_dsntmp = addr_succeed; addr_dsntmp; addr_dsntmp = addr_dsntmp->next) ) { /* copy and relink address_item and send report with all of them at once later */ - address_item *addr_next; - addr_next = addr_senddsn; + address_item * addr_next = addr_senddsn; addr_senddsn = store_get(sizeof(address_item)); - memcpy(addr_senddsn, addr_dsntmp, sizeof(address_item)); + *addr_senddsn = *addr_dsntmp; addr_senddsn->next = addr_next; } else @@ -7254,8 +7294,9 @@ if (addr_senddsn) /* Write the original email out */ + tctx.u.fd = fileno(f); tctx.options = topt_add_return_path | topt_no_body; - transport_write_message(fileno(f), &tctx, 0); + transport_write_message(&tctx, 0); fflush(f); fprintf(f,"\n--%s--\n", bound); @@ -7714,11 +7755,12 @@ wording. */ transport_ctx tctx = {0}; transport_instance tb = {0}; + tctx.u.fd = fileno(f); tctx.tblock = &tb; tctx.options = topt; tb.add_headers = dsnnotifyhdr; - transport_write_message(fileno(f), &tctx, 0); + transport_write_message(&tctx, 0); } fflush(f); @@ -8179,12 +8221,13 @@ else if (addr_defer != (address_item *)(+1)) fflush(f); /* header only as required by RFC. only failure DSN needs to honor RET=FULL */ + tctx.u.fd = fileno(f); tctx.options = topt_add_return_path | topt_no_body; transport_filter_argv = NULL; /* Just in case */ return_path = sender_address; /* In case not previously set */ /* Write the original email out */ - transport_write_message(fileno(f), &tctx, 0); + transport_write_message(&tctx, 0); fflush(f); fprintf(f,"\n--%s--\n", bound); @@ -8414,6 +8457,72 @@ deliver_datafile = -1; return new_sender_address; } + + +void +delivery_re_exec(int exec_type) +{ +uschar * s; + +if (cutthrough.fd >= 0 && cutthrough.callout_hold_only) + { + int pfd[2], channel_fd = cutthrough.fd, pid; + + smtp_peer_options = cutthrough.peer_options; + continue_sequence = 0; + +#ifdef SUPPORT_TLS + if (cutthrough.is_tls) + { + smtp_peer_options |= PEER_OFFERED_TLS; + sending_ip_address = cutthrough.snd_ip; + sending_port = cutthrough.snd_port; + + s = US"socketpair"; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, pfd) != 0) + goto fail; + + s = US"fork"; + if ((pid = fork()) < 0) + goto fail; + + else if (pid == 0) /* child: fork again to totally dosconnect */ + { + close(pfd[1]); + if ((pid = fork())) + _exit(pid ? EXIT_FAILURE : EXIT_SUCCESS); + smtp_proxy_tls(big_buffer, big_buffer_size, pfd[0], 5*60); + exim_exit(0); + } + + close(pfd[0]); + waitpid(pid, NULL, 0); + (void) close(channel_fd); /* release the client socket */ + channel_fd = pfd[1]; + } +#endif + + transport_do_pass_socket(cutthrough.transport, cutthrough.host.name, + cutthrough.host.address, message_id, channel_fd); + } +else + { + cancel_cutthrough_connection(TRUE, "non-continued delivery"); + (void) child_exec_exim(exec_type, FALSE, NULL, FALSE, 2, US"-Mc", message_id); + } +/* Control does not return here. */ + +fail: + log_write(0, + LOG_MAIN | (exec_type == CEE_EXEC_EXIT ? LOG_PANIC : LOG_PANIC_DIE), + "delivery re-exec failed: %s", strerror(errno)); + + /* Get here if exec_type == CEE_EXEC_EXIT. + Note: this must be _exit(), not exit(). */ + + _exit(EX_EXECFAILED); +} + /* vi: aw ai sw=2 */ /* End of deliver.c */