commit aa7751be078fe9a20efa2cf2a8856fadb98f4178 Author: Jeremy Harris Date: Sun Jun 28 15:14:02 2015 +0100 Compiler quietening diff --git a/src/src/dns.c b/src/src/dns.c index 64958d9..a239bec 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -40,7 +40,6 @@ fakens_search(const uschar *domain, int type, uschar *answerptr, int size) { int len = Ustrlen(domain); int asize = size; /* Locally modified */ -uschar *endname; uschar name[256]; uschar utilname[256]; uschar *aptr = answerptr; /* Locally modified */ @@ -51,7 +50,6 @@ struct stat statbuf; if (domain[len - 1] == '.') len--; Ustrncpy(name, domain, len); name[len] = 0; -endname = name + len; /* Look for the fakens utility, and if it exists, call it. */ @@ -86,7 +84,7 @@ if (stat(CS utilname, &statbuf) >= 0) asize -= rc; /* may need to be passed on to res_search(). */ } - /* If we ran out of output buffer before exhasting the return, + /* If we ran out of output buffer before exhausting the return, carry on reading and counting it. */ if (asize == 0) diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index cf0a5d6..711ee86 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -3293,7 +3293,7 @@ while (done <= 0) pid_t pid; int start, end, sender_domain, recipient_domain; int ptr, size, rc; - int c, i; + int c; auth_instance *au; uschar *orcpt = NULL; int flags; diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index e1dcd77..d8377fd 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -708,7 +708,7 @@ if ( (nid = OBJ_sn2nid (CCS exp_curve)) == NID_undef if (!(ecdh = EC_KEY_new_by_curve_name(nid))) { - tls_error("Unable to create ec curve", host, NULL); + tls_error(US"Unable to create ec curve", host, NULL); return FALSE; } diff --git a/src/src/utf8.c b/src/src/utf8.c index a0ec003..8a7cf38 100644 --- a/src/src/utf8.c +++ b/src/src/utf8.c @@ -127,7 +127,7 @@ if ((rc = punycode_decode(p_len, CCS alabel+4, &p_len, p, NULL)) != PUNYCODE_SUC return NULL; } -s = stringprep_ucs4_to_utf8(p, p_len, NULL, &p_len); +s = US stringprep_ucs4_to_utf8(p, p_len, NULL, &p_len); res = string_copyn(s, p_len); free(s); return res; commit 5b881b5a8e0d7bc540f4b63cc9559d2cb1775965 Author: Heiko Schlittermann (HS12-RIPE) Date: Thu Jul 30 09:43:51 2015 +0200 Docs: Add a note about the maximum spam bar length diff --git a/src/src/spam.h b/src/src/spam.h index 05ab655..2fe7380 100644 --- a/src/src/spam.h +++ b/src/src/spam.h @@ -12,7 +12,8 @@ /* timeout for reading and writing spamd */ #define SPAMD_TIMEOUT 120 -/* maximum length of the spam bar */ +/* maximum length of the spam bar, please update the + * spec, the max length is mentioned there */ #define MAX_SPAM_BAR_CHARS 50 /* SHUT_WR seems to be undefined on Unixware ? */ commit 98716abe2b636d275e866f3ad6374cb70bf6e504 Author: Jeremy Harris Date: Sun Aug 2 13:44:31 2015 +0100 Testsuite: Add testcase for OCSP-nonaware client, to supporting server. Bug 1664 The logfile here is for (I hope) the passing case, though the fixed GnuTLS library is not yet available. Also due to the bug, client-gnutls is not usable for the test; client-openssl must be used - meaning that a GnuTLS-only system cannot run the testcase: OCSP-GnuTLS/5650 OCSP stapling, server ** Command 15 ("client-ssl", starting at line 98) ** Return code 127 (expected 0) diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 61ed0e8..e2ac17c 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -842,7 +842,7 @@ if ( !host /* server */ gnutls_certificate_set_ocsp_status_request_function(state->x509_cred, server_ocsp_stapling_cb, state->exp_tls_ocsp_file); - DEBUG(D_tls) debug_printf("Set OCSP response file %s\n", &state->exp_tls_ocsp_file); + DEBUG(D_tls) debug_printf("OCSP response file = %s\n", state->exp_tls_ocsp_file); } #endif commit 9196d5bf543d75a81ae0825a352920d27241c325 Author: Jeremy Harris Date: Sun Aug 2 13:53:15 2015 +0100 GnuTLS: avoid using OCSP on buggy library versions. Bug 1664 diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index e2ac17c..8aabc5c 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -176,6 +176,8 @@ static const char * const exim_default_gnutls_priority = "NORMAL"; static BOOL exim_gnutls_base_init_done = FALSE; +static BOOL gnutls_buggy_ocsp = FALSE; + /* ------------------------------------------------------------------------ */ /* macros */ @@ -831,18 +833,25 @@ if ( !host /* server */ && tls_ocsp_file ) { - if (!expand_check(tls_ocsp_file, US"tls_ocsp_file", - &state->exp_tls_ocsp_file)) - return DEFER; + if (gnutls_buggy_ocsp) + { + DEBUG(D_tls) debug_printf("GnuTLS library is buggy for OCSP; avoiding\n"); + } + else + { + if (!expand_check(tls_ocsp_file, US"tls_ocsp_file", + &state->exp_tls_ocsp_file)) + return DEFER; - /* Use the full callback method for stapling just to get observability. - More efficient would be to read the file once only, if it never changed - (due to SNI). Would need restart on file update, or watch datestamp. */ + /* Use the full callback method for stapling just to get observability. + More efficient would be to read the file once only, if it never changed + (due to SNI). Would need restart on file update, or watch datestamp. */ - gnutls_certificate_set_ocsp_status_request_function(state->x509_cred, - server_ocsp_stapling_cb, state->exp_tls_ocsp_file); + gnutls_certificate_set_ocsp_status_request_function(state->x509_cred, + server_ocsp_stapling_cb, state->exp_tls_ocsp_file); - DEBUG(D_tls) debug_printf("OCSP response file = %s\n", state->exp_tls_ocsp_file); + DEBUG(D_tls) debug_printf("OCSP response file = %s\n", state->exp_tls_ocsp_file); + } } #endif @@ -1011,6 +1020,35 @@ return OK; * Initialize for GnuTLS * *************************************************/ + +static BOOL +tls_is_buggy_ocsp(void) +{ +const uschar * s; +uschar maj, mid, mic; + +s = CUS gnutls_check_version(NULL); +maj = atoi(CCS s); +if (maj == 3) + { + while (*s && *s != '.') s++; + mid = atoi(CCS ++s); + if (mid <= 2) + return TRUE; + else if (mid >= 5) + return FALSE; + else + { + while (*s && *s != '.') s++; + mic = atoi(CCS ++s); + return mic <= (mid == 3 ? 16 : 3); + } + } +return FALSE; +} + + + /* Called from both server and client code. In the case of a server, errors before actual TLS negotiation return DEFER. @@ -1074,6 +1112,9 @@ if (!exim_gnutls_base_init_done) } #endif + if ((gnutls_buggy_ocsp = tls_is_buggy_ocsp())) + log_write(0, LOG_MAIN, "OCSP unusable with this GnuTLS library version"); + exim_gnutls_base_init_done = TRUE; } commit 63f0dbe0ca0aba7be3bc8807f45c8703a1cfafe1 Author: Jeremy Harris Date: Thu Aug 6 21:38:33 2015 +0100 OpenSSL: fix complile on pre-EC-capable library versions diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index d8377fd..b1dccb8 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -659,15 +659,15 @@ Returns: TRUE if OK (nothing to set up, or setup worked) static BOOL init_ecdh(SSL_CTX * sctx, host_item * host) { +#ifdef OPENSSL_NO_ECDH +return TRUE; +#else + EC_KEY * ecdh; uschar * exp_curve; int nid; BOOL rv; -#ifdef OPENSSL_NO_ECDH -return TRUE; -#else - if (host) /* No ECDH setup for clients, only for servers */ return TRUE; commit 755762fd4c420cabbcba4a9c79947e926fa82219 Author: Heiko Schlittermann (HS12-RIPE) Date: Sun Aug 9 23:29:44 2015 +0200 Compiler quietening diff --git a/src/src/malware.c b/src/src/malware.c index 141c6ea..96af1e8 100644 --- a/src/src/malware.c +++ b/src/src/malware.c @@ -886,7 +886,7 @@ if (!malware_ok) string_sprintf("unable to read result (%s)", strerror(errno)), sock); - for (p[bread] = '\0'; q = Ustrchr(p, '\n'); p = q+1) + for (p[bread] = '\0'; (q = Ustrchr(p, '\n')); p = q+1) { *q = '\0'; @@ -1880,6 +1880,9 @@ if (!malware_ok) /* here for any unexpected response from the scanner */ goto endloop; + + case AVA_DONE: log_write(0, LOG_PANIC, "%s:%d:%s: should not happen", + __FILE__, __LINE__, __FUNCTION__); } } } diff --git a/src/src/spam.c b/src/src/spam.c index ca8d207..457aa3a 100644 --- a/src/src/spam.c +++ b/src/src/spam.c @@ -297,7 +297,7 @@ start = time(NULL); sd = (spamd_address_container *)store_get(sizeof(spamd_address_container)); for (sublist = address, args = 0, spamd_param_init(sd); - s = string_nextinlist(&sublist, &sublist_sep, NULL, 0); + (s = string_nextinlist(&sublist, &sublist_sep, NULL, 0)); args++ ) { diff --git a/src/src/tlscert-openssl.c b/src/src/tlscert-openssl.c index 72808a7..19db040 100644 --- a/src/src/tlscert-openssl.c +++ b/src/src/tlscert-openssl.c @@ -127,7 +127,7 @@ else { struct tm tm; struct tm * tm_p = &tm; - BOOL mod_tz; + BOOL mod_tz = TRUE; uschar * tz = to_tz(US"GMT0"); /* need to call strptime with baseline TZ */ /* Parse OpenSSL ASN1_TIME_print output. A shame there seems to @@ -164,7 +164,7 @@ else } } - if (mod_tz); + if (mod_tz) restore_tz(tz); } BIO_free(bp); commit 4bd6107db73131e1b48f1902833fd7c637c08bda Author: Heiko Schlittermann (HS12-RIPE) Date: Mon Aug 10 00:39:36 2015 +0200 Really re-select() when interrupted. diff --git a/src/src/ip.c b/src/src/ip.c index ead7299..cb54f16 100644 --- a/src/src/ip.c +++ b/src/src/ip.c @@ -499,7 +499,7 @@ do /* If the socket is ready, break out of the loop. */ } -while (!FD_ISSET(fd, &select_inset)); +while (rc < 0 || !FD_ISSET(fd, &select_inset)); return TRUE; } commit 505d976aa23de4294751162dee6466e335c96fbf Author: Heiko Schlittermann (HS12-RIPE) Date: Tue Aug 11 09:13:11 2015 +0200 Build: Make test_{os,parse,dbfn,string} work diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base index 1d5a5f6..95110e6 100644 --- a/src/OS/Makefile-Base +++ b/src/OS/Makefile-Base @@ -743,11 +743,11 @@ sa-os.o: $(HDRS) os.c # These are the test targets themselves test_dbfn: config.h dbfn.c dummies.o sa-globals.o sa-os.o store.o \ - string.o tod.o version.o + string.o tod.o version.o utf8.o $(CC) -c $(CFLAGS) $(INCLUDE) -DSTAND_ALONE dbfn.c $(LNCC) -o test_dbfn $(LFLAGS) dbfn.o \ dummies.o sa-globals.o sa-os.o store.o string.o \ - tod.o version.o $(LIBS) $(DBMLIB) + tod.o version.o utf8.o $(LIBS) $(DBMLIB) $(LDFLAGS) rm -f dbfn.o test_host: config.h child.c host.c dns.c dummies.c sa-globals.o os.o \ @@ -761,23 +761,24 @@ test_host: config.h child.c host.c dns.c dummies.c sa-globals.o os.o \ tod.o tree.o $(LIBS) $(LIBRESOLV) rm -f child.o dummies.o host.o dns.o -test_os: os.h os.c dummies.o sa-globals.o store.o string.o tod.o +test_os: os.h os.c dummies.o sa-globals.o store.o string.o tod.o utf8.o $(CC) -c $(CFLAGS) $(INCLUDE) -DSTAND_ALONE os.c $(LNCC) -o test_os $(LFLAGS) os.o dummies.o \ - sa-globals.o store.o string.o tod.o $(LIBS) + sa-globals.o store.o string.o tod.o utf8.o $(LIBS) $(LDFLAGS) rm -f os.o test_parse: config.h parse.c dummies.o sa-globals.o \ - store.o string.o tod.o version.o + store.o string.o tod.o version.o utf8.o $(CC) -c $(CFLAGS) $(INCLUDE) -DSTAND_ALONE parse.c $(LNCC) -o test_parse $(LFLAGS) parse.o \ - dummies.o sa-globals.o store.o string.o tod.o version.o + dummies.o sa-globals.o store.o string.o tod.o version.o \ + utf8.o $(LDFLAGS) rm -f parse.o -test_string: config.h string.c dummies.o sa-globals.o store.o tod.o +test_string: config.h string.c dummies.o sa-globals.o store.o tod.o utf8.o $(CC) -c $(CFLAGS) $(INCLUDE) -DSTAND_ALONE string.c $(LNCC) -o test_string $(LFLAGS) -DSTAND_ALONE string.o \ - dummies.o sa-globals.o store.o tod.o $(LIBS) + dummies.o sa-globals.o store.o tod.o utf8.o $(LIBS) $(LDFLAGS) rm -f string.o # End diff --git a/src/src/host.c b/src/src/host.c index 94126d4..31c2bbf 100644 --- a/src/src/host.c +++ b/src/src/host.c @@ -3212,7 +3212,7 @@ while (Ufgets(buffer, 256, stdin) != NULL) else { int flags = whichrrs; - dnssec d; + dnssec_domains d; h.name = buffer; h.next = NULL; commit 2ef7ed082481b2dccd3c2e0eae849b24bf0b172a Author: Heiko Schlittermann (HS12-RIPE) Date: Tue Aug 11 17:36:29 2015 +0200 Fix ESMTP MAIL command option processing If the address containes spaces, the option processing was confused. diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 711ee86..effc636 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -9,6 +9,7 @@ #include "exim.h" +#include /* Initialize for TCP wrappers if so configured. It appears that the macro @@ -232,6 +233,7 @@ static uschar *protocols[] = { /* Sanity check and validate optional args to MAIL FROM: envelope */ enum { + ENV_MAIL_OPT_NULL, ENV_MAIL_OPT_SIZE, ENV_MAIL_OPT_BODY, ENV_MAIL_OPT_AUTH, #ifndef DISABLE_PRDR ENV_MAIL_OPT_PRDR, @@ -240,7 +242,6 @@ enum { #ifdef EXPERIMENTAL_INTERNATIONAL ENV_MAIL_OPT_UTF8, #endif - ENV_MAIL_OPT_NULL }; typedef struct { uschar * name; /* option requested during MAIL cmd */ @@ -260,7 +261,8 @@ static env_mail_type_t env_mail_type_list[] = { #ifdef EXPERIMENTAL_INTERNATIONAL { US"SMTPUTF8",ENV_MAIL_OPT_UTF8, FALSE }, /* rfc6531 */ #endif - { US"NULL", ENV_MAIL_OPT_NULL, FALSE } + /* keep this the last entry */ + { US"NULL", ENV_MAIL_OPT_NULL, FALSE }, }; /* When reading SMTP from a remote host, we have to use our own versions of the @@ -3887,7 +3889,7 @@ while (done <= 0) if (!extract_option(&name, &value)) break; for (mail_args = env_mail_type_list; - (char *)mail_args < (char *)env_mail_type_list + sizeof(env_mail_type_list); + mail_args->value != ENV_MAIL_OPT_NULL; mail_args++ ) if (strcmpic(name, mail_args->name) == 0) @@ -4066,15 +4068,17 @@ while (done <= 0) } break; #endif - /* Unknown option. Stick back the terminator characters and break + /* No valid option. Stick back the terminator characters and break the loop. Do the name-terminator second as extract_option sets - value==name when it found no equal-sign. - An error for a malformed address will occur. */ - default: + value==name when it found no equal-sign. + An error for a malformed address will occur. */ + case ENV_MAIL_OPT_NULL: value[-1] = '='; name[-1] = ' '; arg_error = TRUE; break; + + default: assert(0); } /* Break out of for loop if switch() had bad argument or when start of the email address is reached */ commit 4fb7df6d044a39151e72346ac0d67ac09686f704 Author: Jeremy Harris Date: Tue Aug 11 22:54:53 2015 +0100 GnuTLS: avoid whining about OCSP when not requested by config diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 8aabc5c..fe18094 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -176,7 +176,9 @@ static const char * const exim_default_gnutls_priority = "NORMAL"; static BOOL exim_gnutls_base_init_done = FALSE; +#ifndef DISABLE_OCSP static BOOL gnutls_buggy_ocsp = FALSE; +#endif /* ------------------------------------------------------------------------ */ @@ -1021,6 +1023,8 @@ return OK; *************************************************/ +#ifndef DISABLE_OCSP + static BOOL tls_is_buggy_ocsp(void) { @@ -1047,6 +1051,7 @@ if (maj == 3) return FALSE; } +#endif /* Called from both server and client code. In the case of a server, errors @@ -1112,8 +1117,10 @@ if (!exim_gnutls_base_init_done) } #endif - if ((gnutls_buggy_ocsp = tls_is_buggy_ocsp())) +#ifndef DISABLE_OCSP + if (tls_ocsp_file && (gnutls_buggy_ocsp = tls_is_buggy_ocsp())) log_write(0, LOG_MAIN, "OCSP unusable with this GnuTLS library version"); +#endif exim_gnutls_base_init_done = TRUE; } commit c528cec4dbfdb6e367a6ac0ed72e2e768a9c4392 Author: Heiko Schlittermann (HS12-RIPE) Date: Wed Aug 12 23:45:44 2015 +0200 Adjust the timeout after interrupted select() diff --git a/src/src/ip.c b/src/src/ip.c index cb54f16..7991a58 100644 --- a/src/src/ip.c +++ b/src/src/ip.c @@ -451,8 +451,8 @@ BOOL fd_ready(int fd, int timeout) { fd_set select_inset; -struct timeval tv; time_t start_recv = time(NULL); +int time_left = timeout; int rc; if (timeout <= 0) @@ -464,10 +464,9 @@ if (timeout <= 0) do { + struct timeval tv = { time_left, 0 }; FD_ZERO (&select_inset); FD_SET (fd, &select_inset); - tv.tv_sec = timeout; - tv.tv_usec = 0; /*DEBUG(D_transport) debug_printf("waiting for data on fd\n");*/ rc = select(fd + 1, (SELECT_ARG2_TYPE *)&select_inset, NULL, NULL, &tv); @@ -479,25 +478,24 @@ do Aug 2004: Somebody set up a cron job that ran exiwhat every 2 minutes, making the interrupt not at all rare. Since the timeout is typically more than 2 minutes, the effect was to block the timeout completely. To prevent this - happening again, we do an explicit time test. */ + happening again, we do an explicit time test and adjust the timeout + accordingly */ if (rc < 0 && errno == EINTR) { DEBUG(D_transport) debug_printf("EINTR while waiting for socket data\n"); - if (time(NULL) - start_recv < timeout) continue; - DEBUG(D_transport) debug_printf("total wait time exceeds timeout\n"); + /* Watch out, 'continue' jumps to the condition, not to the loops top */ + if (time_left = timeout - (time(NULL) - start_recv)) continue; } - /* Handle a timeout, and treat any other select error as a timeout, including - an EINTR when we have been in this loop for longer than timeout. */ - if (rc <= 0) { errno = ETIMEDOUT; return FALSE; } - /* If the socket is ready, break out of the loop. */ + /* Checking the FD_ISSET is not enough, if we're interrupted, the + select_inset may still contain the 'input'. */ } while (rc < 0 || !FD_ISSET(fd, &select_inset)); return TRUE; commit 85ff3cf9f3ab78c4dfa9f9ff34d27e6fe8f73c39 Author: Heiko Schlittermann (HS12-RIPE) Date: Thu Aug 13 00:20:12 2015 +0200 Fix timeout adjustment in c528cec4 diff --git a/src/src/ip.c b/src/src/ip.c index 7991a58..2d71705 100644 --- a/src/src/ip.c +++ b/src/src/ip.c @@ -455,7 +455,7 @@ time_t start_recv = time(NULL); int time_left = timeout; int rc; -if (timeout <= 0) +if (time_left <= 0) { errno = ETIMEDOUT; return FALSE; @@ -484,8 +484,10 @@ do if (rc < 0 && errno == EINTR) { DEBUG(D_transport) debug_printf("EINTR while waiting for socket data\n"); + /* Watch out, 'continue' jumps to the condition, not to the loops top */ - if (time_left = timeout - (time(NULL) - start_recv)) continue; + time_left = timeout - (time(NULL) - start_recv); + if (time_left > 0) continue; } if (rc <= 0) commit 6c6d6e483411af2c087ff258f4041d38eb65e775 Author: Tony Finch Date: Thu Aug 13 15:16:48 2015 +0100 Overhaul the debug_selector and log_selector machinery to support variable-length bit vectors. No functional change. diff --git a/src/src/acl.c b/src/src/acl.c index 91ee571..f2e0ef2 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -4287,7 +4287,7 @@ while (acl != NULL) case ACL_WARN: if (cond == OK) acl_warn(where, *user_msgptr, *log_msgptr); - else if (cond == DEFER && (log_extra_selector & LX_acl_warn_skipped) != 0) + else if (cond == DEFER && LOGGING(acl_warn_skipped)) log_write(0, LOG_MAIN, "%s Warning: ACL \"warn\" statement skipped: " "condition test deferred%s%s", host_and_ident(TRUE), (*log_msgptr == NULL)? US"" : US": ", diff --git a/src/src/daemon.c b/src/src/daemon.c index 894a96f..2d10387 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -145,7 +145,7 @@ int dup_accept_socket = -1; int max_for_this_host = 0; int wfsize = 0; int wfptr = 0; -int use_log_write_selector = log_write_selector; +int save_log_selector = *log_selector; uschar *whofrom = NULL; void *reset_point = store_get(0); @@ -206,11 +206,11 @@ memory is reclaimed. */ whofrom = string_append(whofrom, &wfsize, &wfptr, 3, "[", sender_host_address, "]"); -if ((log_extra_selector & LX_incoming_port) != 0) +if (LOGGING(incoming_port)) whofrom = string_append(whofrom, &wfsize, &wfptr, 2, ":", string_sprintf("%d", sender_host_port)); -if ((log_extra_selector & LX_incoming_interface) != 0) +if (LOGGING(incoming_interface)) whofrom = string_append(whofrom, &wfsize, &wfptr, 4, " I=[", interface_address, "]:", string_sprintf("%d", interface_port)); @@ -338,11 +338,11 @@ the generalized logging code each time when the selector is false. If the selector is set, check whether the host is on the list for logging. If not, arrange to unset the selector in the subprocess. */ -if ((log_write_selector & L_smtp_connection) != 0) +if (LOGGING(smtp_connection)) { uschar *list = hosts_connection_nolog; if (list != NULL && verify_check_host(&list) == OK) - use_log_write_selector &= ~L_smtp_connection; + save_log_selector &= ~L_smtp_connection; else log_write(L_smtp_connection, LOG_MAIN, "SMTP connection from %s " "(TCP/IP connection count = %d)", whofrom, smtp_accept_count + 1); @@ -372,7 +372,7 @@ if (pid == 0) /* May have been modified for the subprocess */ - log_write_selector = use_log_write_selector; + *log_selector = save_log_selector; /* Get the local interface address into permanent store */ diff --git a/src/src/deliver.c b/src/src/deliver.c index 78f8f4b..c796de0 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -682,7 +682,7 @@ d_hostlog(uschar * s, int * sizep, int * ptrp, address_item * addr) { s = string_append(s, sizep, ptrp, 5, US" H=", addr->host_used->name, US" [", addr->host_used->address, US"]"); - if ((log_extra_selector & LX_outgoing_port) != 0) + if (LOGGING(outgoing_port)) s = string_append(s, sizep, ptrp, 2, US":", string_sprintf("%d", addr->host_used->port)); return s; @@ -692,10 +692,9 @@ d_hostlog(uschar * s, int * sizep, int * ptrp, address_item * addr) static uschar * d_tlslog(uschar * s, int * sizep, int * ptrp, address_item * addr) { - if ((log_extra_selector & LX_tls_cipher) != 0 && addr->cipher != NULL) + if (LOGGING(tls_cipher) && addr->cipher != NULL) s = string_append(s, sizep, ptrp, 2, US" X=", addr->cipher); - if ((log_extra_selector & LX_tls_certificate_verified) != 0 && - addr->cipher != NULL) + if (LOGGING(tls_certificate_verified) && addr->cipher != NULL) s = string_append(s, sizep, ptrp, 2, US" CV=", testflag(addr, af_cert_verified) ? @@ -706,7 +705,7 @@ d_tlslog(uschar * s, int * sizep, int * ptrp, address_item * addr) #endif "yes" : "no"); - if ((log_extra_selector & LX_tls_peerdn) != 0 && addr->peerdn != NULL) + if (LOGGING(tls_peerdn) && addr->peerdn != NULL) s = string_append(s, sizep, ptrp, 3, US" DN=\"", string_printing(addr->peerdn), US"\""); return s; @@ -808,7 +807,7 @@ pointer to a single host item in their host list, for use by the transport. */ s = reset_point = store_get(size); -log_address = string_log_address(addr, (log_write_selector & L_all_parents) != 0, TRUE); +log_address = string_log_address(addr, LOGGING(all_parents), TRUE); if (msg) s = string_append(s, &size, &ptr, 3, host_and_ident(TRUE), US" ", log_address); else @@ -817,11 +816,11 @@ else s = string_append(s, &size, &ptr, 2, US"> ", log_address); } -if (log_extra_selector & LX_incoming_interface && sending_ip_address) +if (LOGGING(incoming_interface) && sending_ip_address) s = string_append(s, &size, &ptr, 3, US" I=[", sending_ip_address, US"]"); /* for the port: string_sprintf("%d", sending_port) */ -if ((log_extra_selector & LX_sender_on_delivery) != 0 || msg) +if (LOGGING(sender_on_delivery) || msg) s = string_append(s, &size, &ptr, 3, US" F=<", #ifdef EXPERIMENTAL_INTERNATIONAL testflag(addr, af_utf8_downcvt) @@ -841,8 +840,7 @@ delivery; indeed, I did for some time, until this statement crashed. The case when it is not set is for a delivery to /dev/null which is optimised by not being run at all. */ -if (used_return_path != NULL && - (log_extra_selector & LX_return_path_on_delivery) != 0) +if (used_return_path != NULL && LOGGING(return_path_on_delivery)) s = string_append(s, &size, &ptr, 3, US" P=<", used_return_path, US">"); if (msg) @@ -854,7 +852,7 @@ if (addr->router != NULL) s = string_append(s, &size, &ptr, 2, US" T=", addr->transport->name); -if ((log_extra_selector & LX_delivery_size) != 0) +if (LOGGING(delivery_size)) s = string_append(s, &size, &ptr, 2, US" S=", string_sprintf("%d", transport_count)); @@ -901,7 +899,7 @@ else if (addr->auth_id) { s = string_append(s, &size, &ptr, 2, US":", addr->auth_id); - if (log_extra_selector & LX_smtp_mailauth && addr->auth_sndr) + if (LOGGING(smtp_mailauth) && addr->auth_sndr) s = string_append(s, &size, &ptr, 2, US":", addr->auth_sndr); } } @@ -914,8 +912,7 @@ else /* confirmation message (SMTP (host_used) and LMTP (driver_name)) */ -if (log_extra_selector & LX_smtp_confirmation && - addr->message && +if (LOGGING(smtp_confirmation) && addr->message && (addr->host_used || Ustrcmp(addr->transport->driver_name, "lmtp") == 0)) { unsigned i; @@ -935,11 +932,11 @@ if (log_extra_selector & LX_smtp_confirmation && /* Time on queue and actual time taken to deliver */ -if ((log_extra_selector & LX_queue_time) != 0) +if (LOGGING(queue_time)) s = string_append(s, &size, &ptr, 2, US" QT=", readconf_printtime( (int) ((long)time(NULL) - (long)received_time)) ); -if ((log_extra_selector & LX_deliver_time) != 0) +if (LOGGING(deliver_time)) s = string_append(s, &size, &ptr, 2, US" DT=", readconf_printtime(addr->more_errno)); @@ -1230,8 +1227,7 @@ else if (result == DEFER || result == PANIC) /* Create the address string for logging. Must not do this earlier, because an OK result may be changed to FAIL when a pipe returns text. */ - log_address = string_log_address(addr, - (log_write_selector & L_all_parents) != 0, result == OK); + log_address = string_log_address(addr, LOGGING(all_parents), result == OK); s = string_cat(s, &size, &ptr, log_address, Ustrlen(log_address)); @@ -1342,18 +1338,16 @@ else /* Create the address string for logging. Must not do this earlier, because an OK result may be changed to FAIL when a pipe returns text. */ - log_address = string_log_address(addr, - (log_write_selector & L_all_parents) != 0, result == OK); + log_address = string_log_address(addr, LOGGING(all_parents), result == OK); s = string_cat(s, &size, &ptr, log_address, Ustrlen(log_address)); - if ((log_extra_selector & LX_sender_on_delivery) != 0) + if (LOGGING(sender_on_delivery)) s = string_append(s, &size, &ptr, 3, US" F=<", sender_address, US">"); /* Return path may not be set if no delivery actually happened */ - if (used_return_path != NULL && - (log_extra_selector & LX_return_path_on_delivery) != 0) + if (used_return_path != NULL && LOGGING(return_path_on_delivery)) s = string_append(s, &size, &ptr, 3, US" P=<", used_return_path, US">"); if (addr->router != NULL) @@ -4449,7 +4443,7 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) } /* Local interface address/port */ - if (log_extra_selector & LX_incoming_interface && sending_ip_address) + if (LOGGING(incoming_interface) && sending_ip_address) { uschar * ptr = big_buffer; sprintf(CS ptr, "%.128s", sending_ip_address); @@ -7365,7 +7359,7 @@ if (addr_defer == NULL) /* Log the end of this message, with queue time if requested. */ - if ((log_extra_selector & LX_queue_time_overall) != 0) + if (LOGGING(queue_time_overall)) log_write(0, LOG_MAIN, "Completed QT=%s", readconf_printtime( (int) ((long)time(NULL) - (long)received_time)) ); else diff --git a/src/src/exim.c b/src/src/exim.c index f9d57ab..d7cb5d8 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -1639,6 +1639,10 @@ if (log_buffer == NULL) exit(EXIT_FAILURE); } +/* Initialize the default log options. */ + +bits_set(log_selector, log_selector_size, log_default); + /* Set log_stderr to stderr, provided that stderr exists. This gets reset to NULL when the daemon is run and the file is closed. We have to use this indirection, because some systems don't allow writing to the variable "stderr". @@ -2451,8 +2455,8 @@ for (i = 1; i < argc; i++) argrest++; } if (*argrest != 0) - decode_bits(&selector, NULL, D_memory, 0, argrest, debug_options, - debug_options_count, US"debug", 0); + decode_bits(&selector, 1, debug_notall, argrest, + debug_options, debug_options_count, US"debug", 0); debug_selector = selector; } break; @@ -3787,14 +3791,17 @@ else /* Handle the decoding of logging options. */ -decode_bits(&log_write_selector, &log_extra_selector, 0, 0, +decode_bits(log_selector, log_selector_size, log_notall, log_selector_string, log_options, log_options_count, US"log", 0); DEBUG(D_any) { + int i; debug_printf("configuration file is %s\n", config_main_filename); - debug_printf("log selectors = %08x %08x\n", log_write_selector, - log_extra_selector); + debug_printf("log selectors ="); + for (i = 0; i < log_selector_size; i++) + debug_printf(" %08x", log_selector[i]); + debug_printf("\n"); } /* If domain literals are not allowed, check the sender address that was @@ -4001,7 +4008,7 @@ a debugging feature for finding out what arguments certain MUAs actually use. Don't attempt it if logging is disabled, or if listing variables or if verifying/testing addresses or expansions. */ -if (((debug_selector & D_any) != 0 || (log_extra_selector & LX_arguments) != 0) +if (((debug_selector & D_any) != 0 || LOGGING(arguments)) && really_exim && !list_options && !checking) { int i; @@ -4036,7 +4043,7 @@ if (((debug_selector & D_any) != 0 || (log_extra_selector & LX_arguments) != 0) while (*p) p++; } - if ((log_extra_selector & LX_arguments) != 0) + if (LOGGING(arguments)) log_write(0, LOG_MAIN, "%s", big_buffer); else debug_printf("%s\n", big_buffer); @@ -5021,7 +5028,7 @@ if (host_checking) sender_host_address); if (verify_check_host(&hosts_connection_nolog) == OK) - log_write_selector &= ~L_smtp_connection; + BIT_CLEAR(log_selector, log_selector_size, Li_smtp_connection); log_write(L_smtp_connection, LOG_MAIN, "%s", smtp_get_connection_info()); /* NOTE: We do *not* call smtp_log_no_mail() if smtp_start_session() fails, @@ -5195,7 +5202,7 @@ if (smtp_input) smtp_in = stdin; smtp_out = stdout; if (verify_check_host(&hosts_connection_nolog) == OK) - log_write_selector &= ~L_smtp_connection; + BIT_CLEAR(log_selector, log_selector_size, Li_smtp_connection); log_write(L_smtp_connection, LOG_MAIN, "%s", smtp_get_connection_info()); if (!smtp_start_session()) { diff --git a/src/src/functions.h b/src/src/functions.h index 0257904..4af0017 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -99,6 +99,9 @@ extern int auth_get_no64_data(uschar **, uschar *); extern uschar *auth_xtextencode(uschar *, int); extern int auth_xtextdecode(uschar *, uschar **); +extern void bits_clear(unsigned int *, size_t, int *); +extern void bits_set(unsigned int *, size_t, int *); + extern void cancel_cutthrough_connection(const char *); extern int check_host(void *, const uschar *, const uschar **, uschar **); extern uschar **child_exec_exim(int, BOOL, int *, BOOL, int, ...); @@ -123,8 +126,8 @@ extern void debug_print_ids(uschar *); extern void debug_print_string(uschar *); extern void debug_print_tree(tree_node *); extern void debug_vprintf(const char *, va_list); -extern void decode_bits(unsigned int *, unsigned int *, - int, int, uschar *, bit_table *, int, uschar *, int); +extern void decode_bits(unsigned int *, size_t, int *, + uschar *, bit_table *, int, uschar *, int); extern address_item *deliver_make_addr(uschar *, BOOL); extern void deliver_init(void); extern void delivery_log(int, address_item *, int, uschar *); diff --git a/src/src/globals.c b/src/src/globals.c index 66baffe..1344b5a 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -536,40 +536,45 @@ uschar *dccifd_options = US"header"; BOOL debug_daemon = FALSE; int debug_fd = -1; FILE *debug_file = NULL; -bit_table debug_options[] = { - { US"acl", D_acl }, - { US"all", D_all }, - { US"auth", D_auth }, - { US"deliver", D_deliver }, - { US"dns", D_dns }, - { US"dnsbl", D_dnsbl }, - { US"exec", D_exec }, - { US"expand", D_expand }, - { US"filter", D_filter }, - { US"hints_lookup", D_hints_lookup }, - { US"host_lookup", D_host_lookup }, - { US"ident", D_ident }, - { US"interface", D_interface }, - { US"lists", D_lists }, - { US"load", D_load }, - { US"local_scan", D_local_scan }, - { US"lookup", D_lookup }, - { US"memory", D_memory }, - { US"pid", D_pid }, - { US"process_info", D_process_info }, - { US"queue_run", D_queue_run }, - { US"receive", D_receive }, - { US"resolver", D_resolver }, - { US"retry", D_retry }, - { US"rewrite", D_rewrite }, - { US"route", D_route }, - { US"timestamp", D_timestamp }, - { US"tls", D_tls }, - { US"transport", D_transport }, - { US"uid", D_uid }, - { US"verify", D_verify } +int debug_notall[] = { + Di_memory, + -1 }; -int debug_options_count = sizeof(debug_options)/sizeof(bit_table); +bit_table debug_options[] = { /* must be in alphabetical order */ + BIT_TABLE(D, acl), + BIT_TABLE(D, all), + BIT_TABLE(D, auth), + BIT_TABLE(D, deliver), + BIT_TABLE(D, dns), + BIT_TABLE(D, dnsbl), + BIT_TABLE(D, exec), + BIT_TABLE(D, expand), + BIT_TABLE(D, filter), + BIT_TABLE(D, hints_lookup), + BIT_TABLE(D, host_lookup), + BIT_TABLE(D, ident), + BIT_TABLE(D, interface), + BIT_TABLE(D, lists), + BIT_TABLE(D, load), + BIT_TABLE(D, local_scan), + BIT_TABLE(D, lookup), + BIT_TABLE(D, memory), + BIT_TABLE(D, pid), + BIT_TABLE(D, process_info), + BIT_TABLE(D, queue_run), + BIT_TABLE(D, receive), + BIT_TABLE(D, resolver), + BIT_TABLE(D, retry), + BIT_TABLE(D, rewrite), + BIT_TABLE(D, route), + BIT_TABLE(D, timestamp), + BIT_TABLE(D, tls), + BIT_TABLE(D, transport), + BIT_TABLE(D, uid), + BIT_TABLE(D, verify), +}; +int debug_options_count = nelem(debug_options); + unsigned int debug_selector = 0; int delay_warning[DELAY_WARNING_SIZE] = { DELAY_WARNING_SIZE, 1, 24*60*60 }; uschar *delay_warning_condition= @@ -813,78 +818,91 @@ uid_t local_user_uid = (uid_t)(-1); tree_node *localpartlist_anchor= NULL; int localpartlist_count = 0; uschar *log_buffer = NULL; -unsigned int log_extra_selector = LX_default; + +int log_default[] = { /* for initializing log_selector */ + Li_acl_warn_skipped, + Li_connection_reject, + Li_delay_delivery, + Li_dnslist_defer, + Li_etrn, + Li_host_lookup_failed, + Li_lost_incoming_connection, + Li_queue_run, + Li_rejected_header, + Li_retry_defer, + Li_sender_verify_fail, + Li_size_reject, + Li_skip_delivery, + Li_smtp_confirmation, + Li_tls_certificate_verified, + Li_tls_cipher, + -1 +}; + uschar *log_file_path = US LOG_FILE_PATH "\0<--------------Space to patch log_file_path->"; -/* Those log options with L_xxx identifiers have values less than 0x800000 and -are the ones that get put into log_write_selector. They can be used in calls to -log_write() to test for the bit. The options with LX_xxx identifiers have -values greater than 0x80000000 and are put into log_extra_selector (without the -top bit). They are never used in calls to log_write(), but are tested -independently. This separation became necessary when the number of log -selectors was getting close to filling a 32-bit word. */ - -/* Note that this list must be in alphabetical order. */ - -bit_table log_options[] = { - { US"8bitmime", LX_8bitmime }, - { US"acl_warn_skipped", LX_acl_warn_skipped }, - { US"address_rewrite", L_address_rewrite }, - { US"all", L_all }, - { US"all_parents", L_all_parents }, - { US"arguments", LX_arguments }, - { US"connection_reject", L_connection_reject }, - { US"delay_delivery", L_delay_delivery }, - { US"deliver_time", LX_deliver_time }, - { US"delivery_size", LX_delivery_size }, - { US"dnslist_defer", L_dnslist_defer }, - { US"etrn", L_etrn }, - { US"host_lookup_failed", L_host_lookup_failed }, - { US"ident_timeout", LX_ident_timeout }, - { US"incoming_interface", LX_incoming_interface }, - { US"incoming_port", LX_incoming_port }, - { US"lost_incoming_connection", L_lost_incoming_connection }, - { US"outgoing_port", LX_outgoing_port }, - { US"pid", LX_pid }, +int log_notall[] = { + -1 +}; +bit_table log_options[] = { /* must be in alphabetical order */ + BIT_TABLE(L, 8bitmime), + BIT_TABLE(L, acl_warn_skipped), + BIT_TABLE(L, address_rewrite), + BIT_TABLE(L, all), + BIT_TABLE(L, all_parents), + BIT_TABLE(L, arguments), + BIT_TABLE(L, connection_reject), + BIT_TABLE(L, delay_delivery), + BIT_TABLE(L, deliver_time), + BIT_TABLE(L, delivery_size), + BIT_TABLE(L, dnslist_defer), + BIT_TABLE(L, etrn), + BIT_TABLE(L, host_lookup_failed), + BIT_TABLE(L, ident_timeout), + BIT_TABLE(L, incoming_interface), + BIT_TABLE(L, incoming_port), + BIT_TABLE(L, lost_incoming_connection), + BIT_TABLE(L, outgoing_port), + BIT_TABLE(L, pid), #ifdef EXPERIMENTAL_PROXY - { US"proxy", LX_proxy }, + BIT_TABLE(L, proxy), #endif - { US"queue_run", L_queue_run }, - { US"queue_time", LX_queue_time }, - { US"queue_time_overall", LX_queue_time_overall }, - { US"received_recipients", LX_received_recipients }, - { US"received_sender", LX_received_sender }, - { US"rejected_header", LX_rejected_header }, - { US"rejected_headers", LX_rejected_header }, - { US"retry_defer", L_retry_defer }, - { US"return_path_on_delivery", LX_return_path_on_delivery }, - { US"sender_on_delivery", LX_sender_on_delivery }, - { US"sender_verify_fail", LX_sender_verify_fail }, - { US"size_reject", L_size_reject }, - { US"skip_delivery", L_skip_delivery }, - { US"smtp_confirmation", LX_smtp_confirmation }, - { US"smtp_connection", L_smtp_connection }, - { US"smtp_incomplete_transaction", L_smtp_incomplete_transaction }, - { US"smtp_mailauth", LX_smtp_mailauth }, - { US"smtp_no_mail", LX_smtp_no_mail }, - { US"smtp_protocol_error", L_smtp_protocol_error }, - { US"smtp_syntax_error", L_smtp_syntax_error }, - { US"subject", LX_subject }, - { US"tls_certificate_verified", LX_tls_certificate_verified }, - { US"tls_cipher", LX_tls_cipher }, - { US"tls_peerdn", LX_tls_peerdn }, - { US"tls_sni", LX_tls_sni }, - { US"unknown_in_list", LX_unknown_in_list } + BIT_TABLE(L, queue_run), + BIT_TABLE(L, queue_time), + BIT_TABLE(L, queue_time_overall), + BIT_TABLE(L, received_recipients), + BIT_TABLE(L, received_sender), + BIT_TABLE(L, rejected_header), + { US"rejected_headers", Li_rejected_header }, + BIT_TABLE(L, retry_defer), + BIT_TABLE(L, return_path_on_delivery), + BIT_TABLE(L, sender_on_delivery), + BIT_TABLE(L, sender_verify_fail), + BIT_TABLE(L, size_reject), + BIT_TABLE(L, skip_delivery), + BIT_TABLE(L, smtp_confirmation), + BIT_TABLE(L, smtp_connection), + BIT_TABLE(L, smtp_incomplete_transaction), + BIT_TABLE(L, smtp_mailauth), + BIT_TABLE(L, smtp_no_mail), + BIT_TABLE(L, smtp_protocol_error), + BIT_TABLE(L, smtp_syntax_error), + BIT_TABLE(L, subject), + BIT_TABLE(L, tls_certificate_verified), + BIT_TABLE(L, tls_cipher), + BIT_TABLE(L, tls_peerdn), + BIT_TABLE(L, tls_sni), + BIT_TABLE(L, unknown_in_list), }; +int log_options_count = nelem(log_options); -int log_options_count = sizeof(log_options)/sizeof(bit_table); int log_reject_target = 0; +unsigned int log_selector[log_selector_size]; /* initialized in main() */ uschar *log_selector_string = NULL; FILE *log_stderr = NULL; BOOL log_testing_mode = FALSE; BOOL log_timezone = FALSE; -unsigned int log_write_selector= L_default; uschar *login_sender_address = NULL; uschar *lookup_dnssec_authenticated = NULL; int lookup_open_max = 25; diff --git a/src/src/globals.h b/src/src/globals.h index ab03302..978a4cc 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -319,6 +319,7 @@ extern uschar *dccifd_options; /* options for the dccifd daemon */ extern BOOL debug_daemon; /* Debug the daemon process only */ extern int debug_fd; /* The fd for debug_file */ extern FILE *debug_file; /* Where to write debugging info */ +extern int debug_notall[]; /* Debug options excluded from +all */ extern bit_table debug_options[]; /* Table of debug options */ extern int debug_options_count; /* Size of table */ extern int delay_warning[]; /* Times between warnings */ @@ -531,16 +532,17 @@ extern uid_t local_user_uid; /* As it says; may be set in routers */ extern tree_node *localpartlist_anchor;/* Tree of defined localpart lists */ extern int localpartlist_count; /* Number defined */ extern uschar *log_buffer; /* For constructing log entries */ -extern unsigned int log_extra_selector;/* Bit map of logging options other than used by log_write() */ +extern int log_default[]; /* Initialization list for log_selector */ extern uschar *log_file_path; /* If unset, use default */ +extern int log_notall[]; /* Log options excluded from +all */ extern bit_table log_options[]; /* Table of options */ extern int log_options_count; /* Size of table */ extern int log_reject_target; /* Target log for ACL rejections */ +extern unsigned int log_selector[]; /* Bit map of logging options */ extern uschar *log_selector_string; /* As supplied in the config */ extern FILE *log_stderr; /* Copy of stderr for log use, or NULL */ extern BOOL log_testing_mode; /* TRUE in various testing modes */ extern BOOL log_timezone; /* TRUE to include the timezone in log lines */ -extern unsigned int log_write_selector;/* Bit map of logging options for log_write() */ extern uschar *login_sender_address; /* The actual sender address */ extern lookup_info **lookup_list; /* Array of pointers to available lookups */ extern int lookup_list_count; /* Number of entries in the list */ diff --git a/src/src/host.c b/src/src/host.c index 31c2bbf..5c69c7f 100644 --- a/src/src/host.c +++ b/src/src/host.c @@ -544,7 +544,7 @@ use this directly as the first item for Received: because it ain't an RFC 2822 domain. Sigh. */ address = string_sprintf("[%s]:%d", sender_host_address, sender_host_port); -if ((log_extra_selector & LX_incoming_port) == 0 || sender_host_port <= 0) +if (!LOGGING(incoming_port) || sender_host_port <= 0) *(Ustrrchr(address, ':')) = 0; /* If there's no EHLO/HELO data, we can't show it. */ @@ -695,8 +695,7 @@ else { uschar *flag = useflag? US"H=" : US""; uschar *iface = US""; - if ((log_extra_selector & LX_incoming_interface) != 0 && - interface_address != NULL) + if (LOGGING(incoming_interface) && interface_address != NULL) iface = string_sprintf(" I=[%s]:%d", interface_address, interface_port); if (sender_ident == NULL) (void)string_format(big_buffer, big_buffer_size, "%s%s%s", diff --git a/src/src/log.c b/src/src/log.c index 11b3edf..b2d1fcf 100644 --- a/src/src/log.c +++ b/src/src/log.c @@ -613,7 +613,7 @@ If a message_id exists, we include it after the timestamp. Arguments: selector write to main log or LOG_INFO only if this value is zero, or if - its bit is set in log_write_selector + its bit is set in log_selector[0] flags each bit indicates some independent action: LOG_SENDER add raw sender to the message LOG_RECIPIENTS add raw recipients list to message @@ -749,15 +749,12 @@ DEBUG(D_any|D_v) Ustrcpy(ptr, "LOG:"); ptr += 4; - /* Show the options that were passed into the call. These are those whose - flag values do not have the 0x80000000 bit in them. Note that this - automatically exclude the "all" setting. */ + /* Show the selector that was passed into the call. */ for (i = 0; i < log_options_count; i++) { unsigned int bit = log_options[i].bit; - if ((bit & 0x80000000) != 0) continue; - if ((selector & bit) != 0) + if (bit < BITWORDSIZE && selector == BIT(bit)) { *ptr++ = ' '; Ustrcpy(ptr, log_options[i].name); @@ -809,7 +806,7 @@ ptr = log_buffer; sprintf(CS ptr, "%s ", tod_stamp(tod_log)); while(*ptr) ptr++; -if ((log_extra_selector & LX_pid) != 0) +if (LOGGING(pid)) { sprintf(CS ptr, "[%d] ", (int)getpid()); while (*ptr) ptr++; @@ -869,7 +866,7 @@ or unless there is no log_stderr (expn called from daemon, for example). */ if (!really_exim || log_testing_mode) { if (debug_selector == 0 && log_stderr != NULL && - (selector == 0 || (selector & log_write_selector) != 0)) + (selector == 0 || (selector & log_selector[0]) != 0)) { if (host_checking) fprintf(log_stderr, "LOG: %s", CS(log_buffer + 20)); /* no timestamp */ @@ -887,7 +884,7 @@ has been renamed. Therefore, do a stat() and see if the inode has changed, and if so, re-open. */ if ((flags & LOG_MAIN) != 0 && - (selector == 0 || (selector & log_write_selector) != 0)) + (selector == 0 || (selector & log_selector[0]) != 0)) { if ((logging_mode & LOG_MODE_SYSLOG) != 0 && (syslog_duplication || (flags & (LOG_REJECT|LOG_PANIC)) == 0)) @@ -956,7 +953,7 @@ if ((flags & LOG_REJECT) != 0) { header_line *h; - if (header_list != NULL && (log_extra_selector & LX_rejected_header) != 0) + if (header_list != NULL && LOGGING(rejected_header)) { if (recipients_count > 0) { @@ -1142,6 +1139,35 @@ syslog_open = FALSE; /************************************************* +* Multi-bit set or clear * +*************************************************/ + +/* These functions take a list of bit indexes (terminated by -1) and +clear or set the corresponding bits in the selector. + +Arguments: + selector address of the bit string + selsize number of words in the bit string + bits list of bits to set +*/ + +void +bits_clear(unsigned int *selector, size_t selsize, int *bits) +{ +for(; *bits != -1; ++bits) + BIT_CLEAR(selector, selsize, *bits); +} + +void +bits_set(unsigned int *selector, size_t selsize, int *bits) +{ +for(; *bits != -1; ++bits) + BIT_SET(selector, selsize, *bits); +} + + + +/************************************************* * Decode bit settings for log/debug * *************************************************/ @@ -1151,13 +1177,9 @@ also recognizes a numeric setting of the form =, but this is not intended for user use. It's an easy way for Exim to pass the debug settings when it is re-exec'ed. -The log options are held in two unsigned ints (because there became too many -for one). The top bit in the table means "put in 2nd selector". This does not -yet apply to debug options, so the "=" facility sets only the first selector. - -The "all" selector, which must be equal to 0xffffffff, is recognized specially. -It sets all the bits in both selectors. However, there is a facility for then -unsetting certain bits, because we want to turn off "memory" in the debug case. +The option table is a list of names and bit indexes. The index -1 +means "set all bits, except for those listed in notall". The notall +list is terminated by -1. The action taken for bad values varies depending upon why we're here. For log messages, or if the debugging is triggered from config, then we write @@ -1165,10 +1187,9 @@ to the log on the way out. For debug setting triggered from the command-line, we treat it as an unknown option: error message to stderr and die. Arguments: - selector1 address of the first bit string - selector2 address of the second bit string, or NULL - notall1 bits to exclude from "all" for selector1 - notall2 bits to exclude from "all" for selector2 + selector address of the bit string + selsize number of words in the bit string + notall list of bits to exclude from "all" string the configured string options the table of option names count size of table @@ -1179,9 +1200,8 @@ Returns: nothing on success - bomb out on failure */ void -decode_bits(unsigned int *selector1, unsigned int *selector2, int notall1, - int notall2, uschar *string, bit_table *options, int count, uschar *which, - int flags) +decode_bits(unsigned int *selector, size_t selsize, int *notall, + uschar *string, bit_table *options, int count, uschar *which, int flags) { uschar *errmsg; if (string == NULL) return; @@ -1189,7 +1209,8 @@ if (string == NULL) return; if (*string == '=') { char *end; /* Not uschar */ - *selector1 = strtoul(CS string+1, &end, 0); + memset(selector, 0, sizeof(*selector)*selsize); + *selector = strtoul(CS string+1, &end, 0); if (*end == 0) return; errmsg = string_sprintf("malformed numeric %s_selector setting: %s", which, string); @@ -1232,40 +1253,22 @@ else for(;;) if (middle->name[len] != 0) c = -1; else { unsigned int bit = middle->bit; - unsigned int *selector; - - /* The value with all bits set means "force all bits in both selectors" - in the case where two are being handled. However, the top bit in the - second selector is never set. When setting, some bits can be excluded. - */ - - if (bit == 0xffffffff) - { - if (adding) - { - *selector1 = 0xffffffff ^ notall1; - if (selector2 != NULL) *selector2 = 0x7fffffff ^ notall2; - } - else - { - *selector1 = 0; - if (selector2 != NULL) *selector2 = 0; - } - } - - /* Otherwise, the 0x80000000 bit means "this value, without the top - bit, belongs in the second selector". */ - else - { - if ((bit & 0x80000000) != 0) - { - selector = selector2; - bit &= 0x7fffffff; - } - else selector = selector1; - if (adding) *selector |= bit; else *selector &= ~bit; - } + if (bit == -1) + { + if (adding) + { + memset(selector, -1, sizeof(*selector)*selsize); + bits_clear(selector, selsize, notall); + } + else + memset(selector, 0, sizeof(*selector)*selsize); + } + else if (adding) + BIT_SET(selector, selsize, bit); + else + BIT_CLEAR(selector, selsize, bit); + break; /* Out of loop to match selector name */ } } @@ -1335,10 +1338,8 @@ if (tag_name != NULL && (Ustrchr(tag_name, '/') != NULL)) debug_selector = D_default; if (opts) - { - decode_bits(&debug_selector, NULL, D_memory, 0, opts, + decode_bits(&debug_selector, 1, debug_notall, opts, debug_options, debug_options_count, US"debug", DEBUG_FROM_CONFIG); - } /* When activating from a transport process we may never have logged at all resulting in certain setup not having been done. Hack this for now so we diff --git a/src/src/macros.h b/src/src/macros.h index 61f9ca6..d63025e 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -321,46 +321,84 @@ for having to swallow the rest of an SMTP message is whether the value is #define END_SIZE 4 /* Reading ended because message too big */ #define END_WERROR 5 /* Write error while reading the message */ -/* Options bits for debugging; D_v and D_local_scan are also in local_scan.h */ - -#define D_v 0x00000001 -#define D_local_scan 0x00000002 - -#define D_acl 0x00000004 -#define D_auth 0x00000008 -#define D_deliver 0x00000010 -#define D_dns 0x00000020 -#define D_dnsbl 0x00000040 -#define D_exec 0x00000080 -#define D_expand 0x00000100 -#define D_filter 0x00000200 -#define D_hints_lookup 0x00000400 -#define D_host_lookup 0x00000800 -#define D_ident 0x00001000 -#define D_interface 0x00002000 -#define D_lists 0x00004000 -#define D_load 0x00008000 -#define D_lookup 0x00010000 -#define D_memory 0x00020000 -#define D_pid 0x00040000 -#define D_process_info 0x00080000 -#define D_queue_run 0x00100000 -#define D_receive 0x00200000 -#define D_resolver 0x00400000 -#define D_retry 0x00800000 -#define D_rewrite 0x01000000 -#define D_route 0x02000000 -#define D_timestamp 0x04000000 -#define D_tls 0x08000000 -#define D_transport 0x10000000 -#define D_uid 0x20000000 -#define D_verify 0x40000000 - -/* The D_all value must always have all bits set, as it is recognized specially -by the function that decodes debug and log selectors. This is to enable it to -set all the bits in a multi-word selector. Debug doesn't use this yet, but we -are getting close. In fact, we want to omit "memory" for -d+all, but can't -handle this here. It is fudged externally. */ +/* Bit masks for debug and log selectors */ + +/* Assume words are 32 bits wide. Tiny waste of space on 64 bit +platforms, but this ensures bit vectors always work the same way. */ +#define BITWORDSIZE 32 + +/* This macro is for single-word bit vectors: the debug selector, +and the first word of the log selector. */ +#define BIT(n) (1 << (n)) + +/* And these are for multi-word vectors. */ +#define BITWORD(n) ( (n) / BITWORDSIZE) +#define BITMASK(n) (1 << (n) % BITWORDSIZE) + +#define BIT_CLEAR(s,z,n) ((s)[BITWORD(n)] &= ~BITMASK(n)) +#define BIT_SET(s,z,n) ((s)[BITWORD(n)] |= BITMASK(n)) +#define BIT_TEST(s,z,n) (((s)[BITWORD(n)] & BITMASK(n)) != 0) + +/* Used in globals.c for initializing bit_table structures. T will be either +D or L correspondong to the debug and log selector bits declared below. */ + +#define BIT_TABLE(T,name) { US #name, T##i_##name } + +/* IOTA allows us to keep an implicit sequential count, like a simple enum, +but we can have sequentially numbered identifiers which are not declared +sequentially. We use this for more compact declarations of bit indexes and +masks, alternating between sequential bit index and corresponding mask. */ + +#define IOTA(iota) (__LINE__ - iota) +#define IOTA_INIT(zero) (__LINE__ - zero + 1) + +/* Options bits for debugging. DEBUG_BIT() declares both a bit index and the +corresponding mask. Di_all is a special value recognized by decode_bits(). + +Exim's code assumes in a number of places that the debug_selector is one +word, and this is exposed in the local_scan ABI. The D_v and D_local_scan bit +masks are part of the local_scan API so are #defined in local_scan.h */ + +#define DEBUG_BIT(name) Di_##name = IOTA(Di_iota), D_##name = BIT(Di_##name) + +enum { + Di_all = -1, + Di_v = 0, + Di_local_scan = 1, + + Di_iota = IOTA_INIT(2), + DEBUG_BIT(acl), + DEBUG_BIT(auth), + DEBUG_BIT(deliver), + DEBUG_BIT(dns), + DEBUG_BIT(dnsbl), + DEBUG_BIT(exec), + DEBUG_BIT(expand), + DEBUG_BIT(filter), + DEBUG_BIT(hints_lookup), + DEBUG_BIT(host_lookup), + DEBUG_BIT(ident), + DEBUG_BIT(interface), + DEBUG_BIT(lists), + DEBUG_BIT(load), + DEBUG_BIT(lookup), + DEBUG_BIT(memory), + DEBUG_BIT(pid), + DEBUG_BIT(process_info), + DEBUG_BIT(queue_run), + DEBUG_BIT(receive), + DEBUG_BIT(resolver), + DEBUG_BIT(retry), + DEBUG_BIT(rewrite), + DEBUG_BIT(route), + DEBUG_BIT(timestamp), + DEBUG_BIT(tls), + DEBUG_BIT(transport), + DEBUG_BIT(uid), + DEBUG_BIT(verify), +}; + +/* Multi-bit debug masks */ #define D_all 0xffffffff @@ -380,81 +418,67 @@ handle this here. It is fudged externally. */ D_timestamp | \ D_resolver)) -/* Options bits for logging. Those that will end up in log_write_selector have -values < 0x80000000. They can be used in calls to log_write(). The others have -values > 0x80000000 and are put into log_extra_selector (without the top bit). -These are only ever tested independently. "All" is a magic value that is used -only in the name table to set all options in both bit maps. */ - -/* The L_all value must always have all bits set, as it is recognized specially -by the function that decodes debug and log selectors. This is to enable it to -set all the bits in a multi-word selector. */ - -#define L_all 0xffffffff - -#define L_address_rewrite 0x00000001 -#define L_all_parents 0x00000002 -#define L_connection_reject 0x00000004 -#define L_delay_delivery 0x00000008 -#define L_dnslist_defer 0x00000010 -#define L_etrn 0x00000020 -#define L_host_lookup_failed 0x00000040 -#define L_lost_incoming_connection 0x00000080 -#define L_queue_run 0x00000100 -#define L_retry_defer 0x00000200 -#define L_size_reject 0x00000400 -#define L_skip_delivery 0x00000800 -#define L_smtp_connection 0x00001000 -#define L_smtp_incomplete_transaction 0x00002000 -#define L_smtp_protocol_error 0x00004000 -#define L_smtp_syntax_error 0x00008000 - -#define LX_acl_warn_skipped 0x80000001 -#define LX_arguments 0x80000002 -#define LX_deliver_time 0x80000004 -#define LX_delivery_size 0x80000008 -#define LX_ident_timeout 0x80000010 -#define LX_incoming_interface 0x80000020 -#define LX_incoming_port 0x80000040 -#define LX_outgoing_port 0x80000080 -#define LX_pid 0x80000100 -#define LX_queue_time 0x80000200 -#define LX_queue_time_overall 0x80000400 -#define LX_received_sender 0x80000800 -#define LX_received_recipients 0x80001000 -#define LX_rejected_header 0x80002000 -#define LX_return_path_on_delivery 0x80004000 -#define LX_sender_on_delivery 0x80008000 -#define LX_sender_verify_fail 0x80010000 -#define LX_smtp_confirmation 0x80020000 -#define LX_smtp_no_mail 0x80040000 -#define LX_subject 0x80080000 -#define LX_tls_certificate_verified 0x80100000 -#define LX_tls_cipher 0x80200000 -#define LX_tls_peerdn 0x80400000 -#define LX_tls_sni 0x80800000 -#define LX_unknown_in_list 0x81000000 -#define LX_8bitmime 0x82000000 -#define LX_smtp_mailauth 0x84000000 -#define LX_proxy 0x88000000 - -#define L_default (L_connection_reject | \ - L_delay_delivery | \ - L_dnslist_defer | \ - L_etrn | \ - L_host_lookup_failed | \ - L_lost_incoming_connection | \ - L_queue_run | \ - L_retry_defer | \ - L_size_reject | \ - L_skip_delivery) - -#define LX_default ((LX_acl_warn_skipped | \ - LX_rejected_header | \ - LX_sender_verify_fail | \ - LX_smtp_confirmation | \ - LX_tls_certificate_verified| \ - LX_tls_cipher) & 0x7fffffff) +/* Options bits for logging. Those that have values < BITWORDSIZE can be used +in calls to log_write(). The others are put into later words in log_selector +and are only ever tested independently, so they do not need bit mask +declarations. The Li_all value is recognized specially by decode_bits(). */ + +#define LOG_BIT(name) Li_##name = IOTA(Li_iota), L_##name = BIT(Li_##name) + +enum { + Li_all = -1, + + Li_iota = IOTA_INIT(0), + LOG_BIT(address_rewrite), + LOG_BIT(all_parents), + LOG_BIT(connection_reject), + LOG_BIT(delay_delivery), + LOG_BIT(dnslist_defer), + LOG_BIT(etrn), + LOG_BIT(host_lookup_failed), + LOG_BIT(lost_incoming_connection), + LOG_BIT(queue_run), + LOG_BIT(retry_defer), + LOG_BIT(size_reject), + LOG_BIT(skip_delivery), + LOG_BIT(smtp_connection), + LOG_BIT(smtp_incomplete_transaction), + LOG_BIT(smtp_protocol_error), + LOG_BIT(smtp_syntax_error), + + Li_acl_warn_skipped = BITWORDSIZE, + Li_arguments, + Li_deliver_time, + Li_delivery_size, + Li_ident_timeout, + Li_incoming_interface, + Li_incoming_port, + Li_outgoing_port, + Li_pid, + Li_queue_time, + Li_queue_time_overall, + Li_received_sender, + Li_received_recipients, + Li_rejected_header, + Li_return_path_on_delivery, + Li_sender_on_delivery, + Li_sender_verify_fail, + Li_smtp_confirmation, + Li_smtp_no_mail, + Li_subject, + Li_tls_certificate_verified, + Li_tls_cipher, + Li_tls_peerdn, + Li_tls_sni, + Li_unknown_in_list, + Li_8bitmime, + Li_smtp_mailauth, + Li_proxy, + + log_selector_size = BITWORD(Li_proxy) + 1 +}; + +#define LOGGING(opt) BIT_TEST(log_selector, log_selector_size, Li_##opt) /* Private error numbers for delivery failures, set negative so as not to conflict with system errno values. */ diff --git a/src/src/match.c b/src/src/match.c index 9e47110..fa42187 100644 --- a/src/src/match.c +++ b/src/src/match.c @@ -771,7 +771,7 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) include_unknown? "yes":"no", error); if (!include_unknown) { - if ((log_extra_selector & LX_unknown_in_list) != 0) + if (LOGGING(unknown_in_list)) log_write(0, LOG_MAIN, "list matching forced to fail: %s", error); return FAIL; } @@ -880,7 +880,7 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) (void)fclose(f); if (!include_unknown) { - if ((log_extra_selector & LX_unknown_in_list) != 0) + if (LOGGING(unknown_in_list)) log_write(0, LOG_MAIN, "list matching forced to fail: %s", error); return FAIL; } diff --git a/src/src/rda.c b/src/src/rda.c index 7596466..2afd6dc 100644 --- a/src/src/rda.c +++ b/src/src/rda.c @@ -635,7 +635,7 @@ if ((pid = fork()) == 0) { DEBUG(D_rewrite) debug_printf("turned off address rewrite logging (not " "root or exim in this process)\n"); - log_write_selector &= ~L_address_rewrite; + BIT_CLEAR(log_selector, log_selector_size, Li_address_rewrite); } /* Now do the business */ diff --git a/src/src/receive.c b/src/src/receive.c index 64cf1ae..b430ee2 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -1118,8 +1118,7 @@ add_host_info_for_log(uschar *s, int *sizeptr, int *ptrptr) if (sender_fullhost != NULL) { s = string_append(s, sizeptr, ptrptr, 2, US" H=", sender_fullhost); - if ((log_extra_selector & LX_incoming_interface) != 0 && - interface_address != NULL) + if (LOGGING(incoming_interface) && interface_address != NULL) { uschar *ss = string_sprintf(" I=[%s]:%d", interface_address, interface_port); @@ -2529,7 +2528,7 @@ if (msgid_header == NULL && rewriting. Must copy the count, because later ACLs and the local_scan() function may mess with the real recipients. */ -if ((log_extra_selector & LX_received_recipients) != 0) +if (LOGGING(received_recipients)) { raw_recipients = store_get(recipients_count * sizeof(uschar *)); for (i = 0; i < recipients_count; i++) @@ -3573,7 +3572,7 @@ else goto TEMPREJECT; case LOCAL_SCAN_REJECT_NOLOGHDR: - log_extra_selector &= ~LX_rejected_header; + BIT_CLEAR(log_selector, log_selector_size, Li_rejected_header); /* Fall through */ case LOCAL_SCAN_REJECT: @@ -3582,7 +3581,7 @@ else break; case LOCAL_SCAN_TEMPREJECT_NOLOGHDR: - log_extra_selector &= ~LX_rejected_header; + BIT_CLEAR(log_selector, log_selector_size, Li_rejected_header); /* Fall through */ case LOCAL_SCAN_TEMPREJECT: @@ -3747,15 +3746,15 @@ if (message_reference != NULL) s = add_host_info_for_log(s, &size, &sptr); #ifdef SUPPORT_TLS -if (log_extra_selector & LX_tls_cipher && tls_in.cipher) +if (LOGGING(tls_cipher) && tls_in.cipher) s = string_append(s, &size, &sptr, 2, US" X=", tls_in.cipher); -if (log_extra_selector & LX_tls_certificate_verified && tls_in.cipher) +if (LOGGING(tls_certificate_verified) && tls_in.cipher) s = string_append(s, &size, &sptr, 2, US" CV=", tls_in.certificate_verified? "yes":"no"); -if (log_extra_selector & LX_tls_peerdn && tls_in.peerdn) +if (LOGGING(tls_peerdn) && tls_in.peerdn) s = string_append(s, &size, &sptr, 3, US" DN=\"", string_printing(tls_in.peerdn), US"\""); -if (log_extra_selector & LX_tls_sni && tls_in.sni) +if (LOGGING(tls_sni) && tls_in.sni) s = string_append(s, &size, &sptr, 3, US" SNI=\"", string_printing(tls_in.sni), US"\""); #endif @@ -3766,7 +3765,7 @@ if (sender_host_authenticated) if (authenticated_id != NULL) { s = string_append(s, &size, &sptr, 2, US":", authenticated_id); - if (log_extra_selector & LX_smtp_mailauth && authenticated_sender != NULL) + if (LOGGING(smtp_mailauth) && authenticated_sender != NULL) s = string_append(s, &size, &sptr, 2, US":", authenticated_sender); } } @@ -3777,7 +3776,7 @@ if (prdr_requested) #endif #ifdef EXPERIMENTAL_PROXY -if (proxy_session && log_extra_selector & LX_proxy) +if (proxy_session && LOGGING(proxy)) s = string_append(s, &size, &sptr, 2, US" PRX=", proxy_host_address); #endif @@ -3788,7 +3787,7 @@ s = string_append(s, &size, &sptr, 2, US" S=", big_buffer); 0 ... no BODY= used 7 ... 7BIT 8 ... 8BITMIME */ -if (log_extra_selector & LX_8bitmime) +if (LOGGING(8bitmime)) { sprintf(CS big_buffer, "%d", body_8bitmime); s = string_append(s, &size, &sptr, 2, US" M8S=", big_buffer); @@ -3814,7 +3813,7 @@ if (msgid_header != NULL) /* If subject logging is turned on, create suitable printing-character text. By expanding $h_subject: we make use of the MIME decoding. */ -if ((log_extra_selector & LX_subject) != 0 && subject_header != NULL) +if (LOGGING(subject) && subject_header != NULL) { int i; uschar *p = big_buffer; @@ -4003,8 +4002,8 @@ if(!smtp_reply) #endif { log_write(0, LOG_MAIN | - (((log_extra_selector & LX_received_recipients) != 0)? LOG_RECIPIENTS : 0) | - (((log_extra_selector & LX_received_sender) != 0)? LOG_SENDER : 0), + (LOGGING(received_recipients)? LOG_RECIPIENTS : 0) | + (LOGGING(received_sender)? LOG_SENDER : 0), "%s", s); /* Log any control actions taken by an ACL or local_scan(). */ diff --git a/src/src/rewrite.c b/src/src/rewrite.c index 296fe8c..ca7fb6a 100644 --- a/src/src/rewrite.c +++ b/src/src/rewrite.c @@ -247,8 +247,7 @@ for (rule = rewrite_rules; /* We have a validly rewritten address */ - if ((log_write_selector & L_address_rewrite) != 0 || - (debug_selector & D_rewrite) != 0) + if (LOGGING(address_rewrite) || (debug_selector & D_rewrite) != 0) { int i; const uschar *where = CUS"?"; diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index effc636..9982451 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -1234,8 +1234,7 @@ if (sender_host_unknown || sender_host_notsocket) if (is_inetd) return string_sprintf("SMTP connection from %s (via inetd)", hostname); -if ((log_extra_selector & LX_incoming_interface) != 0 && - interface_address != NULL) +if (LOGGING(incoming_interface) && interface_address != NULL) return string_sprintf("SMTP connection from %s I=[%s]:%d", hostname, interface_address, interface_port); @@ -1260,16 +1259,15 @@ s_tlslog(uschar * s, int * sizep, int * ptrp) int size = sizep ? *sizep : 0; int ptr = ptrp ? *ptrp : 0; - if ((log_extra_selector & LX_tls_cipher) != 0 && tls_in.cipher != NULL) + if (LOGGING(tls_cipher) && tls_in.cipher != NULL) s = string_append(s, &size, &ptr, 2, US" X=", tls_in.cipher); - if ((log_extra_selector & LX_tls_certificate_verified) != 0 && - tls_in.cipher != NULL) + if (LOGGING(tls_certificate_verified) && tls_in.cipher != NULL) s = string_append(s, &size, &ptr, 2, US" CV=", tls_in.certificate_verified? "yes":"no"); - if ((log_extra_selector & LX_tls_peerdn) != 0 && tls_in.peerdn != NULL) + if (LOGGING(tls_peerdn) && tls_in.peerdn != NULL) s = string_append(s, &size, &ptr, 3, US" DN=\"", string_printing(tls_in.peerdn), US"\""); - if ((log_extra_selector & LX_tls_sni) != 0 && tls_in.sni != NULL) + if (LOGGING(tls_sni) && tls_in.sni != NULL) s = string_append(s, &size, &ptr, 3, US" SNI=\"", string_printing(tls_in.sni), US"\""); @@ -1301,7 +1299,7 @@ smtp_log_no_mail(void) int size, ptr, i; uschar *s, *sep; -if (smtp_mailcmd_count > 0 || (log_extra_selector & LX_smtp_no_mail) == 0) +if (smtp_mailcmd_count > 0 || !LOGGING(smtp_no_mail)) return; s = NULL; @@ -2510,8 +2508,8 @@ static void incomplete_transaction_log(uschar *what) { if (sender_address == NULL || /* No transaction in progress */ - (log_write_selector & L_smtp_incomplete_transaction) == 0 /* Not logging */ - ) return; + !LOGGING(smtp_incomplete_transaction)) + return; /* Build list of recipients for logging */ @@ -2762,7 +2760,7 @@ if (sender_verified_failed != NULL && setflag(sender_verified_failed, af_sverify_told); - if (rc != FAIL || (log_extra_selector & LX_sender_verify_fail) != 0) + if (rc != FAIL || LOGGING(sender_verify_fail)) log_write(0, LOG_MAIN|LOG_REJECT, "%s sender verify %s for <%s>%s", host_and_ident(TRUE), ((sender_verified_failed->special_action & 255) == DEFER)? "defer":"fail", diff --git a/src/src/structs.h b/src/src/structs.h index 6f143d6..438b521 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -38,7 +38,7 @@ typedef struct macro_item { typedef struct bit_table { uschar *name; - unsigned int bit; + int bit; } bit_table; /* Block for holding a uid and gid, possibly unset, and an initgroups flag. */ diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index b1dccb8..73ac807 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -1111,8 +1111,7 @@ len = SSL_get_tlsext_status_ocsp_resp(s, &p); if(!p) { /* Expect this when we requested ocsp but got none */ - if ( cbinfo->u_ocsp.client.verify_required - && log_extra_selector & LX_tls_cipher) + if (cbinfo->u_ocsp.client.verify_required && LOGGING(tls_cipher)) log_write(0, LOG_MAIN, "Received TLS status callback, null content"); else DEBUG(D_tls) debug_printf(" null\n"); @@ -1122,7 +1121,7 @@ if(!p) if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len))) { tls_out.ocsp = OCSP_FAILED; - if (log_extra_selector & LX_tls_cipher) + if (LOGGING(tls_cipher)) log_write(0, LOG_MAIN, "Received TLS cert status response, parse error"); else DEBUG(D_tls) debug_printf(" parse error\n"); @@ -1132,7 +1131,7 @@ if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len))) if(!(bs = OCSP_response_get1_basic(rsp))) { tls_out.ocsp = OCSP_FAILED; - if (log_extra_selector & LX_tls_cipher) + if (LOGGING(tls_cipher)) log_write(0, LOG_MAIN, "Received TLS cert status response, error parsing response"); else DEBUG(D_tls) debug_printf(" error parsing response\n"); @@ -1163,7 +1162,7 @@ if(!(bs = OCSP_response_get1_basic(rsp))) cbinfo->u_ocsp.client.verify_store, 0)) <= 0) { tls_out.ocsp = OCSP_FAILED; - if (log_extra_selector & LX_tls_cipher) + if (LOGGING(tls_cipher)) log_write(0, LOG_MAIN, "Received TLS cert status response, itself unverifiable"); BIO_printf(bp, "OCSP response verify failure\n"); ERR_print_errors(bp); diff --git a/src/src/transports/lmtp.c b/src/src/transports/lmtp.c index 0cd89af..1f4d7a6 100644 --- a/src/src/transports/lmtp.c +++ b/src/src/transports/lmtp.c @@ -654,7 +654,7 @@ if (send_data) if (lmtp_read_response(out, buffer, sizeof(buffer), '2', timeout)) { addr->transport_return = OK; - if ((log_extra_selector & LX_smtp_confirmation) != 0) + if (LOGGING(smtp_confirmation)) { const uschar *s = string_printing(buffer); /* de-const safe here as string_printing known to have alloc'n'copied */ diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index a952413..609dba3 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -638,7 +638,7 @@ if (addr->message) } else { - if (log_extra_selector & LX_outgoing_port) + if (LOGGING(outgoing_port)) message = string_sprintf("%s:%d", message, host->port == PORT_NONE ? 25 : host->port); log_write(0, LOG_MAIN, "%s %s", message, strerror(addr->basic_errno)); @@ -2380,7 +2380,7 @@ if (!ok) ok = TRUE; else if ( #ifndef EXPERIMENTAL_EVENT - (log_extra_selector & LX_smtp_confirmation) != 0 && + LOGGING(smtp_confirmation) && #endif !lmtp ) @@ -2435,7 +2435,7 @@ if (!ok) ok = TRUE; else continue; } completed_address = TRUE; /* NOW we can set this flag */ - if ((log_extra_selector & LX_smtp_confirmation) != 0) + if (LOGGING(smtp_confirmation)) { const uschar *s = string_printing(buffer); /* deconst cast ok here as string_printing was checked to have alloc'n'copied */ diff --git a/src/src/verify.c b/src/src/verify.c index e00e7b9..7992d58 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -2916,7 +2916,7 @@ if (ip_bind(sock, host_af, interface_address, 0) < 0) if (ip_connect(sock, host_af, sender_host_address, port, rfc1413_query_timeout) < 0) { - if (errno == ETIMEDOUT && (log_extra_selector & LX_ident_timeout) != 0) + if (errno == ETIMEDOUT && LOGGING(ident_timeout)) { log_write(0, LOG_MAIN, "ident connection to %s timed out", sender_host_address); commit ac881e2749754fbe167b5f38784dd85b088571cf Author: Tony Finch Date: Thu Aug 13 15:16:51 2015 +0100 Improve the consistency of logging incoming and outgoing interfaces. The I= interface field on outgoing lines is now after the H= remote host field, same as incoming lines. There is a separate outgoing_interface log selector which allows you to disable the outgoing I= field. (slight massaging by JH) diff --git a/src/src/deliver.c b/src/src/deliver.c index c796de0..0e7cea3 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -676,39 +676,78 @@ while (addr->parent != NULL) +/************************************************* +* Delivery logging support functions * +*************************************************/ + +/* The LOGGING() checks in d_log_interface() are complicated for backwards +compatibility. When outgoing interface logging was originally added, it was +conditional on just incoming_interface (which is off by default). The +outgoing_interface option is on by default to preserve this behaviour, but +you can enable incoming_interface and disable outgoing_interface to get I= +fields on incoming lines only. + +Arguments: + s The log line buffer + sizep Pointer to the buffer size + ptrp Pointer to current index into buffer + addr The address to be logged + +Returns: New value for s +*/ static uschar * -d_hostlog(uschar * s, int * sizep, int * ptrp, address_item * addr) +d_log_interface(uschar *s, int *sizep, int *ptrp) { - s = string_append(s, sizep, ptrp, 5, US" H=", addr->host_used->name, - US" [", addr->host_used->address, US"]"); +if (LOGGING(incoming_interface) && LOGGING(outgoing_interface) + && sending_ip_address != NULL) + { + s = string_append(s, sizep, ptrp, 2, US" I=[", sending_ip_address); if (LOGGING(outgoing_port)) - s = string_append(s, sizep, ptrp, 2, US":", string_sprintf("%d", - addr->host_used->port)); - return s; + s = string_append(s, sizep, ptrp, 2, US"]:", + string_sprintf("%d", sending_port)); + else + s = string_cat(s, sizep, ptrp, "]", 1); + } +return s; } + + +static uschar * +d_hostlog(uschar *s, int *sizep, int *ptrp, address_item *addr) +{ +s = string_append(s, sizep, ptrp, 5, US" H=", addr->host_used->name, + US" [", addr->host_used->address, US"]"); +if (LOGGING(outgoing_port)) + s = string_append(s, sizep, ptrp, 2, US":", string_sprintf("%d", + addr->host_used->port)); +return d_log_interface(s, sizep, ptrp); +} + + + #ifdef SUPPORT_TLS static uschar * d_tlslog(uschar * s, int * sizep, int * ptrp, address_item * addr) { - if (LOGGING(tls_cipher) && addr->cipher != NULL) - s = string_append(s, sizep, ptrp, 2, US" X=", addr->cipher); - if (LOGGING(tls_certificate_verified) && addr->cipher != NULL) - s = string_append(s, sizep, ptrp, 2, US" CV=", - testflag(addr, af_cert_verified) - ? +if (LOGGING(tls_cipher) && addr->cipher != NULL) + s = string_append(s, sizep, ptrp, 2, US" X=", addr->cipher); +if (LOGGING(tls_certificate_verified) && addr->cipher != NULL) + s = string_append(s, sizep, ptrp, 2, US" CV=", + testflag(addr, af_cert_verified) + ? #ifdef EXPERIMENTAL_DANE - testflag(addr, af_dane_verified) - ? "dane" - : + testflag(addr, af_dane_verified) + ? "dane" + : #endif - "yes" - : "no"); - if (LOGGING(tls_peerdn) && addr->peerdn != NULL) - s = string_append(s, sizep, ptrp, 3, US" DN=\"", - string_printing(addr->peerdn), US"\""); - return s; + "yes" + : "no"); +if (LOGGING(tls_peerdn) && addr->peerdn != NULL) + s = string_append(s, sizep, ptrp, 3, US" DN=\"", + string_printing(addr->peerdn), US"\""); +return s; } #endif @@ -816,10 +855,6 @@ else s = string_append(s, &size, &ptr, 2, US"> ", log_address); } -if (LOGGING(incoming_interface) && sending_ip_address) - s = string_append(s, &size, &ptr, 3, US" I=[", sending_ip_address, US"]"); - /* for the port: string_sprintf("%d", sending_port) */ - if (LOGGING(sender_on_delivery) || msg) s = string_append(s, &size, &ptr, 3, US" F=<", #ifdef EXPERIMENTAL_INTERNATIONAL @@ -862,6 +897,7 @@ if (addr->transport->info->local) { if (addr->host_list) s = string_append(s, &size, &ptr, 2, US" H=", addr->host_list->name); + s = d_log_interface(s, &size, &ptr); if (addr->shadow_message != NULL) s = string_cat(s, &size, &ptr, addr->shadow_message, Ustrlen(addr->shadow_message)); diff --git a/src/src/globals.c b/src/src/globals.c index 1344b5a..4188b4d 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -827,6 +827,7 @@ int log_default[] = { /* for initializing log_selector */ Li_etrn, Li_host_lookup_failed, Li_lost_incoming_connection, + Li_outgoing_interface, /* see d_log_interface in deliver.c */ Li_queue_run, Li_rejected_header, Li_retry_defer, @@ -863,6 +864,7 @@ bit_table log_options[] = { /* must be in alphabetical order */ BIT_TABLE(L, incoming_interface), BIT_TABLE(L, incoming_port), BIT_TABLE(L, lost_incoming_connection), + BIT_TABLE(L, outgoing_interface), BIT_TABLE(L, outgoing_port), BIT_TABLE(L, pid), #ifdef EXPERIMENTAL_PROXY diff --git a/src/src/log.c b/src/src/log.c index b2d1fcf..558c000 100644 --- a/src/src/log.c +++ b/src/src/log.c @@ -753,8 +753,8 @@ DEBUG(D_any|D_v) for (i = 0; i < log_options_count; i++) { - unsigned int bit = log_options[i].bit; - if (bit < BITWORDSIZE && selector == BIT(bit)) + unsigned int bitnum = log_options[i].bit; + if (bitnum < BITWORDSIZE && selector == BIT(bitnum)) { *ptr++ = ' '; Ustrcpy(ptr, log_options[i].name); diff --git a/src/src/macros.h b/src/src/macros.h index d63025e..0ce24f8 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -474,8 +474,9 @@ enum { Li_8bitmime, Li_smtp_mailauth, Li_proxy, + Li_outgoing_interface, - log_selector_size = BITWORD(Li_proxy) + 1 + log_selector_size = BITWORD(Li_outgoing_interface) + 1 }; #define LOGGING(opt) BIT_TEST(log_selector, log_selector_size, Li_##opt) commit 6b51df8340eacc95e3def9a4376506610e91996c Author: Heiko Schlittermann (HS12-RIPE) Date: Wed Aug 19 15:22:41 2015 +0200 Fix post-transport-crash. The crash probably was introduced in a39bd74d3e94 and needs 'split_spool_directory=yes' to expose. Thanks to Wolfgang Breyha, who found the same fix. diff --git a/src/src/transport.c b/src/src/transport.c index fa6f869..a6ad3ed 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -1752,7 +1752,7 @@ while (1) { if (split_spool_directory) sprintf(CS spool_file, "%s%c/%s-D", - spool_dir, new_message_id[5], msgq[i].message_id); + spool_dir, msgq[i].message_id[5], msgq[i].message_id); else sprintf(CS spool_file, "%s%s-D", spool_dir, msgq[i].message_id); commit b20b82a0b4169cb23380a373ed2a898b0cb337d2 Author: Heiko Schlittermann (HS12-RIPE) Date: Fri Aug 21 12:26:50 2015 +0200 Add a .ctags file to src diff --git a/src/.ctags b/src/.ctags new file mode 100644 index 0000000..c764086 --- /dev/null +++ b/src/.ctags @@ -0,0 +1,2 @@ +--recurse +--exclude=build-* commit dadff1d47e54962b0fdf98e8ce5cef42b6cb7fb5 Author: Heiko Schlittermann (HS12-RIPE) Date: Thu Aug 20 13:58:06 2015 +0200 Fix post-transport-crash: safeguard for missing spool BUG 1671 Based on a proposal from Wolfgang Breyha. diff --git a/src/src/deliver.c b/src/src/deliver.c index 0e7cea3..b5aa9b9 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -9,6 +9,7 @@ #include "exim.h" +#include /* Data block for keeping track of subprocesses for parallel remote @@ -7934,17 +7935,36 @@ if (!regex_IGNOREQUOTA) regex_IGNOREQUOTA = uschar * deliver_get_sender_address (uschar * id) { +int rc; +uschar * new_sender_address, + * save_sender_address; + if (!spool_open_datafile(id)) return NULL; +/* Save and restore the global sender_address. I'm not sure if we should +not save/restore all the other global variables too, because +spool_read_header() may change all of them. But OTOH, when this +deliver_get_sender_address() gets called, the current message is done +already and nobody needs the globals anymore. (HS12, 2015-08-21) */ + sprintf(CS spoolname, "%s-H", id); -if (spool_read_header(spoolname, TRUE, TRUE) != spool_read_OK) +save_sender_address = sender_address; + +rc = spool_read_header(spoolname, TRUE, TRUE); + +new_sender_address = sender_address; +sender_address = save_sender_address; + +if (rc != spool_read_OK) return NULL; +assert(new_sender_address); + (void)close(deliver_datafile); deliver_datafile = -1; -return sender_address; +return new_sender_address; } /* vi: aw ai sw=2 diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 609dba3..c93f2ef 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -1274,14 +1274,19 @@ we will veto this new message. */ static BOOL smtp_are_same_identities(uschar * message_id, smtp_compare_t * s_compare) { -uschar * save_sender_address = sender_address; -uschar * current_local_identity = + +uschar * message_local_identity, + * current_local_identity, + * new_sender_address; + +current_local_identity = smtp_local_identity(s_compare->current_sender_address, s_compare->tblock); -uschar * new_sender_address = deliver_get_sender_address(message_id); -uschar * message_local_identity = - smtp_local_identity(new_sender_address, s_compare->tblock); -sender_address = save_sender_address; +if (!(new_sender_address = deliver_get_sender_address(message_id))) + return 0; + +message_local_identity = + smtp_local_identity(new_sender_address, s_compare->tblock); return Ustrcmp(current_local_identity, message_local_identity) == 0; } commit 3703d8187af01d13ca71f7918c7ef78529bb784d Author: Jeremy Harris Date: Fri Aug 21 18:08:39 2015 +0100 Remember the fail reason for verify=headers_syntax. Bug 264 diff --git a/src/src/acl.c b/src/src/acl.c index f2e0ef2..064ee6c 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -1803,27 +1803,27 @@ switch(vp->value) test whether it was successful or not. (This is for optional verification; for mandatory verification, the connection doesn't last this long.) */ - if (tls_in.certificate_verified) return OK; - *user_msgptr = US"no verified certificate"; - return FAIL; + if (tls_in.certificate_verified) return OK; + *user_msgptr = US"no verified certificate"; + return FAIL; case VERIFY_HELO: /* We can test the result of optional HELO verification that might have occurred earlier. If not, we can attempt the verification now. */ - if (!helo_verified && !helo_verify_failed) smtp_verify_helo(); - return helo_verified? OK : FAIL; + if (!helo_verified && !helo_verify_failed) smtp_verify_helo(); + return helo_verified? OK : FAIL; case VERIFY_CSA: /* Do Client SMTP Authorization checks in a separate function, and turn the result code into user-friendly strings. */ - rc = acl_verify_csa(list); - *log_msgptr = *user_msgptr = string_sprintf("client SMTP authorization %s", + rc = acl_verify_csa(list); + *log_msgptr = *user_msgptr = string_sprintf("client SMTP authorization %s", csa_reason_string[rc]); - csa_status = csa_status_string[rc]; - DEBUG(D_acl) debug_printf("CSA result %s\n", csa_status); - return csa_return_code[rc]; + csa_status = csa_status_string[rc]; + DEBUG(D_acl) debug_printf("CSA result %s\n", csa_status); + return csa_return_code[rc]; case VERIFY_HDR_SYNTAX: /* Check that all relevant header lines have the correct syntax. If there is @@ -1832,8 +1832,11 @@ switch(vp->value) always). */ rc = verify_check_headers(log_msgptr); - if (rc != OK && smtp_return_error_details && *log_msgptr != NULL) - *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr); + if (rc != OK && *log_msgptr) + if (smtp_return_error_details) + *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr); + else + acl_verify_message = *log_msgptr; return rc; case VERIFY_HDR_NAMES_ASCII: @@ -3788,7 +3791,8 @@ for (; cb != NULL; cb = cb->next) case ACLC_VERIFY: rc = acl_verify(where, addr, arg, user_msgptr, log_msgptr, basic_errno); - acl_verify_message = *user_msgptr; + if (*user_msgptr) + acl_verify_message = *user_msgptr; if (verb == ACL_WARN) *user_msgptr = NULL; break; commit c8899c20aa08c9ae6a4c291aad23ba90512bebe4 Author: Jeremy Harris Date: Tue Aug 25 10:36:27 2015 +0100 Close logs after daemon-process exceptional write. Bug 728 diff --git a/src/src/daemon.c b/src/src/daemon.c index 2d10387..e1ff9a1 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -735,6 +735,7 @@ else (void)close(dup_accept_socket); /* Release any store used in this process, including the store used for holding the incoming host address and an expanded active_hostname. */ +log_close_all(); store_reset(reset_point); sender_host_address = NULL; } commit f38917cc94ab337c15ff70c254dd564ee2dcafe7 Author: Jeremy Harris Date: Tue Sep 8 23:05:20 2015 +0100 Capture substrings in ACL regex= . Bug 425. diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults index c33e098..596e651 100644 --- a/src/src/config.h.defaults +++ b/src/src/config.h.defaults @@ -116,6 +116,8 @@ it's a default value. */ #define RADIUS_CONFIG_FILE #define RADIUS_LIB_TYPE +#define REGEX_VARS 9 + #define ROUTER_ACCEPT #define ROUTER_DNSLOOKUP #define ROUTER_IPLITERAL diff --git a/src/src/exim.c b/src/src/exim.c index d7cb5d8..999b94c 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -1753,6 +1753,8 @@ regex_whitelisted_macro = regex_must_compile(US"^[A-Za-z0-9_/.-]*$", FALSE, TRUE); #endif +for (i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL; + /* If the program is called as "mailq" treat it as equivalent to "exim -bp"; this seems to be a generally accepted convention, since one finds symbolic diff --git a/src/src/expand.c b/src/src/expand.c index 89e0ac7..1bff521 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -1726,7 +1726,14 @@ if (Ustrncmp(name, "auth", 4) == 0) uschar *endptr; int n = Ustrtoul(name + 4, &endptr, 10); if (*endptr == 0 && n != 0 && n <= AUTH_VARS) - return (auth_vars[n-1] == NULL)? US"" : auth_vars[n-1]; + return !auth_vars[n-1] ? US"" : auth_vars[n-1]; + } +else if (Ustrncmp(name, "regex", 5) == 0) + { + uschar *endptr; + int n = Ustrtoul(name + 5, &endptr, 10); + if (*endptr == 0 && n != 0 && n <= REGEX_VARS) + return !regex_vars[n-1] ? US"" : regex_vars[n-1]; } /* For all other variables, search the table */ diff --git a/src/src/globals.c b/src/src/globals.c index 4188b4d..8445f00 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -1090,8 +1090,9 @@ const pcre *regex_From = NULL; const pcre *regex_IGNOREQUOTA = NULL; const pcre *regex_PIPELINING = NULL; const pcre *regex_SIZE = NULL; -const pcre *regex_smtp_code = NULL; const pcre *regex_ismsgid = NULL; +const pcre *regex_smtp_code = NULL; +uschar *regex_vars[REGEX_VARS]; #ifdef WHITELIST_D_MACROS const pcre *regex_whitelisted_macro = NULL; #endif diff --git a/src/src/globals.h b/src/src/globals.h index 978a4cc..3c69e43 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -717,8 +717,9 @@ extern const pcre *regex_From; /* For recognizing "From_" lines */ extern const pcre *regex_IGNOREQUOTA; /* For recognizing IGNOREQUOTA (LMTP) */ extern const pcre *regex_PIPELINING; /* For recognizing PIPELINING */ extern const pcre *regex_SIZE; /* For recognizing SIZE settings */ -extern const pcre *regex_smtp_code; /* For recognizing SMTP codes */ extern const pcre *regex_ismsgid; /* Compiled r.e. for message it */ +extern const pcre *regex_smtp_code; /* For recognizing SMTP codes */ +extern uschar *regex_vars[]; /* $regexN variables */ #ifdef WHITELIST_D_MACROS extern const pcre *regex_whitelisted_macro; /* For -D macro values */ #endif diff --git a/src/src/regex.c b/src/src/regex.c index ed73b6e..93422fa 100644 --- a/src/src/regex.c +++ b/src/src/regex.c @@ -25,109 +25,120 @@ uschar regex_match_string_buffer[1024]; extern FILE *mime_stream; extern uschar *mime_current_boundary; -int -regex(const uschar **listptr) +static pcre_list * +compile(const uschar * list) { int sep = 0; - const uschar *list = *listptr; uschar *regex_string; uschar regex_string_buffer[1024]; - unsigned long mbox_size; - FILE *mbox_file; - pcre *re; - pcre_list *re_list_head = NULL; - pcre_list *re_list_item; const char *pcre_error; int pcre_erroffset; + pcre_list *re_list_head = NULL; + pcre_list *ri; + + /* precompile our regexes */ + while ((regex_string = string_nextinlist(&list, &sep, + regex_string_buffer, + sizeof(regex_string_buffer))) != NULL) { + pcre *re; + + /* parse option */ + if ( (strcmpic(regex_string,US"false") == 0) || + (Ustrcmp(regex_string,"0") == 0) ) + continue; /* explicitly no matching */ + + /* compile our regular expression */ + if (!(re = pcre_compile( CS regex_string, + 0, &pcre_error, &pcre_erroffset, NULL ))) { + log_write(0, LOG_MAIN, + "regex acl condition warning - error in regex '%s': %s at offset %d, skipped.", + regex_string, pcre_error, pcre_erroffset); + continue; + } + + ri = store_get(sizeof(pcre_list)); + ri->re = re; + ri->pcre_text = string_copy(regex_string); + ri->next = re_list_head; + re_list_head = ri; + } + return re_list_head; +} + +static int +matcher(pcre_list * re_list_head, uschar * linebuffer, int len) +{ + pcre_list * ri; + + for(ri = re_list_head; ri; ri = ri->next) + { + int ovec[3*(REGEX_VARS+1)]; + int n, nn; + + /* try matcher on the line */ + n = pcre_exec(ri->re, NULL, + CS linebuffer, len, 0, 0, + ovec, nelem(ovec)); + if (n > 0) + { + Ustrncpy(regex_match_string_buffer, ri->pcre_text, 1023); + regex_match_string = regex_match_string_buffer; + + for (nn = 1; nn < n; nn++) + regex_vars[nn-1] = + string_copyn(linebuffer + ovec[nn*2], ovec[nn*2+1] - ovec[nn*2]); + + return OK; + } + } + return FAIL; +} + +int +regex(const uschar **listptr) +{ + unsigned long mbox_size; + FILE *mbox_file; + pcre_list *re_list_head; uschar *linebuffer; long f_pos = 0; + int ret = FAIL; /* reset expansion variable */ regex_match_string = NULL; - if (mime_stream == NULL) { - /* We are in the DATA ACL */ + if (mime_stream == NULL) { /* We are in the DATA ACL */ mbox_file = spool_mbox(&mbox_size, NULL); - if (mbox_file == NULL) { - /* error while spooling */ + if (mbox_file == NULL) { /* error while spooling */ log_write(0, LOG_MAIN|LOG_PANIC, "regex acl condition: error while creating mbox spool file"); return DEFER; - }; + } } else { f_pos = ftell(mime_stream); mbox_file = mime_stream; - }; + } /* precompile our regexes */ - while ((regex_string = string_nextinlist(&list, &sep, - regex_string_buffer, - sizeof(regex_string_buffer))) != NULL) { - - /* parse option */ - if ( (strcmpic(regex_string,US"false") == 0) || - (Ustrcmp(regex_string,"0") == 0) ) { - /* explicitly no matching */ - continue; - }; - - /* compile our regular expression */ - re = pcre_compile( CS regex_string, - 0, - &pcre_error, - &pcre_erroffset, - NULL ); - - if (re == NULL) { - log_write(0, LOG_MAIN, - "regex acl condition warning - error in regex '%s': %s at offset %d, skipped.", regex_string, pcre_error, pcre_erroffset); - continue; - } - else { - re_list_item = store_get(sizeof(pcre_list)); - re_list_item->re = re; - re_list_item->pcre_text = string_copy(regex_string); - re_list_item->next = re_list_head; - re_list_head = re_list_item; - }; - }; - - /* no regexes -> nothing to do */ - if (re_list_head == NULL) { - return FAIL; - }; + if (!(re_list_head = compile(*listptr))) + return FAIL; /* no regexes -> nothing to do */ /* match each line against all regexes */ linebuffer = store_get(32767); while (fgets(CS linebuffer, 32767, mbox_file) != NULL) { - if ( (mime_stream != NULL) && (mime_current_boundary != NULL) ) { - /* check boundary */ - if (Ustrncmp(linebuffer,"--",2) == 0) { - if (Ustrncmp((linebuffer+2),mime_current_boundary,Ustrlen(mime_current_boundary)) == 0) - /* found boundary */ - break; - }; - }; - re_list_item = re_list_head; - do { - /* try matcher on the line */ - if (pcre_exec(re_list_item->re, NULL, CS linebuffer, - (int)Ustrlen(linebuffer), 0, 0, NULL, 0) >= 0) { - Ustrncpy(regex_match_string_buffer, re_list_item->pcre_text, 1023); - regex_match_string = regex_match_string_buffer; - if (mime_stream == NULL) - (void)fclose(mbox_file); - else { - clearerr(mime_stream); - fseek(mime_stream,f_pos,SEEK_SET); - }; - return OK; - }; - re_list_item = re_list_item->next; - } while (re_list_item != NULL); - }; + if ( mime_stream && mime_current_boundary /* check boundary */ + && Ustrncmp(linebuffer,"--",2) == 0 + && Ustrncmp((linebuffer+2),mime_current_boundary,Ustrlen(mime_current_boundary)) == 0) + break; /* found boundary */ + + if ((ret = matcher(re_list_head, linebuffer, (int)Ustrlen(linebuffer))) == OK) + goto done; + } + /* no matches ... */ + +done: if (mime_stream == NULL) (void)fclose(mbox_file); else { @@ -135,67 +146,25 @@ regex(const uschar **listptr) fseek(mime_stream,f_pos,SEEK_SET); }; - /* no matches ... */ - return FAIL; + return ret; } int mime_regex(const uschar **listptr) { - int sep = 0; - const uschar *list = *listptr; - uschar *regex_string; - uschar regex_string_buffer[1024]; - pcre *re; pcre_list *re_list_head = NULL; - pcre_list *re_list_item; - const char *pcre_error; - int pcre_erroffset; FILE *f; uschar *mime_subject = NULL; int mime_subject_len = 0; + int ret; /* reset expansion variable */ regex_match_string = NULL; /* precompile our regexes */ - while ((regex_string = string_nextinlist(&list, &sep, - regex_string_buffer, - sizeof(regex_string_buffer))) != NULL) { - - /* parse option */ - if ( (strcmpic(regex_string,US"false") == 0) || - (Ustrcmp(regex_string,"0") == 0) ) { - /* explicitly no matching */ - continue; - }; - - /* compile our regular expression */ - re = pcre_compile( CS regex_string, - 0, - &pcre_error, - &pcre_erroffset, - NULL ); - - if (re == NULL) { - log_write(0, LOG_MAIN, - "regex acl condition warning - error in regex '%s': %s at offset %d, skipped.", regex_string, pcre_error, pcre_erroffset); - continue; - } - else { - re_list_item = store_get(sizeof(pcre_list)); - re_list_item->re = re; - re_list_item->pcre_text = string_copy(regex_string); - re_list_item->next = re_list_head; - re_list_head = re_list_item; - }; - }; - - /* no regexes -> nothing to do */ - if (re_list_head == NULL) { - return FAIL; - }; + if (!(re_list_head = compile(*listptr))) + return FAIL; /* no regexes -> nothing to do */ /* check if the file is already decoded */ if (mime_decoded_filename == NULL) { @@ -207,43 +176,25 @@ mime_regex(const uschar **listptr) log_write(0, LOG_MAIN, "mime_regex acl condition warning - could not decode MIME part to file."); return DEFER; - }; - }; - + } + } /* open file */ - f = fopen(CS mime_decoded_filename, "rb"); - if (f == NULL) { - /* open failed */ + if (!(f = fopen(CS mime_decoded_filename, "rb"))) { log_write(0, LOG_MAIN, - "mime_regex acl condition warning - can't open '%s' for reading.", mime_decoded_filename); + "mime_regex acl condition warning - can't open '%s' for reading.", + mime_decoded_filename); return DEFER; - }; + } /* get 32k memory */ mime_subject = (uschar *)store_get(32767); - /* read max 32k chars from file */ mime_subject_len = fread(mime_subject, 1, 32766, f); - re_list_item = re_list_head; - do { - /* try matcher on the mmapped file */ - debug_printf("Matching '%s'\n", re_list_item->pcre_text); - if (pcre_exec(re_list_item->re, NULL, CS mime_subject, - mime_subject_len, 0, 0, NULL, 0) >= 0) { - Ustrncpy(regex_match_string_buffer, re_list_item->pcre_text, 1023); - regex_match_string = regex_match_string_buffer; - (void)fclose(f); - return OK; - }; - re_list_item = re_list_item->next; - } while (re_list_item != NULL); - + ret = matcher(re_list_head, mime_subject, mime_subject_len); (void)fclose(f); - - /* no matches ... */ - return FAIL; + return ret; } #endif /* WITH_CONTENT_SCAN */ commit 895fbaf26d3450d4eeacbad8fe04c328a77645f0 Author: Jeremy Harris Date: Wed Sep 9 16:03:38 2015 +0100 DSN: Under EXPERIMENTAL_DSN_INFO add extras to bounce messages. Bug 1686 diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults index 596e651..6af3b4d 100644 --- a/src/src/config.h.defaults +++ b/src/src/config.h.defaults @@ -172,6 +172,7 @@ it's a default value. */ #define EXPERIMENTAL_BRIGHTMAIL #define EXPERIMENTAL_DANE #define EXPERIMENTAL_DCC +#define EXPERIMENTAL_DSN_INFO #define EXPERIMENTAL_DMARC #define EXPERIMENTAL_EVENT #define EXPERIMENTAL_INTERNATIONAL diff --git a/src/src/deliver.c b/src/src/deliver.c index b5aa9b9..3f22dc9 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -3223,41 +3223,56 @@ while (!done) break; } - 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 */ - - if (*ptr != 0) + switch (subid) { - 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++; +#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 - /* Finished with this address */ + 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++); - addr = addr->next; + /* 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++; + + /* Finished with this address */ + + addr = addr->next; + break; + } break; /* Local interface address/port */ @@ -4423,7 +4438,6 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) for (r = addr->retries; r != NULL; r = r->next) { - uschar *ptr; sprintf(CS big_buffer, "%c%.500s", r->flags, r->key); ptr = big_buffer + Ustrlen(big_buffer+2) + 3; memcpy(ptr, &(r->basic_errno), sizeof(r->basic_errno)); @@ -4438,11 +4452,31 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) rmt_dlv_checked_write(fd, 'R', '0', big_buffer, ptr - big_buffer); } - /* The rest of the information goes in an 'A' item. */ +#ifdef EXPERIMENTAL_DSN_INFO +/*um, are they really per-addr? Other per-conn stuff is not (auth, tls). But host_used is! */ + if (addr->smtp_greeting) + { + ptr = big_buffer; + DEBUG(D_deliver) debug_printf("smtp_greeting '%s'\n", addr->smtp_greeting); + sprintf(CS ptr, "%.128s", addr->smtp_greeting); + while(*ptr++); + if (addr->helo_response) + { + DEBUG(D_deliver) debug_printf("helo_response '%s'\n", addr->helo_response); + sprintf(CS ptr, "%.128s", addr->helo_response); + while(*ptr++); + } + else + *ptr++ = '\0'; + rmt_dlv_checked_write(fd, 'A', '1', big_buffer, ptr - big_buffer); + } +#endif + + /* The rest of the information goes in an 'A0' item. */ - ptr = big_buffer + 2; sprintf(CS big_buffer, "%c%c", addr->transport_return, addr->special_action); + ptr = big_buffer + 2; memcpy(ptr, &(addr->basic_errno), sizeof(addr->basic_errno)); ptr += sizeof(addr->basic_errno); memcpy(ptr, &(addr->more_errno), sizeof(addr->more_errno)); @@ -4480,7 +4514,11 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) } /* Local interface address/port */ +#ifdef EXPERIMENTAL_DSN_INFO + if (sending_ip_address) +#else if (LOGGING(incoming_interface) && sending_ip_address) +#endif { uschar * ptr = big_buffer; sprintf(CS ptr, "%.128s", sending_ip_address); @@ -7209,16 +7247,32 @@ wording. */ for (addr = handled_addr; addr; addr = addr->next) { + host_item * hu; fprintf(f, "Action: failed\n" "Final-Recipient: rfc822;%s\n" "Status: 5.0.0\n", addr->address); - if (addr->host_used && addr->host_used->name) - { - fprintf(f, "Remote-MTA: dns; %s\n", - addr->host_used->name); - print_dsn_diagnostic_code(addr, f); - } + if ((hu = addr->host_used) && hu->name) + { + const uschar * s; + fprintf(f, "Remote-MTA: dns; %s\n", + hu->name); +#ifdef EXPERIMENTAL_DSN_INFO + if (hu->address) + { + uschar * p = hu->port == 25 + ? US"" : string_sprintf(":%d", hu->port); + fprintf(f, "Remote-MTA: X-ip; [%s]%s\n", hu->address, p); + } + if ((s = addr->smtp_greeting) && *s) + fprintf(f, "X-Remote-MTA-smtp-greeting: X-str; %s\n", s); + if ((s = addr->helo_response) && *s) + fprintf(f, "X-Remote-MTA-helo-response: X-str; %s\n", s); + if ((s = addr->message) && *s) + fprintf(f, "X-Exim-Diagnostic: X-str; %s\n", s); +#endif + print_dsn_diagnostic_code(addr, f); + } fputc('\n', f); } diff --git a/src/src/exim.c b/src/src/exim.c index 999b94c..084d649 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -847,6 +847,12 @@ fprintf(f, "Support for:"); #ifdef EXPERIMENTAL_DMARC fprintf(f, " Experimental_DMARC"); #endif +#ifdef EXPERIMENTAL_DSN_INFO + fprintf(f, " Experimental_DSN_info"); +#endif +#ifdef EXPERIMENTAL_INTERNATIONAL + fprintf(f, " Experimental_International"); +#endif #ifdef EXPERIMENTAL_PROXY fprintf(f, " Experimental_Proxy"); #endif @@ -859,9 +865,6 @@ fprintf(f, "Support for:"); #ifdef EXPERIMENTAL_SOCKS fprintf(f, " Experimental_SOCKS"); #endif -#ifdef EXPERIMENTAL_INTERNATIONAL - fprintf(f, " Experimental_International"); -#endif fprintf(f, "\n"); fprintf(f, "Lookups (built-in):"); diff --git a/src/src/globals.c b/src/src/globals.c index 8445f00..f3b6791 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -354,13 +354,17 @@ address_item address_defaults = { NULL, /* return_filename */ NULL, /* self_hostname */ NULL, /* shadow_message */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS NULL, /* cipher */ NULL, /* ourcert */ NULL, /* peercert */ NULL, /* peerdn */ OCSP_NOT_REQ, /* ocsp */ - #endif +#endif +#ifdef EXPERIMENTAL_DSN_INFO + NULL, /* smtp_greeting */ + NULL, /* helo_response */ +#endif NULL, /* authenticator */ NULL, /* auth_id */ NULL, /* auth_sndr */ diff --git a/src/src/structs.h b/src/src/structs.h index 438b521..db9e843 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -561,13 +561,18 @@ typedef struct address_item { uschar *self_hostname; /* after self=pass */ uschar *shadow_message; /* info about shadow transporting */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS uschar *cipher; /* Cipher used for transport */ void *ourcert; /* Certificate offered to peer, binary */ void *peercert; /* Certificate from peer, binary */ uschar *peerdn; /* DN of server's certificate */ int ocsp; /* OCSP status of peer cert */ - #endif +#endif + +#ifdef EXPERIMENTAL_DSN_INFO + const uschar *smtp_greeting; /* peer self-identification */ + const uschar *helo_response; /* peer message */ +#endif uschar *authenticator; /* auth driver name used by transport */ uschar *auth_id; /* auth "login" name used by transport */ diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index c93f2ef..ac40460 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -440,6 +440,8 @@ Arguments: rc to put in each address's transport_return field pass_message if TRUE, set the "pass message" flag in the address host if set, mark addrs as having used this host + smtp_greeting from peer + helo_response from peer If errno_value has the special value ERRNO_CONNECTTIMEOUT, ETIMEDOUT is put in the errno field, and RTEF_CTOUT is ORed into the more_errno field, to indicate @@ -450,7 +452,11 @@ Returns: nothing static void set_errno(address_item *addrlist, int errno_value, uschar *msg, int rc, - BOOL pass_message, host_item * host) + BOOL pass_message, host_item * host +#ifdef EXPERIMENTAL_DSN_INFO + , const uschar * smtp_greeting, const uschar * helo_response +#endif + ) { address_item *addr; int orvalue = 0; @@ -459,7 +465,7 @@ if (errno_value == ERRNO_CONNECTTIMEOUT) errno_value = ETIMEDOUT; orvalue = RTEF_CTOUT; } -for (addr = addrlist; addr != NULL; addr = addr->next) +for (addr = addrlist; addr; addr = addr->next) if (addr->transport_return >= PENDING) { addr->basic_errno = errno_value; @@ -471,10 +477,31 @@ for (addr = addrlist; addr != NULL; addr = addr->next) } addr->transport_return = rc; if (host) + { addr->host_used = host; +#ifdef EXPERIMENTAL_DSN_INFO + if (smtp_greeting) + {uschar * s = Ustrchr(smtp_greeting, '\n'); if (s) *s = '\0';} + addr->smtp_greeting = smtp_greeting; + + if (helo_response) + {uschar * s = Ustrchr(helo_response, '\n'); if (s) *s = '\0';} + addr->helo_response = helo_response; +#endif + } } } +static void +set_errno_nohost(address_item *addrlist, int errno_value, uschar *msg, int rc, + BOOL pass_message) +{ +set_errno(addrlist, errno_value, msg, rc, pass_message, NULL +#ifdef EXPERIMENTAL_DSN_INFO + , NULL, NULL +#endif + ); +} /************************************************* @@ -847,7 +874,7 @@ while (count-- > 0) { uschar *message = string_sprintf("SMTP timeout after RCPT TO:<%s>", transport_rcpt_address(addr, include_affixes)); - set_errno(addrlist, ETIMEDOUT, message, DEFER, FALSE, NULL); + set_errno_nohost(addrlist, ETIMEDOUT, message, DEFER, FALSE); retry_add_item(addr, addr->address_retry_key, 0); update_waiting = FALSE; return -1; @@ -1096,8 +1123,8 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1)) /* Internal problem, message in buffer. */ case ERROR: - set_errno(addrlist, ERRNO_AUTHPROB, string_copy(buffer), - DEFER, FALSE, NULL); + set_errno_nohost(addrlist, ERRNO_AUTHPROB, string_copy(buffer), + DEFER, FALSE); return ERROR; } @@ -1111,9 +1138,9 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1)) if (require_auth == OK && !smtp_authenticated) { - set_errno(addrlist, ERRNO_AUTHFAIL, + set_errno_nohost(addrlist, ERRNO_AUTHFAIL, string_sprintf("authentication required but %s", fail_reason), DEFER, - FALSE, NULL); + FALSE); return DEFER; } @@ -1152,7 +1179,7 @@ if (ob->authenticated_sender != NULL) { uschar *message = string_sprintf("failed to expand " "authenticated_sender: %s", expand_string_message); - set_errno(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, NULL); + set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE); return TRUE; } } @@ -1381,6 +1408,10 @@ smtp_outblock outblock; int max_rcpt = tblock->max_addresses; uschar *igquotstr = US""; +#ifdef EXPERIMENTAL_DSN_INFO +uschar *smtp_greeting = NULL; +uschar *helo_response = NULL; +#endif uschar *helo_data = NULL; uschar *message = NULL; @@ -1432,8 +1463,8 @@ tls_modify_variables(&tls_out); #ifndef SUPPORT_TLS if (smtps) { - set_errno(addrlist, ERRNO_TLSFAILURE, US"TLS support not available", - DEFER, FALSE, NULL); + set_errno_nohost(addrlist, ERRNO_TLSFAILURE, US"TLS support not available", + DEFER, FALSE); return ERROR; } #endif @@ -1450,8 +1481,8 @@ if (continue_hostname == NULL) if (inblock.sock < 0) { - set_errno(addrlist, (errno == ETIMEDOUT)? ERRNO_CONNECTTIMEOUT : errno, - NULL, DEFER, FALSE, NULL); + set_errno_nohost(addrlist, (errno == ETIMEDOUT)? ERRNO_CONNECTTIMEOUT : errno, + NULL, DEFER, FALSE); return DEFER; } @@ -1469,18 +1500,18 @@ if (continue_hostname == NULL) && dane_required /* do not error on only dane-requested */ ) { - set_errno(addrlist, ERRNO_DNSDEFER, + set_errno_nohost(addrlist, ERRNO_DNSDEFER, string_sprintf("DANE error: tlsa lookup %s", rc == DEFER ? "DEFER" : "FAIL"), - rc, FALSE, NULL); + rc, FALSE); return rc; } } else if (dane_required) { - set_errno(addrlist, ERRNO_DNSDEFER, + set_errno_nohost(addrlist, ERRNO_DNSDEFER, string_sprintf("DANE error: %s lookup not DNSSEC", host->name), - FAIL, FALSE, NULL); + FAIL, FALSE); return FAIL; } @@ -1501,7 +1532,7 @@ if (continue_hostname == NULL) if ((helo_data = string_domain_utf8_to_alabel(helo_data, &errstr)), errstr) { errstr = string_sprintf("failed to expand helo_data: %s", errstr); - set_errno(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE, NULL); + set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE); yield = DEFER; goto SEND_QUIT; } @@ -1514,8 +1545,12 @@ if (continue_hostname == NULL) if (!smtps) { - if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', - ob->command_timeout)) goto RESPONSE_FAILED; + BOOL good_response = smtp_read_response(&inblock, buffer, sizeof(buffer), + '2', ob->command_timeout); +#ifdef EXPERIMENTAL_DSN_INFO + smtp_greeting = string_copy(buffer); +#endif + if (!good_response) goto RESPONSE_FAILED; #ifdef EXPERIMENTAL_EVENT { @@ -1525,9 +1560,9 @@ if (continue_hostname == NULL) s = event_raise(tblock->event_action, US"smtp:connect", buffer); if (s) { - set_errno(addrlist, ERRNO_EXPANDFAIL, + set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, string_sprintf("deferred by smtp:connect event expansion: %s", s), - DEFER, FALSE, NULL); + DEFER, FALSE); yield = DEFER; goto SEND_QUIT; } @@ -1541,7 +1576,7 @@ if (continue_hostname == NULL) { uschar *message = string_sprintf("failed to expand helo_data: %s", expand_string_message); - set_errno(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, NULL); + set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE); yield = DEFER; goto SEND_QUIT; } @@ -1606,9 +1641,18 @@ goto SEND_QUIT; if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', ob->command_timeout)) { - if (errno != 0 || buffer[0] == 0 || lmtp) goto RESPONSE_FAILED; + if (errno != 0 || buffer[0] == 0 || lmtp) + { +#ifdef EXPERIMENTAL_DSN_INFO + helo_response = string_copy(buffer); +#endif + goto RESPONSE_FAILED; + } esmtp = FALSE; } +#ifdef EXPERIMENTAL_DSN_INFO + helo_response = string_copy(buffer); +#endif } else { @@ -1618,10 +1662,16 @@ goto SEND_QUIT; if (!esmtp) { + BOOL good_response; + if (smtp_write_command(&outblock, FALSE, "HELO %s\r\n", helo_data) < 0) goto SEND_FAILED; - if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', - ob->command_timeout)) goto RESPONSE_FAILED; + good_response = smtp_read_response(&inblock, buffer, sizeof(buffer), + '2', ob->command_timeout); +#ifdef EXPERIMENTAL_DSN_INFO + helo_response = string_copy(buffer); +#endif + if (!good_response) goto RESPONSE_FAILED; } /* Set IGNOREQUOTA if the response to LHLO specifies support and the @@ -1671,6 +1721,11 @@ error messages. Note that smtp_use_size and smtp_use_pipelining will have been set from the command line if they were set in the process that passed the connection on. */ +/*XXX continue case needs to propagate DSN_INFO, prob. in deliver.c +as the contine goes via transport_pass_socket() and doublefork and exec. +It does not wait. Unclear how we keep separate host's responses +separate - we could match up by host ip+port as a bodge. */ + else { inblock.sock = outblock.sock = fileno(stdin); @@ -1749,7 +1804,7 @@ if ( tls_offered /* TLS session is set up */ - for (addr = addrlist; addr != NULL; addr = addr->next) + for (addr = addrlist; addr; addr = addr->next) if (addr->transport_return == PENDING_DEFER) { addr->cipher = tls_out.cipher; @@ -1774,6 +1829,8 @@ start of the Exim process (in exim.c). */ if (tls_out.active >= 0) { char *greeting_cmd; + BOOL good_response; + if (helo_data == NULL) { helo_data = expand_string(ob->helo_data); @@ -1781,7 +1838,7 @@ if (tls_out.active >= 0) { uschar *message = string_sprintf("failed to expand helo_data: %s", expand_string_message); - set_errno(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, NULL); + set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE); yield = DEFER; goto SEND_QUIT; } @@ -1790,8 +1847,12 @@ if (tls_out.active >= 0) /* For SMTPS we need to wait for the initial OK response. */ if (smtps) { - if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', - ob->command_timeout)) goto RESPONSE_FAILED; + good_response = smtp_read_response(&inblock, buffer, sizeof(buffer), + '2', ob->command_timeout); +#ifdef EXPERIMENTAL_DSN_INFO + smtp_greeting = string_copy(buffer); +#endif + if (!good_response) goto RESPONSE_FAILED; } if (esmtp) @@ -1806,9 +1867,12 @@ if (tls_out.active >= 0) if (smtp_write_command(&outblock, FALSE, "%s %s\r\n", lmtp? "LHLO" : greeting_cmd, helo_data) < 0) goto SEND_FAILED; - if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', - ob->command_timeout)) - goto RESPONSE_FAILED; + good_response = smtp_read_response(&inblock, buffer, sizeof(buffer), + '2', ob->command_timeout); +#ifdef EXPERIMENTAL_DSN_INFO + helo_response = string_copy(buffer); +#endif + if (!good_response) goto RESPONSE_FAILED; } /* If the host is required to use a secure channel, ensure that we @@ -1935,8 +1999,8 @@ if (tblock->filter_command != NULL) if (!rc) { - set_errno(addrlist->next, addrlist->basic_errno, addrlist->message, DEFER, - FALSE, NULL); + set_errno_nohost(addrlist->next, addrlist->basic_errno, addrlist->message, DEFER, + FALSE); yield = ERROR; goto SEND_QUIT; } @@ -2065,7 +2129,7 @@ pending_MAIL = TRUE; /* The block starts with MAIL */ { if (s = string_address_utf8_to_alabel(return_path, &errstr), errstr) { - set_errno(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE, NULL); + set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE); yield = ERROR; goto SEND_QUIT; } @@ -2217,8 +2281,8 @@ if (mua_wrapper) if (badaddr->transport_return != PENDING_OK) { /*XXX could we find a better errno than 0 here? */ - set_errno(addrlist, 0, badaddr->message, FAIL, - testflag(badaddr, af_pass_message), NULL); + set_errno_nohost(addrlist, 0, badaddr->message, FAIL, + testflag(badaddr, af_pass_message)); ok = FALSE; break; } @@ -2475,7 +2539,7 @@ if (!ok) ok = TRUE; else else sprintf(CS buffer, "%.500s\n", addr->unique); - DEBUG(D_deliver) debug_printf("journalling %s", buffer); + DEBUG(D_deliver) debug_printf("journalling %s\n", buffer); len = Ustrlen(CS buffer); if (write(journal_fd, buffer, len) != len) log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for " @@ -2512,7 +2576,7 @@ if (!ok) ok = TRUE; else else sprintf(CS buffer, "%.500s\n", addr->unique); - DEBUG(D_deliver) debug_printf("journalling(PRDR) %s", buffer); + DEBUG(D_deliver) debug_printf("journalling(PRDR) %s\n", buffer); len = Ustrlen(CS buffer); if (write(journal_fd, buffer, len) != len) log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for " @@ -2542,22 +2606,27 @@ the problem is not related to this specific message. */ if (!ok) { - int code; + int code, set_rc; + uschar * set_message; RESPONSE_FAILED: - save_errno = errno; - message = NULL; - send_quit = check_response(host, &save_errno, addrlist->more_errno, - buffer, &code, &message, &pass_message); - goto FAILED; + { + save_errno = errno; + message = NULL; + send_quit = check_response(host, &save_errno, addrlist->more_errno, + buffer, &code, &message, &pass_message); + goto FAILED; + } SEND_FAILED: - save_errno = errno; - code = '4'; - message = US string_sprintf("send() to %s [%s] failed: %s", - host->name, host->address, strerror(save_errno)); - send_quit = FALSE; - goto FAILED; + { + save_errno = errno; + code = '4'; + message = US string_sprintf("send() to %s [%s] failed: %s", + host->name, host->address, strerror(save_errno)); + send_quit = FALSE; + goto FAILED; + } /* This label is jumped to directly when a TLS negotiation has failed, or was not done for a host for which it is required. Values will be set @@ -2578,16 +2647,14 @@ if (!ok) FAILED: ok = FALSE; /* For when reached by GOTO */ + set_message = message; if (setting_up) { if (code == '5') - set_errno(addrlist, save_errno, message, FAIL, pass_message, host); + set_rc = FAIL; else - { - set_errno(addrlist, save_errno, message, DEFER, pass_message, host); - yield = DEFER; - } + yield = set_rc = DEFER; } /* We want to handle timeouts after MAIL or "." and loss of connection after @@ -2646,14 +2713,15 @@ if (!ok) if (message_error) { if (mua_wrapper) code = '5'; /* Force hard failure in wrapper mode */ - set_errno(addrlist, save_errno, message, (code == '5')? FAIL : DEFER, - pass_message, host); /* If there's an errno, the message contains just the identity of the host. */ - if (code != '5') /* Anything other than 5 is treated as temporary */ + if (code == '5') + set_rc = FAIL; + else /* Anything other than 5 is treated as temporary */ { + set_rc = DEFER; if (save_errno > 0) message = US string_sprintf("%s: %s", message, strerror(save_errno)); if (host->next != NULL) log_write(0, LOG_MAIN, "%s", message); @@ -2670,11 +2738,17 @@ if (!ok) else { + set_rc = DEFER; yield = (save_errno == ERRNO_CHHEADER_FAIL || save_errno == ERRNO_FILTER_FAIL)? ERROR : DEFER; - set_errno(addrlist, save_errno, message, DEFER, pass_message, host); } } + + set_errno(addrlist, save_errno, set_message, set_rc, pass_message, host +#ifdef EXPERIMENTAL_DSN_INFO + , smtp_greeting, helo_response +#endif + ); } @@ -2787,6 +2861,9 @@ if (completed_address && ok && send_quit) /* If the socket is successfully passed, we musn't send QUIT (or indeed anything!) from here. */ +/*XXX DSN_INFO: assume likely to do new HELO; but for greet we'll want to +propagate it from the initial +*/ if (ok && transport_pass_socket(tblock->name, host->name, host->address, new_message_id, inblock.sock)) { @@ -2796,7 +2873,11 @@ if (completed_address && ok && send_quit) /* If RSET failed and there are addresses left, they get deferred. */ - else set_errno(first_addr, errno, msg, DEFER, FALSE, host); + else set_errno(first_addr, errno, msg, DEFER, FALSE, host +#ifdef EXPERIMENTAL_DSN_INFO + , smtp_greeting, helo_response +#endif + ); } } @@ -2938,6 +3019,10 @@ for (addr = addrlist; addr != NULL; addr = addr->next) addr->peerdn = NULL; addr->ocsp = OCSP_NOT_REQ; #endif +#ifdef EXPERIMENTAL_DSN_INFO + addr->smtp_greeting = NULL; + addr->helo_response = NULL; +#endif } return first_addr; } @@ -3479,7 +3564,7 @@ for (cutoff_retry = 0; expired && if (dont_deliver) { host_item *host2; - set_errno(addrlist, 0, NULL, OK, FALSE, NULL); + set_errno_nohost(addrlist, 0, NULL, OK, FALSE); for (addr = addrlist; addr != NULL; addr = addr->next) { addr->host_used = host;