diff -urN ../exim-4.93.orig/OS/Makefile-Base ./OS/Makefile-Base --- ../exim-4.93.orig/OS/Makefile-Base 2019-12-08 14:53:48.000000000 +0200 +++ ./OS/Makefile-Base 2020-02-21 00:30:45.560843000 +0200 @@ -868,9 +868,9 @@ em_xs.o: ../exim_monitor/em_xs.c ../exim_monitor/em_hdr.h em_version.o: ../exim_monitor/em_version.c ../exim_monitor/em_hdr.h $(MONBIN): $(HDRS) - @echo "$(CC) exim_monitor/`echo $@ | sed 's/o$$/c/'`" - $(FE)$(CC) -o $@ -c $(CFLAGS) -I. -I../exim_monitor $(INCLUDE) $(XINCLUDE) \ - ../exim_monitor/`echo $@ | sed 's/o$$/c/'` + @echo "$(CC) exim_monitor/$(@:.o=.c)" + $(FE)$(CC) -o $@ -c $(CFLAGS) -DCOMPILE_UTILITY -I. -I../exim_monitor $(INCLUDE) $(XINCLUDE) \ + ../exim_monitor/$(@:.o=.c) # Targets for the various libraries that Exim uses. diff -urN ../exim-4.93.orig/exim_monitor/em_version.c ./exim_monitor/em_version.c --- ../exim-4.93.orig/exim_monitor/em_version.c 2019-12-08 14:53:48.000000000 +0200 +++ ./exim_monitor/em_version.c 2020-02-21 00:30:45.565721000 +0200 @@ -5,6 +5,8 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +#define EM_VERSION_C + #include "mytypes.h" #include "store.h" #include "macros.h" diff -urN ../exim-4.93.orig/src/arc.c ./src/arc.c --- ../exim-4.93.orig/src/arc.c 2019-12-08 14:53:48.000000000 +0200 +++ ./src/arc.c 2020-02-21 00:30:45.568211000 +0200 @@ -381,7 +381,7 @@ static uschar * arc_insert_hdr(arc_ctx * ctx, header_line * h, unsigned off, unsigned hoff, - BOOL instance_only) + BOOL instance_only, arc_line ** alp_ret) { unsigned i; arc_set * as; @@ -401,6 +401,7 @@ if (*(alp = (arc_line **)(US as + hoff))) return US"dup hdr"; *alp = al; +if (alp_ret) *alp_ret = al; return NULL; } @@ -424,7 +425,7 @@ debug_printf("ARC: found AAR: %.*s\n", len, h->text); } if ((e = arc_insert_hdr(ctx, h, ARC_HDRLEN_AAR, offsetof(arc_set, hdr_aar), - TRUE))) + TRUE, NULL))) { DEBUG(D_acl) debug_printf("inserting AAR: %s\n", e); return US"inserting AAR"; @@ -443,15 +444,13 @@ debug_printf("ARC: found AMS: %.*s\n", len, h->text); } if ((e = arc_insert_hdr(ctx, h, ARC_HDRLEN_AMS, offsetof(arc_set, hdr_ams), - instance_only))) + instance_only, &ams))) { DEBUG(D_acl) debug_printf("inserting AMS: %s\n", e); return US"inserting AMS"; } /* defaults */ - /*XXX dubious selection of ams here */ - ams = ctx->arcset_chain->hdr_ams; if (!ams->c.data) { ams->c_head.data = US"simple"; ams->c_head.len = 6; @@ -469,7 +468,7 @@ debug_printf("ARC: found AS: %.*s\n", len, h->text); } if ((e = arc_insert_hdr(ctx, h, ARC_HDRLEN_AS, offsetof(arc_set, hdr_as), - instance_only))) + instance_only, NULL))) { DEBUG(D_acl) debug_printf("inserting AS: %s\n", e); return US"inserting AS"; diff -urN ../exim-4.93.orig/src/auths/README ./src/auths/README --- ../exim-4.93.orig/src/auths/README 2019-12-08 14:53:48.000000000 +0200 +++ ./src/auths/README 2020-02-21 00:30:45.568335000 +0200 @@ -34,7 +34,7 @@ the server and/or client functions are available for this authenticator. Typically this depends on whether server or client configuration options have been set, but it is also possible to have an authenticator that has only one of -the server or client functions. +the server or client functions. The function may not touch big_buffer. SERVER AUTHENTICATION diff -urN ../exim-4.93.orig/src/auths/call_pam.c ./src/auths/call_pam.c --- ../exim-4.93.orig/src/auths/call_pam.c 2019-12-08 14:53:48.000000000 +0200 +++ ./src/auths/call_pam.c 2020-02-21 00:30:45.568725000 +0200 @@ -69,9 +69,11 @@ int sep = 0; struct pam_response *reply; -if (pam_arg_ended) return PAM_CONV_ERR; +/* It seems that PAM frees reply[] */ -reply = store_get(sizeof(struct pam_response) * num_msg, FALSE); +if ( pam_arg_ended + || !(reply = malloc(sizeof(struct pam_response) * num_msg))) + return PAM_CONV_ERR; for (int i = 0; i < num_msg; i++) { @@ -80,25 +82,26 @@ { case PAM_PROMPT_ECHO_ON: case PAM_PROMPT_ECHO_OFF: - arg = string_nextinlist(&pam_args, &sep, big_buffer, big_buffer_size); - if (arg == NULL) - { - arg = US""; - pam_arg_ended = TRUE; - } - reply[i].resp = CS string_copy_perm(arg, FALSE); /* PAM frees resp */ - reply[i].resp_retcode = PAM_SUCCESS; - break; + arg = string_nextinlist(&pam_args, &sep, big_buffer, big_buffer_size); + if (!arg) + { + arg = US""; + pam_arg_ended = TRUE; + } + reply[i].resp = CS string_copy_malloc(arg); /* PAM frees resp */ + reply[i].resp_retcode = PAM_SUCCESS; + break; case PAM_TEXT_INFO: /* Just acknowledge messages */ case PAM_ERROR_MSG: - reply[i].resp_retcode = PAM_SUCCESS; - reply[i].resp = NULL; - break; + reply[i].resp_retcode = PAM_SUCCESS; + reply[i].resp = NULL; + break; default: /* Must be an error of some sort... */ - pam_conv_had_error = TRUE; - return PAM_CONV_ERR; + free(reply); + pam_conv_had_error = TRUE; + return PAM_CONV_ERR; } } diff -urN ../exim-4.93.orig/src/auths/cyrus_sasl.c ./src/auths/cyrus_sasl.c --- ../exim-4.93.orig/src/auths/cyrus_sasl.c 2019-12-08 14:53:48.000000000 +0200 +++ ./src/auths/cyrus_sasl.c 2020-02-21 00:30:45.569185000 +0200 @@ -347,10 +347,10 @@ } else { - /* make sure that we have a null-terminated string */ - out2 = string_copyn(output, outlen); + /* auth_get_data() takes a length-specfied block of binary + which can include zeroes; no terminating NUL is needed */ - if ((rc = auth_get_data(&input, out2, outlen)) != OK) + if ((rc = auth_get_data(&input, output, outlen)) != OK) { /* we couldn't get the data, so free up the library before * returning whatever error we get */ diff -urN ../exim-4.93.orig/src/auths/gsasl_exim.c ./src/auths/gsasl_exim.c --- ../exim-4.93.orig/src/auths/gsasl_exim.c 2019-12-08 14:53:48.000000000 +0200 +++ ./src/auths/gsasl_exim.c 2020-02-21 00:30:45.569784000 +0200 @@ -461,11 +461,11 @@ case GSASL_VALIDATE_SIMPLE: /* GSASL_AUTHID, GSASL_AUTHZID, and GSASL_PASSWORD */ propval = US gsasl_property_fast(sctx, GSASL_AUTHID); - auth_vars[0] = expand_nstring[1] = propval ? propval : US""; + auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US""; propval = US gsasl_property_fast(sctx, GSASL_AUTHZID); - auth_vars[1] = expand_nstring[2] = propval ? propval : US""; + auth_vars[1] = expand_nstring[2] = propval ? string_copy(propval) : US""; propval = US gsasl_property_fast(sctx, GSASL_PASSWORD); - auth_vars[2] = expand_nstring[3] = propval ? propval : US""; + auth_vars[2] = expand_nstring[3] = propval ? string_copy(propval) : US""; expand_nmax = 3; for (int i = 1; i <= 3; ++i) expand_nlength[i] = Ustrlen(expand_nstring[i]); @@ -483,7 +483,7 @@ } propval = US gsasl_property_fast(sctx, GSASL_AUTHZID); /* We always set $auth1, even if only to empty string. */ - auth_vars[0] = expand_nstring[1] = propval ? propval : US""; + auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US""; expand_nlength[1] = Ustrlen(expand_nstring[1]); expand_nmax = 1; @@ -501,7 +501,7 @@ } propval = US gsasl_property_fast(sctx, GSASL_ANONYMOUS_TOKEN); /* We always set $auth1, even if only to empty string. */ - auth_vars[0] = expand_nstring[1] = propval ? propval : US""; + auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US""; expand_nlength[1] = Ustrlen(expand_nstring[1]); expand_nmax = 1; @@ -521,9 +521,9 @@ to the first release of Exim with this authenticator, they've been switched to match the ordering of GSASL_VALIDATE_SIMPLE. */ propval = US gsasl_property_fast(sctx, GSASL_GSSAPI_DISPLAY_NAME); - auth_vars[0] = expand_nstring[1] = propval ? propval : US""; + auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US""; propval = US gsasl_property_fast(sctx, GSASL_AUTHZID); - auth_vars[1] = expand_nstring[2] = propval ? propval : US""; + auth_vars[1] = expand_nstring[2] = propval ? string_copy(propval) : US""; expand_nmax = 2; for (int i = 1; i <= 2; ++i) expand_nlength[i] = Ustrlen(expand_nstring[i]); @@ -558,11 +558,11 @@ needing to add more glue, since avoiding that is a large part of the point of SASL. */ propval = US gsasl_property_fast(sctx, GSASL_AUTHID); - auth_vars[0] = expand_nstring[1] = propval ? propval : US""; + auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US""; propval = US gsasl_property_fast(sctx, GSASL_AUTHZID); - auth_vars[1] = expand_nstring[2] = propval ? propval : US""; + auth_vars[1] = expand_nstring[2] = propval ? string_copy(propval) : US""; propval = US gsasl_property_fast(sctx, GSASL_REALM); - auth_vars[2] = expand_nstring[3] = propval ? propval : US""; + auth_vars[2] = expand_nstring[3] = propval ? string_copy(propval) : US""; expand_nmax = 3; for (int i = 1; i <= 3; ++i) expand_nlength[i] = Ustrlen(expand_nstring[i]); diff -urN ../exim-4.93.orig/src/auths/heimdal_gssapi.c ./src/auths/heimdal_gssapi.c --- ../exim-4.93.orig/src/auths/heimdal_gssapi.c 2019-12-08 14:53:48.000000000 +0200 +++ ./src/auths/heimdal_gssapi.c 2020-02-21 00:30:45.570022000 +0200 @@ -200,16 +200,6 @@ krb5_free_context(context); -/* RFC 4121 section 5.2, SHOULD support 64K input buffers */ -if (big_buffer_size < (64 * 1024)) - { - uschar *newbuf; - big_buffer_size = 64 * 1024; - newbuf = store_malloc(big_buffer_size); - store_free(big_buffer); - big_buffer = newbuf; - } - ablock->server = TRUE; } @@ -415,7 +405,7 @@ NULL, /* conf_state: no confidentiality applied */ &gbufdesc_out /* output buffer */ ); - if (GSS_ERROR(maj_stat) + if (GSS_ERROR(maj_stat)) { exim_gssapi_error_defer(NULL, maj_stat, min_stat, "gss_wrap(SASL state after auth)"); diff -urN ../exim-4.93.orig/src/drtables.c ./src/drtables.c --- ../exim-4.93.orig/src/drtables.c 2019-12-08 14:53:48.000000000 +0200 +++ ./src/drtables.c 2020-02-21 00:30:45.576595000 +0200 @@ -740,10 +740,11 @@ dl = dlopen(CS big_buffer, RTLD_NOW);// TJ was LAZY if (dl == NULL) { - fprintf(stderr, "Error loading %s: %s\n", name, dlerror()); - moduleerrors++; - log_write(0, LOG_MAIN|LOG_PANIC, "Error loading lookup module %s: %s\n", name, dlerror()); - continue; + errormessage = dlerror(); + fprintf(stderr, "Error loading %s: %s\n", name, errormessage); + log_write(0, LOG_MAIN|LOG_PANIC, "Error loading lookup module %s: %s\n", name, errormessage); + moduleerrors++; + continue; } /* FreeBSD nsdispatch() can trigger dlerror() errors about @@ -756,16 +757,16 @@ info = (struct lookup_module_info*) dlsym(dl, "_lookup_module_info"); if ((errormsg = dlerror()) != NULL) { fprintf(stderr, "%s does not appear to be a lookup module (%s)\n", name, errormsg); + log_write(0, LOG_MAIN|LOG_PANIC, "%s does not appear to be a lookup module (%s)\n", name, errormsg); dlclose(dl); moduleerrors++; - log_write(0, LOG_MAIN|LOG_PANIC, "%s does not appear to be a lookup module (%s)\n", name, errormsg); continue; } if (info->magic != LOOKUP_MODULE_INFO_MAGIC) { fprintf(stderr, "Lookup module %s is not compatible with this version of Exim\n", name); + log_write(0, LOG_MAIN|LOG_PANIC, "Lookup module %s is not compatible with this version of Exim\n", name); dlclose(dl); moduleerrors++; - log_write(0, LOG_MAIN|LOG_PANIC, "Lookup module %s is not compatible with this version of Exim\n", name); continue; } diff -urN ../exim-4.93.orig/src/exim.c ./src/exim.c --- ../exim-4.93.orig/src/exim.c 2019-12-08 14:53:48.000000000 +0200 +++ ./src/exim.c 2020-02-21 00:30:45.578083000 +0200 @@ -4822,8 +4822,9 @@ { while (recipients_arg < argc) { - uschar *s = argv[recipients_arg++]; - while (*s != 0) + /* Supplied addresses are tainted since they come from a user */ + uschar * s = string_copy_taint(argv[recipients_arg++], TRUE); + while (*s) { BOOL finished = FALSE; uschar *ss = parse_find_address_end(s, FALSE); @@ -4831,16 +4832,16 @@ test_address(s, flags, &exit_value); s = ss; if (!finished) - while (*(++s) != 0 && (*s == ',' || isspace(*s))); + while (*++s == ',' || isspace(*s)) ; } } } else for (;;) { - uschar *s = get_stdinput(NULL, NULL); - if (s == NULL) break; - test_address(s, flags, &exit_value); + uschar * s = get_stdinput(NULL, NULL); + if (!s) break; + test_address(string_copy_taint(s, TRUE), flags, &exit_value); } route_tidyup(); @@ -5334,13 +5335,13 @@ raw_sender = string_copy(sender_address); - /* Loop for each argument */ + /* Loop for each argument (supplied by user hence tainted) */ for (int i = 0; i < count; i++) { int start, end, domain; - uschar *errmess; - uschar *s = list[i]; + uschar * errmess; + uschar * s = string_copy_taint(list[i], TRUE); /* Loop for each comma-separated address */ diff -urN ../exim-4.93.orig/src/exim_dbmbuild.c ./src/exim_dbmbuild.c --- ../exim-4.93.orig/src/exim_dbmbuild.c 2019-12-08 14:53:48.000000000 +0200 +++ ./src/exim_dbmbuild.c 2020-02-21 00:30:45.578488000 +0200 @@ -56,6 +56,11 @@ uschar * string_sprintf_trc(const char * a, const uschar * b, unsigned c, ...) { return NULL; } +BOOL +string_format_trc(uschar * buf, int len, const uschar * func, unsigned line, + const char * fmt, ...) +{ return FALSE; } + struct global_flags f; unsigned int log_selector[1]; diff -urN ../exim-4.93.orig/src/exim_dbutil.c ./src/exim_dbutil.c --- ../exim-4.93.orig/src/exim_dbutil.c 2019-12-08 14:53:48.000000000 +0200 +++ ./src/exim_dbutil.c 2020-02-21 00:30:45.578777000 +0200 @@ -59,6 +59,10 @@ uschar * string_sprintf_trc(const char * fmt, const uschar * func, unsigned line, ...) { return NULL; } +BOOL +string_format_trc(uschar * buf, int len, const uschar * func, unsigned line, + const char * fmt, ...) +{ return FALSE; } struct global_flags f; unsigned int log_selector[1]; diff -urN ../exim-4.93.orig/src/functions.h ./src/functions.h --- ../exim-4.93.orig/src/functions.h 2019-12-08 14:53:48.000000000 +0200 +++ ./src/functions.h 2020-02-21 00:30:45.582701000 +0200 @@ -187,6 +187,7 @@ extern uschar *deliver_get_sender_address (uschar *id); extern void delivery_re_exec(int); +extern void die_tainted(const uschar *, const uschar *, int); extern BOOL directory_make(const uschar *, const uschar *, int, BOOL); #ifndef DISABLE_DKIM extern uschar *dkim_exim_query_dns_txt(const uschar *); @@ -602,6 +603,58 @@ extern ssize_t write_to_fd_buf(int, const uschar *, size_t); +/******************************************************************************/ +/* Predicate: if an address is in a tainted pool. +By extension, a variable pointing to this address is tainted. +*/ + +static inline BOOL +is_tainted(const void * p) +{ +#if defined(COMPILE_UTILITY) || defined(MACRO_PREDEF) || defined(EM_VERSION_C) +return FALSE; + +#else +extern BOOL is_tainted_fn(const void *); +return is_tainted_fn(p); +#endif +} + +/******************************************************************************/ +/* String functions */ +static inline uschar * __Ustrcat(uschar * dst, const uschar * src, const char * func, int line) +{ +#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) +if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrcat", CUS func, line); +#endif +return US strcat(CS dst, CCS src); +} +static inline uschar * __Ustrcpy(uschar * dst, const uschar * src, const char * func, int line) +{ +#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) +if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrcpy", CUS func, line); +#endif +return US strcpy(CS dst, CCS src); +} +static inline uschar * __Ustrncat(uschar * dst, const uschar * src, size_t n, const char * func, int line) +{ +#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) +if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrncat", CUS func, line); +#endif +return US strncat(CS dst, CCS src, n); +} +static inline uschar * __Ustrncpy(uschar * dst, const uschar * src, size_t n, const char * func, int line) +{ +#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) +if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrncpy", CUS func, line); +#endif +return US strncpy(CS dst, CCS src, n); +} +/*XXX will likely need unchecked copy also */ + + +/******************************************************************************/ + #if !defined(MACRO_PREDEF) && !defined(COMPILE_UTILITY) /* exim_chown - in some NFSv4 setups *seemes* to be an issue with chown(, ). @@ -634,8 +687,8 @@ return chown(CCS name, owner, group) ? exim_chown_failure(-1, name, owner, group) : 0; } - #endif /* !MACRO_PREDEF && !COMPILE_UTILITY */ + /******************************************************************************/ /* String functions */ @@ -902,9 +955,18 @@ static inline uschar * spool_fname(const uschar * purpose, const uschar * subdir, const uschar * fname, - const uschar * suffix) + const uschar * suffix) { +#ifdef COMPILE_UTILITY /* version avoiding string-extension */ +int len = Ustrlen(spool_directory) + 1 + Ustrlen(queue_name) + 1 + Ustrlen(purpose) + 1 + + Ustrlen(subdir) + 1 + Ustrlen(fname) + Ustrlen(suffix) + 1; +uschar * buf = store_get(len, FALSE); +string_format(buf, len, "%s/%s/%s/%s/%s%s", + spool_directory, queue_name, purpose, subdir, fname, suffix); +return buf; +#else return spool_q_fname(purpose, queue_name, subdir, fname, suffix); +#endif } static inline void diff -urN ../exim-4.93.orig/src/globals.c ./src/globals.c --- ../exim-4.93.orig/src/globals.c 2019-12-08 14:53:48.000000000 +0200 +++ ./src/globals.c 2020-02-21 00:30:45.583222000 +0200 @@ -311,6 +311,7 @@ .synchronous_delivery = FALSE, .system_filtering = FALSE, + .taint_check_slow = FALSE, .tcp_fastopen_ok = FALSE, .tcp_in_fastopen = FALSE, .tcp_in_fastopen_data = FALSE, @@ -844,7 +845,7 @@ uschar *dmarc_history_file = NULL; uschar *dmarc_status = NULL; uschar *dmarc_status_text = NULL; -uschar *dmarc_tld_file = US DMARC_TLD_FILE; +uschar *dmarc_tld_file = NULL; uschar *dmarc_used_domain = NULL; #endif diff -urN ../exim-4.93.orig/src/globals.h ./src/globals.h --- ../exim-4.93.orig/src/globals.h 2019-12-08 14:53:48.000000000 +0200 +++ ./src/globals.h 2020-02-21 00:30:45.583630000 +0200 @@ -272,6 +272,7 @@ BOOL synchronous_delivery :1; /* TRUE if -odi is set */ BOOL system_filtering :1; /* TRUE when running system filter */ + BOOL taint_check_slow :1; /* malloc/mmap are not returning distinct ranges */ BOOL tcp_fastopen_ok :1; /* appears to be supported by kernel */ BOOL tcp_in_fastopen :1; /* conn usefully used fastopen */ BOOL tcp_in_fastopen_data :1; /* fastopen carried data */ diff -urN ../exim-4.93.orig/src/ip.c ./src/ip.c --- ../exim-4.93.orig/src/ip.c 2019-12-08 14:53:48.000000000 +0200 +++ ./src/ip.c 2020-02-21 00:30:45.584976000 +0200 @@ -245,7 +245,7 @@ sigalrm_seen = FALSE; if (timeout > 0) ALARM(timeout); -#ifdef TCP_FASTOPEN +#if defined(TCP_FASTOPEN) && (defined(MSG_FASTOPEN) || defined(EXIM_TFO_CONNECTX)) /* TCP Fast Open, if the system has a cookie from a previous call to this peer, can send data in the SYN packet. The peer can send data before it gets our ACK of its SYN,ACK - the latter is useful for @@ -255,8 +255,8 @@ if (fastopen_blob && f.tcp_fastopen_ok) { # ifdef MSG_FASTOPEN - /* This is a Linux implementation. It might be useable on FreeBSD; I have - not checked. */ + /* This is a Linux implementation. FreeBSD does not seem to have MSG_FASTOPEN so + how to get TFO is unknown. */ if ((rc = sendto(sock, fastopen_blob->data, fastopen_blob->len, MSG_FASTOPEN | MSG_DONTWAIT, s_ptr, s_len)) >= 0) @@ -269,28 +269,34 @@ /*XXX also seen on successful TFO, sigh */ tcp_out_fastopen = fastopen_blob->len > 0 ? TFO_ATTEMPTED_DATA : TFO_ATTEMPTED_NODATA; } - else if (errno == EINPROGRESS) /* expected if we had no cookie for peer */ + else switch (errno) + { + case EINPROGRESS: /* expected if we had no cookie for peer */ /* seen for no-data, proper TFO option, both cookie-request and with-cookie cases */ /* apparently no visibility of the diffference at this point */ /* seen for with-data, proper TFO opt, cookie-req */ /* with netwk delay, post-conn tcp_info sees unacked 1 for R, 2 for C; code in smtp_out.c */ /* ? older Experimental TFO option behaviour ? */ - { /* queue unsent data */ - DEBUG(D_transport|D_v) debug_printf(" TFO mode sendto, %s data: EINPROGRESS\n", - fastopen_blob->len > 0 ? "with" : "no"); - if (!fastopen_blob->data) - { - tcp_out_fastopen = TFO_ATTEMPTED_NODATA; /* we tried; unknown if useful yet */ - rc = 0; - } - else - rc = send(sock, fastopen_blob->data, fastopen_blob->len, 0); - } - else if(errno == EOPNOTSUPP) - { - DEBUG(D_transport) - debug_printf("Tried TCP Fast Open but apparently not enabled by sysctl\n"); - goto legacy_connect; + DEBUG(D_transport|D_v) debug_printf(" TFO mode sendto, %s data: EINPROGRESS\n", + fastopen_blob->len > 0 ? "with" : "no"); + if (!fastopen_blob->data) + { + tcp_out_fastopen = TFO_ATTEMPTED_NODATA; /* we tried; unknown if useful yet */ + rc = 0; + } + else /* queue unsent data */ + rc = send(sock, fastopen_blob->data, fastopen_blob->len, 0); + break; + + case EOPNOTSUPP: + DEBUG(D_transport) + debug_printf("Tried TCP Fast Open but apparently not enabled by sysctl\n"); + goto legacy_connect; + + case EPIPE: + DEBUG(D_transport) + debug_printf("Tried TCP Fast Open but kernel too old to support it\n"); + goto legacy_connect; } # endif # ifdef EXIM_TFO_CONNECTX diff -urN ../exim-4.93.orig/src/local_scan.h ./src/local_scan.h --- ../exim-4.93.orig/src/local_scan.h 2019-12-08 14:53:48.000000000 +0200 +++ ./src/local_scan.h 2020-02-21 00:30:45.585099000 +0200 @@ -193,6 +193,14 @@ string_sprintf_trc(fmt, US __FUNCTION__, __LINE__, __VA_ARGS__) extern uschar *string_sprintf_trc(const char *, const uschar *, unsigned, ...) ALMOST_PRINTF(1,4); +#define store_get(size, tainted) \ + store_get_3(size, tainted, __FUNCTION__, __LINE__) +extern void *store_get_3(int, BOOL, const char *, int) ALLOC ALLOC_SIZE(1) WARN_UNUSED_RESULT; +#define store_get_perm(size, tainted) \ + store_get_perm_3(size, tainted, __FUNCTION__, __LINE__) +extern void *store_get_perm_3(int, BOOL, const char *, int) ALLOC ALLOC_SIZE(1) WARN_UNUSED_RESULT; + + #if defined(LOCAL_SCAN) || defined(DLFUNC_IMPL) /* When compiling a local_scan() file we want to rename a published API, so that we can use an inlined implementation in the compiles of the main Exim files, diff -urN ../exim-4.93.orig/src/macros.h ./src/macros.h --- ../exim-4.93.orig/src/macros.h 2019-12-08 14:53:48.000000000 +0200 +++ ./src/macros.h 2020-02-21 00:30:45.588869000 +0200 @@ -152,12 +152,19 @@ into big_buffer_size and in some circumstances increased. It should be at least as long as the maximum path length. */ -#if defined PATH_MAX && PATH_MAX > 16384 +#ifdef AUTH_HEIMDAL_GSSAPI + /* RFC 4121 section 5.2, SHOULD support 64K input buffers */ +# define __BIG_BUFFER_SIZE 65536 +#else +# define __BIG_BUFFER_SIZE 16384 +#endif + +#if defined PATH_MAX && PATH_MAX > __BIG_BUFFER_SIZE # define BIG_BUFFER_SIZE PATH_MAX -#elif defined MAXPATHLEN && MAXPATHLEN > 16384 +#elif defined MAXPATHLEN && MAXPATHLEN > __BIG_BUFFER_SIZE # define BIG_BUFFER_SIZE MAXPATHLEN #else -# define BIG_BUFFER_SIZE 16384 +# define BIG_BUFFER_SIZE __BIG_BUFFER_SIZE #endif /* header size of pipe content diff -urN ../exim-4.93.orig/src/mytypes.h ./src/mytypes.h --- ../exim-4.93.orig/src/mytypes.h 2019-12-08 14:53:48.000000000 +0200 +++ ./src/mytypes.h 2020-02-21 00:30:45.590342000 +0200 @@ -100,19 +100,15 @@ #define Uread(f,b,l) read(f,CS(b),l) #define Urename(s,t) rename(CCS(s),CCS(t)) #define Ustat(s,t) stat(CCS(s),t) -#define Ustrcat(s,t) __Ustrcat(s, CUS(t), __FUNCTION__, __LINE__) #define Ustrchr(s,n) US strchr(CCS(s),n) #define CUstrchr(s,n) CUS strchr(CCS(s),n) #define CUstrerror(n) CUS strerror(n) #define Ustrcmp(s,t) strcmp(CCS(s),CCS(t)) -#define Ustrcpy(s,t) __Ustrcpy(s, CUS(t), __FUNCTION__, __LINE__) #define Ustrcpy_nt(s,t) strcpy(CS s, CCS t) /* no taint check */ #define Ustrcspn(s,t) strcspn(CCS(s),CCS(t)) #define Ustrftime(s,m,f,t) strftime(CS(s),m,f,t) #define Ustrlen(s) (int)strlen(CCS(s)) -#define Ustrncat(s,t,n) __Ustrncat(s, CUS(t),n, __FUNCTION__, __LINE__) #define Ustrncmp(s,t,n) strncmp(CCS(s),CCS(t),n) -#define Ustrncpy(s,t,n) __Ustrncpy(s, CUS(t),n, __FUNCTION__, __LINE__) #define Ustrncpy_nt(s,t,n) strncpy(CS s, CCS t, n) /* no taint check */ #define Ustrpbrk(s,t) strpbrk(CCS(s),CCS(t)) #define Ustrrchr(s,n) US strrchr(CCS(s),n) @@ -125,57 +121,17 @@ #define Ustrtoul(s,t,b) strtoul(CCS(s),CSS(t),b) #define Uunlink(s) unlink(CCS(s)) -extern void die_tainted(const uschar *, const uschar *, int); - -/* Predicate: if an address is in a tainted pool. -By extension, a variable pointing to this address is tainted. -*/ - -static inline BOOL -is_tainted(const void * p) -{ -#if defined(COMPILE_UTILITY) || defined(MACRO_PREDEF) -return FALSE; - -#elif defined(TAINT_CHECK_SLOW) -extern BOOL is_tainted_fn(const void *); -return is_tainted_fn(p); - +#ifdef EM_VERSION_C +# define Ustrcat(s,t) strcat(CS(s), CCS(t)) +# define Ustrcpy(s,t) strcpy(CS(s), CCS(t)) +# define Ustrncat(s,t,n) strncat(CS(s), CCS(t), n) +# define Ustrncpy(s,t,n) strncpy(CS(s), CCS(t), n) #else -extern void * tainted_base, * tainted_top; -return p >= tainted_base && p < tainted_top; +# define Ustrcat(s,t) __Ustrcat(s, CUS(t), __FUNCTION__, __LINE__) +# define Ustrcpy(s,t) __Ustrcpy(s, CUS(t), __FUNCTION__, __LINE__) +# define Ustrncat(s,t,n) __Ustrncat(s, CUS(t), n, __FUNCTION__, __LINE__) +# define Ustrncpy(s,t,n) __Ustrncpy(s, CUS(t), n, __FUNCTION__, __LINE__) #endif -} - -static inline uschar * __Ustrcat(uschar * dst, const uschar * src, const char * func, int line) -{ -#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) -if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrcat", CUS func, line); -#endif -return US strcat(CS dst, CCS src); -} -static inline uschar * __Ustrcpy(uschar * dst, const uschar * src, const char * func, int line) -{ -#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) -if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrcpy", CUS func, line); -#endif -return US strcpy(CS dst, CCS src); -} -static inline uschar * __Ustrncat(uschar * dst, const uschar * src, size_t n, const char * func, int line) -{ -#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) -if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrncat", CUS func, line); -#endif -return US strncat(CS dst, CCS src, n); -} -static inline uschar * __Ustrncpy(uschar * dst, const uschar * src, size_t n, const char * func, int line) -{ -#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) -if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrncpy", CUS func, line); -#endif -return US strncpy(CS dst, CCS src, n); -} -/*XXX will likely need unchecked copy also */ #endif /* End of mytypes.h */ diff -urN ../exim-4.93.orig/src/readconf.c ./src/readconf.c --- ../exim-4.93.orig/src/readconf.c 2019-12-08 14:53:48.000000000 +0200 +++ ./src/readconf.c 2020-02-21 00:30:45.593379000 +0200 @@ -3788,6 +3788,7 @@ if (!d->driver_name) log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "no driver defined for %s \"%s\"", class, d->name); + /* s is using big_buffer, so this call had better not */ (d->info->init)(d); d = NULL; } diff -urN ../exim-4.93.orig/src/receive.c ./src/receive.c --- ../exim-4.93.orig/src/receive.c 2019-12-08 14:53:48.000000000 +0200 +++ ./src/receive.c 2020-02-21 00:30:45.594517000 +0200 @@ -1703,10 +1703,6 @@ header_line *received_header; BOOL msgid_header_newly_created = FALSE; -#ifdef SUPPORT_DMARC -int dmarc_up = 0; -#endif - /* Variables for use when building the Received: header. */ uschar *timestamp; @@ -1768,7 +1764,7 @@ #endif #ifdef SUPPORT_DMARC -dmarc_up = dmarc_init(); /* initialize libopendmarc */ +if (sender_host_address) dmarc_init(); /* initialize libopendmarc */ #endif /* Remember the time of reception. Exim uses time+pid for uniqueness of message @@ -3499,7 +3495,7 @@ #endif /* WITH_CONTENT_SCAN */ #ifdef SUPPORT_DMARC - dmarc_up = dmarc_store_data(from_header); + dmarc_store_data(from_header); #endif #ifndef DISABLE_PRDR diff -urN ../exim-4.93.orig/src/retry.c ./src/retry.c --- ../exim-4.93.orig/src/retry.c 2019-12-08 14:53:48.000000000 +0200 +++ ./src/retry.c 2020-02-21 00:30:45.594884000 +0200 @@ -659,7 +659,8 @@ /* Read a retry record from the database or construct a new one. Ignore an old one if it is too old since it was last updated. */ - retry_record = dbfn_read(dbm_file, rti->key); + retry_record = dbfn_read_with_length(dbm_file, rti->key, + &message_space); if ( retry_record && now - retry_record->time_stamp > retry_data_expire) retry_record = NULL; @@ -675,7 +676,7 @@ retry_record->expired = FALSE; retry_record->text[0] = 0; /* just in case */ } - else message_space = Ustrlen(retry_record->text); + else message_space -= sizeof(dbdata_retry); /* Compute how long this destination has been failing */ @@ -806,15 +807,17 @@ if (next_try - now > retry_interval_max) next_try = now + retry_interval_max; - /* If the new message length is greater than the previous one, we - have to copy the record first. */ + /* If the new message length is greater than the previous one, we have + to copy the record first. If we're using an old one, the read used + tainted memory so we're ok to write into it. */ - if (message_length > message_space) - { - dbdata_retry *newr = store_get(sizeof(dbdata_retry) + message_length, FALSE); - memcpy(newr, retry_record, sizeof(dbdata_retry)); - retry_record = newr; - } + if (message_length > message_space) + { + dbdata_retry * newr = + store_get(sizeof(dbdata_retry) + message_length, is_tainted(message)); + memcpy(newr, retry_record, sizeof(dbdata_retry)); + retry_record = newr; + } /* Set up the retry record; message_length may be less than the string length for very long error strings. */ diff -urN ../exim-4.93.orig/src/smtp_in.c ./src/smtp_in.c --- ../exim-4.93.orig/src/smtp_in.c 2019-12-08 14:53:48.000000000 +0200 +++ ./src/smtp_in.c 2020-02-21 00:30:45.600183000 +0200 @@ -2087,6 +2087,7 @@ #endif #ifdef EXPERIMENTAL_ARC arc_state = arc_state_reason = NULL; +arc_received_instance = 0; #endif dsn_ret = 0; dsn_envid = NULL; diff -urN ../exim-4.93.orig/src/spf.c ./src/spf.c --- ../exim-4.93.orig/src/spf.c 2019-12-08 14:53:48.000000000 +0200 +++ ./src/spf.c 2020-02-21 00:30:45.600757000 +0200 @@ -37,62 +37,116 @@ static SPF_dns_rr_t * SPF_dns_exim_lookup(SPF_dns_server_t *spf_dns_server, -const char *domain, ns_type rr_type, int should_cache) + const char *domain, ns_type rr_type, int should_cache) { dns_answer * dnsa = store_get_dns_answer(); dns_scan dnss; SPF_dns_rr_t * spfrr; +unsigned found = 0; -DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup\n"); +SPF_dns_rr_t srr = { + .domain = CS domain, /* query information */ + .domain_buf_len = 0, + .rr_type = rr_type, -if (dns_lookup(dnsa, US domain, rr_type, NULL) == DNS_SUCCEED) - for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr; - rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) - if ( rr->type == rr_type - && Ustrncmp(rr->data+1, "v=spf1", 6) == 0) - { - gstring * g = NULL; - uschar chunk_len; - uschar * s; - SPF_dns_rr_t srr = { - .domain = CS rr->name, /* query information */ - .domain_buf_len = DNS_MAXNAME, - .rr_type = rr->type, + .rr_buf_len = 0, /* answer information */ + .rr_buf_num = 0, /* no free of s */ + .utc_ttl = 0, - .num_rr = 1, /* answer information */ - .rr = NULL, - .rr_buf_len = 0, - .rr_buf_num = 0, - .ttl = rr->ttl, - .utc_ttl = 0, - .herrno = NETDB_SUCCESS, + .hook = NULL, /* misc information */ + .source = spf_dns_server +}; +int dns_rc; - .hook = NULL, /* misc information */ - .source = spf_dns_server - }; +DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup '%s'\n", domain); - for (int off = 0; off < rr->size; off += chunk_len) +switch (dns_rc = dns_lookup(dnsa, US domain, rr_type, NULL)) + { + case DNS_SUCCEED: srr.herrno = NETDB_SUCCESS; break; + case DNS_AGAIN: srr.herrno = TRY_AGAIN; break; + case DNS_NOMATCH: srr.herrno = HOST_NOT_FOUND; break; + case DNS_NODATA: srr.herrno = NO_DATA; break; + case DNS_FAIL: + default: srr.herrno = NO_RECOVERY; break; + } + +for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr; + rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) + if (rr->type == rr_type) found++; + +if (found == 0) + { + SPF_dns_rr_dup(&spfrr, &srr); + return spfrr; + } + +srr.rr = store_malloc(sizeof(SPF_dns_rr_data_t) * found); + +found = 0; +for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr; + rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) + if (rr->type == rr_type) + { + const uschar * s = rr->data; + + srr.ttl = rr->ttl; + switch(rr_type) + { + case T_MX: + s += 2; /* skip the MX precedence field */ + case T_PTR: { - chunk_len = (rr->data)[off++]; - g = string_catn(g, US ((rr->data)+off), chunk_len); + uschar * buf = store_malloc(256); + (void)dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, s, + (DN_EXPAND_ARG4_TYPE)buf, 256); + s = buf; + break; } - if (!g) + + case T_TXT: { - HDEBUG(D_host_lookup) debug_printf("IP address lookup yielded an " - "empty name: treated as non-existent host name\n"); - continue; - } - gstring_release_unused(g); - s = string_copy_malloc(string_from_gstring(g)); - srr.rr = (void *) &s; + gstring * g = NULL; + uschar chunk_len; - /* spfrr->rr must have been malloc()d for this */ - SPF_dns_rr_dup(&spfrr, &srr); + if (strncmpic(rr->data+1, US"v=spf1", 6) != 0) + { + HDEBUG(D_host_lookup) debug_printf("not an spf record\n"); + continue; + } - return spfrr; + for (int off = 0; off < rr->size; off += chunk_len) + { + if (!(chunk_len = s[off++])) break; + g = string_catn(g, s+off, chunk_len); + } + if (!g) + continue; + gstring_release_unused(g); + s = string_copy_malloc(string_from_gstring(g)); + break; + } + + case T_A: + case T_AAAA: + default: + { + uschar * buf = store_malloc(dnsa->answerlen + 1); + s = memcpy(buf, s, dnsa->answerlen + 1); + break; + } } + DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup '%s'\n", s); + srr.rr[found++] = (void *) s; + } -SPF_dns_rr_dup(&spfrr, spf_nxdomain); +/* Did we filter out all TXT RRs? Return NO_DATA instead of SUCCESS with +empty ANSWER section. */ + +if (!(srr.num_rr = found)) + srr.herrno = NO_DATA; + +/* spfrr->rr must have been malloc()d for this */ +SPF_dns_rr_dup(&spfrr, &srr); return spfrr; } @@ -101,14 +155,11 @@ SPF_dns_server_t * SPF_dns_exim_new(int debug) { -SPF_dns_server_t *spf_dns_server; +SPF_dns_server_t * spf_dns_server = store_malloc(sizeof(SPF_dns_server_t)); DEBUG(D_receive) debug_printf("SPF_dns_exim_new\n"); -if (!(spf_dns_server = malloc(sizeof(SPF_dns_server_t)))) - return NULL; memset(spf_dns_server, 0, sizeof(SPF_dns_server_t)); - spf_dns_server->destroy = NULL; spf_dns_server->lookup = SPF_dns_exim_lookup; spf_dns_server->get_spf = NULL; diff -urN ../exim-4.93.orig/src/store.c ./src/store.c --- ../exim-4.93.orig/src/store.c 2019-12-08 14:53:48.000000000 +0200 +++ ./src/store.c 2020-02-21 00:30:45.601859000 +0200 @@ -102,13 +102,6 @@ static void *next_yield[NPOOLS]; static int yield_length[NPOOLS] = { -1, -1, -1, -1, -1, -1 }; -/* The limits of the tainted pools. Tracking these on new allocations enables -a fast is_tainted implementation. We assume the kernel only allocates mmaps using -one side or the other of data+heap, not both. */ - -void * tainted_base = (void *)-1; -void * tainted_top = (void *)0; - /* pool_malloc holds the amount of memory used by the store pools; this goes up and down as store is reset or released. nonpool_malloc is the total got by malloc from other calls; this doesn't go down because it is just freed by @@ -162,32 +155,34 @@ /******************************************************************************/ -/* Slower version check, for use when platform intermixes malloc and mmap area -addresses. */ +/* Test if a pointer refers to tainted memory. +Slower version check, for use when platform intermixes malloc and mmap area +addresses. Test against the current-block of all tainted pools first, then all +blocks of all tainted pools. + +Return: TRUE iff tainted +*/ + BOOL is_tainted_fn(const void * p) { storeblock * b; -int pool; -for (pool = 0; pool < nelem(chainbase); pool++) +for (int pool = POOL_TAINT_BASE; pool < nelem(chainbase); pool++) if ((b = current_block[pool])) { - char * bc = CS b + ALIGNED_SIZEOF_STOREBLOCK; - if (CS p >= bc && CS p <= bc + b->length) goto hit; + uschar * bc = US b + ALIGNED_SIZEOF_STOREBLOCK; + if (US p >= bc && US p <= bc + b->length) return TRUE; } -for (pool = 0; pool < nelem(chainbase); pool++) +for (int pool = POOL_TAINT_BASE; pool < nelem(chainbase); pool++) for (b = chainbase[pool]; b; b = b->next) { - char * bc = CS b + ALIGNED_SIZEOF_STOREBLOCK; - if (CS p >= bc && CS p <= bc + b->length) goto hit; + uschar * bc = US b + ALIGNED_SIZEOF_STOREBLOCK; + if (US p >= bc && US p <= bc + b->length) return TRUE; } return FALSE; - -hit: -return pool >= POOL_TAINT_BASE; } @@ -199,6 +194,7 @@ } + /************************************************* * Get a block from the current pool * *************************************************/ @@ -730,7 +726,7 @@ BOOL release_ok = !tainted && store_last_get[pool] == block; uschar * newtext; -#ifndef MACRO_PREDEF +#if !defined(MACRO_PREDEF) && !defined(COMPILE_UTILITY) if (is_tainted(block) != tainted) die_tainted(US"store_newblock", CUS func, linenumber); #endif @@ -786,9 +782,6 @@ PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0))) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to mmap %d bytes of memory: " "called from line %d of %s", size, line, func); - -if (yield < tainted_base) tainted_base = yield; -if ((top = US yield + size) > tainted_top) tainted_top = top; return store_alloc_tail(yield, size, func, line, US"Mmap"); } diff -urN ../exim-4.93.orig/src/store.h ./src/store.h --- ../exim-4.93.orig/src/store.h 2019-12-08 14:53:48.000000000 +0200 +++ ./src/store.h 2020-02-21 00:30:45.601914000 +0200 @@ -34,10 +34,7 @@ #define store_free(addr) \ store_free_3(addr, __FUNCTION__, __LINE__) -#define store_get(size, tainted) \ - store_get_3(size, tainted, __FUNCTION__, __LINE__) -#define store_get_perm(size, tainted) \ - store_get_perm_3(size, tainted, __FUNCTION__, __LINE__) +/* store_get & store_get_perm are in local_scan.h */ #define store_malloc(size) \ store_malloc_3(size, __FUNCTION__, __LINE__) #define store_mark(void) \ @@ -55,8 +52,7 @@ extern BOOL store_extend_3(void *, BOOL, int, int, const char *, int); extern void store_free_3(void *, const char *, int); -extern void *store_get_3(int, BOOL, const char *, int) ALLOC ALLOC_SIZE(1) WARN_UNUSED_RESULT; -extern void *store_get_perm_3(int, BOOL, const char *, int) ALLOC ALLOC_SIZE(1) WARN_UNUSED_RESULT; +/* store_get_3 & store_get_perm_3 are in local_scan.h */ extern void *store_malloc_3(int, const char *, int) ALLOC ALLOC_SIZE(1) WARN_UNUSED_RESULT; extern rmark store_mark_3(const char *, int); extern void *store_newblock_3(void *, BOOL, int, int, const char *, int); diff -urN ../exim-4.93.orig/src/string.c ./src/string.c --- ../exim-4.93.orig/src/string.c 2019-12-08 14:53:48.000000000 +0200 +++ ./src/string.c 2020-02-21 00:30:45.602251000 +0200 @@ -678,12 +678,20 @@ uschar * string_sprintf_trc(const char *format, const uschar * func, unsigned line, ...) { -gstring * g; -va_list ap; +#ifdef COMPILE_UTILITY +uschar buffer[STRING_SPRINTF_BUFFER_SIZE]; +gstring gs = { .size = STRING_SPRINTF_BUFFER_SIZE, .ptr = 0, .s = buffer }; +gstring * g = &gs; +unsigned flags = 0; +#else +gstring * g = NULL; +unsigned flags = SVFMT_REBUFFER|SVFMT_EXTEND; +#endif +va_list ap; va_start(ap, line); -g = string_vformat_trc(NULL, func, line, STRING_SPRINTF_BUFFER_SIZE, - SVFMT_REBUFFER|SVFMT_EXTEND, format, ap); +g = string_vformat_trc(g, func, line, STRING_SPRINTF_BUFFER_SIZE, + flags, format, ap); va_end(ap); if (!g) @@ -692,8 +700,12 @@ " called from %s %d\n", STRING_SPRINTF_BUFFER_SIZE, format, func, line); +#ifdef COMPILE_UTILITY +return string_copyn(g->s, g->ptr); +#else gstring_release_unused(g); return string_from_gstring(g); +#endif } diff -urN ../exim-4.93.orig/src/structs.h ./src/structs.h --- ../exim-4.93.orig/src/structs.h 2019-12-08 14:53:48.000000000 +0200 +++ ./src/structs.h 2020-02-21 00:30:45.602487000 +0200 @@ -648,11 +648,11 @@ unsigned int domain_cache[(MAX_NAMED_LIST * 2)/32]; unsigned int localpart_cache[(MAX_NAMED_LIST * 2)/32]; int mode; /* mode for local transporting to a file */ + int basic_errno; /* status after failure */ int more_errno; /* additional error information */ /* (may need to hold a timestamp) */ unsigned int delivery_usec; /* subsecond part of delivery time */ - short int basic_errno; /* status after failure */ unsigned short child_count; /* number of child addresses */ short int return_file; /* fileno of return data file */ short int special_action; /* ( used when when deferred or failed */ diff -urN ../exim-4.93.orig/src/tls-gnu.c ./src/tls-gnu.c --- ../exim-4.93.orig/src/tls-gnu.c 2019-12-08 14:53:48.000000000 +0200 +++ ./src/tls-gnu.c 2020-02-21 00:30:45.603138000 +0200 @@ -181,6 +181,10 @@ BOOL peer_dane_verified; BOOL trigger_sni_changes; BOOL have_set_peerdn; +#ifdef SUPPORT_CORK + BOOL corked:1; +#endif + const struct host_item *host; /* NULL if server */ gnutls_x509_crt_t peercert; uschar *peerdn; @@ -3309,9 +3313,8 @@ size_t left = len; exim_gnutls_state_st * state = ct_ctx ? ct_ctx : &state_server; #ifdef SUPPORT_CORK -static BOOL corked = FALSE; -if (more && !corked) gnutls_record_cork(state->session); +if (more && !state->corked) gnutls_record_cork(state->session); #endif DEBUG(D_tls) debug_printf("%s(%p, " SIZE_T_FMT "%s)\n", __FUNCTION__, @@ -3352,10 +3355,10 @@ } #ifdef SUPPORT_CORK -if (more != corked) +if (more != state->corked) { if (!more) (void) gnutls_record_uncork(state->session, 0); - corked = more; + state->corked = more; } #endif diff -urN ../exim-4.93.orig/src/transports/smtp.c ./src/transports/smtp.c --- ../exim-4.93.orig/src/transports/smtp.c 2019-12-08 14:53:48.000000000 +0200 +++ ./src/transports/smtp.c 2020-02-21 00:30:45.608145000 +0200 @@ -895,8 +895,9 @@ sx->ehlo_resp = er->data; dbfn_close(dbm_file); DEBUG(D_transport) debug_printf( - "EHLO response bits from cache: cleartext 0x%04x crypted 0x%04x\n", - er->data.cleartext_features, er->data.crypted_features); + "EHLO response bits from cache: cleartext 0x%04x/0x%04x crypted 0x%04x/0x%04x\n", + er->data.cleartext_features, er->data.cleartext_auths, + er->data.crypted_features, er->data.crypted_auths); return TRUE; } dbfn_close(dbm_file); @@ -1904,39 +1905,39 @@ sx->lmtp = strcmpic(ob->protocol, US"lmtp") == 0; sx->smtps = strcmpic(ob->protocol, US"smtps") == 0; -sx->ok = FALSE; +/* sx->ok = FALSE; */ sx->send_rset = TRUE; sx->send_quit = TRUE; sx->setting_up = TRUE; sx->esmtp = TRUE; -sx->esmtp_sent = FALSE; +/* sx->esmtp_sent = FALSE; */ #ifdef SUPPORT_I18N -sx->utf8_needed = FALSE; +/* sx->utf8_needed = FALSE; */ #endif sx->dsn_all_lasthop = TRUE; #ifdef SUPPORT_DANE -sx->conn_args.dane = FALSE; +/* sx->conn_args.dane = FALSE; */ sx->dane_required = verify_check_given_host(CUSS &ob->hosts_require_dane, sx->conn_args.host) == OK; #endif #ifdef SUPPORT_PIPE_CONNECT -sx->early_pipe_active = sx->early_pipe_ok = FALSE; -sx->ehlo_resp.cleartext_features = sx->ehlo_resp.crypted_features = 0; -sx->pending_BANNER = sx->pending_EHLO = FALSE; +/* sx->early_pipe_active = sx->early_pipe_ok = FALSE; */ +/* sx->ehlo_resp.cleartext_features = sx->ehlo_resp.crypted_features = 0; */ +/* sx->pending_BANNER = sx->pending_EHLO = sx->pending_MAIL = FALSE; */ #endif if ((sx->max_rcpt = sx->conn_args.tblock->max_addresses) == 0) sx->max_rcpt = 999999; -sx->peer_offered = 0; -sx->avoid_option = 0; +/* sx->peer_offered = 0; */ +/* sx->avoid_option = 0; */ sx->igquotstr = US""; if (!sx->helo_data) sx->helo_data = ob->helo_data; #ifdef EXPERIMENTAL_DSN_INFO -sx->smtp_greeting = NULL; -sx->helo_response = NULL; +/* sx->smtp_greeting = NULL; */ +/* sx->helo_response = NULL; */ #endif smtp_command = US"initial connection"; -sx->buffer[0] = '\0'; +/* sx->buffer[0] = '\0'; */ /* Set up the buffer for reading SMTP response packets. */ @@ -1950,9 +1951,9 @@ sx->outblock.buffer = sx->outbuffer; sx->outblock.buffersize = sizeof(sx->outbuffer); sx->outblock.ptr = sx->outbuffer; -sx->outblock.cmd_count = 0; -sx->outblock.authenticating = FALSE; -sx->outblock.conn_args = NULL; +/* sx->outblock.cmd_count = 0; */ +/* sx->outblock.authenticating = FALSE; */ +/* sx->outblock.conn_args = NULL; */ /* Reset the parameters of a TLS session. */ @@ -3410,27 +3411,27 @@ BOOL pass_message = FALSE; uschar *message = NULL; uschar new_message_id[MESSAGE_ID_LENGTH + 1]; +smtp_context * sx = store_get(sizeof(*sx), TRUE); /* tainted, for the data buffers */ -smtp_context sx; - gettimeofday(&start_delivery_time, NULL); suppress_tls = suppress_tls; /* stop compiler warning when no TLS support */ *message_defer = FALSE; -sx.addrlist = addrlist; -sx.conn_args.host = host; -sx.conn_args.host_af = host_af, -sx.port = defport; -sx.conn_args.interface = interface; -sx.helo_data = NULL; -sx.conn_args.tblock = tblock; -sx.verify = FALSE; -sx.sync_addr = sx.first_addr = addrlist; +memset(sx, 0, sizeof(*sx)); +sx->addrlist = addrlist; +sx->conn_args.host = host; +sx->conn_args.host_af = host_af, +sx->port = defport; +sx->conn_args.interface = interface; +sx->helo_data = NULL; +sx->conn_args.tblock = tblock; +/* sx->verify = FALSE; */ +sx->sync_addr = sx->first_addr = addrlist; /* Get the channel set up ready for a message (MAIL FROM being the next SMTP command to send */ -if ((rc = smtp_setup_conn(&sx, suppress_tls)) != OK) +if ((rc = smtp_setup_conn(sx, suppress_tls)) != OK) return rc; /* If there is a filter command specified for this transport, we can now @@ -3456,10 +3457,10 @@ if ( transport_filter_argv && *transport_filter_argv && **transport_filter_argv - && sx.peer_offered & OPTION_CHUNKING + && sx->peer_offered & OPTION_CHUNKING ) { - sx.peer_offered &= ~OPTION_CHUNKING; + sx->peer_offered &= ~OPTION_CHUNKING; DEBUG(D_transport) debug_printf("CHUNKING not usable due to transport filter\n"); } } @@ -3473,11 +3474,11 @@ transaction to handle. */ SEND_MESSAGE: -sx.from_addr = return_path; -sx.sync_addr = sx.first_addr; -sx.ok = FALSE; -sx.send_rset = TRUE; -sx.completed_addr = FALSE; +sx->from_addr = return_path; +sx->sync_addr = sx->first_addr; +sx->ok = FALSE; +sx->send_rset = TRUE; +sx->completed_addr = FALSE; /* If we are a continued-connection-after-verify the MAIL and RCPT @@ -3487,10 +3488,10 @@ if (continue_hostname && continue_sequence == 1) { - sx.peer_offered = smtp_peer_options; - sx.pending_MAIL = FALSE; - sx.ok = TRUE; - sx.next_addr = NULL; + sx->peer_offered = smtp_peer_options; + /* sx->pending_MAIL = FALSE; */ + sx->ok = TRUE; + /* sx->next_addr = NULL; */ for (address_item * addr = addrlist; addr; addr = addr->next) addr->transport_return = PENDING_OK; @@ -3499,7 +3500,7 @@ { /* Initiate a message transfer. */ - switch(smtp_write_mail_and_rcpt_cmds(&sx, &yield)) + switch(smtp_write_mail_and_rcpt_cmds(sx, &yield)) { case 0: break; case -1: case -2: goto RESPONSE_FAILED; @@ -3517,13 +3518,13 @@ address_item * a; unsigned cnt; - for (a = sx.first_addr, cnt = 0; a && cnt < sx.max_rcpt; a = a->next, cnt++) + for (a = sx->first_addr, cnt = 0; a && cnt < sx->max_rcpt; a = a->next, cnt++) if (a->transport_return != PENDING_OK) { /*XXX could we find a better errno than 0 here? */ set_errno_nohost(addrlist, 0, a->message, FAIL, testflag(a, af_pass_message)); - sx.ok = FALSE; + sx->ok = FALSE; break; } } @@ -3537,20 +3538,20 @@ If using CHUNKING, do not send a BDAT until we know how big a chunk we want to send is. */ -if ( !(sx.peer_offered & OPTION_CHUNKING) - && (sx.ok || (pipelining_active && !mua_wrapper))) +if ( !(sx->peer_offered & OPTION_CHUNKING) + && (sx->ok || (pipelining_active && !mua_wrapper))) { - int count = smtp_write_command(&sx, SCMD_FLUSH, "DATA\r\n"); + int count = smtp_write_command(sx, SCMD_FLUSH, "DATA\r\n"); if (count < 0) goto SEND_FAILED; - switch(sync_responses(&sx, count, sx.ok ? +1 : -1)) + switch(sync_responses(sx, count, sx->ok ? +1 : -1)) { - case 3: sx.ok = TRUE; /* 2xx & 5xx => OK & progress made */ - case 2: sx.completed_addr = TRUE; /* 5xx (only) => progress made */ + case 3: sx->ok = TRUE; /* 2xx & 5xx => OK & progress made */ + case 2: sx->completed_addr = TRUE; /* 5xx (only) => progress made */ break; - case 1: sx.ok = TRUE; /* 2xx (only) => OK, but if LMTP, */ - if (!sx.lmtp) sx.completed_addr = TRUE; /* can't tell about progress yet */ + case 1: sx->ok = TRUE; /* 2xx (only) => OK, but if LMTP, */ + if (!sx->lmtp) sx->completed_addr = TRUE; /* can't tell about progress yet */ case 0: break; /* No 2xx or 5xx, but no probs */ case -1: goto END_OFF; /* Timeout on RCPT */ @@ -3573,18 +3574,18 @@ well as body. Set the appropriate timeout value to be used for each chunk. (Haven't been able to make it work using select() for writing yet.) */ -if ( !sx.ok - && (!(sx.peer_offered & OPTION_CHUNKING) || !pipelining_active)) +if ( !sx->ok + && (!(sx->peer_offered & OPTION_CHUNKING) || !pipelining_active)) { /* Save the first address of the next batch. */ - sx.first_addr = sx.next_addr; + sx->first_addr = sx->next_addr; - sx.ok = TRUE; + sx->ok = TRUE; } else { transport_ctx tctx = { - .u = {.fd = sx.cctx.sock}, /*XXX will this need TLS info? */ + .u = {.fd = sx->cctx.sock}, /*XXX will this need TLS info? */ .tblock = tblock, .addr = addrlist, .check_string = US".", @@ -3603,32 +3604,32 @@ of responses. The callback needs a whole bunch of state so set up a transport-context structure to be passed around. */ - if (sx.peer_offered & OPTION_CHUNKING) + if (sx->peer_offered & OPTION_CHUNKING) { tctx.check_string = tctx.escape_string = NULL; tctx.options |= topt_use_bdat; tctx.chunk_cb = smtp_chunk_cmd_callback; - sx.pending_BDAT = FALSE; - sx.good_RCPT = sx.ok; - sx.cmd_count = 0; - tctx.smtp_context = &sx; + sx->pending_BDAT = FALSE; + sx->good_RCPT = sx->ok; + sx->cmd_count = 0; + tctx.smtp_context = sx; } else tctx.options |= topt_end_dot; /* Save the first address of the next batch. */ - sx.first_addr = sx.next_addr; + sx->first_addr = sx->next_addr; /* Responses from CHUNKING commands go in buffer. Otherwise, there has not been a response. */ - sx.buffer[0] = 0; + sx->buffer[0] = 0; sigalrm_seen = FALSE; transport_write_timeout = ob->data_timeout; smtp_command = US"sending data block"; /* For error messages */ DEBUG(D_transport|D_v) - if (sx.peer_offered & OPTION_CHUNKING) + if (sx->peer_offered & OPTION_CHUNKING) debug_printf(" will write message using CHUNKING\n"); else debug_printf(" SMTP>> writing message and terminating \".\"\n"); @@ -3651,7 +3652,7 @@ if (!f.expand_string_forcedfail) { message = US"failed to expand arc_sign"; - sx.ok = FALSE; + sx->ok = FALSE; goto SEND_FAILED; } } @@ -3668,9 +3669,9 @@ report_time_since(&t0, US"dkim_exim_sign_init (delta)"); # endif } - sx.ok = dkim_transport_write_message(&tctx, &ob->dkim, CUSS &message); + sx->ok = dkim_transport_write_message(&tctx, &ob->dkim, CUSS &message); #else - sx.ok = transport_write_message(&tctx, 0); + sx->ok = transport_write_message(&tctx, 0); #endif /* transport_write_message() uses write() because it is called from other @@ -3684,7 +3685,7 @@ or the failure of a transport filter or the expansion of added headers. Or, when CHUNKING, it can be a protocol-detected failure. */ - if (!sx.ok) + if (!sx->ok) if (message) goto SEND_FAILED; else goto RESPONSE_FAILED; @@ -3696,17 +3697,17 @@ smtp_command = US"end of data"; - if (sx.peer_offered & OPTION_CHUNKING && sx.cmd_count > 1) + if (sx->peer_offered & OPTION_CHUNKING && sx->cmd_count > 1) { /* Reap any outstanding MAIL & RCPT commands, but not a DATA-go-ahead */ - switch(sync_responses(&sx, sx.cmd_count-1, 0)) + switch(sync_responses(sx, sx->cmd_count-1, 0)) { - case 3: sx.ok = TRUE; /* 2xx & 5xx => OK & progress made */ - case 2: sx.completed_addr = TRUE; /* 5xx (only) => progress made */ + case 3: sx->ok = TRUE; /* 2xx & 5xx => OK & progress made */ + case 2: sx->completed_addr = TRUE; /* 5xx (only) => progress made */ break; - case 1: sx.ok = TRUE; /* 2xx (only) => OK, but if LMTP, */ - if (!sx.lmtp) sx.completed_addr = TRUE; /* can't tell about progress yet */ + case 1: sx->ok = TRUE; /* 2xx (only) => OK, but if LMTP, */ + if (!sx->lmtp) sx->completed_addr = TRUE; /* can't tell about progress yet */ case 0: break; /* No 2xx or 5xx, but no probs */ case -1: goto END_OFF; /* Timeout on RCPT */ @@ -3725,17 +3726,17 @@ individual responses, before going on with the overall response. If we don't get the warning then deal with per non-PRDR. */ - if(sx.prdr_active) + if(sx->prdr_active) { - sx.ok = smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '3', ob->final_timeout); - if (!sx.ok && errno == 0) switch(sx.buffer[0]) + sx->ok = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '3', ob->final_timeout); + if (!sx->ok && errno == 0) switch(sx->buffer[0]) { - case '2': sx.prdr_active = FALSE; - sx.ok = TRUE; + case '2': sx->prdr_active = FALSE; + sx->ok = TRUE; break; case '4': errno = ERRNO_DATA4XX; addrlist->more_errno |= - ((sx.buffer[1] - '0')*10 + sx.buffer[2] - '0') << 8; + ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8; break; } } @@ -3745,14 +3746,14 @@ /* For non-PRDR SMTP, we now read a single response that applies to the whole message. If it is OK, then all the addresses have been delivered. */ - if (!sx.lmtp) + if (!sx->lmtp) { - sx.ok = smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2', + sx->ok = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2', ob->final_timeout); - if (!sx.ok && errno == 0 && sx.buffer[0] == '4') + if (!sx->ok && errno == 0 && sx->buffer[0] == '4') { errno = ERRNO_DATA4XX; - addrlist->more_errno |= ((sx.buffer[1] - '0')*10 + sx.buffer[2] - '0') << 8; + addrlist->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8; } } @@ -3768,7 +3769,7 @@ software before the spool gets updated. Also record the final SMTP confirmation if needed (for SMTP only). */ - if (sx.ok) + if (sx->ok) { int flag = '='; struct timeval delivery_time; @@ -3776,7 +3777,7 @@ uschar * conf = NULL; timesince(&delivery_time, &start_delivery_time); - sx.send_rset = FALSE; + sx->send_rset = FALSE; pipelining_active = FALSE; /* Set up confirmation if needed - applies only to SMTP */ @@ -3785,18 +3786,18 @@ #ifdef DISABLE_EVENT LOGGING(smtp_confirmation) && #endif - !sx.lmtp + !sx->lmtp ) { - const uschar *s = string_printing(sx.buffer); + const uschar *s = string_printing(sx->buffer); /* deconst cast ok here as string_printing was checked to have alloc'n'copied */ - conf = (s == sx.buffer)? US string_copy(s) : US s; + conf = (s == sx->buffer)? US string_copy(s) : US s; } /* Process all transported addresses - for LMTP or PRDR, read a status for each one. */ - for (address_item * addr = addrlist; addr != sx.first_addr; addr = addr->next) + for (address_item * addr = addrlist; addr != sx->first_addr; addr = addr->next) { if (addr->transport_return != PENDING_OK) continue; @@ -3806,43 +3807,43 @@ it doesn't get tried again too soon. */ #ifndef DISABLE_PRDR - if (sx.lmtp || sx.prdr_active) + if (sx->lmtp || sx->prdr_active) #else - if (sx.lmtp) + if (sx->lmtp) #endif { - if (!smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2', + if (!smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2', ob->final_timeout)) { - if (errno != 0 || sx.buffer[0] == 0) goto RESPONSE_FAILED; + if (errno != 0 || sx->buffer[0] == 0) goto RESPONSE_FAILED; addr->message = string_sprintf( #ifndef DISABLE_PRDR - "%s error after %s: %s", sx.prdr_active ? "PRDR":"LMTP", + "%s error after %s: %s", sx->prdr_active ? "PRDR":"LMTP", #else "LMTP error after %s: %s", #endif - data_command, string_printing(sx.buffer)); + data_command, string_printing(sx->buffer)); setflag(addr, af_pass_message); /* Allow message to go to user */ - if (sx.buffer[0] == '5') + if (sx->buffer[0] == '5') addr->transport_return = FAIL; else { errno = ERRNO_DATA4XX; - addr->more_errno |= ((sx.buffer[1] - '0')*10 + sx.buffer[2] - '0') << 8; + addr->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8; addr->transport_return = DEFER; #ifndef DISABLE_PRDR - if (!sx.prdr_active) + if (!sx->prdr_active) #endif retry_add_item(addr, addr->address_retry_key, 0); } continue; } - sx.completed_addr = TRUE; /* NOW we can set this flag */ + sx->completed_addr = TRUE; /* NOW we can set this flag */ if (LOGGING(smtp_confirmation)) { - const uschar *s = string_printing(sx.buffer); + const uschar *s = string_printing(sx->buffer); /* deconst cast ok here as string_printing was checked to have alloc'n'copied */ - conf = (s == sx.buffer) ? US string_copy(s) : US s; + conf = (s == sx->buffer) ? US string_copy(s) : US s; } } @@ -3862,18 +3863,18 @@ if (tcp_out_fastopen >= TFO_USED_NODATA) setflag(addr, af_tcp_fastopen); if (tcp_out_fastopen >= TFO_USED_DATA) setflag(addr, af_tcp_fastopen_data); } - if (sx.pipelining_used) setflag(addr, af_pipelining); + if (sx->pipelining_used) setflag(addr, af_pipelining); #ifdef SUPPORT_PIPE_CONNECT - if (sx.early_pipe_active) setflag(addr, af_early_pipe); + if (sx->early_pipe_active) setflag(addr, af_early_pipe); #endif #ifndef DISABLE_PRDR - if (sx.prdr_active) setflag(addr, af_prdr_used); + if (sx->prdr_active) setflag(addr, af_prdr_used); #endif - if (sx.peer_offered & OPTION_CHUNKING) setflag(addr, af_chunking_used); + if (sx->peer_offered & OPTION_CHUNKING) setflag(addr, af_chunking_used); flag = '-'; #ifndef DISABLE_PRDR - if (!sx.prdr_active) + if (!sx->prdr_active) #endif { /* Update the journal. For homonymic addresses, use the base address plus @@ -3882,37 +3883,37 @@ write error, as it may prove possible to update the spool file later. */ if (testflag(addr, af_homonym)) - sprintf(CS sx.buffer, "%.500s/%s\n", addr->unique + 3, tblock->name); + sprintf(CS sx->buffer, "%.500s/%s\n", addr->unique + 3, tblock->name); else - sprintf(CS sx.buffer, "%.500s\n", addr->unique); + sprintf(CS sx->buffer, "%.500s\n", addr->unique); - DEBUG(D_deliver) debug_printf("S:journalling %s\n", sx.buffer); - len = Ustrlen(CS sx.buffer); - if (write(journal_fd, sx.buffer, len) != len) + DEBUG(D_deliver) debug_printf("S:journalling %s\n", sx->buffer); + len = Ustrlen(CS sx->buffer); + if (write(journal_fd, sx->buffer, len) != len) log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for " - "%s: %s", sx.buffer, strerror(errno)); + "%s: %s", sx->buffer, strerror(errno)); } } #ifndef DISABLE_PRDR - if (sx.prdr_active) + if (sx->prdr_active) { const uschar * overall_message; /* PRDR - get the final, overall response. For any non-success upgrade all the address statuses. */ - sx.ok = smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2', + sx->ok = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2', ob->final_timeout); - if (!sx.ok) + if (!sx->ok) { - if(errno == 0 && sx.buffer[0] == '4') + if(errno == 0 && sx->buffer[0] == '4') { errno = ERRNO_DATA4XX; - addrlist->more_errno |= ((sx.buffer[1] - '0')*10 + sx.buffer[2] - '0') << 8; + addrlist->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8; } - for (address_item * addr = addrlist; addr != sx.first_addr; addr = addr->next) - if (sx.buffer[0] == '5' || addr->transport_return == OK) + for (address_item * addr = addrlist; addr != sx->first_addr; addr = addr->next) + if (sx->buffer[0] == '5' || addr->transport_return == OK) addr->transport_return = PENDING_OK; /* allow set_errno action */ goto RESPONSE_FAILED; } @@ -3920,24 +3921,24 @@ /* Append the overall response to the individual PRDR response for logging and update the journal, or setup retry. */ - overall_message = string_printing(sx.buffer); - for (address_item * addr = addrlist; addr != sx.first_addr; addr = addr->next) + overall_message = string_printing(sx->buffer); + for (address_item * addr = addrlist; addr != sx->first_addr; addr = addr->next) if (addr->transport_return == OK) addr->message = string_sprintf("%s\\n%s", addr->message, overall_message); - for (address_item * addr = addrlist; addr != sx.first_addr; addr = addr->next) + for (address_item * addr = addrlist; addr != sx->first_addr; addr = addr->next) if (addr->transport_return == OK) { if (testflag(addr, af_homonym)) - sprintf(CS sx.buffer, "%.500s/%s\n", addr->unique + 3, tblock->name); + sprintf(CS sx->buffer, "%.500s/%s\n", addr->unique + 3, tblock->name); else - sprintf(CS sx.buffer, "%.500s\n", addr->unique); + sprintf(CS sx->buffer, "%.500s\n", addr->unique); - DEBUG(D_deliver) debug_printf("journalling(PRDR) %s\n", sx.buffer); - len = Ustrlen(CS sx.buffer); - if (write(journal_fd, sx.buffer, len) != len) + DEBUG(D_deliver) debug_printf("journalling(PRDR) %s\n", sx->buffer); + len = Ustrlen(CS sx->buffer); + if (write(journal_fd, sx->buffer, len) != len) log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for " - "%s: %s", sx.buffer, strerror(errno)); + "%s: %s", sx->buffer, strerror(errno)); } else if (addr->transport_return == DEFER) retry_add_item(addr, addr->address_retry_key, -2); @@ -3961,7 +3962,7 @@ here during the setting up phase (i.e. before MAIL FROM) then always defer, as the problem is not related to this specific message. */ -if (!sx.ok) +if (!sx->ok) { int code, set_rc; uschar * set_message; @@ -3970,8 +3971,8 @@ { save_errno = errno; message = NULL; - sx.send_quit = check_response(host, &save_errno, addrlist->more_errno, - sx.buffer, &code, &message, &pass_message); + sx->send_quit = check_response(host, &save_errno, addrlist->more_errno, + sx->buffer, &code, &message, &pass_message); goto FAILED; } @@ -3981,7 +3982,7 @@ code = '4'; message = string_sprintf("send() to %s [%s] failed: %s", host->name, host->address, message ? message : US strerror(save_errno)); - sx.send_quit = FALSE; + sx->send_quit = FALSE; goto FAILED; } @@ -3989,7 +3990,7 @@ { BOOL message_error; - sx.ok = FALSE; /* For when reached by GOTO */ + sx->ok = FALSE; /* For when reached by GOTO */ set_message = message; /* We want to handle timeouts after MAIL or "." and loss of connection after @@ -4051,7 +4052,7 @@ if (save_errno > 0) message = US string_sprintf("%s: %s", message, strerror(save_errno)); - write_logs(host, message, sx.first_addr ? sx.first_addr->basic_errno : 0); + write_logs(host, message, sx->first_addr ? sx->first_addr->basic_errno : 0); *message_defer = TRUE; } @@ -4084,7 +4085,7 @@ set_errno(addrlist, save_errno, set_message, set_rc, pass_message, host #ifdef EXPERIMENTAL_DSN_INFO - , sx.smtp_greeting, sx.helo_response + , sx->smtp_greeting, sx->helo_response #endif ); } @@ -4120,10 +4121,10 @@ DEBUG(D_transport) debug_printf("ok=%d send_quit=%d send_rset=%d continue_more=%d " - "yield=%d first_address is %sNULL\n", sx.ok, sx.send_quit, - sx.send_rset, f.continue_more, yield, sx.first_addr ? "not " : ""); + "yield=%d first_address is %sNULL\n", sx->ok, sx->send_quit, + sx->send_rset, f.continue_more, yield, sx->first_addr ? "not " : ""); -if (sx.completed_addr && sx.ok && sx.send_quit) +if (sx->completed_addr && sx->ok && sx->send_quit) { BOOL more; smtp_compare_t t_compare; @@ -4131,7 +4132,7 @@ t_compare.tblock = tblock; t_compare.current_sender_address = sender_address; - if ( sx.first_addr != NULL + if ( sx->first_addr != NULL || f.continue_more || ( #ifndef DISABLE_TLS @@ -4148,20 +4149,20 @@ uschar *msg; BOOL pass_message; - if (sx.send_rset) - if (! (sx.ok = smtp_write_command(&sx, SCMD_FLUSH, "RSET\r\n") >= 0)) + if (sx->send_rset) + if (! (sx->ok = smtp_write_command(sx, SCMD_FLUSH, "RSET\r\n") >= 0)) { msg = US string_sprintf("send() to %s [%s] failed: %s", host->name, host->address, strerror(errno)); - sx.send_quit = FALSE; + sx->send_quit = FALSE; } - else if (! (sx.ok = smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), + else if (! (sx->ok = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2', ob->command_timeout))) { int code; - sx.send_quit = check_response(host, &errno, 0, sx.buffer, &code, &msg, + sx->send_quit = check_response(host, &errno, 0, sx->buffer, &code, &msg, &pass_message); - if (!sx.send_quit) + if (!sx->send_quit) { DEBUG(D_transport) debug_printf("H=%s [%s] %s\n", host->name, host->address, msg); @@ -4170,15 +4171,15 @@ /* Either RSET was not needed, or it succeeded */ - if (sx.ok) + if (sx->ok) { #ifndef DISABLE_TLS int pfd[2]; #endif - int socket_fd = sx.cctx.sock; + int socket_fd = sx->cctx.sock; - if (sx.first_addr != NULL) /* More addresses still to be sent */ + if (sx->first_addr != NULL) /* More addresses still to be sent */ { /* in this run of the transport */ continue_sequence++; /* Causes * in logging */ goto SEND_MESSAGE; @@ -4200,16 +4201,16 @@ a new EHLO. If we don't get a good response, we don't attempt to pass the socket on. */ - tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_WAIT); - sx.cctx.tls_ctx = NULL; + tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_WAIT); + sx->cctx.tls_ctx = NULL; smtp_peer_options = smtp_peer_options_wrap; - sx.ok = !sx.smtps - && smtp_write_command(&sx, SCMD_FLUSH, "EHLO %s\r\n", sx.helo_data) + sx->ok = !sx->smtps + && smtp_write_command(sx, SCMD_FLUSH, "EHLO %s\r\n", sx->helo_data) >= 0 - && smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), + && smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2', ob->command_timeout); - if (sx.ok && f.continue_more) + if (sx->ok && f.continue_more) return yield; /* More addresses for another run */ } else @@ -4217,13 +4218,13 @@ /* Set up a pipe for proxying TLS for the new transport process */ smtp_peer_options |= OPTION_TLS; - if ((sx.ok = socketpair(AF_UNIX, SOCK_STREAM, 0, pfd) == 0)) + if ((sx->ok = socketpair(AF_UNIX, SOCK_STREAM, 0, pfd) == 0)) socket_fd = pfd[1]; else - set_errno(sx.first_addr, errno, US"internal allocation problem", + set_errno(sx->first_addr, errno, US"internal allocation problem", DEFER, FALSE, host # ifdef EXPERIMENTAL_DSN_INFO - , sx.smtp_greeting, sx.helo_response + , sx->smtp_greeting, sx->helo_response # endif ); } @@ -4238,10 +4239,10 @@ /*XXX DSN_INFO: assume likely to do new HELO; but for greet we'll want to propagate it from the initial */ - if (sx.ok && transport_pass_socket(tblock->name, host->name, + if (sx->ok && transport_pass_socket(tblock->name, host->name, host->address, new_message_id, socket_fd)) { - sx.send_quit = FALSE; + sx->send_quit = FALSE; /* We have passed the client socket to a fresh transport process. If TLS is still active, we need to proxy it for the transport we @@ -4256,7 +4257,7 @@ { testharness_pause_ms(100); /* let parent debug out */ /* does not return */ - smtp_proxy_tls(sx.cctx.tls_ctx, sx.buffer, sizeof(sx.buffer), pfd, + smtp_proxy_tls(sx->cctx.tls_ctx, sx->buffer, sizeof(sx->buffer), pfd, ob->command_timeout); } @@ -4266,10 +4267,10 @@ close(pfd[0]); /* tidy the inter-proc to disconn the proxy proc */ waitpid(pid, NULL, 0); - tls_close(sx.cctx.tls_ctx, TLS_NO_SHUTDOWN); - sx.cctx.tls_ctx = NULL; - (void)close(sx.cctx.sock); - sx.cctx.sock = -1; + tls_close(sx->cctx.tls_ctx, TLS_NO_SHUTDOWN); + sx->cctx.tls_ctx = NULL; + (void)close(sx->cctx.sock); + sx->cctx.sock = -1; continue_transport = NULL; continue_hostname = NULL; return yield; @@ -4282,9 +4283,9 @@ /* If RSET failed and there are addresses left, they get deferred. */ else - set_errno(sx.first_addr, errno, msg, DEFER, FALSE, host + set_errno(sx->first_addr, errno, msg, DEFER, FALSE, host #ifdef EXPERIMENTAL_DSN_INFO - , sx.smtp_greeting, sx.helo_response + , sx->smtp_greeting, sx->helo_response #endif ); } @@ -4310,15 +4311,15 @@ SEND_QUIT: #ifdef TCP_CORK -(void) setsockopt(sx.cctx.sock, IPPROTO_TCP, TCP_CORK, US &on, sizeof(on)); +(void) setsockopt(sx->cctx.sock, IPPROTO_TCP, TCP_CORK, US &on, sizeof(on)); #endif -if (sx.send_quit) (void)smtp_write_command(&sx, SCMD_FLUSH, "QUIT\r\n"); +if (sx->send_quit) (void)smtp_write_command(sx, SCMD_FLUSH, "QUIT\r\n"); END_OFF: #ifndef DISABLE_TLS -tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT); -sx.cctx.tls_ctx = NULL; +tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT); +sx->cctx.tls_ctx = NULL; #endif /* Close the socket, and return the appropriate value, first setting @@ -4332,16 +4333,16 @@ case continue_more won't get set. */ HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP(close)>>\n"); -if (sx.send_quit) +if (sx->send_quit) { - shutdown(sx.cctx.sock, SHUT_WR); + shutdown(sx->cctx.sock, SHUT_WR); millisleep(20); testharness_pause_ms(200); - if (fcntl(sx.cctx.sock, F_SETFL, O_NONBLOCK) == 0) - for (int i = 16; read(sx.cctx.sock, sx.inbuffer, sizeof(sx.inbuffer)) > 0 && i > 0;) + if (fcntl(sx->cctx.sock, F_SETFL, O_NONBLOCK) == 0) + for (int i = 16; read(sx->cctx.sock, sx->inbuffer, sizeof(sx->inbuffer)) > 0 && i > 0;) i--; /* drain socket */ } -(void)close(sx.cctx.sock); +(void)close(sx->cctx.sock); #ifndef DISABLE_EVENT (void) event_raise(tblock->event_action, US"tcp:close", NULL); diff -urN ../exim-4.93.orig/src/verify.c ./src/verify.c --- ../exim-4.93.orig/src/verify.c 2019-12-08 14:53:48.000000000 +0200 +++ ./src/verify.c 2020-02-21 00:30:45.610614000 +0200 @@ -574,6 +574,7 @@ { smtp_transport_options_block *ob = (smtp_transport_options_block *)addr->transport->options_block; + smtp_context * sx = NULL; /* The information wasn't available in the cache, so we have to do a real callout and save the result in the cache for next time, unless no_cache is set, @@ -625,8 +626,7 @@ { int host_af; int port = 25; - uschar *interface = NULL; /* Outgoing interface to use; NULL => any */ - smtp_context sx; + uschar * interface = NULL; /* Outgoing interface to use; NULL => any */ if (!host->address) { @@ -666,15 +666,18 @@ log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address, addr->message); - sx.addrlist = addr; - sx.conn_args.host = host; - sx.conn_args.host_af = host_af, - sx.port = port; - sx.conn_args.interface = interface; - sx.helo_data = tf->helo_data; - sx.conn_args.tblock = addr->transport; - sx.verify = TRUE; + if (!sx) sx = store_get(sizeof(*sx), TRUE); /* tainted buffers */ + memset(sx, 0, sizeof(*sx)); + sx->addrlist = addr; + sx->conn_args.host = host; + sx->conn_args.host_af = host_af, + sx->port = port; + sx->conn_args.interface = interface; + sx->helo_data = tf->helo_data; + sx->conn_args.tblock = addr->transport; + sx->verify = TRUE; + tls_retry_connection: /* Set the address state so that errors are recorded in it */ @@ -686,7 +689,7 @@ SMTP command to send. If we tried TLS but it failed, try again without if permitted */ - yield = smtp_setup_conn(&sx, FALSE); + yield = smtp_setup_conn(sx, FALSE); #ifndef DISABLE_TLS if ( yield == DEFER && addr->basic_errno == ERRNO_TLSFAILURE @@ -698,7 +701,7 @@ "%s: callout unencrypted to %s [%s] (not in hosts_require_tls)", addr->message, host->name, host->address); addr->transport_return = PENDING_DEFER; - yield = smtp_setup_conn(&sx, TRUE); + yield = smtp_setup_conn(sx, TRUE); } #endif if (yield != OK) @@ -728,11 +731,11 @@ addr->authenticator = client_authenticator; addr->auth_id = client_authenticated_id; - sx.from_addr = from_address; - sx.first_addr = sx.sync_addr = addr; - sx.ok = FALSE; /*XXX these 3 last might not be needed for verify? */ - sx.send_rset = TRUE; - sx.completed_addr = FALSE; + sx->from_addr = from_address; + sx->first_addr = sx->sync_addr = addr; + sx->ok = FALSE; /*XXX these 3 last might not be needed for verify? */ + sx->send_rset = TRUE; + sx->completed_addr = FALSE; new_domain_record.result = old_domain_cache_result == ccache_reject_mfnull ? ccache_reject_mfnull : ccache_accept; @@ -789,12 +792,12 @@ Avoid using a SIZE option on the MAIL for all random-rcpt checks. */ - sx.avoid_option = OPTION_SIZE; + sx->avoid_option = OPTION_SIZE; /* Remember when we last did a random test */ new_domain_record.random_stamp = time(NULL); - if (smtp_write_mail_and_rcpt_cmds(&sx, &yield) == 0) + if (smtp_write_mail_and_rcpt_cmds(sx, &yield) == 0) switch(addr->transport_return) { case PENDING_OK: /* random was accepted, unfortunately */ @@ -805,36 +808,36 @@ goto no_conn; case FAIL: /* rejected: the preferred result */ new_domain_record.random_result = ccache_reject; - sx.avoid_option = 0; + sx->avoid_option = 0; /* Between each check, issue RSET, because some servers accept only one recipient after MAIL FROM:<>. XXX We don't care about that for postmaster_full. Should we? */ if ((done = - smtp_write_command(&sx, SCMD_FLUSH, "RSET\r\n") >= 0 && - smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2', callout))) + smtp_write_command(sx, SCMD_FLUSH, "RSET\r\n") >= 0 && + smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2', callout))) break; HDEBUG(D_acl|D_v) debug_printf_indent("problem after random/rset/mfrom; reopen conn\n"); random_local_part = NULL; #ifndef DISABLE_TLS - tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT); + tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT); #endif HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP(close)>>\n"); - (void)close(sx.cctx.sock); - sx.cctx.sock = -1; + (void)close(sx->cctx.sock); + sx->cctx.sock = -1; #ifndef DISABLE_EVENT (void) event_raise(addr->transport->event_action, US"tcp:close", NULL); #endif addr->address = main_address; addr->transport_return = PENDING_DEFER; - sx.first_addr = sx.sync_addr = addr; - sx.ok = FALSE; - sx.send_rset = TRUE; - sx.completed_addr = FALSE; + sx->first_addr = sx->sync_addr = addr; + sx->ok = FALSE; + sx->send_rset = TRUE; + sx->completed_addr = FALSE; goto tls_retry_connection; case DEFER: /* 4xx response to random */ break; /* Just to be clear. ccache_unknown, !done. */ @@ -843,10 +846,10 @@ /* Re-setup for main verify, or for the error message when failing */ addr->address = main_address; addr->transport_return = PENDING_DEFER; - sx.first_addr = sx.sync_addr = addr; - sx.ok = FALSE; - sx.send_rset = TRUE; - sx.completed_addr = FALSE; + sx->first_addr = sx->sync_addr = addr; + sx->ok = FALSE; + sx->send_rset = TRUE; + sx->completed_addr = FALSE; } else done = TRUE; @@ -857,10 +860,10 @@ if (done) { if (!(options & vopt_is_recipient && options & vopt_callout_no_cache)) - sx.avoid_option = OPTION_SIZE; + sx->avoid_option = OPTION_SIZE; done = FALSE; - switch(smtp_write_mail_and_rcpt_cmds(&sx, &yield)) + switch(smtp_write_mail_and_rcpt_cmds(sx, &yield)) { case 0: switch(addr->transport_return) /* ok so far */ { @@ -878,7 +881,7 @@ case -1: /* MAIL response error */ *failure_ptr = US"mail"; - if (errno == 0 && sx.buffer[0] == '5') + if (errno == 0 && sx->buffer[0] == '5') { setflag(addr, af_verify_nsfail); if (from_address[0] == 0) @@ -908,8 +911,8 @@ cancel_cutthrough_connection(TRUE, US"postmaster verify"); HDEBUG(D_acl|D_v) debug_printf_indent("Cutthrough cancelled by presence of postmaster verify\n"); - done = smtp_write_command(&sx, SCMD_FLUSH, "RSET\r\n") >= 0 - && smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2', callout); + done = smtp_write_command(sx, SCMD_FLUSH, "RSET\r\n") >= 0 + && smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2', callout); if (done) { @@ -919,23 +922,23 @@ addr->address = string_sprintf("postmaster@%.1000s", addr->domain); addr->transport_return = PENDING_DEFER; - sx.from_addr = pm_mailfrom; - sx.first_addr = sx.sync_addr = addr; - sx.ok = FALSE; - sx.send_rset = TRUE; - sx.completed_addr = FALSE; - sx.avoid_option = OPTION_SIZE; + sx->from_addr = pm_mailfrom; + sx->first_addr = sx->sync_addr = addr; + sx->ok = FALSE; + sx->send_rset = TRUE; + sx->completed_addr = FALSE; + sx->avoid_option = OPTION_SIZE; - if( smtp_write_mail_and_rcpt_cmds(&sx, &yield) == 0 + if( smtp_write_mail_and_rcpt_cmds(sx, &yield) == 0 && addr->transport_return == PENDING_OK ) done = TRUE; else done = (options & vopt_callout_fullpm) != 0 - && smtp_write_command(&sx, SCMD_FLUSH, + && smtp_write_command(sx, SCMD_FLUSH, "RCPT TO:\r\n") >= 0 - && smtp_read_response(&sx, sx.buffer, - sizeof(sx.buffer), '2', callout); + && smtp_read_response(sx, sx->buffer, + sizeof(sx->buffer), '2', callout); /* Sort out the cache record */ @@ -943,7 +946,7 @@ if (done) new_domain_record.postmaster_result = ccache_accept; - else if (errno == 0 && sx.buffer[0] == '5') + else if (errno == 0 && sx->buffer[0] == '5') { *failure_ptr = US"postmaster"; setflag(addr, af_verify_pmfail); @@ -968,7 +971,7 @@ { case ETIMEDOUT: HDEBUG(D_verify) debug_printf("SMTP timeout\n"); - sx.send_quit = FALSE; + sx->send_quit = FALSE; break; #ifdef SUPPORT_I18N @@ -986,11 +989,11 @@ break; #endif case ECONNREFUSED: - sx.send_quit = FALSE; + sx->send_quit = FALSE; break; case 0: - if (*sx.buffer == 0) Ustrcpy(sx.buffer, US"connection dropped"); + if (*sx->buffer == 0) Ustrcpy(sx->buffer, US"connection dropped"); /*XXX test here is ugly; seem to have a split of responsibility for building this message. Need to rationalise. Where is it done @@ -999,7 +1002,7 @@ */ if (!addr->message) addr->message = string_sprintf("response to \"%s\" was: %s", - big_buffer, string_printing(sx.buffer)); + big_buffer, string_printing(sx->buffer)); /* RFC 5321 section 4.2: the text portion of the response may have only HT, SP, Printable US-ASCII. Deal with awkward chars by cutting the @@ -1007,14 +1010,14 @@ just become a multiline response (but wrapped in the error code we produce). */ - for (uschar * s = sx.buffer; - *s && s < sx.buffer + sizeof(sx.buffer); + for (uschar * s = sx->buffer; + *s && s < sx->buffer + sizeof(sx->buffer); s++) { uschar c = *s; if (c != '\t' && c != '\n' && (c < ' ' || c > '~')) { - if (s - sx.buffer < sizeof(sx.buffer) - 12) + if (s - sx->buffer < sizeof(sx->buffer) - 12) memcpy(s, "(truncated)", 12); else *s = '\0'; @@ -1022,13 +1025,13 @@ } } addr->user_message = options & vopt_is_recipient - ? string_sprintf("Callout verification failed:\n%s", sx.buffer) + ? string_sprintf("Callout verification failed:\n%s", sx->buffer) : string_sprintf("Called: %s\nSent: %s\nResponse: %s", - host->address, big_buffer, sx.buffer); + host->address, big_buffer, sx->buffer); /* Hard rejection ends the process */ - if (sx.buffer[0] == '5') /* Address rejected */ + if (sx->buffer[0] == '5') /* Address rejected */ { yield = FAIL; done = TRUE; @@ -1075,7 +1078,7 @@ && !random_local_part && !pm_mailfrom && cutthrough.cctx.sock < 0 - && !sx.lmtp + && !sx->lmtp ) { HDEBUG(D_acl|D_v) debug_printf_indent("holding verify callout open for %s\n", @@ -1085,7 +1088,7 @@ cutthrough.callout_hold_only = !cutthrough.delivery; cutthrough.is_tls = tls_out.active.sock >= 0; /* We assume no buffer in use in the outblock */ - cutthrough.cctx = sx.cctx; + cutthrough.cctx = sx->cctx; cutthrough.nrcpt = 1; cutthrough.transport = addr->transport->name; cutthrough.interface = interface; @@ -1121,23 +1124,23 @@ /* Ensure no cutthrough on multiple verifies that were incompatible */ if (options & vopt_callout_recipsender) cancel_cutthrough_connection(TRUE, US"not usable for cutthrough"); - if (sx.send_quit) - if (smtp_write_command(&sx, SCMD_FLUSH, "QUIT\r\n") != -1) + if (sx->send_quit) + if (smtp_write_command(sx, SCMD_FLUSH, "QUIT\r\n") != -1) /* Wait a short time for response, and discard it */ - smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2', 1); + smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2', 1); - if (sx.cctx.sock >= 0) + if (sx->cctx.sock >= 0) { #ifndef DISABLE_TLS - if (sx.cctx.tls_ctx) + if (sx->cctx.tls_ctx) { - tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT); - sx.cctx.tls_ctx = NULL; + tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT); + sx->cctx.tls_ctx = NULL; } #endif HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP(close)>>\n"); - (void)close(sx.cctx.sock); - sx.cctx.sock = -1; + (void)close(sx->cctx.sock); + sx->cctx.sock = -1; #ifndef DISABLE_EVENT (void) event_raise(addr->transport->event_action, US"tcp:close", NULL); #endif diff -urN ../exim-4.93.orig/src/version.h ./src/version.h --- ../exim-4.93.orig/src/version.h 2019-12-08 20:06:37.000000000 +0200 +++ ./src/version.h 2020-02-21 00:30:46.390346000 +0200 @@ -1,5 +1,5 @@ /* automatically generated file - see ../scripts/reversion */ -#define EXIM_RELEASE_VERSION "4.93" +#define EXIM_RELEASE_VERSION "4.93.0.4-9-26b045604" #ifdef EXIM_VARIANT_VERSION #define EXIM_VERSION_STR EXIM_RELEASE_VERSION "-" EXIM_VARIANT_VERSION #else diff -urN ../exim-4.93.orig/src/version.sh ./src/version.sh --- ../exim-4.93.orig/src/version.sh 2019-12-08 20:06:37.000000000 +0200 +++ ./src/version.sh 2020-02-21 00:30:46.390172000 +0200 @@ -1,3 +1,3 @@ # automatically generated file - see ../scripts/reversion -EXIM_RELEASE_VERSION="4.93" +EXIM_RELEASE_VERSION="4.93.0.4-9-26b045604" EXIM_COMPILE_NUMBER="1"