commit 099223c706b1e6d9d844679c56bdcbf5d832f15f Author: Heiko Schlittermann (HS12-RIPE) Date: Wed May 31 23:08:56 2017 +0200 Cleanup (prevent repeated use of -p/-oMr to avoid mem leak) (cherry picked from commit 65e061b76867a9ea7aeeb535341b790b90ae6c21) (cherry picked from commit 35a043657fa583a0ce96be9da4fff22cb0232c4e) diff --git a/src/src/exim.c b/src/src/exim.c index a6a1ea82..394bf84f 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -3092,7 +3092,14 @@ for (i = 1; i < argc; i++) /* -oMr: Received protocol */ - else if (Ustrcmp(argrest, "Mr") == 0) received_protocol = argv[++i]; + else if (Ustrcmp(argrest, "Mr") == 0) + + if (received_protocol) + { + fprintf(stderr, "received_protocol is set already\n"); + exit(EXIT_FAILURE); + } + else received_protocol = argv[++i]; /* -oMs: Set sender host name */ @@ -3188,7 +3195,15 @@ for (i = 1; i < argc; i++) if (*argrest != 0) { - uschar *hn = Ustrchr(argrest, ':'); + uschar *hn; + + if (received_protocol) + { + fprintf(stderr, "received_protocol is set already\n"); + exit(EXIT_FAILURE); + } + + hn = Ustrchr(argrest, ':'); if (hn == NULL) { received_protocol = argrest; commit 6b3ab9337d87e3a0585fb990fa2dd40d99c89381 Author: Heiko Schlittermann (HS12-RIPE) Date: Tue Jun 13 17:36:44 2017 +0200 Fix log line corruption for DKIM status (cherry picked from commit 31323b30449a59713ed7a99f86e356cb1b9d0fb7) diff --git a/src/src/dkim.c b/src/src/dkim.c index f644fbfd..044ee3e2 100644 --- a/src/src/dkim.c +++ b/src/src/dkim.c @@ -169,12 +169,12 @@ for (sig = dkim_signatures; sig; sig = sig->next) : sig->algo == PDKIM_ALGO_RSA_SHA1 ? "rsa-sha1" : "err", string_sprintf(" b=%d", (int)sig->sighash.len > -1 ? sig->sighash.len * 8 : 0)); - if ((s= sig->identity)) string_append(logmsg, &size, &ptr, 2, " i=", s); - if (sig->created > 0) string_append(logmsg, &size, &ptr, 1, + if ((s= sig->identity)) logmsg = string_append(logmsg, &size, &ptr, 2, " i=", s); + if (sig->created > 0) logmsg = string_append(logmsg, &size, &ptr, 1, string_sprintf(" t=%lu", sig->created)); - if (sig->expires > 0) string_append(logmsg, &size, &ptr, 1, + if (sig->expires > 0) logmsg = string_append(logmsg, &size, &ptr, 1, string_sprintf(" x=%lu", sig->expires)); - if (sig->bodylength > -1) string_append(logmsg, &size, &ptr, 1, + if (sig->bodylength > -1) logmsg = string_append(logmsg, &size, &ptr, 1, string_sprintf(" l=%lu", sig->bodylength)); switch (sig->verify_status) commit 97689c3c739d48ee649fbb40f550346b592a97e5 Author: Jeremy Harris Date: Sun Apr 2 14:54:39 2017 +0100 Openssl: disable session-tickets by default and session-cache always (cherry picked from commit 7006ee24ecfd9d8f405f70d38cc36bdd91f8de87) diff --git a/src/src/readconf.c b/src/src/readconf.c index 790f0731..6a59dda7 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -3787,14 +3787,14 @@ if (tls_dh_max_bits < 1024) "tls_dh_max_bits is too small, must be at least 1024 for interop"); /* If openssl_options is set, validate it */ -if (openssl_options != NULL) +if (openssl_options) { # ifdef USE_GNUTLS log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "openssl_options is set but we're using GnuTLS"); # else long dummy; - if (!(tls_openssl_options_parse(openssl_options, &dummy))) + if (!tls_openssl_options_parse(openssl_options, &dummy)) log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "openssl_options parse error: %s", openssl_options); # endif diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 392d59dd..60c07402 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -1426,9 +1426,9 @@ tls_init(SSL_CTX **ctxp, host_item *host, uschar *dhparam, uschar *certificate, #endif address_item *addr, tls_ext_ctx_cb ** cbp) { +SSL_CTX * ctx; long init_options; int rc; -BOOL okay; tls_ext_ctx_cb * cbinfo; cbinfo = store_malloc(sizeof(tls_ext_ctx_cb)); @@ -1499,10 +1499,10 @@ if (!RAND_status()) /* Set up the information callback, which outputs if debugging is at a suitable level. */ -DEBUG(D_tls) SSL_CTX_set_info_callback(*ctxp, (void (*)())info_callback); +DEBUG(D_tls) SSL_CTX_set_info_callback(ctx, (void (*)())info_callback); /* Automatically re-try reads/writes after renegotiation. */ -(void) SSL_CTX_set_mode(*ctxp, SSL_MODE_AUTO_RETRY); +(void) SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); /* Apply administrator-supplied work-arounds. Historically we applied just one requested option, @@ -1513,20 +1513,23 @@ grandfathered in the first one as the default value for "openssl_options". No OpenSSL version number checks: the options we accept depend upon the availability of the option value macros from OpenSSL. */ -okay = tls_openssl_options_parse(openssl_options, &init_options); -if (!okay) +if (!tls_openssl_options_parse(openssl_options, &init_options)) return tls_error(US"openssl_options parsing failed", host, NULL); if (init_options) { DEBUG(D_tls) debug_printf("setting SSL CTX options: %#lx\n", init_options); - if (!(SSL_CTX_set_options(*ctxp, init_options))) + if (!(SSL_CTX_set_options(ctx, init_options))) return tls_error(string_sprintf( "SSL_CTX_set_option(%#lx)", init_options), host, NULL); } else DEBUG(D_tls) debug_printf("no SSL CTX options to set\n"); +/* Disable session cache unconditionally */ + +(void) SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); + /* Initialize with DH parameters if supplied */ /* Initialize ECDH temp key parameter selection */ @@ -1560,14 +1563,14 @@ if (host == NULL) /* server */ callback is invoked. */ if (cbinfo->u_ocsp.server.file) { - SSL_CTX_set_tlsext_status_cb(server_ctx, tls_server_stapling_cb); - SSL_CTX_set_tlsext_status_arg(server_ctx, cbinfo); + SSL_CTX_set_tlsext_status_cb(ctx, tls_server_stapling_cb); + SSL_CTX_set_tlsext_status_arg(ctx, cbinfo); } # endif /* We always do this, so that $tls_sni is available even if not used in tls_certificate */ - SSL_CTX_set_tlsext_servername_callback(*ctxp, tls_servername_cb); - SSL_CTX_set_tlsext_servername_arg(*ctxp, cbinfo); + SSL_CTX_set_tlsext_servername_callback(ctx, tls_servername_cb); + SSL_CTX_set_tlsext_servername_arg(ctx, cbinfo); } # ifndef DISABLE_OCSP else /* client */ @@ -1578,8 +1581,8 @@ else /* client */ DEBUG(D_tls) debug_printf("failed to create store for stapling verify\n"); return FAIL; } - SSL_CTX_set_tlsext_status_cb(*ctxp, tls_client_stapling_cb); - SSL_CTX_set_tlsext_status_arg(*ctxp, cbinfo); + SSL_CTX_set_tlsext_status_cb(ctx, tls_client_stapling_cb); + SSL_CTX_set_tlsext_status_arg(ctx, cbinfo); } # endif #endif @@ -1588,15 +1591,16 @@ cbinfo->verify_cert_hostnames = NULL; #ifdef EXIM_HAVE_EPHEM_RSA_KEX /* Set up the RSA callback */ -SSL_CTX_set_tmp_rsa_callback(*ctxp, rsa_callback); +SSL_CTX_set_tmp_rsa_callback(ctx, rsa_callback); #endif /* Finally, set the timeout, and we are done */ -SSL_CTX_set_timeout(*ctxp, ssl_session_timeout); +SSL_CTX_set_timeout(ctx, ssl_session_timeout); DEBUG(D_tls) debug_printf("Initialized TLS\n"); *cbp = cbinfo; +*ctxp = ctx; return OK; } @@ -2955,7 +2959,7 @@ uschar *s, *end; uschar keep_c; BOOL adding, item_parsed; -result = 0L; +result = SSL_OP_NO_TICKET; /* Prior to 4.80 we or'd in SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; removed * from default because it increases BEAST susceptibility. */ #ifdef SSL_OP_NO_SSLv2 @@ -2965,7 +2969,7 @@ result |= SSL_OP_NO_SSLv2; result |= SSL_OP_SINGLE_DH_USE; #endif -if (option_spec == NULL) +if (!option_spec) { *results = result; return TRUE; commit c92df897f31aae2f87f69349eda4ad4f295d11b2 Author: Jeremy Harris Date: Sat Apr 15 16:22:52 2017 +0100 Transport: fix smtp under combo of mua_wrapper and limited max_rcpt (cherry picked from commit 4c2471caf34b901ee481cd1e742b3620e734b16b) diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 0bbd8d96..ecb4e79a 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -789,7 +789,9 @@ with an address by scanning for the next address whose status is PENDING_DEFER. while (count-- > 0) { - while (addr->transport_return != PENDING_DEFER) addr = addr->next; + while (addr->transport_return != PENDING_DEFER) + if (!(addr = addr->next)) + return -2; /* The address was accepted */ addr->host_used = sx->host; @@ -2435,7 +2437,8 @@ for (addr = sx->first_addr, address_count = 0; ? dsn_support_yes : dsn_support_no; address_count++; - no_flush = pipelining_active && !sx->verify && (!mua_wrapper || addr->next); + no_flush = pipelining_active && !sx->verify + && (!mua_wrapper || addr->next && address_count < sx->max_rcpt); build_rcptcmd_options(sx, addr); @@ -2636,16 +2639,36 @@ RCPT. */ if (mua_wrapper) { - address_item *badaddr; - for (badaddr = sx.first_addr; badaddr; badaddr = badaddr->next) - if (badaddr->transport_return != PENDING_OK) - { - /*XXX could we find a better errno than 0 here? */ - set_errno_nohost(addrlist, 0, badaddr->message, FAIL, - testflag(badaddr, af_pass_message)); - sx.ok = FALSE; - break; - } + /* Initiate a message transfer. */ + + switch(smtp_write_mail_and_rcpt_cmds(&sx, &yield)) + { + case 0: break; + case -1: case -2: goto RESPONSE_FAILED; + case -3: goto END_OFF; + case -4: goto SEND_QUIT; + default: goto SEND_FAILED; + } + + /* If we are an MUA wrapper, abort if any RCPTs were rejected, either + permanently or temporarily. We should have flushed and synced after the last + RCPT. */ + + if (mua_wrapper) + { + address_item * a; + unsigned 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; + break; + } + } } /* If ok is TRUE, we know we have got at least one good recipient, and must now commit bd3dff5030691a21eed9e27535595b1801b7e432 Author: Jeremy Harris Date: Thu Jun 29 20:34:20 2017 +0100 Openssl: disable session-tickets by default and session-cache always This reverts the changes in tls-openssl.c tls_init() to the minimal set needed for the fix diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 60c07402..cd7720f7 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -1426,7 +1426,6 @@ tls_init(SSL_CTX **ctxp, host_item *host, uschar *dhparam, uschar *certificate, #endif address_item *addr, tls_ext_ctx_cb ** cbp) { -SSL_CTX * ctx; long init_options; int rc; tls_ext_ctx_cb * cbinfo; @@ -1499,10 +1498,10 @@ if (!RAND_status()) /* Set up the information callback, which outputs if debugging is at a suitable level. */ -DEBUG(D_tls) SSL_CTX_set_info_callback(ctx, (void (*)())info_callback); +DEBUG(D_tls) SSL_CTX_set_info_callback(*ctxp, (void (*)())info_callback); /* Automatically re-try reads/writes after renegotiation. */ -(void) SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); +(void) SSL_CTX_set_mode(*ctxp, SSL_MODE_AUTO_RETRY); /* Apply administrator-supplied work-arounds. Historically we applied just one requested option, @@ -1519,7 +1518,7 @@ if (!tls_openssl_options_parse(openssl_options, &init_options)) if (init_options) { DEBUG(D_tls) debug_printf("setting SSL CTX options: %#lx\n", init_options); - if (!(SSL_CTX_set_options(ctx, init_options))) + if (!(SSL_CTX_set_options(*ctxp, init_options))) return tls_error(string_sprintf( "SSL_CTX_set_option(%#lx)", init_options), host, NULL); } @@ -1528,7 +1527,7 @@ else /* Disable session cache unconditionally */ -(void) SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); +(void) SSL_CTX_set_session_cache_mode(*ctxp, SSL_SESS_CACHE_OFF); /* Initialize with DH parameters if supplied */ /* Initialize ECDH temp key parameter selection */ @@ -1563,14 +1562,14 @@ if (host == NULL) /* server */ callback is invoked. */ if (cbinfo->u_ocsp.server.file) { - SSL_CTX_set_tlsext_status_cb(ctx, tls_server_stapling_cb); - SSL_CTX_set_tlsext_status_arg(ctx, cbinfo); + SSL_CTX_set_tlsext_status_cb(*ctxp, tls_server_stapling_cb); + SSL_CTX_set_tlsext_status_arg(*ctxp, cbinfo); } # endif /* We always do this, so that $tls_sni is available even if not used in tls_certificate */ - SSL_CTX_set_tlsext_servername_callback(ctx, tls_servername_cb); - SSL_CTX_set_tlsext_servername_arg(ctx, cbinfo); + SSL_CTX_set_tlsext_servername_callback(*ctxp, tls_servername_cb); + SSL_CTX_set_tlsext_servername_arg(*ctxp, cbinfo); } # ifndef DISABLE_OCSP else /* client */ @@ -1581,8 +1580,8 @@ else /* client */ DEBUG(D_tls) debug_printf("failed to create store for stapling verify\n"); return FAIL; } - SSL_CTX_set_tlsext_status_cb(ctx, tls_client_stapling_cb); - SSL_CTX_set_tlsext_status_arg(ctx, cbinfo); + SSL_CTX_set_tlsext_status_cb(*ctxp, tls_client_stapling_cb); + SSL_CTX_set_tlsext_status_arg(*ctxp, cbinfo); } # endif #endif @@ -1591,16 +1590,15 @@ cbinfo->verify_cert_hostnames = NULL; #ifdef EXIM_HAVE_EPHEM_RSA_KEX /* Set up the RSA callback */ -SSL_CTX_set_tmp_rsa_callback(ctx, rsa_callback); +SSL_CTX_set_tmp_rsa_callback(*ctxp, rsa_callback); #endif /* Finally, set the timeout, and we are done */ -SSL_CTX_set_timeout(ctx, ssl_session_timeout); +SSL_CTX_set_timeout(*ctxp, ssl_session_timeout); DEBUG(D_tls) debug_printf("Initialized TLS\n"); *cbp = cbinfo; -*ctxp = ctx; return OK; } commit aa105ab62ac896fea1ed39c715018b75bdbd1cde Author: Jeremy Harris Date: Thu Jun 29 21:14:40 2017 +0100 Transport: fix smtp under combo of mua_wrapper and limited max_rcpt Fix mis-merge diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index ecb4e79a..2f41adbb 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -2639,36 +2639,18 @@ RCPT. */ if (mua_wrapper) { - /* Initiate a message transfer. */ + address_item * a; + unsigned cnt; - switch(smtp_write_mail_and_rcpt_cmds(&sx, &yield)) - { - case 0: break; - case -1: case -2: goto RESPONSE_FAILED; - case -3: goto END_OFF; - case -4: goto SEND_QUIT; - default: goto SEND_FAILED; - } - - /* If we are an MUA wrapper, abort if any RCPTs were rejected, either - permanently or temporarily. We should have flushed and synced after the last - RCPT. */ - - if (mua_wrapper) - { - address_item * a; - unsigned 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; - break; - } - } + 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; + break; + } } /* If ok is TRUE, we know we have got at least one good recipient, and must now commit 98bf975ca462bebeaa1325d72381847c5118ff14 Author: Jeremy Harris Date: Sat Jul 15 20:06:01 2017 +0100 Use the BDB "environment" so that a database config file is not looked for. Bug 2141 This is a squashed pair, cfb9cf20cb and 0a6c178c6c, picked from the master branch. diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base index f6b42f35..df4f8542 100644 --- a/src/OS/Makefile-Base +++ b/src/OS/Makefile-Base @@ -557,7 +557,8 @@ exim_tidydb.o: $(HDRS) exim_dbutil.c exim_dbmbuild.o: $(HDRS) exim_dbmbuild.c @echo "$(CC) exim_dbmbuild.c" - $(FE)$(CC) -c $(CFLAGS) $(INCLUDE) -o exim_dbmbuild.o exim_dbmbuild.c + $(FE)$(CC) -c $(CFLAGS) $(INCLUDE) -DCOMPILE_UTILITY \ + -o exim_dbmbuild.o exim_dbmbuild.c # Utilities use special versions of some modules - typically with debugging # calls cut out. diff --git a/src/src/acl.c b/src/src/acl.c index 60fa9775..2fff4b70 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -2356,8 +2356,7 @@ if (t != NULL) /* We aren't using a pre-computed rate, so get a previously recorded rate from the database, which will be updated and written back if required. */ -dbm = dbfn_open(US"ratelimit", O_RDWR, &dbblock, TRUE); -if (dbm == NULL) +if (!(dbm = dbfn_open(US"ratelimit", O_RDWR, &dbblock, TRUE))) { store_pool = old_pool; sender_rate = NULL; diff --git a/src/src/dbfn.c b/src/src/dbfn.c index c9c6fb70..5529fe93 100644 --- a/src/src/dbfn.c +++ b/src/src/dbfn.c @@ -91,7 +91,7 @@ int rc, save_errno; BOOL read_only = flags == O_RDONLY; BOOL created = FALSE; flock_t lock_data; -uschar buffer[256]; +uschar dirname[256], filename[256]; /* The first thing to do is to open a separate file on which to lock. This ensures that Exim has exclusive use of the database before it even tries to @@ -106,19 +106,20 @@ make the directory as well, just in case. We won't be doing this many times unnecessarily, because usually the lock file will be there. If the directory exists, there is no error. */ -sprintf(CS buffer, "%s/db/%s.lockfile", spool_directory, name); +snprintf(CS dirname, sizeof(dirname), "%s/db", spool_directory); +snprintf(CS filename, sizeof(filename), "%s/%s.lockfile", dirname, name); -if ((dbblock->lockfd = Uopen(buffer, O_RDWR, EXIMDB_LOCKFILE_MODE)) < 0) +if ((dbblock->lockfd = Uopen(filename, O_RDWR, EXIMDB_LOCKFILE_MODE)) < 0) { created = TRUE; (void)directory_make(spool_directory, US"db", EXIMDB_DIRECTORY_MODE, TRUE); - dbblock->lockfd = Uopen(buffer, O_RDWR|O_CREAT, EXIMDB_LOCKFILE_MODE); + dbblock->lockfd = Uopen(filename, O_RDWR|O_CREAT, EXIMDB_LOCKFILE_MODE); } if (dbblock->lockfd < 0) { log_write(0, LOG_MAIN, "%s", - string_open_failed(errno, "database lock file %s", buffer)); + string_open_failed(errno, "database lock file %s", filename)); errno = 0; /* Indicates locking failure */ return NULL; } @@ -130,7 +131,7 @@ lock_data.l_type = read_only? F_RDLCK : F_WRLCK; lock_data.l_whence = lock_data.l_start = lock_data.l_len = 0; DEBUG(D_hints_lookup|D_retry|D_route|D_deliver) - debug_printf("locking %s\n", buffer); + debug_printf("locking %s\n", filename); sigalrm_seen = FALSE; alarm(EXIMDB_LOCK_TIMEOUT); @@ -141,14 +142,14 @@ if (sigalrm_seen) errno = ETIMEDOUT; if (rc < 0) { log_write(0, LOG_MAIN|LOG_PANIC, "Failed to get %s lock for %s: %s", - read_only ? "read" : "write", buffer, + read_only ? "read" : "write", filename, errno == ETIMEDOUT ? "timed out" : strerror(errno)); (void)close(dbblock->lockfd); errno = 0; /* Indicates locking failure */ return NULL; } -DEBUG(D_hints_lookup) debug_printf("locked %s\n", buffer); +DEBUG(D_hints_lookup) debug_printf("locked %s\n", filename); /* At this point we have an opened and locked separate lock file, that is, exclusive access to the database, so we can go ahead and open it. If we are @@ -159,18 +160,15 @@ databases - often this is caused by non-matching db.h and the library. To make it easy to pin this down, there are now debug statements on either side of the open call. */ -sprintf(CS buffer, "%s/db/%s", spool_directory, name); -DEBUG(D_hints_lookup) debug_printf("EXIM_DBOPEN(%s)\n", buffer); -EXIM_DBOPEN(buffer, flags, EXIMDB_MODE, &(dbblock->dbptr)); -DEBUG(D_hints_lookup) debug_printf("returned from EXIM_DBOPEN\n"); +snprintf(CS filename, sizeof(filename), "%s/%s", dirname, name); +EXIM_DBOPEN(filename, dirname, flags, EXIMDB_MODE, &(dbblock->dbptr)); if (!dbblock->dbptr && errno == ENOENT && flags == O_RDWR) { DEBUG(D_hints_lookup) - debug_printf("%s appears not to exist: trying to create\n", buffer); + debug_printf("%s appears not to exist: trying to create\n", filename); created = TRUE; - EXIM_DBOPEN(buffer, flags|O_CREAT, EXIMDB_MODE, &(dbblock->dbptr)); - DEBUG(D_hints_lookup) debug_printf("returned from EXIM_DBOPEN\n"); + EXIM_DBOPEN(filename, dirname, flags|O_CREAT, EXIMDB_MODE, &(dbblock->dbptr)); } save_errno = errno; @@ -193,22 +191,22 @@ if (created && geteuid() == root_uid) { DIR *dd; struct dirent *ent; - uschar *lastname = Ustrrchr(buffer, '/') + 1; + uschar *lastname = Ustrrchr(filename, '/') + 1; int namelen = Ustrlen(name); *lastname = 0; - dd = opendir(CS buffer); + dd = opendir(CS filename); while ((ent = readdir(dd))) if (Ustrncmp(ent->d_name, name, namelen) == 0) { struct stat statbuf; Ustrcpy(lastname, ent->d_name); - if (Ustat(buffer, &statbuf) >= 0 && statbuf.st_uid != exim_uid) + if (Ustat(filename, &statbuf) >= 0 && statbuf.st_uid != exim_uid) { - DEBUG(D_hints_lookup) debug_printf("ensuring %s is owned by exim\n", buffer); - if (Uchown(buffer, exim_uid, exim_gid)) - DEBUG(D_hints_lookup) debug_printf("failed setting %s to owned by exim\n", buffer); + DEBUG(D_hints_lookup) debug_printf("ensuring %s is owned by exim\n", filename); + if (Uchown(filename, exim_uid, exim_gid)) + DEBUG(D_hints_lookup) debug_printf("failed setting %s to owned by exim\n", filename); } } @@ -216,25 +214,25 @@ if (created && geteuid() == root_uid) } /* If the open has failed, return NULL, leaving errno set. If lof is TRUE, -log the event - also for debugging - but not if the file just doesn't exist. */ +log the event - also for debugging - but debug only if the file just doesn't +exist. */ if (!dbblock->dbptr) { - if (save_errno != ENOENT) - if (lof) - log_write(0, LOG_MAIN, "%s", string_open_failed(save_errno, "DB file %s", - buffer)); - else - DEBUG(D_hints_lookup) - debug_printf("%s", CS string_open_failed(save_errno, "DB file %s\n", - buffer)); + if (lof && save_errno != ENOENT) + log_write(0, LOG_MAIN, "%s", string_open_failed(save_errno, "DB file %s", + filename)); + else + DEBUG(D_hints_lookup) + debug_printf("%s\n", CS string_open_failed(save_errno, "DB file %s", + filename)); (void)close(dbblock->lockfd); errno = save_errno; return NULL; } DEBUG(D_hints_lookup) - debug_printf("opened hints database %s: flags=%s\n", buffer, + debug_printf("opened hints database %s: flags=%s\n", filename, flags == O_RDONLY ? "O_RDONLY" : flags == O_RDWR ? "O_RDWR" : flags == (O_RDWR|O_CREAT) ? "O_RDWR|O_CREAT" @@ -531,7 +529,7 @@ while (Ufgets(buffer, 256, stdin) != NULL) odb = dbfn_open(s, O_RDWR, dbblock + i, TRUE); stop = clock(); - if (odb != NULL) + if (odb) { current = i; printf("opened %d\n", current); diff --git a/src/src/dbstuff.h b/src/src/dbstuff.h index 576941b6..a4ee603b 100644 --- a/src/src/dbstuff.h +++ b/src/src/dbstuff.h @@ -39,7 +39,7 @@ tdb_traverse to be called) */ /* Access functions */ /* EXIM_DBOPEN - sets *dbpp to point to an EXIM_DB, NULL if failed */ -#define EXIM_DBOPEN(name, flags, mode, dbpp) \ +#define EXIM_DBOPEN__(name, dirname, flags, mode, dbpp) \ *(dbpp) = tdb_open(CS name, 0, TDB_DEFAULT, flags, mode) /* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */ @@ -77,7 +77,7 @@ free() must not die when passed NULL */ #define EXIM_DBDELETE_CURSOR(cursor) free(cursor) /* EXIM_DBCLOSE */ -#define EXIM_DBCLOSE(db) tdb_close(db) +#define EXIM_DBCLOSE__(db) tdb_close(db) /* Datum access types - these are intended to be assignable */ @@ -113,8 +113,8 @@ definition of DB_VERSION_STRING, which is present in versions 2.x onwards. */ /***************** Berkeley db 3.x/4.x native definitions ******************/ /* Basic DB type */ -#define EXIM_DB DB - +#if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1) +# define EXIM_DB DB_ENV /* Cursor type, for scanning */ #define EXIM_CURSOR DBC @@ -122,78 +122,161 @@ definition of DB_VERSION_STRING, which is present in versions 2.x onwards. */ #define EXIM_DATUM DBT /* Some text for messages */ -#define EXIM_DBTYPE "db (v3/4)" +#define EXIM_DBTYPE "db (v4.1+)" /* Access functions */ /* EXIM_DBOPEN - sets *dbpp to point to an EXIM_DB, NULL if failed. The -API changed for DB 4.1. */ +API changed for DB 4.1. - and we also starting using the "env" with a +specified working dir, to avoid the DBCONFIG file trap. */ + +# define ENV_TO_DB(env) ((DB *)((env)->app_private)) + +# define EXIM_DBOPEN__(name, dirname, flags, mode, dbpp) \ + if ( db_env_create(dbpp, 0) != 0 \ + || ((*dbpp)->set_errcall(*dbpp, dbfn_bdb_error_callback), 0) \ + || (*dbpp)->open(*dbpp, CS dirname, DB_CREATE|DB_INIT_MPOOL|DB_PRIVATE, 0) != 0\ + ) \ + *dbpp = NULL; \ + else if (db_create((DB **) &((*dbpp)->app_private), *dbpp, 0) != 0) \ + { \ + ((DB_ENV *)(*dbpp))->close((DB_ENV *)(*dbpp), 0); \ + *dbpp = NULL; \ + } \ + else if (ENV_TO_DB(*dbpp)->open(ENV_TO_DB(*dbpp), NULL, CS name, NULL, \ + (flags) == O_RDONLY ? DB_UNKNOWN : DB_HASH, \ + (flags) == O_RDONLY ? DB_RDONLY : DB_CREATE, \ + mode) != 0 \ + ) \ + { \ + ENV_TO_DB(*dbpp)->close(ENV_TO_DB(*dbpp), 0); \ + ((DB_ENV *)(*dbpp))->close((DB_ENV *)(*dbpp), 0); \ + *dbpp = NULL; \ + } -#if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1) -#define EXIM_DBOPEN(name, flags, mode, dbpp) \ - if (db_create(dbpp, NULL, 0) != 0 || \ - ((*dbpp)->set_errcall(*dbpp, dbfn_bdb_error_callback), \ - ((*dbpp)->open)(*dbpp, NULL, CS name, NULL, \ - ((flags) == O_RDONLY)? DB_UNKNOWN : DB_HASH, \ - ((flags) == O_RDONLY)? DB_RDONLY : DB_CREATE, \ - mode)) != 0) *(dbpp) = NULL -#else -#define EXIM_DBOPEN(name, flags, mode, dbpp) \ +/* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */ +# define EXIM_DBGET(db, key, data) \ + (ENV_TO_DB(db)->get(ENV_TO_DB(db), NULL, &key, &data, 0) == 0) + +/* EXIM_DBPUT - returns nothing useful, assumes replace mode */ +# define EXIM_DBPUT(db, key, data) \ + ENV_TO_DB(db)->put(ENV_TO_DB(db), NULL, &key, &data, 0) + +/* EXIM_DBPUTB - non-overwriting for use by dbmbuild */ +# define EXIM_DBPUTB(db, key, data) \ + ENV_TO_DB(db)->put(ENV_TO_DB(db), NULL, &key, &data, DB_NOOVERWRITE) + +/* Return values from EXIM_DBPUTB */ + +# define EXIM_DBPUTB_OK 0 +# define EXIM_DBPUTB_DUP DB_KEYEXIST + +/* EXIM_DBDEL */ +# define EXIM_DBDEL(db, key) ENV_TO_DB(db)->del(ENV_TO_DB(db), NULL, &key, 0) + +/* EXIM_DBCREATE_CURSOR - initialize for scanning operation */ + +# define EXIM_DBCREATE_CURSOR(db, cursor) \ + ENV_TO_DB(db)->cursor(ENV_TO_DB(db), NULL, cursor, 0) + +/* EXIM_DBSCAN - returns TRUE if data is returned, FALSE at end */ +# define EXIM_DBSCAN(db, key, data, first, cursor) \ + ((cursor)->c_get(cursor, &key, &data, \ + (first? DB_FIRST : DB_NEXT)) == 0) + +/* EXIM_DBDELETE_CURSOR - terminate scanning operation */ +# define EXIM_DBDELETE_CURSOR(cursor) \ + (cursor)->c_close(cursor) + +/* EXIM_DBCLOSE */ +# define EXIM_DBCLOSE__(db) \ + (ENV_TO_DB(db)->close(ENV_TO_DB(db), 0) , ((DB_ENV *)(db))->close((DB_ENV *)(db), DB_FORCESYNC)) + +/* Datum access types - these are intended to be assignable. */ + +# define EXIM_DATUM_SIZE(datum) (datum).size +# define EXIM_DATUM_DATA(datum) (datum).data + +/* The whole datum structure contains other fields that must be cleared +before use, but we don't have to free anything after reading data. */ + +# define EXIM_DATUM_INIT(datum) memset(&datum, 0, sizeof(datum)) +# define EXIM_DATUM_FREE(datum) + +#else /* pre- 4.1 */ + +# define EXIM_DB DB + +/* Cursor type, for scanning */ +# define EXIM_CURSOR DBC + +/* The datum type used for queries */ +# define EXIM_DATUM DBT + +/* Some text for messages */ +# define EXIM_DBTYPE "db (v3/4)" + +/* Access functions */ + +/* EXIM_DBOPEN - sets *dbpp to point to an EXIM_DB, NULL if failed. */ + +# define EXIM_DBOPEN__(name, dirname, flags, mode, dbpp) \ if (db_create(dbpp, NULL, 0) != 0 || \ ((*dbpp)->set_errcall(*dbpp, dbfn_bdb_error_callback), \ ((*dbpp)->open)(*dbpp, CS name, NULL, \ ((flags) == O_RDONLY)? DB_UNKNOWN : DB_HASH, \ ((flags) == O_RDONLY)? DB_RDONLY : DB_CREATE, \ mode)) != 0) *(dbpp) = NULL -#endif /* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */ -#define EXIM_DBGET(db, key, data) \ +# define EXIM_DBGET(db, key, data) \ ((db)->get(db, NULL, &key, &data, 0) == 0) /* EXIM_DBPUT - returns nothing useful, assumes replace mode */ -#define EXIM_DBPUT(db, key, data) \ +# define EXIM_DBPUT(db, key, data) \ (db)->put(db, NULL, &key, &data, 0) /* EXIM_DBPUTB - non-overwriting for use by dbmbuild */ -#define EXIM_DBPUTB(db, key, data) \ +# define EXIM_DBPUTB(db, key, data) \ (db)->put(db, NULL, &key, &data, DB_NOOVERWRITE) /* Return values from EXIM_DBPUTB */ -#define EXIM_DBPUTB_OK 0 -#define EXIM_DBPUTB_DUP DB_KEYEXIST +# define EXIM_DBPUTB_OK 0 +# define EXIM_DBPUTB_DUP DB_KEYEXIST /* EXIM_DBDEL */ -#define EXIM_DBDEL(db, key) (db)->del(db, NULL, &key, 0) +# define EXIM_DBDEL(db, key) (db)->del(db, NULL, &key, 0) /* EXIM_DBCREATE_CURSOR - initialize for scanning operation */ -#define EXIM_DBCREATE_CURSOR(db, cursor) \ +# define EXIM_DBCREATE_CURSOR(db, cursor) \ (db)->cursor(db, NULL, cursor, 0) /* EXIM_DBSCAN - returns TRUE if data is returned, FALSE at end */ -#define EXIM_DBSCAN(db, key, data, first, cursor) \ +# define EXIM_DBSCAN(db, key, data, first, cursor) \ ((cursor)->c_get(cursor, &key, &data, \ (first? DB_FIRST : DB_NEXT)) == 0) /* EXIM_DBDELETE_CURSOR - terminate scanning operation */ -#define EXIM_DBDELETE_CURSOR(cursor) \ +# define EXIM_DBDELETE_CURSOR(cursor) \ (cursor)->c_close(cursor) /* EXIM_DBCLOSE */ -#define EXIM_DBCLOSE(db) (db)->close(db, 0) +# define EXIM_DBCLOSE__(db) (db)->close(db, 0) /* Datum access types - these are intended to be assignable. */ -#define EXIM_DATUM_SIZE(datum) (datum).size -#define EXIM_DATUM_DATA(datum) (datum).data +# define EXIM_DATUM_SIZE(datum) (datum).size +# define EXIM_DATUM_DATA(datum) (datum).data /* The whole datum structure contains other fields that must be cleared before use, but we don't have to free anything after reading data. */ -#define EXIM_DATUM_INIT(datum) memset(&datum, 0, sizeof(datum)) -#define EXIM_DATUM_FREE(datum) +# define EXIM_DATUM_INIT(datum) memset(&datum, 0, sizeof(datum)) +# define EXIM_DATUM_FREE(datum) + +#endif #else /* DB_VERSION_MAJOR >= 3 */ @@ -215,7 +298,7 @@ before use, but we don't have to free anything after reading data. */ /* Access functions */ /* EXIM_DBOPEN - sets *dbpp to point to an EXIM_DB, NULL if failed */ -#define EXIM_DBOPEN(name, flags, mode, dbpp) \ +#define EXIM_DBOPEN__(name, dirname, flags, mode, dbpp) \ if ((errno = db_open(CS name, DB_HASH, \ ((flags) == O_RDONLY)? DB_RDONLY : DB_CREATE, \ mode, NULL, NULL, dbpp)) != 0) *(dbpp) = NULL @@ -264,7 +347,7 @@ the new option that is available, so I guess that it happened at 2.5.x. */ (cursor)->c_close(cursor) /* EXIM_DBCLOSE */ -#define EXIM_DBCLOSE(db) (db)->close(db, 0) +#define EXIM_DBCLOSE__(db) (db)->close(db, 0) /* Datum access types - these are intended to be assignable. */ @@ -312,7 +395,7 @@ before been able to pass successfully. */ /* Access functions */ /* EXIM_DBOPEN - sets *dbpp to point to an EXIM_DB, NULL if failed */ -#define EXIM_DBOPEN(name, flags, mode, dbpp) \ +#define EXIM_DBOPEN__(name, dirname, flags, mode, dbpp) \ *(dbpp) = dbopen(CS name, flags, mode, DB_HASH, NULL) /* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */ @@ -347,7 +430,7 @@ refer to cursor, to keep picky compilers happy. */ #define EXIM_DBDELETE_CURSOR(cursor) { cursor = cursor; } /* EXIM_DBCLOSE */ -#define EXIM_DBCLOSE(db) (db)->close(db) +#define EXIM_DBCLOSE__(db) (db)->close(db) /* Datum access types - these are intended to be assignable */ @@ -389,7 +472,7 @@ typedef struct { /* Access functions */ /* EXIM_DBOPEN - returns a EXIM_DB *, NULL if failed */ -#define EXIM_DBOPEN(name, flags, mode, dbpp) \ +#define EXIM_DBOPEN__(name, dirname, flags, mode, dbpp) \ { (*(dbpp)) = (EXIM_DB *) malloc(sizeof(EXIM_DB));\ if (*(dbpp) != NULL) { \ (*(dbpp))->lkey.dptr = NULL;\ @@ -435,7 +518,7 @@ refer to cursor, to keep picky compilers happy. */ #define EXIM_DBDELETE_CURSOR(cursor) { cursor = cursor; } /* EXIM_DBCLOSE */ -#define EXIM_DBCLOSE(db) \ +#define EXIM_DBCLOSE__(db) \ { gdbm_close((db)->gdbm);\ if ((db)->lkey.dptr != NULL) free((db)->lkey.dptr);\ free(db); } @@ -478,7 +561,7 @@ interface */ /* Access functions */ /* EXIM_DBOPEN - returns a EXIM_DB *, NULL if failed */ -#define EXIM_DBOPEN(name, flags, mode, dbpp) \ +#define EXIM_DBOPEN__(name, dirname, flags, mode, dbpp) \ *(dbpp) = dbm_open(CS name, flags, mode) /* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */ @@ -513,7 +596,7 @@ refer to cursor, to keep picky compilers happy. */ #define EXIM_DBDELETE_CURSOR(cursor) { cursor = cursor; } /* EXIM_DBCLOSE */ -#define EXIM_DBCLOSE(db) dbm_close(db) +#define EXIM_DBCLOSE__(db) dbm_close(db) /* Datum access types - these are intended to be assignable */ @@ -528,6 +611,34 @@ after reading data. */ #endif /* USE_GDBM */ + + + + +# ifdef COMPILE_UTILITY + +# define EXIM_DBOPEN(name, dirname, flags, mode, dbpp) \ + EXIM_DBOPEN__(name, dirname, flags, mode, dbpp) +# define EXIM_DBCLOSE(db) EXIM_DBCLOSE__(db) + +# else + +# define EXIM_DBOPEN(name, dirname, flags, mode, dbpp) \ + do { \ + DEBUG(D_hints_lookup) \ + debug_printf("EXIM_DBOPEN: file <%s> dir <%s> flags 0x%x\n", \ + (name), (dirname), flags); \ + EXIM_DBOPEN__(name, dirname, flags, mode, dbpp); \ + DEBUG(D_hints_lookup) debug_printf("returned from EXIM_DBOPEN: %p\n", *dbpp); \ + } while(0) +# define EXIM_DBCLOSE(db) \ + do { \ + DEBUG(D_hints_lookup) debug_printf("EXIM_DBCLOSE(%p)\n", db); \ + EXIM_DBCLOSE__(db); \ + } while(0) + +# endif + /********************* End of dbm library definitions **********************/ diff --git a/src/src/exim_dbmbuild.c b/src/src/exim_dbmbuild.c index 85ae9012..7431bbc0 100644 --- a/src/src/exim_dbmbuild.c +++ b/src/src/exim_dbmbuild.c @@ -30,6 +30,7 @@ characters. */ #include "exim.h" +uschar * spool_directory = NULL; /* dummy for dbstuff.h */ #define max_insize 20000 #define max_outsize 100000 @@ -151,6 +152,7 @@ uschar *bptr; uschar keybuffer[256]; uschar temp_dbmname[512]; uschar real_dbmname[512]; +uschar dirname[512]; uschar *buffer = malloc(max_outsize); uschar *line = malloc(max_insize); @@ -205,10 +207,14 @@ if (strlen(argv[arg+1]) > sizeof(temp_dbmname) - 20) Ustrcpy(temp_dbmname, argv[arg+1]); Ustrcat(temp_dbmname, ".dbmbuild_temp"); +Ustrcpy(dirname, temp_dbmname); +if ((bptr = Ustrrchr(dirname, '/'))) + *bptr = '\0'; + /* It is apparently necessary to open with O_RDWR for this to work with gdbm-1.7.3, though no reading is actually going to be done. */ -EXIM_DBOPEN(temp_dbmname, O_RDWR|O_CREAT|O_EXCL, 0644, &d); +EXIM_DBOPEN(temp_dbmname, dirname, O_RDWR|O_CREAT|O_EXCL, 0644, &d); if (d == NULL) { diff --git a/src/src/exim_dbutil.c b/src/src/exim_dbutil.c index c710772e..fb455bbd 100644 --- a/src/src/exim_dbutil.c +++ b/src/src/exim_dbutil.c @@ -253,18 +253,19 @@ dbfn_open(uschar *name, int flags, open_db *dbblock, BOOL lof) int rc; struct flock lock_data; BOOL read_only = flags == O_RDONLY; -uschar buffer[256]; +uschar dirname[256], filename[256]; /* The first thing to do is to open a separate file on which to lock. This ensures that Exim has exclusive use of the database before it even tries to open it. If there is a database, there should be a lock file in existence. */ -sprintf(CS buffer, "%s/db/%.200s.lockfile", spool_directory, name); +snprintf(CS dirname, sizeof(dirname), "%s/db", spool_directory); +snprintf(CS filename, sizeof(filename), "%s/%.200s.lockfile", dirname, name); -dbblock->lockfd = Uopen(buffer, flags, 0); +dbblock->lockfd = Uopen(filename, flags, 0); if (dbblock->lockfd < 0) { - printf("** Failed to open database lock file %s: %s\n", buffer, + printf("** Failed to open database lock file %s: %s\n", filename, strerror(errno)); return NULL; } @@ -286,7 +287,7 @@ if (rc < 0) { printf("** Failed to get %s lock for %s: %s", flags & O_WRONLY ? "write" : "read", - buffer, + filename, errno == ETIMEDOUT ? "timed out" : strerror(errno)); (void)close(dbblock->lockfd); return NULL; @@ -295,12 +296,12 @@ if (rc < 0) /* At this point we have an opened and locked separate lock file, that is, exclusive access to the database, so we can go ahead and open it. */ -sprintf(CS buffer, "%s/db/%s", spool_directory, name); -EXIM_DBOPEN(buffer, flags, 0, &(dbblock->dbptr)); +sprintf(CS filename, "%s/%s", dirname, name); +EXIM_DBOPEN(filename, dirname, flags, 0, &(dbblock->dbptr)); if (dbblock->dbptr == NULL) { - printf("** Failed to open DBM file %s for %s:\n %s%s\n", buffer, + printf("** Failed to open DBM file %s for %s:\n %s%s\n", filename, read_only? "reading" : "writing", strerror(errno), #ifdef USE_DB " (or Berkeley DB error while opening)" @@ -516,15 +517,16 @@ uschar keybuffer[1024]; dbdata_type = check_args(argc, argv, US"dumpdb", US""); spool_directory = argv[1]; -dbm = dbfn_open(argv[2], O_RDONLY, &dbblock, FALSE); -if (dbm == NULL) exit(1); +if (!(dbm = dbfn_open(argv[2], O_RDONLY, &dbblock, FALSE))) + exit(1); /* Scan the file, formatting the information for each entry. Note that data is returned in a malloc'ed block, in order that it be correctly aligned. */ -key = dbfn_scan(dbm, TRUE, &cursor); -while (key != NULL) +for (key = dbfn_scan(dbm, TRUE, &cursor); + key; + key = dbfn_scan(dbm, FALSE, &cursor)) { dbdata_retry *retry; dbdata_wait *wait; @@ -546,9 +548,8 @@ while (key != NULL) return 1; } Ustrcpy(keybuffer, key); - value = dbfn_read_with_length(dbm, keybuffer, &length); - if (value == NULL) + if (!(value = dbfn_read_with_length(dbm, keybuffer, &length))) fprintf(stderr, "**** Entry \"%s\" was in the key scan, but the record " "was not found in the file - something is wrong!\n", CS keybuffer); @@ -668,7 +669,6 @@ while (key != NULL) } store_reset(value); } - key = dbfn_scan(dbm, FALSE, &cursor); } dbfn_close(dbm); @@ -775,8 +775,9 @@ for(;;) { int verify = 1; spool_directory = argv[1]; - dbm = dbfn_open(argv[2], O_RDWR, &dbblock, FALSE); - if (dbm == NULL) continue; + + if (!(dbm = dbfn_open(argv[2], O_RDWR, &dbblock, FALSE))) + continue; if (Ustrcmp(field, "d") == 0) { @@ -972,11 +973,10 @@ for(;;) /* Handle a read request, or verify after an update. */ spool_directory = argv[1]; - dbm = dbfn_open(argv[2], O_RDONLY, &dbblock, FALSE); - if (dbm == NULL) continue; + if (!(dbm = dbfn_open(argv[2], O_RDONLY, &dbblock, FALSE))) + continue; - record = dbfn_read_with_length(dbm, name, &oldlength); - if (record == NULL) + if (!(record = dbfn_read_with_length(dbm, name, &oldlength))) { printf("record %s not found\n", name); name[0] = 0; @@ -1159,8 +1159,8 @@ oldest = time(NULL) - maxkeep; printf("Tidying Exim hints database %s/db/%s\n", argv[1], argv[2]); spool_directory = argv[1]; -dbm = dbfn_open(argv[2], O_RDWR, &dbblock, FALSE); -if (dbm == NULL) exit(1); +if (!(dbm = dbfn_open(argv[2], O_RDWR, &dbblock, FALSE))) + exit(1); /* Prepare for building file names */ @@ -1173,14 +1173,14 @@ to the file while scanning it. Pity the man page doesn't warn you about that. Therefore, we scan and build a list of all the keys. Then we use that to read the records and possibly update them. */ -key = dbfn_scan(dbm, TRUE, &cursor); -while (key != NULL) +for (key = dbfn_scan(dbm, TRUE, &cursor); + key; + key = dbfn_scan(dbm, FALSE, &cursor)) { key_item *k = store_get(sizeof(key_item) + Ustrlen(key)); k->next = keychain; keychain = k; Ustrcpy(k->key, key); - key = dbfn_scan(dbm, FALSE, &cursor); } /* Now scan the collected keys and operate on the records, resetting @@ -1188,7 +1188,7 @@ the store each time round. */ reset_point = store_get(0); -while (keychain != NULL) +while (keychain) { dbdata_generic *value; diff --git a/src/src/lookups/dbmdb.c b/src/src/lookups/dbmdb.c index b8c42d59..4b03c35f 100644 --- a/src/src/lookups/dbmdb.c +++ b/src/src/lookups/dbmdb.c @@ -18,8 +18,12 @@ static void * dbmdb_open(uschar *filename, uschar **errmsg) { +uschar * dirname = string_copy(filename); +uschar * s; EXIM_DB *yield = NULL; -EXIM_DBOPEN(filename, O_RDONLY, 0, &yield); + +if ((s = Ustrrchr(dirname, '/'))) *s = '\0'; +EXIM_DBOPEN(filename, dirname, O_RDONLY, 0, &yield); if (yield == NULL) { int save_errno = errno; diff --git a/src/src/transports/autoreply.c b/src/src/transports/autoreply.c index f07cd83c..de9d1d81 100644 --- a/src/src/transports/autoreply.c +++ b/src/src/transports/autoreply.c @@ -335,33 +335,22 @@ else file_expand = ob->file_expand; return_message = ob->return_message; - if ((from != NULL && - (from = checkexpand(from, addr, tblock->name, cke_hdr)) == NULL) || - (reply_to != NULL && - (reply_to = checkexpand(reply_to, addr, tblock->name, cke_hdr)) == NULL) || - (to != NULL && - (to = checkexpand(to, addr, tblock->name, cke_hdr)) == NULL) || - (cc != NULL && - (cc = checkexpand(cc, addr, tblock->name, cke_hdr)) == NULL) || - (bcc != NULL && - (bcc = checkexpand(bcc, addr, tblock->name, cke_hdr)) == NULL) || - (subject != NULL && - (subject = checkexpand(subject, addr, tblock->name, cke_hdr)) == NULL) || - (headers != NULL && - (headers = checkexpand(headers, addr, tblock->name, cke_text)) == NULL) || - (text != NULL && - (text = checkexpand(text, addr, tblock->name, cke_text)) == NULL) || - (file != NULL && - (file = checkexpand(file, addr, tblock->name, cke_file)) == NULL) || - (logfile != NULL && - (logfile = checkexpand(logfile, addr, tblock->name, cke_file)) == NULL) || - (oncelog != NULL && - (oncelog = checkexpand(oncelog, addr, tblock->name, cke_file)) == NULL) || - (oncerepeat != NULL && - (oncerepeat = checkexpand(oncerepeat, addr, tblock->name, cke_file)) == NULL)) + if ( from && !(from = checkexpand(from, addr, tblock->name, cke_hdr)) + || reply_to && !(reply_to = checkexpand(reply_to, addr, tblock->name, cke_hdr)) + || to && !(to = checkexpand(to, addr, tblock->name, cke_hdr)) + || cc && !(cc = checkexpand(cc, addr, tblock->name, cke_hdr)) + || bcc && !(bcc = checkexpand(bcc, addr, tblock->name, cke_hdr)) + || subject && !(subject = checkexpand(subject, addr, tblock->name, cke_hdr)) + || headers && !(headers = checkexpand(headers, addr, tblock->name, cke_text)) + || text && !(text = checkexpand(text, addr, tblock->name, cke_text)) + || file && !(file = checkexpand(file, addr, tblock->name, cke_file)) + || logfile && !(logfile = checkexpand(logfile, addr, tblock->name, cke_file)) + || oncelog && !(oncelog = checkexpand(oncelog, addr, tblock->name, cke_file)) + || oncerepeat && !(oncerepeat = checkexpand(oncerepeat, addr, tblock->name, cke_file)) + ) return FALSE; - if (oncerepeat != NULL) + if (oncerepeat) { once_repeat_sec = readconf_readtime(oncerepeat, 0, FALSE); if (once_repeat_sec < 0) @@ -377,11 +366,11 @@ else /* If the never_mail option is set, we have to scan all the recipients and remove those that match. */ -if (ob->never_mail != NULL) +if (ob->never_mail) { const uschar *never_mail = expand_string(ob->never_mail); - if (never_mail == NULL) + if (!never_mail) { addr->transport_return = FAIL; addr->message = string_sprintf("Failed to expand \"%s\" for " @@ -389,11 +378,11 @@ if (ob->never_mail != NULL) return FALSE; } - if (to != NULL) check_never_mail(&to, never_mail); - if (cc != NULL) check_never_mail(&cc, never_mail); - if (bcc != NULL) check_never_mail(&bcc, never_mail); + if (to) check_never_mail(&to, never_mail); + if (cc) check_never_mail(&cc, never_mail); + if (bcc) check_never_mail(&bcc, never_mail); - if (to == NULL && cc == NULL && bcc == NULL) + if (!to && !cc && !bcc) { DEBUG(D_transport) debug_printf("*** all recipients removed by never_mail\n"); @@ -419,7 +408,7 @@ recipient, the effect might not be quite as envisaged. If once_file_size is set, instead of a dbm file, we use a regular file containing a circular buffer recipient cache. */ -if (oncelog != NULL && *oncelog != 0 && to != NULL) +if (oncelog && *oncelog != 0 && to) { time_t then = 0; @@ -427,7 +416,7 @@ if (oncelog != NULL && *oncelog != 0 && to != NULL) if (ob->once_file_size > 0) { - uschar *p; + uschar * p, * nextp; struct stat statbuf; cache_fd = Uopen(oncelog, O_CREAT|O_RDWR, ob->mode); @@ -464,18 +453,16 @@ if (oncelog != NULL && *oncelog != 0 && to != NULL) zero. If we find a match, put the time into "then", and the place where it was found into "cache_time". Otherwise, "then" is left at zero. */ - p = cache_buff; - while (p < cache_buff + cache_size) + for (p = cache_buff; p < cache_buff + cache_size; p = nextp) { uschar *s = p + sizeof(time_t); - uschar *nextp = s + Ustrlen(s) + 1; + nextp = s + Ustrlen(s) + 1; if (Ustrcmp(to, s) == 0) { memcpy(&then, p, sizeof(time_t)); cache_time = p; break; } - p = nextp; } } @@ -484,8 +471,12 @@ if (oncelog != NULL && *oncelog != 0 && to != NULL) else { EXIM_DATUM key_datum, result_datum; - EXIM_DBOPEN(oncelog, O_RDWR|O_CREAT, ob->mode, &dbm_file); - if (dbm_file == NULL) + uschar * dirname = string_copy(oncelog); + uschar * s; + + if ((s = Ustrrchr(dirname, '/'))) *s = '\0'; + EXIM_DBOPEN(oncelog, dirname, O_RDWR|O_CREAT, ob->mode, &dbm_file); + if (!dbm_file) { addr->transport_return = DEFER; addr->message = string_sprintf("Failed to open %s file %s when sending " @@ -509,10 +500,9 @@ if (oncelog != NULL && *oncelog != 0 && to != NULL) can be abolished. */ if (EXIM_DATUM_SIZE(result_datum) == sizeof(time_t)) - { memcpy(&then, EXIM_DATUM_DATA(result_datum), sizeof(time_t)); - } - else then = now; + else + then = now; } } @@ -544,10 +534,10 @@ if (oncelog != NULL && *oncelog != 0 && to != NULL) /* We are going to send a message. Ensure any requested file is available. */ -if (file != NULL) +if (file) { ff = Ufopen(file, "rb"); - if (ff == NULL && !ob->file_optional) + if (!ff && !ob->file_optional) { addr->transport_return = DEFER; addr->message = string_sprintf("Failed to open file %s when sending " @@ -568,6 +558,7 @@ if (pid < 0) addr->message = string_sprintf("Failed to create child process to send " "message from %s transport: %s", tblock->name, strerror(errno)); DEBUG(D_transport) debug_printf("%s\n", addr->message); + if (dbm_file) EXIM_DBCLOSE(dbm_file); return FALSE; } @@ -577,20 +568,20 @@ are newlines in it which might, if placed earlier, screw up other headers. */ f = fdopen(fd, "wb"); -if (from != NULL) fprintf(f, "From: %s\n", from); -if (reply_to != NULL) fprintf(f, "Reply-To: %s\n", reply_to); -if (to != NULL) fprintf(f, "To: %s\n", to); -if (cc != NULL) fprintf(f, "Cc: %s\n", cc); -if (bcc != NULL) fprintf(f, "Bcc: %s\n", bcc); -if (subject != NULL) fprintf(f, "Subject: %s\n", subject); +if (from) fprintf(f, "From: %s\n", from); +if (reply_to) fprintf(f, "Reply-To: %s\n", reply_to); +if (to) fprintf(f, "To: %s\n", to); +if (cc) fprintf(f, "Cc: %s\n", cc); +if (bcc) fprintf(f, "Bcc: %s\n", bcc); +if (subject) fprintf(f, "Subject: %s\n", subject); /* Generate In-Reply-To from the message_id header; there should always be one, but code defensively. */ -for (h = header_list; h != NULL; h = h->next) +for (h = header_list; h; h = h->next) if (h->type == htype_id) break; -if (h != NULL) +if (h) { message_id = Ustrchr(h->text, ':') + 1; while (isspace(*message_id)) message_id++; @@ -600,12 +591,12 @@ if (h != NULL) /* Generate a References header if there is at least one of Message-ID:, References:, or In-Reply-To: (see RFC 2822). */ -for (h = header_list; h != NULL; h = h->next) +for (h = header_list; h; h = h->next) if (h->type != htype_old && strncmpic(US"References:", h->text, 11) == 0) break; -if (h == NULL) - for (h = header_list; h != NULL; h = h->next) +if (!h) + for (h = header_list; h; h = h->next) if (h->type != htype_old && strncmpic(US"In-Reply-To:", h->text, 12) == 0) break; @@ -614,10 +605,10 @@ limit, some systems do not like headers growing beyond recognition. Keep the first message ID for the thread root and the last few for the position inside the thread, up to a maximum of 12 altogether. */ -if (h != NULL || message_id != NULL) +if (h || message_id) { fprintf(f, "References:"); - if (h != NULL) + if (h) { uschar *s, *id, *error; uschar *referenced_ids[12]; @@ -641,8 +632,8 @@ if (h != NULL || message_id != NULL) /* The message id will have a newline on the end of it. */ - if (message_id != NULL) fprintf(f, " %s", message_id); - else fprintf(f, "\n"); + if (message_id) fprintf(f, " %s", message_id); + else fprintf(f, "\n"); } /* Add an Auto-Submitted: header */ @@ -651,16 +642,16 @@ fprintf(f, "Auto-Submitted: auto-replied\n"); /* Add any specially requested headers */ -if (headers != NULL) fprintf(f, "%s\n", headers); +if (headers) fprintf(f, "%s\n", headers); fprintf(f, "\n"); -if (text != NULL) +if (text) { fprintf(f, "%s", CS text); if (text[Ustrlen(text)-1] != '\n') fprintf(f, "\n"); } -if (ff != NULL) +if (ff) { while (Ufgets(big_buffer, big_buffer_size, ff) != NULL) { @@ -669,11 +660,11 @@ if (ff != NULL) uschar *s = expand_string(big_buffer); DEBUG(D_transport) { - if (s == NULL) + if (!s) debug_printf("error while expanding line from file:\n %s\n %s\n", big_buffer, expand_string_message); } - fprintf(f, "%s", (s == NULL)? CS big_buffer : CS s); + fprintf(f, "%s", s ? CS s : CS big_buffer); } else fprintf(f, "%s", CS big_buffer); } @@ -789,7 +780,6 @@ try will skip, of course. However, if there were no recipients in the message, we do not fail. */ if (rc != 0) - { if (rc == EXIT_NORECIPIENTS) { DEBUG(D_any) debug_printf("%s transport: message contained no recipients\n", @@ -802,7 +792,6 @@ if (rc != 0) "transport (%d)", tblock->name, rc); goto END_OFF; } - } /* Log the sending of the message if successful and required. If the file fails to open, it's hard to know what to do. We cannot write to the Exim @@ -813,7 +802,7 @@ file opened for appending, in order to avoid interleaving of output from different processes. The log_buffer can be used exactly as for main log writing. */ -if (logfile != NULL) +if (logfile) { int log_fd = Uopen(logfile, O_WRONLY|O_APPEND|O_CREAT, ob->mode); if (log_fd >= 0) @@ -822,37 +811,37 @@ if (logfile != NULL) DEBUG(D_transport) debug_printf("logging message details\n"); sprintf(CS ptr, "%s\n", tod_stamp(tod_log)); while(*ptr) ptr++; - if (from != NULL) + if (from) { (void)string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer), " From: %s\n", from); while(*ptr) ptr++; } - if (to != NULL) + if (to) { (void)string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer), " To: %s\n", to); while(*ptr) ptr++; } - if (cc != NULL) + if (cc) { (void)string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer), " Cc: %s\n", cc); while(*ptr) ptr++; } - if (bcc != NULL) + if (bcc) { (void)string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer), " Bcc: %s\n", bcc); while(*ptr) ptr++; } - if (subject != NULL) + if (subject) { (void)string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer), " Subject: %s\n", subject); while(*ptr) ptr++; } - if (headers != NULL) + if (headers) { (void)string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer), " %s\n", headers); commit 9d74d5e01f4d61fa65e419a3b7ba027a2fe32470 Author: Jeremy Harris Date: Tue Jul 18 23:10:33 2017 +0100 Fix cache-cold random callout verify. Bug 2147 diff --git a/src/src/verify.c b/src/src/verify.c index 9c4776b3..ba5e84d1 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -789,7 +789,9 @@ tls_retry_connection: { case PENDING_OK: new_domain_record.random_result = ccache_accept; - break; + yield = OK; /* Only usable result we can return */ + done = TRUE; + goto no_conn; case FAIL: new_domain_record.random_result = ccache_reject; commit f1a8a73ce75847ddbd05901ba85b2a9da8c09e20 Author: Jeremy Harris Date: Sat Jul 22 16:38:08 2017 +0100 On callout avoid SIZE every time but noncacheable rcpt main-verify. Bug 2151 diff --git a/src/src/exim.c b/src/src/exim.c index 394bf84f..b0ba8a02 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -2738,7 +2738,7 @@ for (i = 1; i < argc; i++) /* -MCD: set the smtp_use_dsn flag; this indicates that the host that exim is connected to supports the esmtp extension DSN */ - case 'D': smtp_peer_options |= PEER_OFFERED_DSN; break; + case 'D': smtp_peer_options |= OPTION_DSN; break; /* -MCG: set the queue name, to a non-default value */ @@ -2748,12 +2748,12 @@ for (i = 1; i < argc; i++) /* -MCK: the peer offered CHUNKING. Must precede -MC */ - case 'K': smtp_peer_options |= PEER_OFFERED_CHUNKING; break; + case 'K': smtp_peer_options |= OPTION_CHUNKING; break; /* -MCP: set the smtp_use_pipelining flag; this is useful only when it preceded -MC (see above) */ - case 'P': smtp_peer_options |= PEER_OFFERED_PIPE; break; + case 'P': smtp_peer_options |= OPTION_PIPE; break; /* -MCQ: pass on the pid of the queue-running process that started this chain of deliveries and the fd of its synchronizing pipe; this @@ -2768,14 +2768,14 @@ for (i = 1; i < argc; i++) /* -MCS: set the smtp_use_size flag; this is useful only when it precedes -MC (see above) */ - case 'S': smtp_peer_options |= PEER_OFFERED_SIZE; break; + case 'S': smtp_peer_options |= OPTION_SIZE; break; #ifdef SUPPORT_TLS /* -MCT: set the tls_offered flag; this is useful only when it precedes -MC (see above). The flag indicates that the host to which Exim is connected has offered TLS support. */ - case 'T': smtp_peer_options |= PEER_OFFERED_TLS; break; + case 'T': smtp_peer_options |= OPTION_TLS; break; #endif default: badarg = TRUE; break; diff --git a/src/src/macros.h b/src/src/macros.h index 2692714f..c334a71c 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -959,14 +959,14 @@ enum { FILTER_UNSET, FILTER_FORWARD, FILTER_EXIM, FILTER_SIEVE }; /* Codes for ESMTP facilities offered by peer */ -#define PEER_OFFERED_TLS BIT(0) -#define PEER_OFFERED_IGNQ BIT(1) -#define PEER_OFFERED_PRDR BIT(2) -#define PEER_OFFERED_UTF8 BIT(3) -#define PEER_OFFERED_DSN BIT(4) -#define PEER_OFFERED_PIPE BIT(5) -#define PEER_OFFERED_SIZE BIT(6) -#define PEER_OFFERED_CHUNKING BIT(7) +#define OPTION_TLS BIT(0) +#define OPTION_IGNQ BIT(1) +#define OPTION_PRDR BIT(2) +#define OPTION_UTF8 BIT(3) +#define OPTION_DSN BIT(4) +#define OPTION_PIPE BIT(5) +#define OPTION_SIZE BIT(6) +#define OPTION_CHUNKING BIT(7) /* Argument for *_getc */ diff --git a/src/src/transport.c b/src/src/transport.c index b8a4636b..c3d84417 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -1977,12 +1977,12 @@ if ((pid = fork()) == 0) if (smtp_authenticated) argv[i++] = US"-MCA"; - if (smtp_peer_options & PEER_OFFERED_CHUNKING) argv[i++] = US"-MCK"; - if (smtp_peer_options & PEER_OFFERED_DSN) argv[i++] = US"-MCD"; - if (smtp_peer_options & PEER_OFFERED_PIPE) argv[i++] = US"-MCP"; - if (smtp_peer_options & PEER_OFFERED_SIZE) argv[i++] = US"-MCS"; + if (smtp_peer_options & OPTION_CHUNKING) argv[i++] = US"-MCK"; + if (smtp_peer_options & OPTION_DSN) argv[i++] = US"-MCD"; + if (smtp_peer_options & OPTION_PIPE) argv[i++] = US"-MCP"; + if (smtp_peer_options & OPTION_SIZE) argv[i++] = US"-MCS"; #ifdef SUPPORT_TLS - if (smtp_peer_options & PEER_OFFERED_TLS) argv[i++] = US"-MCT"; + if (smtp_peer_options & OPTION_TLS) argv[i++] = US"-MCT"; #endif if (queue_run_pid != (pid_t)0) diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 2f41adbb..13dada2b 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -1293,44 +1293,44 @@ ehlo_response(uschar * buf, uschar checks) size_t bsize = Ustrlen(buf); #ifdef SUPPORT_TLS -if ( checks & PEER_OFFERED_TLS +if ( checks & OPTION_TLS && pcre_exec(regex_STARTTLS, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0) - checks &= ~PEER_OFFERED_TLS; + checks &= ~OPTION_TLS; #endif -if ( checks & PEER_OFFERED_IGNQ +if ( checks & OPTION_IGNQ && pcre_exec(regex_IGNOREQUOTA, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0) - checks &= ~PEER_OFFERED_IGNQ; + checks &= ~OPTION_IGNQ; -if ( checks & PEER_OFFERED_CHUNKING +if ( checks & OPTION_CHUNKING && pcre_exec(regex_CHUNKING, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0) - checks &= ~PEER_OFFERED_CHUNKING; + checks &= ~OPTION_CHUNKING; #ifndef DISABLE_PRDR -if ( checks & PEER_OFFERED_PRDR +if ( checks & OPTION_PRDR && pcre_exec(regex_PRDR, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0) - checks &= ~PEER_OFFERED_PRDR; + checks &= ~OPTION_PRDR; #endif #ifdef SUPPORT_I18N -if ( checks & PEER_OFFERED_UTF8 +if ( checks & OPTION_UTF8 && pcre_exec(regex_UTF8, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0) - checks &= ~PEER_OFFERED_UTF8; + checks &= ~OPTION_UTF8; #endif -if ( checks & PEER_OFFERED_DSN +if ( checks & OPTION_DSN && pcre_exec(regex_DSN, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0) - checks &= ~PEER_OFFERED_DSN; + checks &= ~OPTION_DSN; -if ( checks & PEER_OFFERED_PIPE +if ( checks & OPTION_PIPE && pcre_exec(regex_PIPELINING, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0) - checks &= ~PEER_OFFERED_PIPE; + checks &= ~OPTION_PIPE; -if ( checks & PEER_OFFERED_SIZE +if ( checks & OPTION_SIZE && pcre_exec(regex_SIZE, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0) - checks &= ~PEER_OFFERED_SIZE; + checks &= ~OPTION_SIZE; return checks; } @@ -1481,6 +1481,7 @@ sx->dane_required = verify_check_given_host(&sx->ob->hosts_require_dane, sx->hos if ((sx->max_rcpt = sx->tblock->max_addresses) == 0) sx->max_rcpt = 999999; sx->peer_offered = 0; +sx->avoid_option = 0; sx->igquotstr = US""; if (!sx->helo_data) sx->helo_data = sx->ob->helo_data; #ifdef EXPERIMENTAL_DSN_INFO @@ -1717,7 +1718,7 @@ goto SEND_QUIT; #ifdef SUPPORT_TLS if (sx->smtps) { - smtp_peer_options |= PEER_OFFERED_TLS; + smtp_peer_options |= OPTION_TLS; suppress_tls = FALSE; sx->ob->tls_tempfail_tryclear = FALSE; smtp_command = US"SSL-on-connect"; @@ -1782,18 +1783,18 @@ goto SEND_QUIT; } } - sx->peer_offered = smtp_peer_options = 0; + sx->avoid_option = sx->peer_offered = smtp_peer_options = 0; if (sx->esmtp || sx->lmtp) { sx->peer_offered = ehlo_response(sx->buffer, - PEER_OFFERED_TLS /* others checked later */ + OPTION_TLS /* others checked later */ ); /* Set tls_offered if the response to EHLO specifies support for STARTTLS. */ #ifdef SUPPORT_TLS - smtp_peer_options |= sx->peer_offered & PEER_OFFERED_TLS; + smtp_peer_options |= sx->peer_offered & OPTION_TLS; #endif } } @@ -1827,7 +1828,7 @@ the client not be required to use TLS. If the response is bad, copy the buffer for error analysis. */ #ifdef SUPPORT_TLS -if ( smtp_peer_options & PEER_OFFERED_TLS +if ( smtp_peer_options & OPTION_TLS && !suppress_tls && verify_check_given_host(&sx->ob->hosts_avoid_tls, sx->host) != OK && ( !sx->verify @@ -1972,7 +1973,7 @@ else if ( sx->smtps { errno = ERRNO_TLSREQUIRED; message = string_sprintf("a TLS session is required, but %s", - smtp_peer_options & PEER_OFFERED_TLS + smtp_peer_options & OPTION_TLS ? "an attempt to start TLS failed" : "the server did not offer TLS support"); goto TLS_FAILED; } @@ -1993,60 +1994,60 @@ if (continue_hostname == NULL { sx->peer_offered = ehlo_response(sx->buffer, 0 /* no TLS */ - | (sx->lmtp && sx->ob->lmtp_ignore_quota ? PEER_OFFERED_IGNQ : 0) - | PEER_OFFERED_CHUNKING - | PEER_OFFERED_PRDR + | (sx->lmtp && sx->ob->lmtp_ignore_quota ? OPTION_IGNQ : 0) + | OPTION_CHUNKING + | OPTION_PRDR #ifdef SUPPORT_I18N - | (sx->addrlist->prop.utf8_msg ? PEER_OFFERED_UTF8 : 0) + | (sx->addrlist->prop.utf8_msg ? OPTION_UTF8 : 0) /*XXX if we hand peercaps on to continued-conn processes, must not depend on this addr */ #endif - | PEER_OFFERED_DSN - | PEER_OFFERED_PIPE - | (sx->ob->size_addition >= 0 ? PEER_OFFERED_SIZE : 0) + | OPTION_DSN + | OPTION_PIPE + | (sx->ob->size_addition >= 0 ? OPTION_SIZE : 0) ); /* Set for IGNOREQUOTA if the response to LHLO specifies support and the lmtp_ignore_quota option was set. */ - sx->igquotstr = sx->peer_offered & PEER_OFFERED_IGNQ ? US" IGNOREQUOTA" : US""; + sx->igquotstr = sx->peer_offered & OPTION_IGNQ ? US" IGNOREQUOTA" : US""; /* If the response to EHLO specified support for the SIZE parameter, note this, provided size_addition is non-negative. */ - smtp_peer_options |= sx->peer_offered & PEER_OFFERED_SIZE; + smtp_peer_options |= sx->peer_offered & OPTION_SIZE; /* Note whether the server supports PIPELINING. If hosts_avoid_esmtp matched the current host, esmtp will be false, so PIPELINING can never be used. If the current host matches hosts_avoid_pipelining, don't do it. */ - if ( sx->peer_offered & PEER_OFFERED_PIPE + if ( sx->peer_offered & OPTION_PIPE && verify_check_given_host(&sx->ob->hosts_avoid_pipelining, sx->host) != OK) - smtp_peer_options |= PEER_OFFERED_PIPE; + smtp_peer_options |= OPTION_PIPE; DEBUG(D_transport) debug_printf("%susing PIPELINING\n", - smtp_peer_options & PEER_OFFERED_PIPE ? "" : "not "); + smtp_peer_options & OPTION_PIPE ? "" : "not "); - if ( sx->peer_offered & PEER_OFFERED_CHUNKING + if ( sx->peer_offered & OPTION_CHUNKING && verify_check_given_host(&sx->ob->hosts_try_chunking, sx->host) != OK) - sx->peer_offered &= ~PEER_OFFERED_CHUNKING; + sx->peer_offered &= ~OPTION_CHUNKING; - if (sx->peer_offered & PEER_OFFERED_CHUNKING) + if (sx->peer_offered & OPTION_CHUNKING) {DEBUG(D_transport) debug_printf("CHUNKING usable\n");} #ifndef DISABLE_PRDR - if ( sx->peer_offered & PEER_OFFERED_PRDR + if ( sx->peer_offered & OPTION_PRDR && verify_check_given_host(&sx->ob->hosts_try_prdr, sx->host) != OK) - sx->peer_offered &= ~PEER_OFFERED_PRDR; + sx->peer_offered &= ~OPTION_PRDR; - if (sx->peer_offered & PEER_OFFERED_PRDR) + if (sx->peer_offered & OPTION_PRDR) {DEBUG(D_transport) debug_printf("PRDR usable\n");} #endif /* Note if the server supports DSN */ - smtp_peer_options |= sx->peer_offered & PEER_OFFERED_DSN; + smtp_peer_options |= sx->peer_offered & OPTION_DSN; DEBUG(D_transport) debug_printf("%susing DSN\n", - sx->peer_offered & PEER_OFFERED_DSN ? "" : "not "); + sx->peer_offered & OPTION_DSN ? "" : "not "); /* Note if the response to EHLO specifies support for the AUTH extension. If it has, check that this host is one we want to authenticate to, and do @@ -2063,7 +2064,7 @@ if (continue_hostname == NULL } } } -pipelining_active = !!(smtp_peer_options & PEER_OFFERED_PIPE); +pipelining_active = !!(smtp_peer_options & OPTION_PIPE); /* The setting up of the SMTP call is now complete. Any subsequent errors are message-specific. */ @@ -2081,7 +2082,7 @@ if (sx->addrlist->prop.utf8_msg) } /* If this is an international message we need the host to speak SMTPUTF8 */ -if (sx->utf8_needed && !(sx->peer_offered & PEER_OFFERED_UTF8)) +if (sx->utf8_needed && !(sx->peer_offered & OPTION_UTF8)) { errno = ERRNO_UTF8_FWD; goto RESPONSE_FAILED; @@ -2204,14 +2205,15 @@ int address_count; *p = 0; -/* If we know the receiving MTA supports the SIZE qualification, +/* If we know the receiving MTA supports the SIZE qualification, and we know it, send it, adding something to the message size to allow for imprecision and things that get added en route. Exim keeps the number of lines in a message, so we can give an accurate value for the original message, but we need some additional to handle added headers. (Double "." characters don't get included in the count.) */ -if (sx->peer_offered & PEER_OFFERED_SIZE) +if ( message_size > 0 + && sx->peer_offered & OPTION_SIZE && !(sx->avoid_option & OPTION_SIZE)) { sprintf(CS p, " SIZE=%d", message_size+message_linecount+sx->ob->size_addition); while (*p) p++; @@ -2222,7 +2224,7 @@ if (sx->peer_offered & PEER_OFFERED_SIZE) request that */ sx->prdr_active = FALSE; -if (sx->peer_offered & PEER_OFFERED_PRDR) +if (sx->peer_offered & OPTION_PRDR) for (addr = addrlist; addr; addr = addr->next) if (addr->transport_return == PENDING_DEFER) { @@ -2241,7 +2243,7 @@ if (sx->peer_offered & PEER_OFFERED_PRDR) /* If it supports internationalised messages, and this meesage need that, request it */ -if ( sx->peer_offered & PEER_OFFERED_UTF8 +if ( sx->peer_offered & OPTION_UTF8 && addrlist->prop.utf8_msg && !addrlist->prop.utf8_downcvt ) @@ -2263,7 +2265,7 @@ for (sx->dsn_all_lasthop = TRUE, addr = addrlist, address_count = 0; /* Add any DSN flags to the mail command */ -if (sx->peer_offered & PEER_OFFERED_DSN && !sx->dsn_all_lasthop) +if (sx->peer_offered & OPTION_DSN && !sx->dsn_all_lasthop) { if (dsn_ret == dsn_ret_hdrs) { Ustrcpy(p, " RET=HDRS"); p += 9; } @@ -2299,7 +2301,7 @@ uschar * p = sx->buffer; /* Add any DSN flags to the rcpt command */ -if (sx->peer_offered & PEER_OFFERED_DSN && !(addr->dsn_flags & rf_dsnlasthop)) +if (sx->peer_offered & OPTION_DSN && !(addr->dsn_flags & rf_dsnlasthop)) { if (addr->dsn_flags & rf_dsnflags) { @@ -2369,7 +2371,7 @@ sx->pending_MAIL = TRUE; /* The block starts with MAIL */ the delivery log line. */ if ( sx->addrlist->prop.utf8_msg - && (sx->addrlist->prop.utf8_downcvt || !(sx->peer_offered & PEER_OFFERED_UTF8)) + && (sx->addrlist->prop.utf8_downcvt || !(sx->peer_offered & OPTION_UTF8)) ) { if (s = string_address_utf8_to_alabel(s, &errstr), errstr) @@ -2433,7 +2435,7 @@ for (addr = sx->first_addr, address_count = 0; BOOL no_flush; uschar * rcpt_addr; - addr->dsn_aware = sx->peer_offered & PEER_OFFERED_DSN + addr->dsn_aware = sx->peer_offered & OPTION_DSN ? dsn_support_yes : dsn_support_no; address_count++; @@ -2597,10 +2599,10 @@ if (tblock->filter_command) if ( transport_filter_argv && *transport_filter_argv && **transport_filter_argv - && sx.peer_offered & PEER_OFFERED_CHUNKING + && sx.peer_offered & OPTION_CHUNKING ) { - sx.peer_offered &= ~PEER_OFFERED_CHUNKING; + sx.peer_offered &= ~OPTION_CHUNKING; DEBUG(D_transport) debug_printf("CHUNKING not usable due to transport filter\n"); } } @@ -2661,7 +2663,7 @@ are pipelining. The responses are all handled by sync_responses(). If using CHUNKING, do not send a BDAT until we know how big a chunk we want to send is. */ -if ( !(sx.peer_offered & PEER_OFFERED_CHUNKING) +if ( !(sx.peer_offered & OPTION_CHUNKING) && (sx.ok || (pipelining_active && !mua_wrapper))) { int count = smtp_write_command(&sx.outblock, FALSE, "DATA\r\n"); @@ -2691,7 +2693,7 @@ for handling the SMTP dot-handling protocol, flagging to apply to headers as 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.peer_offered & PEER_OFFERED_CHUNKING) && !sx.ok) +if (!(sx.peer_offered & OPTION_CHUNKING) && !sx.ok) { /* Save the first address of the next batch. */ sx.first_addr = sx.next_addr; @@ -2717,7 +2719,7 @@ else 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 & PEER_OFFERED_CHUNKING) + if (sx.peer_offered & OPTION_CHUNKING) { tctx.check_string = tctx.escape_string = NULL; tctx.options |= topt_use_bdat; @@ -2742,7 +2744,7 @@ else transport_write_timeout = sx.ob->data_timeout; smtp_command = US"sending data block"; /* For error messages */ DEBUG(D_transport|D_v) - if (sx.peer_offered & PEER_OFFERED_CHUNKING) + if (sx.peer_offered & OPTION_CHUNKING) debug_printf(" will write message using CHUNKING\n"); else debug_printf(" SMTP>> writing message and terminating \".\"\n"); @@ -2776,7 +2778,7 @@ else smtp_command = US"end of data"; - if (sx.peer_offered & PEER_OFFERED_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)) @@ -2931,7 +2933,7 @@ else #ifndef DISABLE_PRDR if (sx.prdr_active) addr->flags |= af_prdr_used; #endif - if (sx.peer_offered & PEER_OFFERED_CHUNKING) addr->flags |= af_chunking_used; + if (sx.peer_offered & OPTION_CHUNKING) addr->flags |= af_chunking_used; flag = '-'; #ifndef DISABLE_PRDR diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h index 4bb6d6da..abfd65fc 100644 --- a/src/src/transports/smtp.h +++ b/src/src/transports/smtp.h @@ -127,6 +127,7 @@ typedef struct { int cmd_count; uschar peer_offered; + uschar avoid_option; uschar * igquotstr; uschar * helo_data; #ifdef EXPERIMENTAL_DSN_INFO diff --git a/src/src/verify.c b/src/src/verify.c index ba5e84d1..31c06153 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -779,8 +779,12 @@ tls_retry_connection: postmaster-verify. The sync_responses() would need to be taught about it and we'd need another return code filtering out to here. + + Avoid using a SIZE option on the MAIL for all randon-rcpt checks. */ + sx.avoid_option = OPTION_SIZE; + /* Remember when we last did a random test */ new_domain_record.random_stamp = time(NULL); @@ -792,8 +796,9 @@ tls_retry_connection: yield = OK; /* Only usable result we can return */ done = TRUE; goto no_conn; - case FAIL: + case FAIL: /* the preferred result */ new_domain_record.random_result = ccache_reject; + sx.avoid_option = 0; /* Between each check, issue RSET, because some servers accept only one recipient after MAIL FROM:<>. @@ -838,12 +843,14 @@ tls_retry_connection: else done = TRUE; - /* Main verify. If the host is accepting all local parts, as determined - by the "random" check, we don't need to waste time doing any further - checking. */ + /* Main verify. For rcpt-verify use SIZE if we know it and we're not cacheing; + for sndr-verify never use it. */ if (done) { + if (!(options & vopt_is_recipient && options & vopt_callout_no_cache)) + sx.avoid_option = OPTION_SIZE; + done = FALSE; switch(smtp_write_mail_and_rcpt_cmds(&sx, &yield)) { @@ -852,12 +859,12 @@ tls_retry_connection: case PENDING_OK: done = TRUE; new_address_record.result = ccache_accept; break; - case FAIL: done = TRUE; + case FAIL: done = TRUE; yield = FAIL; *failure_ptr = US"recipient"; new_address_record.result = ccache_reject; break; - default: break; + default: break; } break; @@ -910,6 +917,7 @@ tls_retry_connection: 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 && addr->transport_return == PENDING_OK commit be371985bd77d7d03b0c314a44a3414d8cc2445d Author: Jeremy Harris Date: Sun Jul 16 13:52:57 2017 +0100 Fix build for earlier version Berkeley DB diff --git a/src/src/dbstuff.h b/src/src/dbstuff.h index a4ee603b..aec549f4 100644 --- a/src/src/dbstuff.h +++ b/src/src/dbstuff.h @@ -116,13 +116,19 @@ definition of DB_VERSION_STRING, which is present in versions 2.x onwards. */ #if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1) # define EXIM_DB DB_ENV /* Cursor type, for scanning */ -#define EXIM_CURSOR DBC +# define EXIM_CURSOR DBC /* The datum type used for queries */ -#define EXIM_DATUM DBT +# define EXIM_DATUM DBT /* Some text for messages */ -#define EXIM_DBTYPE "db (v4.1+)" +# define EXIM_DBTYPE "db (v4.1+)" + +/* Only more-recent versions. 5+ ? */ +# ifndef DB_FORCESYNC +# define DB_FORCESYNC 0 +# endif + /* Access functions */ commit 99ab56234b257791ecedd149fc48dc226826d9c2 Author: Jeremy Harris Date: Sun Sep 10 20:23:21 2017 +0100 DKIM: fix signing bug induced by total size of parameter text causing header-line fold between "b=" and terminating ";" of pseudo-header. diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index 4c93de70..7dad1b89 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -288,7 +288,7 @@ found: /* Performs "relaxed" canonicalization of a header. */ static uschar * -pdkim_relax_header(const uschar * header, int crlf) +pdkim_relax_header(const uschar * header, BOOL append_crlf) { BOOL past_field_name = FALSE; BOOL seen_wsp = FALSE; @@ -299,8 +299,8 @@ uschar * q = relaxed; for (p = header; *p; p++) { uschar c = *p; - /* Ignore CR & LF */ - if (c == '\r' || c == '\n') + + if (c == '\r' || c == '\n') /* Ignore CR & LF */ continue; if (c == '\t' || c == ' ') { @@ -312,8 +312,8 @@ for (p = header; *p; p++) else if (!past_field_name && c == ':') { - if (seen_wsp) q--; /* This removes WSP before the colon */ - seen_wsp = TRUE; /* This removes WSP after the colon */ + if (seen_wsp) q--; /* This removes WSP immediately before the colon */ + seen_wsp = TRUE; /* This removes WSP immediately after the colon */ past_field_name = TRUE; } else @@ -326,7 +326,7 @@ for (p = header; *p; p++) if (q > relaxed && q[-1] == ' ') q--; /* Squash eventual trailing SP */ -if (crlf) { *q++ = '\r'; *q++ = '\n'; } +if (append_crlf) { *q++ = '\r'; *q++ = '\n'; } *q = '\0'; return relaxed; } @@ -1303,11 +1303,23 @@ if (sig->bodylength >= 0) } /* Preliminary or final version? */ -base64_b = final ? pdkim_encode_base64(&sig->sighash) : US""; -hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"b=", base64_b); +if (final) + { + base64_b = pdkim_encode_base64(&sig->sighash); + hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"b=", base64_b); -/* add trailing semicolon: I'm not sure if this is actually needed */ -hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, NULL, US";", US""); + /* add trailing semicolon: I'm not sure if this is actually needed */ + hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, NULL, US";", US""); + } +else + { + /* To satisfy the rule "all surrounding whitespace [...] deleted" + ( RFC 6376 section 3.7 ) we ensure there is no whitespace here. Otherwise + the headcat routine could insert a linebreak which the relaxer would reduce + to a single space preceding the terminating semicolon, resulting in an + incorrect header-hash. */ + hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"b=;", US""); + } hdr[hdr_len] = '\0'; return hdr; @@ -1452,7 +1464,7 @@ while (sig) p->value, (q - US p->value) + (p->next ? 1 : 0)); rh = sig->canon_headers == PDKIM_CANON_RELAXED - ? pdkim_relax_header(p->value, 1) /* cook header for relaxed canon */ + ? pdkim_relax_header(p->value, TRUE) /* cook header for relaxed canon */ : string_copy(CUS p->value); /* just copy it for simple canon */ /* Feed header to the hash algorithm */ @@ -1513,7 +1525,7 @@ while (sig) /* cook header for relaxed canon, or just copy it for simple */ uschar * rh = sig->canon_headers == PDKIM_CANON_RELAXED - ? pdkim_relax_header(hdrs->value, 1) + ? pdkim_relax_header(hdrs->value, TRUE) : string_copy(CUS hdrs->value); /* Feed header to the hash algorithm */ @@ -1537,7 +1549,7 @@ while (sig) /* Relax header if necessary */ if (sig->canon_headers == PDKIM_CANON_RELAXED) - sig_hdr = pdkim_relax_header(sig_hdr, 0); + sig_hdr = pdkim_relax_header(sig_hdr, FALSE); DEBUG(D_acl) { commit 70a6b3e5af741d2ec3fb1501875235a28c7bf33e Author: Jeremy Harris Date: Mon Sep 18 17:47:52 2017 +0100 SOCKS: fix unitialized-pointer diff --git a/src/src/transports/smtp_socks.c b/src/src/transports/smtp_socks.c index 55584306..92d13659 100644 --- a/src/src/transports/smtp_socks.c +++ b/src/src/transports/smtp_socks.c @@ -285,7 +285,7 @@ for(;;) sob = &proxies[idx]; /* bodge up a host struct for the proxy */ - proxy.address = sob->proxy_host; + proxy.address = proxy.name = sob->proxy_host; proxy_af = Ustrchr(sob->proxy_host, ':') ? AF_INET6 : AF_INET; if ((fd = smtp_sock_connect(&proxy, proxy_af, sob->port, commit 4b9e57976eaf0611bd5ff666eba60dc2a07f0ea3 Author: Jeremy Harris Date: Sat Jun 3 13:39:18 2017 +0100 Fix crash in transport, on second smtp-connect fail for a list of target hosts Reported as the sequence: 1MX: 554 on connect (banner) 2MX: TCP conn timeout (cherry picked from commit e69636bc9ddf3617be688b07941d7d659d50eaa7) diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 13dada2b..3217d81a 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -2128,25 +2128,34 @@ return OK; /* The failure happened while setting up the call; see if the failure was a 5xx response (this will either be on connection, or following HELO - a 5xx - after EHLO causes it to try HELO). If so, fail all addresses, as this host is - never going to accept them. For other errors during setting up (timeouts or - whatever), defer all addresses, and yield DEFER, so that the host is not - tried again for a while. */ + after EHLO causes it to try HELO). If so, and there are no more hosts to try, + fail all addresses, as this host is never going to accept them. For other + errors during setting up (timeouts or whatever), defer all addresses, and + yield DEFER, so that the host is not tried again for a while. + + XXX This peeking for another host feels like a layering violation. We want + to note the host as unusable, but down here we shouldn't know if this was + the last host to try for the addr(list). Perhaps the upper layer should be + the one to do set_errno() ? The problem is that currently the addr is where + errno etc. are stashed, but until we run out of hosts to try the errors are + host-specific. Maybe we should enhance the host_item definition? */ FAILED: sx->ok = FALSE; /* For when reached by GOTO */ - - yield = code == '5' + set_errno(sx->addrlist, errno, message, + sx->host->next + ? DEFER + : code == '5' #ifdef SUPPORT_I18N - || errno == ERRNO_UTF8_FWD + || errno == ERRNO_UTF8_FWD #endif - ? FAIL : DEFER; - - set_errno(sx->addrlist, errno, message, yield, pass_message, sx->host + ? FAIL : DEFER, + pass_message, sx->host #ifdef EXPERIMENTAL_DSN_INFO , sx->smtp_greeting, sx->helo_response #endif ); + yield = DEFER; } commit 2d2bd7ad2a273ac0e174c9b7df322bf3b0549160 Author: Jeremy Harris Date: Thu Oct 12 14:03:08 2017 +0100 Fix queue_run_in_order to ignore the PID portion of the message ID diff --git a/src/src/queue.c b/src/src/queue.c index 50e4aaef..8db4094f 100644 --- a/src/src/queue.c +++ b/src/src/queue.c @@ -80,7 +80,11 @@ queue_filename *first = NULL; queue_filename **append = &first; while (a && b) - if (Ustrcmp(a->text, b->text) < 0) + { + int d; + if ((d = Ustrncmp(a->text, b->text, 6)) == 0) + d = Ustrcmp(a->text + 14, b->text + 14); + if (d < 0) { *append = a; append= &a->next; @@ -92,6 +96,7 @@ while (a && b) append= &b->next; b = b->next; } + } *append = a ? a : b; return first; @@ -278,7 +283,7 @@ for (; i <= *subcount; i++) if (root[j]) { next = merge_queue_lists(next, root[j]); - root[j] = (j == LOG2_MAXNODES - 1)? next : NULL; + root[j] = j == LOG2_MAXNODES - 1 ? next : NULL; } else { @@ -460,8 +465,8 @@ subsequent iterations. When the first argument of queue_get_spool_list() is -1 (for queue_run_in_ order), it scans all directories and makes a single message list. */ -for (i = (queue_run_in_order? -1 : 0); - i <= (queue_run_in_order? -1 : subcount); +for (i = queue_run_in_order ? -1 : 0; + i <= (queue_run_in_order ? -1 : subcount); i++) { queue_filename *f; diff --git a/src/src/receive.c b/src/src/receive.c index 7980c324..95cf13e1 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -2582,8 +2582,9 @@ letter and it is not used internally. NOTE: If ever the format of message ids is changed, the regular expression for checking that a string is in this format must be updated in a corresponding way. It appears in the initializing code in exim.c. The macro MESSAGE_ID_LENGTH -must also be changed to reflect the correct string length. Then, of course, -other programs that rely on the message id format will need updating too. */ +must also be changed to reflect the correct string length. The queue-sort code +needs to know the layout. Then, of course, other programs that rely on the +message id format will need updating too. */ Ustrncpy(message_id, string_base62((long int)(message_id_tv.tv_sec)), 6); message_id[6] = '-'; commit 9c4e23e62637aab8782f5c847917564966129440 Author: Jeremy Harris Date: Fri Oct 20 23:21:27 2017 +0100 Use safer routine for possibly-overlapping copy Fixes a logging bug seen on aarch64 diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 3217d81a..e50827bc 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -1778,7 +1778,7 @@ goto SEND_QUIT; errno = ERRNO_SMTPCLOSED; goto EHLOHELO_FAILED; } - Ustrncpy(sx->buffer, rsp, sizeof(sx->buffer)/2); + memmove(sx->buffer, rsp, Ustrlen(rsp)); goto RESPONSE_FAILED; } } @@ -2108,11 +2108,6 @@ return OK; sx->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 - in message and errno, and setting_up will always be true. Treat as - a temporary error. */ - EHLOHELO_FAILED: code = '4'; message = string_sprintf("Remote host closed connection in response to %s" @@ -2120,6 +2115,11 @@ return OK; sx->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 + in message and errno, and setting_up will always be true. Treat as + a temporary error. */ + #ifdef SUPPORT_TLS TLS_FAILED: code = '4'; commit 3430cee3038a84ed9719a90bdc879646786d9ebf Author: Heiko Schlittermann (HS12-RIPE) Date: Sat Oct 14 00:24:54 2017 +0200 Fix mariadb/mysql macro confusion diff --git a/src/src/EDITME b/src/src/EDITME index df74aacd..110e8fbe 100644 --- a/src/src/EDITME +++ b/src/src/EDITME @@ -253,7 +253,7 @@ TRANSPORT_SMTP=yes # you perform upgrades and revert them. You should consider the benefit of # embedding the Exim version number into LOOKUP_MODULE_DIR, so that you can # maintain two concurrent sets of modules. -# +# # *BEWARE*: ability to modify the files in LOOKUP_MODULE_DIR is equivalent to # the ability to modify the Exim binary, which is often setuid root! The Exim # developers only intend this functionality be used by OS software packagers @@ -306,6 +306,7 @@ LOOKUP_DNSDB=yes # LOOKUP_IBASE=yes # LOOKUP_LDAP=yes # LOOKUP_MYSQL=yes +# LOOKUP_MYSQL_PC=mariadb # LOOKUP_NIS=yes # LOOKUP_NISPLUS=yes # LOOKUP_ORACLE=yes diff --git a/src/src/lookups/mysql.c b/src/src/lookups/mysql.c index 5cf15af3..b5133bc0 100644 --- a/src/src/lookups/mysql.c +++ b/src/src/lookups/mysql.c @@ -14,6 +14,53 @@ functions. */ #include /* The system header */ +/* We define symbols for *_VERSION_ID (numeric), *_VERSION_STR (char*) +and *_BASE_STR (char*). It's a bit of guesswork. Especially for mariadb +with versions before 10.2, as they do not define there there specific symbols. +*/ + +// Newer (>= 10.2) MariaDB +#if defined MARIADB_VERSION_ID +#define EXIM_MxSQL_VERSION_ID MARIADB_VERSION_ID + +// MySQL defines MYSQL_VERSION_ID, and MariaDB does so +// https://dev.mysql.com/doc/refman/5.7/en/c-api-server-client-versions.html +#elif defined LIBMYSQL_VERSION_ID +#define EXIM_MxSQL_VERSION_ID LIBMYSQL_VERSION_ID +#elif defined MYSQL_VERSION_ID +#define EXIM_MxSQL_VERSION_ID MYSQL_VERSION_ID + +#else +#define EXIM_MYSQL_VERSION_ID 0 +#endif + +// Newer (>= 10.2) MariaDB +#ifdef MARIADB_CLIENT_VERSION_STR +#define EXIM_MxSQL_VERSION_STR MARIADB_CLIENT_VERSION_STR + +// Mysql uses MYSQL_SERVER_VERSION +#elif defined LIBMYSQL_VERSION +#define EXIM_MxSQL_VERSION_STR LIBMYSQL_VERSION +#elif defined MYSQL_SERVER_VERSION +#define EXIM_MxSQL_VERSION_STR MYSQL_SERVER_VERSION + +#else +#define EXIM_MxSQL_VERSION_STR "N.A." +#endif + +#if defined MARIADB_BASE_VERSION +#define EXIM_MxSQL_BASE_STR MARIADB_BASE_VERSION + +#elif defined MARIADB_PACKAGE_VERSION +#define EXIM_MxSQL_BASE_STR "mariadb" + +#elif defined MYSQL_BASE_VERSION +#define EXIM_MxSQL_BASE_STR MYSQL_BASE_VERSION + +#else +#define EXIM_MxSQL_BASE_STR "n.A." +#endif + /* Structure and anchor for caching connections. */ @@ -432,10 +479,10 @@ return quoted; void mysql_version_report(FILE *f) { -fprintf(f, "Library version: MySQL: Compile: %s [%s]\n" - " Runtime: %s\n", - MYSQL_SERVER_VERSION, MYSQL_COMPILATION_COMMENT, - mysql_get_client_info()); +fprintf(f, "Library version: MySQL: Compile: %lu %s [%s]\n" + " Runtime: %lu %s\n", + (long)EXIM_MxSQL_VERSION_ID, EXIM_MxSQL_VERSION_STR, EXIM_MxSQL_BASE_STR, + mysql_get_client_version(), mysql_get_client_info()); #ifdef DYNLOOKUP fprintf(f, " Exim version %s\n", EXIM_VERSION_STR); #endif commit 4090d62a4b25782129cc1643596dc2f6e8f63bde Author: Jeremy Harris Date: Fri Nov 24 20:22:33 2017 +0000 Avoid release of store if there have been later allocations. Bug 2199 diff --git a/src/src/receive.c b/src/src/receive.c index 95cf13e1..20672dbe 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -1772,8 +1772,8 @@ for (;;) (and sometimes lunatic messages can have ones that are 100s of K long) we call store_release() for strings that have been copied - if the string is at the start of a block (and therefore the only thing in it, because we aren't - doing any other gets), the block gets freed. We can only do this because we - know there are no other calls to store_get() going on. */ + doing any other gets), the block gets freed. We can only do this release if + there were no allocations since the once that we want to free. */ if (ptr >= header_size - 4) { @@ -1782,9 +1782,10 @@ for (;;) header_size *= 2; if (!store_extend(next->text, oldsize, header_size)) { + BOOL release_ok = store_last_get[store_pool] == next->text; uschar *newtext = store_get(header_size); memcpy(newtext, next->text, ptr); - store_release(next->text); + if (release_ok) store_release(next->text); next->text = newtext; } }