From 5032d1cf500b102849d9a47867fbb7b34d871683 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Tue, 13 Jan 2015 11:19:32 +0000 Subject: Multi-recipient cutthrough delivery. Bug 1542 Testing and fixes by: Heiko Schlittermann --- src/src/acl.c | 36 ++++---- src/src/globals.c | 7 +- src/src/globals.h | 12 ++- src/src/receive.c | 74 ++++++++-------- src/src/smtp_in.c | 2 +- src/src/verify.c | 261 ++++++++++++++++++++++++++++++++++++++++-------------- 6 files changed, 271 insertions(+), 121 deletions(-) (limited to 'src') diff --git a/src/src/acl.c b/src/src/acl.c index 8fdae0390..06c1c494c 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -3354,19 +3354,27 @@ for (; cb != NULL; cb = cb->next) break; case CONTROL_CUTTHROUGH_DELIVERY: - if (deliver_freeze) - *log_msgptr = US"frozen"; - else if (queue_only_policy) - *log_msgptr = US"queue-only"; - else if (fake_response == FAIL) - *log_msgptr = US"fakereject"; + if (prdr_requested) + /* Too hard to think about for now. We might in future cutthrough + the case where both sides handle prdr and this-node prdr acl + is "accept" */ + *log_msgptr = string_sprintf(US"PRDR on %s reception\n", arg); else { - cutthrough_delivery = TRUE; - break; + if (deliver_freeze) + *log_msgptr = US"frozen"; + else if (queue_only_policy) + *log_msgptr = US"queue-only"; + else if (fake_response == FAIL) + *log_msgptr = US"fakereject"; + else + { + if (rcpt_count == 1) cutthrough.delivery = TRUE; + break; + } + *log_msgptr = string_sprintf("\"control=%s\" on %s item", + arg, *log_msgptr); } - *log_msgptr = string_sprintf("\"control=%s\" on %s item", - arg, *log_msgptr); return ERROR; } break; @@ -4351,9 +4359,9 @@ ratelimiters_cmd = NULL; log_reject_target = LOG_MAIN|LOG_REJECT; #ifndef DISABLE_PRDR -if (where == ACL_WHERE_RCPT || where == ACL_WHERE_PRDR ) +if (where == ACL_WHERE_RCPT || where == ACL_WHERE_PRDR) #else -if (where == ACL_WHERE_RCPT ) +if (where == ACL_WHERE_RCPT) #endif { adb = address_defaults; @@ -4397,9 +4405,7 @@ case ACL_WHERE_RCPT: #ifndef DISABLE_PRDR case ACL_WHERE_PRDR: #endif - if( rcpt_count > 1 ) - cancel_cutthrough_connection("more than one recipient"); - else if (rc == OK && cutthrough_delivery && cutthrough_fd < 0) + if (rc == OK && cutthrough.delivery && rcpt_count > cutthrough.nrcpt) open_cutthrough_connection(addr); break; diff --git a/src/src/globals.c b/src/src/globals.c index a8670e414..a066f3595 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -506,8 +506,11 @@ int continue_sequence = 1; uschar *continue_transport = NULL; uschar *csa_status = NULL; -BOOL cutthrough_delivery = FALSE; -int cutthrough_fd = -1; +cut_t cutthrough = { + FALSE, /* delivery: when to attempt */ + -1, /* fd: open connection */ + 0, /* nrcpt: number of addresses */ +}; BOOL daemon_listen = FALSE; uschar *daemon_smtp_port = US"smtp"; diff --git a/src/src/globals.h b/src/src/globals.h index 5495f54db..b5ea8a49b 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -291,8 +291,16 @@ extern int continue_sequence; /* Sequence num for continued delivery */ extern uschar *continue_transport; /* Transport for continued delivery */ extern uschar *csa_status; /* Client SMTP Authorization result */ -extern BOOL cutthrough_delivery; /* Deliver in foreground */ -extern int cutthrough_fd; /* Connection for ditto */ + +typedef struct { + BOOL delivery; /* When to attempt */ + int fd; /* Open connection */ + int nrcpt; /* Count of addresses */ + uschar * interface; /* (address of) */ + host_item host; /* Host used */ + address_item addr; /* (Chain of) addresses */ +} cut_t; +extern cut_t cutthrough; /* Deliver-concurrently */ extern BOOL daemon_listen; /* True if listening required */ extern uschar *daemon_smtp_port; /* Can be a list of ports */ diff --git a/src/src/receive.c b/src/src/receive.c index 8ab3166d0..977150c1d 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -997,7 +997,7 @@ switch(where) case ACL_WHERE_DKIM: case ACL_WHERE_MIME: case ACL_WHERE_DATA: - if (cutthrough_fd >= 0 && (acl_removed_headers || acl_added_headers)) + if (cutthrough.fd >= 0 && (acl_removed_headers || acl_added_headers)) { log_write(0, LOG_MAIN|LOG_PANIC, "Header modification in data ACLs" " will not take effect on cutthrough deliveries"); @@ -2807,12 +2807,11 @@ if (filter_test != FTEST_NONE) } /* Cutthrough delivery: - We have to create the Received header now rather than at the end of reception, - so the timestamp behaviour is a change to the normal case. - XXX Ensure this gets documented XXX. - Having created it, send the headers to the destination. -*/ -if (cutthrough_fd >= 0) +We have to create the Received header now rather than at the end of reception, +so the timestamp behaviour is a change to the normal case. +XXX Ensure this gets documented XXX. +Having created it, send the headers to the destination. */ +if (cutthrough.fd >= 0) { if (received_count > received_headers_max) { @@ -3184,56 +3183,61 @@ else rc = OK; while ((item = string_nextinlist(&ptr, &sep, itembuf, - sizeof(itembuf))) != NULL) + sizeof(itembuf)))) { /* Prevent running ACL for an empty item */ if (!item || (item[0] == '\0')) continue; - /* Only run ACL once for each domain or identity, no matter how often it - appears in the expanded list. */ - if (seen_items != NULL) + + /* Only run ACL once for each domain or identity, + no matter how often it appears in the expanded list. */ + if (seen_items) { uschar *seen_item = NULL; uschar seen_item_buf[256]; uschar *seen_items_list = seen_items; - int seen_this_item = 0; + BOOL seen_this_item = FALSE; while ((seen_item = string_nextinlist(&seen_items_list, &sep, seen_item_buf, - sizeof(seen_item_buf))) != NULL) - { - if (Ustrcmp(seen_item,item) == 0) - { - seen_this_item = 1; - break; - } - } - - if (seen_this_item > 0) + sizeof(seen_item_buf)))) + if (Ustrcmp(seen_item,item) == 0) + { + seen_this_item = TRUE; + break; + } + + if (seen_this_item) { DEBUG(D_receive) - debug_printf("acl_smtp_dkim: skipping signer %s, already seen\n", item); + debug_printf("acl_smtp_dkim: skipping signer %s, " + "already seen\n", item); continue; } - seen_items = string_append(seen_items,&seen_items_size,&seen_items_offset,1,":"); + seen_items = string_append(seen_items, &seen_items_size, + &seen_items_offset, 1, ":"); } - seen_items = string_append(seen_items,&seen_items_size,&seen_items_offset,1,item); + seen_items = string_append(seen_items, &seen_items_size, + &seen_items_offset, 1, item); seen_items[seen_items_offset] = '\0'; DEBUG(D_receive) - debug_printf("calling acl_smtp_dkim for dkim_cur_signer=%s\n", item); + debug_printf("calling acl_smtp_dkim for dkim_cur_signer=%s\n", + item); dkim_exim_acl_setup(item); - rc = acl_check(ACL_WHERE_DKIM, NULL, acl_smtp_dkim, &user_msg, &log_msg); + rc = acl_check(ACL_WHERE_DKIM, NULL, acl_smtp_dkim, + &user_msg, &log_msg); if (rc != OK) - { - DEBUG(D_receive) - debug_printf("acl_smtp_dkim: acl_check returned %d on %s, skipping remaining items\n", rc, item); - cancel_cutthrough_connection("dkim acl not ok"); - break; - } + { + DEBUG(D_receive) + debug_printf("acl_smtp_dkim: acl_check returned %d on %s, " + "skipping remaining items\n", rc, item); + cancel_cutthrough_connection("dkim acl not ok"); + break; + } } add_acl_headers(ACL_WHERE_DKIM, US"DKIM"); if (rc == DISCARD) @@ -3957,7 +3961,7 @@ for this message. */ XXX We do not handle queue-only, freezing, or blackholes. */ -if(cutthrough_fd >= 0) +if(cutthrough.fd >= 0) { uschar * msg= cutthrough_finaldot(); /* Ask the target system to accept the messsage */ /* Logging was done in finaldot() */ @@ -4103,7 +4107,7 @@ if (smtp_input) case TMP_REJ: message_id[0] = 0; /* Prevent a delivery from starting */ default:break; } - cutthrough_delivery = FALSE; + cutthrough.delivery = FALSE; } /* For batched SMTP, generate an error message on failure, and do diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 4fc2cfd41..4e4cdb825 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -4448,7 +4448,7 @@ while (done <= 0) ACL may have delayed. To handle cutthrough delivery enforce a dummy call to get the DATA command sent. */ - if (acl_smtp_predata == NULL && cutthrough_fd < 0) rc = OK; else + if (acl_smtp_predata == NULL && cutthrough.fd < 0) rc = OK; else { uschar * acl= acl_smtp_predata ? acl_smtp_predata : US"accept"; enable_dollar_recipients = TRUE; diff --git a/src/src/verify.c b/src/src/verify.c index 96740f8f3..4474f2c5b 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -14,7 +14,6 @@ caching was contributed by Kevin Fleming (but I hacked it around a bit). */ #define CUTTHROUGH_CMD_TIMEOUT 30 /* timeout for cutthrough-routing calls */ #define CUTTHROUGH_DATA_TIMEOUT 60 /* timeout for cutthrough-routing calls */ -address_item cutthrough_addr; static smtp_outblock ctblock; uschar ctbuffer[8192]; @@ -39,6 +38,7 @@ static tree_node *dnsbl_cache = NULL; #define MT_NOT 1 #define MT_ALL 2 +static uschar cutthrough_response(char, uschar **); /************************************************* @@ -189,12 +189,12 @@ from_address = US""; if (is_recipient) { - if ((options & vopt_callout_recipsender) != 0) + if (options & vopt_callout_recipsender) { address_key = string_sprintf("%s/<%s>", addr->address, sender_address); from_address = sender_address; } - else if ((options & vopt_callout_recippmaster) != 0) + else if (options & vopt_callout_recippmaster) { address_key = string_sprintf("%s/", addr->address, qualify_domain_sender); @@ -410,6 +410,113 @@ else if (smtp_out != NULL && !disable_callout_flush) mac_smtp_fflush(); +/* cutthrough-multi: if a nonfirst rcpt has the same routing as the first, +and we are holding a cutthrough conn open, we can just append the rcpt to +that conn for verification purposes (and later delivery also). Simplest +coding means skipping this whole loop and doing the append separately. + +We will need to remember it has been appended so that rcpt-acl tail code +can do it there for the non-rcpt-verify case. For this we keep an addresscount. +*/ + + /* Can we re-use an open cutthrough connection? */ + if ( cutthrough.fd >= 0 + && (options & (vopt_callout_recipsender | vopt_callout_recippmaster)) + == vopt_callout_recipsender + && !random_local_part + && !pm_mailfrom + ) + { + if (addr->transport == cutthrough.addr.transport) + for (host = host_list; host; host = host->next) + if (Ustrcmp(host->address, cutthrough.host.address) == 0) + { + int host_af; + uschar *interface = NULL; /* Outgoing interface to use; NULL => any */ + int port = 25; + + deliver_host = host->name; + deliver_host_address = host->address; + deliver_host_port = host->port; + deliver_domain = addr->domain; + transport_name = addr->transport->name; + + host_af = (Ustrchr(host->address, ':') == NULL)? AF_INET:AF_INET6; + + if (!smtp_get_interface(tf->interface, host_af, addr, NULL, &interface, + US"callout") || + !smtp_get_port(tf->port, addr, &port, US"callout")) + log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address, + addr->message); + + if ( ( interface == cutthrough.interface + || ( interface + && cutthrough.interface + && Ustrcmp(interface, cutthrough.interface) == 0 + ) ) + && port == cutthrough.host.port + ) + { + uschar * resp; + + /* Match! Send the RCPT TO, append the addr, set done */ + done = + smtp_write_command(&ctblock, FALSE, "RCPT TO:<%.1000s>\r\n", + transport_rcpt_address(addr, + (addr->transport == NULL)? FALSE : + addr->transport->rcpt_include_affixes)) >= 0 && + cutthrough_response('2', &resp) == '2'; + + /* This would go horribly wrong if a callout fail was ignored by ACL. + We punt by abandoning cutthrough on a reject, like the + first-rcpt does. */ + + if (done) + { + address_item * na = store_get(sizeof(address_item)); + *na = cutthrough.addr; + cutthrough.addr = *addr; + cutthrough.addr.host_used = &cutthrough.host; + cutthrough.addr.next = na; + + cutthrough.nrcpt++; + } + else + { + cancel_cutthrough_connection("recipient rejected"); + if (errno == ETIMEDOUT) + { + HDEBUG(D_verify) debug_printf("SMTP timeout\n"); + } + else if (errno == 0) + { + if (*resp == 0) + Ustrcpy(resp, US"connection dropped"); + + addr->message = + string_sprintf("response to \"%s\" from %s [%s] was: %s", + big_buffer, host->name, host->address, + string_printing(resp)); + + addr->user_message = + string_sprintf("Callout verification failed:\n%s", resp); + + /* Hard rejection ends the process */ + + if (resp[0] == '5') /* Address rejected */ + { + yield = FAIL; + done = TRUE; + } + } + } + } + break; + } + if (!done) + cancel_cutthrough_connection("incompatible connection"); + } + /* Now make connections to the hosts and do real callouts. The list of hosts is passed in as an argument. */ @@ -692,9 +799,9 @@ else ob->command_timeout = callout; rc = tls_client_start(inblock.sock, host, addr, addr->transport -#ifdef EXPERIMENTAL_DANE +# ifdef EXPERIMENTAL_DANE , dane ? &tlsa_dnsa : NULL -#endif +# endif ); ob->command_timeout = oldtimeout; @@ -709,10 +816,10 @@ else ) { (void)close(inblock.sock); -#ifdef EXPERIMENTAL_EVENT +# ifdef EXPERIMENTAL_EVENT (void) event_raise(addr->transport->event_action, US"tcp:close", NULL); -#endif +# endif log_write(0, LOG_MAIN, "TLS session failure: delivering unencrypted " "to %s [%s] (not in hosts_require_tls)", host->name, host->address); suppress_tls = TRUE; @@ -741,9 +848,9 @@ else /* If the host is required to use a secure channel, ensure that we have one. */ if (tls_out.active < 0) if ( -#ifdef EXPERIMENTAL_DANE +# ifdef EXPERIMENTAL_DANE dane || -#endif +# endif verify_check_given_host(&ob->hosts_require_tls, host) == OK ) { @@ -757,7 +864,7 @@ else goto TLS_FAILED; } - #endif /*SUPPORT_TLS*/ +#endif /*SUPPORT_TLS*/ done = TRUE; /* so far so good; have response to HELO */ @@ -765,17 +872,17 @@ else /* For now, transport_filter by cutthrough-delivery is not supported */ /* Need proper integration with the proper transport mechanism. */ - if (cutthrough_delivery) + if (cutthrough.delivery) { if (addr->transport->filter_command) { - cutthrough_delivery= FALSE; + cutthrough.delivery = FALSE; HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of transport filter\n"); } #ifndef DISABLE_DKIM if (ob->dkim_domain) { - cutthrough_delivery= FALSE; + cutthrough.delivery = FALSE; HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of DKIM signing\n"); } #endif @@ -887,6 +994,8 @@ else else if (errno == 0) { + cancel_cutthrough_connection("random-recipient"); + if (randombuffer[0] == '5') new_domain_record.random_result = ccache_reject; @@ -932,8 +1041,9 @@ else if (done && pm_mailfrom != NULL) { - /*XXX not suitable for cutthrough - sequencing problems */ - cutthrough_delivery= FALSE; + /*XXX not suitable for cutthrough - we cannot afford to do an RSET + and lose the original mail-from */ + cancel_cutthrough_connection("postmaster verify"); HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of postmaster verify\n"); done = @@ -1028,30 +1138,34 @@ else /* End the SMTP conversation and close the connection. */ - /* Cutthrough - on a successfull connect and recipient-verify with use-sender - and we have no cutthrough conn so far + /* Cutthrough - on a successfull connect and recipient-verify with + use-sender and we are 1st rcpt and have no cutthrough conn so far here is where we want to leave the conn open */ - if ( cutthrough_delivery + if ( cutthrough.delivery + && rcpt_count == 1 && done && yield == OK && (options & (vopt_callout_recipsender|vopt_callout_recippmaster)) == vopt_callout_recipsender && !random_local_part && !pm_mailfrom - && cutthrough_fd < 0 + && cutthrough.fd < 0 ) { - cutthrough_fd= outblock.sock; /* We assume no buffer in use in the outblock */ - cutthrough_addr = *addr; /* Save the address_item for later logging */ - cutthrough_addr.next = NULL; - cutthrough_addr.host_used = store_get(sizeof(host_item)); - *(cutthrough_addr.host_used) = *host; + cutthrough.fd = outblock.sock; /* We assume no buffer in use in the outblock */ + cutthrough.nrcpt = 1; + cutthrough.interface = interface; + cutthrough.host = *host; + cutthrough.addr = *addr; /* Save the address_item for later logging */ + cutthrough.addr.next = NULL; + cutthrough.addr.host_used = &cutthrough.host; if (addr->parent) - *(cutthrough_addr.parent = store_get(sizeof(address_item)))= *addr->parent; + *(cutthrough.addr.parent = store_get(sizeof(address_item))) = + *addr->parent; ctblock.buffer = ctbuffer; ctblock.buffersize = sizeof(ctbuffer); ctblock.ptr = ctbuffer; /* ctblock.cmd_count = 0; ctblock.authenticating = FALSE; */ - ctblock.sock = cutthrough_fd; + ctblock.sock = cutthrough.fd; } else { @@ -1176,7 +1290,8 @@ address_item addr2; get rewritten. */ addr2 = *addr; -HDEBUG(D_acl) debug_printf("----------- start cutthrough setup ------------\n"); +HDEBUG(D_acl) debug_printf("----------- %s cutthrough setup ------------\n", + rcpt_count > 1 ? "more" : "start"); (void) verify_address(&addr2, NULL, vopt_is_recipient | vopt_callout_recipsender | vopt_callout_no_cache, CUTTHROUGH_CMD_TIMEOUT, -1, -1, @@ -1191,14 +1306,14 @@ return; static BOOL cutthrough_send(int n) { -if(cutthrough_fd < 0) +if(cutthrough.fd < 0) return TRUE; if( #ifdef SUPPORT_TLS - (tls_out.active == cutthrough_fd) ? tls_write(FALSE, ctblock.buffer, n) : + (tls_out.active == cutthrough.fd) ? tls_write(FALSE, ctblock.buffer, n) : #endif - send(cutthrough_fd, ctblock.buffer, n, 0) > 0 + send(cutthrough.fd, ctblock.buffer, n, 0) > 0 ) { transport_count += n; @@ -1230,7 +1345,7 @@ return TRUE; BOOL cutthrough_puts(uschar * cp, int n) { -if (cutthrough_fd < 0) return TRUE; +if (cutthrough.fd < 0) return TRUE; if (_cutthrough_puts(cp, n)) return TRUE; cancel_cutthrough_connection("transmit failed"); return FALSE; @@ -1238,7 +1353,7 @@ return FALSE; static BOOL -_cutthrough_flush_send( void ) +_cutthrough_flush_send(void) { int n= ctblock.ptr-ctblock.buffer; @@ -1251,7 +1366,7 @@ return TRUE; /* Send out any bufferred output. Return boolean success. */ BOOL -cutthrough_flush_send( void ) +cutthrough_flush_send(void) { if (_cutthrough_flush_send()) return TRUE; cancel_cutthrough_connection("transmit failed"); @@ -1260,7 +1375,7 @@ return FALSE; BOOL -cutthrough_put_nl( void ) +cutthrough_put_nl(void) { return cutthrough_puts(US"\r\n", 2); } @@ -1278,7 +1393,7 @@ inblock.buffer = inbuffer; inblock.buffersize = sizeof(inbuffer); inblock.ptr = inbuffer; inblock.ptrend = inbuffer; -inblock.sock = cutthrough_fd; +inblock.sock = cutthrough.fd; /* this relies on (inblock.sock == tls_out.active) */ if(!smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), expect, CUTTHROUGH_DATA_TIMEOUT)) cancel_cutthrough_connection("target timeout on read"); @@ -1286,7 +1401,7 @@ if(!smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), expect, if(copy != NULL) { uschar * cp; - *copy= cp= string_copy(responsebuffer); + *copy = cp = string_copy(responsebuffer); /* Trim the trailing end of line */ cp += Ustrlen(responsebuffer); if(cp > *copy && cp[-1] == '\n') *--cp = '\0'; @@ -1299,9 +1414,9 @@ return responsebuffer[0]; /* Negotiate dataphase with the cutthrough target, returning success boolean */ BOOL -cutthrough_predata( void ) +cutthrough_predata(void) { -if(cutthrough_fd < 0) +if(cutthrough.fd < 0) return FALSE; HDEBUG(D_transport|D_acl|D_v) debug_printf(" SMTP>> DATA\n"); @@ -1332,9 +1447,9 @@ return TRUE; /* Expands newlines to wire format (CR,NL). */ /* Also sends header-terminating blank line. */ BOOL -cutthrough_headers_send( void ) +cutthrough_headers_send(void) { -if(cutthrough_fd < 0) +if(cutthrough.fd < 0) return FALSE; /* We share a routine with the mainline transport to handle header add/remove/rewrites, @@ -1342,10 +1457,12 @@ if(cutthrough_fd < 0) */ HDEBUG(D_acl) debug_printf("----------- start cutthrough headers send -----------\n"); -if (!transport_headers_send(&cutthrough_addr, cutthrough_fd, - cutthrough_addr.transport->add_headers, cutthrough_addr.transport->remove_headers, +if (!transport_headers_send(&cutthrough.addr, cutthrough.fd, + cutthrough.addr.transport->add_headers, + cutthrough.addr.transport->remove_headers, &cutthrough_write_chunk, TRUE, - cutthrough_addr.transport->rewrite_rules, cutthrough_addr.transport->rewrite_existflags)) + cutthrough.addr.transport->rewrite_rules, + cutthrough.addr.transport->rewrite_existflags)) return FALSE; HDEBUG(D_acl) debug_printf("----------- done cutthrough headers send ------------\n"); @@ -1354,9 +1471,9 @@ return TRUE; static void -close_cutthrough_connection( const char * why ) +close_cutthrough_connection(const char * why) { -if(cutthrough_fd >= 0) +if(cutthrough.fd >= 0) { /* We could be sending this after a bunch of data, but that is ok as the only way to cancel the transfer in dataphase is to drop the tcp @@ -1371,18 +1488,18 @@ if(cutthrough_fd >= 0) #ifdef SUPPORT_TLS tls_close(FALSE, TRUE); #endif - (void)close(cutthrough_fd); - cutthrough_fd= -1; + (void)close(cutthrough.fd); + cutthrough.fd = -1; HDEBUG(D_acl) debug_printf("----------- cutthrough shutdown (%s) ------------\n", why); } ctblock.ptr = ctbuffer; } void -cancel_cutthrough_connection( const char * why ) +cancel_cutthrough_connection(const char * why) { close_cutthrough_connection(why); -cutthrough_delivery= FALSE; +cutthrough.delivery = FALSE; } @@ -1394,33 +1511,45 @@ cutthrough_delivery= FALSE; Return smtp response-class digit. */ uschar * -cutthrough_finaldot( void ) +cutthrough_finaldot(void) { +uschar res; +address_item * addr; HDEBUG(D_transport|D_acl|D_v) debug_printf(" SMTP>> .\n"); /* Assume data finshed with new-line */ -if(!cutthrough_puts(US".", 1) || !cutthrough_put_nl() || !cutthrough_flush_send()) - return cutthrough_addr.message; +if( !cutthrough_puts(US".", 1) + || !cutthrough_put_nl() + || !cutthrough_flush_send() + ) + return cutthrough.addr.message; -switch(cutthrough_response('2', &cutthrough_addr.message)) +res = cutthrough_response('2', &cutthrough.addr.message); +for (addr = &cutthrough.addr; addr; addr = addr->next) { - case '2': - delivery_log(LOG_MAIN, &cutthrough_addr, (int)'>', NULL); - close_cutthrough_connection("delivered"); - break; + addr->message = cutthrough.addr.message; + switch(res) + { + case '2': + delivery_log(LOG_MAIN, addr, (int)'>', NULL); + close_cutthrough_connection("delivered"); + break; - case '4': - delivery_log(LOG_MAIN, &cutthrough_addr, 0, US"tmp-reject from cutthrough after DATA:"); - break; + case '4': + delivery_log(LOG_MAIN, addr, 0, + US"tmp-reject from cutthrough after DATA:"); + break; - case '5': - delivery_log(LOG_MAIN|LOG_REJECT, &cutthrough_addr, 0, US"rejected after DATA:"); - break; + case '5': + delivery_log(LOG_MAIN|LOG_REJECT, addr, 0, + US"rejected after DATA:"); + break; - default: - break; + default: + break; + } } - return cutthrough_addr.message; +return cutthrough.addr.message; } -- cgit v1.2.3