X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/82f90600647a5322e9e7b58fc127eb8be839165c..a5ffa9b475a426bc73366db01f7cc92a3811bc3a:/src/src/deliver.c diff --git a/src/src/deliver.c b/src/src/deliver.c index cb4616e6c..fe156c696 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -3366,15 +3366,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 +3388,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 +3453,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 +3612,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 +3890,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 +4426,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, @@ -4492,9 +4499,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 +4608,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 +4723,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 +4787,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 +4901,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) @@ -6020,9 +6046,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; @@ -6138,8 +6162,6 @@ if (process_recipients != RECIP_IGNORE) } #endif } - } - } DEBUG(D_deliver) { @@ -6204,10 +6226,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. */ @@ -6599,7 +6619,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; @@ -6611,7 +6630,6 @@ while (addr_new) /* Loop until all addresses dealt with */ addr->next = okaddr; okaddr = addr; } - } else { addr->basic_errno = ERRNO_QUEUE_DOMAIN; @@ -6646,7 +6664,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); @@ -7038,6 +7056,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 */ @@ -7254,8 +7273,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 +7734,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 +8200,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 +8436,70 @@ 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 */ + { + if ((pid = fork())) + _exit(pid ? EXIT_FAILURE : EXIT_SUCCESS); + smtp_proxy_tls(big_buffer, big_buffer_size, pfd[0], 5*60); + exim_exit(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 */