commit 51894f20e0c03113b4c4e07898ac5e955b21ec41 Merge: caba96329 f70940c94 Author: Jeremy Harris Date: Sat Nov 4 14:20:45 2023 +0000 Merge branch '4.next' diff --git a/src/src/acl.c b/src/src/acl.c index 578cf32cb..cdf60bbb8 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -203,7 +203,14 @@ static condition_def conditions[] = { [ACLC_DELAY] = { US"delay", TRUE, TRUE, ACL_BIT_NOTQUIT }, #ifndef DISABLE_DKIM [ACLC_DKIM_SIGNER] = { US"dkim_signers", TRUE, FALSE, (unsigned int) ~ACL_BIT_DKIM }, - [ACLC_DKIM_STATUS] = { US"dkim_status", TRUE, FALSE, (unsigned int) ~ACL_BIT_DKIM }, + [ACLC_DKIM_STATUS] = { US"dkim_status", TRUE, FALSE, + (unsigned int) + ~(ACL_BIT_DKIM | ACL_BIT_DATA | ACL_BIT_MIME +# ifndef DISABLE_PRDR + | ACL_BIT_PRDR +# endif + ), + }, #endif #ifdef SUPPORT_DMARC [ACLC_DMARC_STATUS] = { US"dmarc_status", TRUE, FALSE, (unsigned int) ~ACL_BIT_DATA }, @@ -3764,8 +3771,14 @@ for (; cb; cb = cb->next) break; case ACLC_DKIM_STATUS: - rc = match_isinlist(dkim_verify_status, - &arg, 0, NULL, NULL, MCL_STRING, TRUE, NULL); + { /* return good for any match */ + const uschar * s = dkim_verify_status ? dkim_verify_status : US"none"; + int sep = 0; + for (uschar * ss; ss = string_nextinlist(&s, &sep, NULL, 0); ) + if ( (rc = match_isinlist(ss, &arg, + 0, NULL, NULL, MCL_STRING, TRUE, NULL)) + == OK) break; + } break; #endif diff --git a/src/src/auths/get_data.c b/src/src/auths/get_data.c index 4c67f17e2..0c9fb37fe 100644 --- a/src/src/auths/get_data.c +++ b/src/src/auths/get_data.c @@ -77,7 +77,7 @@ auth_get_data(uschar ** aptr, const uschar * challenge, int challen) { int c; int p = 0; -smtp_printf("334 %s\r\n", FALSE, b64encode(challenge, challen)); +smtp_printf("334 %s\r\n", SP_NO_MORE, b64encode(challenge, challen)); while ((c = receive_getc(GETC_BUFFER_UNLIMITED)) != '\n' && c != EOF) { if (p >= big_buffer_size - 1) return BAD64; diff --git a/src/src/auths/get_no64_data.c b/src/src/auths/get_no64_data.c index e2cadfbc6..ae11ae5dc 100644 --- a/src/src/auths/get_no64_data.c +++ b/src/src/auths/get_no64_data.c @@ -32,7 +32,7 @@ auth_get_no64_data(uschar **aptr, uschar *challenge) { int c; int p = 0; -smtp_printf("334 %s\r\n", FALSE, challenge); +smtp_printf("334 %s\r\n", SP_NO_MORE, challenge); while ((c = receive_getc(GETC_BUFFER_UNLIMITED)) != '\n' && c != EOF) { if (p >= big_buffer_size - 1) return BAD64; diff --git a/src/src/daemon.c b/src/src/daemon.c index 9709845e5..f2183c735 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -128,7 +128,7 @@ never_error(uschar *log_msg, uschar *smtp_msg, int was_errno) uschar *emsg = was_errno <= 0 ? US"" : string_sprintf(": %s", strerror(was_errno)); log_write(0, LOG_MAIN|LOG_PANIC, "%s%s", log_msg, emsg); -if (smtp_out) smtp_printf("421 %s\r\n", FALSE, smtp_msg); +if (smtp_out) smtp_printf("421 %s\r\n", SP_NO_MORE, smtp_msg); } @@ -233,7 +233,7 @@ if (getsockname(accept_socket, (struct sockaddr *)(&interface_sockaddr), { log_write(0, LOG_MAIN | ((errno == ECONNRESET)? 0 : LOG_PANIC), "getsockname() failed: %s", strerror(errno)); - smtp_printf("421 Local problem: getsockname() failed; please try again later\r\n", FALSE); + smtp_printf("421 Local problem: getsockname() failed; please try again later\r\n", SP_NO_MORE); goto ERROR_RETURN; } @@ -263,7 +263,7 @@ if (smtp_accept_max > 0 && smtp_accept_count >= smtp_accept_max) DEBUG(D_any) debug_printf("rejecting SMTP connection: count=%d max=%d\n", smtp_accept_count, smtp_accept_max); smtp_printf("421 Too many concurrent SMTP connections; " - "please try again later.\r\n", FALSE); + "please try again later.\r\n", SP_NO_MORE); log_write(L_connection_reject, LOG_MAIN, "Connection from %Y refused: too many connections", whofrom); @@ -282,7 +282,7 @@ if (smtp_load_reserve >= 0) { DEBUG(D_any) debug_printf("rejecting SMTP connection: load average = %.2f\n", (double)load_average/1000.0); - smtp_printf("421 Too much load; please try again later.\r\n", FALSE); + smtp_printf("421 Too much load; please try again later.\r\n", SP_NO_MORE); log_write(L_connection_reject, LOG_MAIN, "Connection from %Y refused: load average = %.2f", whofrom, (double)load_average/1000.0); @@ -351,7 +351,7 @@ if (max_for_this_host > 0 && smtp_accept_count >= max_for_this_host) "IP address: count=%d max=%d\n", host_accept_count, max_for_this_host); smtp_printf("421 Too many concurrent SMTP connections " - "from this IP address; please try again later.\r\n", FALSE); + "from this IP address; please try again later.\r\n", SP_NO_MORE); log_write(L_connection_reject, LOG_MAIN, "Connection from %Y refused: too many connections " "from that IP address", whofrom); @@ -445,7 +445,7 @@ if (pid == 0) "(smtp_active_hostname): %s", raw_active_hostname, expand_string_message); smtp_printf("421 Local configuration error; " - "please try again later.\r\n", FALSE); + "please try again later.\r\n", SP_NO_MORE); mac_smtp_fflush(); search_tidyup(); exim_underbar_exit(EXIT_FAILURE); diff --git a/src/src/macros.h b/src/src/macros.h index 2658b9ab7..969393561 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -1159,4 +1159,12 @@ typedef unsigned mcs_flags; #define TSUC_ALLOW_TAINTED_ARGS BIT(1) #define TSUC_ALLOW_RECIPIENTS BIT(2) +/* Flags for smtp_printf */ +#define SP_MORE TRUE +#define SP_NO_MORE FALSE + +/* Flags for smtp_respond */ +#define SR_FINAL TRUE +#define SR_NOT_FINAL FALSE + /* End of macros.h */ diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index 36e559b59..c723ae6c8 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -1868,9 +1868,9 @@ for (pdkim_signature * sig = ctx->sig; sig; sig = sig->next) if (*dkim_verify_min_keysizes) { unsigned minbits; - uschar * ss = expand_getkeyed(US pdkim_keytypes[sig->keytype], + const uschar * ss = expand_getkeyed(US pdkim_keytypes[sig->keytype], dkim_verify_min_keysizes); - if (ss && (minbits = atoi(CS ss)) > sig->keybits) + if (ss && (minbits = atoi(CCS ss)) > sig->keybits) { DEBUG(D_acl) debug_printf("Key too short: Actual: %s %u Minima '%s'\n", pdkim_keytypes[sig->keytype], sig->keybits, dkim_verify_min_keysizes); diff --git a/src/src/receive.c b/src/src/receive.c index 8a400caf0..8190c5941 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -570,7 +570,7 @@ smtp_user_msg(uschar *code, uschar *user_msg) { int len = 3; smtp_message_code(&code, &len, &user_msg, NULL, TRUE); -smtp_respond(code, len, TRUE, user_msg); +smtp_respond(code, len, SR_FINAL, user_msg); } #endif @@ -1457,7 +1457,7 @@ if (!(mbox_file = spool_mbox(&mbox_size, NULL, &mbox_filename))) #ifdef EXPERIMENTAL_DCC dcc_ok = 0; #endif - smtp_respond(US"451", 3, TRUE, US"temporary local problem"); + smtp_respond(US"451", 3, SR_FINAL, US"temporary local problem"); message_id[0] = 0; /* Indicate no message accepted */ *smtp_reply_ptr = US""; /* Indicate reply already sent */ return FALSE; /* Indicate skip to end of receive function */ @@ -2309,7 +2309,7 @@ OVERSIZE: sender_address, sender_fullhost ? " H=" : "", sender_fullhost ? sender_fullhost : US"", sender_ident ? " U=" : "", sender_ident ? sender_ident : US""); - smtp_printf("552 Message header not CRLF terminated\r\n", FALSE); + smtp_printf("552 Message header not CRLF terminated\r\n", SP_NO_MORE); bdat_flush_data(); smtp_reply = US""; goto TIDYUP; /* Skip to end of function */ @@ -3251,10 +3251,9 @@ if (!ferror(spool_data_file) && !(receive_feof)() && message_ended != END_DOT) { Uunlink(spool_name); /* Lose data file when closed */ cancel_cutthrough_connection(TRUE, US"sender closed connection"); - message_id[0] = 0; /* Indicate no message_accepted */ smtp_reply = handle_lost_connection(US""); smtp_yield = FALSE; - goto TIDYUP; /* Skip to end of function */ + goto NOT_ACCEPTED; /* Skip to end of function */ } break; @@ -3597,7 +3596,7 @@ else int all_pass = OK; int all_fail = FAIL; - smtp_printf("353 PRDR content analysis beginning\r\n", TRUE); + smtp_printf("353 PRDR content analysis beginning\r\n", SP_MORE); /* Loop through recipients, responses must be in same order received */ for (unsigned int c = 0; recipients_count > c; c++) { @@ -3661,7 +3660,7 @@ else /* Check the recipients count again, as the MIME ACL might have changed them. */ - if (acl_smtp_data != NULL && recipients_count > 0) + if (acl_smtp_data && recipients_count > 0) { rc = acl_check(ACL_WHERE_DATA, NULL, acl_smtp_data, &user_msg, &log_msg); add_acl_headers(ACL_WHERE_DATA, US"DATA"); @@ -3922,7 +3921,7 @@ else if (smtp_input) if (!smtp_batched_input) { - smtp_respond(smtp_code, 3, TRUE, errmsg); + smtp_respond(smtp_code, 3, SR_FINAL, errmsg); smtp_reply = US""; /* Indicate reply already sent */ goto NOT_ACCEPTED; /* Skip to end of function */ } @@ -4030,11 +4029,6 @@ else receive_messagecount++; -/* Add data size to written header size. We do not count the initial file name -that is in the file, but we do add one extra for the notional blank line that -precedes the data. This total differs from message_size in that it include the -added Received: header and any other headers that got created locally. */ - if (fflush(spool_data_file)) { errmsg = string_sprintf("Spool write error: %s", strerror(errno)); @@ -4054,8 +4048,13 @@ if (fflush(spool_data_file)) /* Does not return */ } } -fstat(data_fd, &statbuf); +/* Add data size to written header size. We do not count the initial file name +that is in the file, but we do add one extra for the notional blank line that +precedes the data. This total differs from message_size in that it include the +added Received: header and any other headers that got created locally. */ + +fstat(data_fd, &statbuf); msg_size += statbuf.st_size - spool_data_start_offset(message_id) + 1; /* Generate a "message received" log entry. We do this by building up a dynamic @@ -4349,9 +4348,9 @@ if(!smtp_reply) #endif { log_write(0, LOG_MAIN | - (LOGGING(received_recipients) ? LOG_RECIPIENTS : 0) | - (LOGGING(received_sender) ? LOG_SENDER : 0), - "%Y", g); + (LOGGING(received_recipients) ? LOG_RECIPIENTS : 0) | + (LOGGING(received_sender) ? LOG_SENDER : 0), + "%Y", g); /* Log any control actions taken by an ACL or local_scan(). */ @@ -4389,9 +4388,16 @@ a queue-runner could grab it in the window. A fflush() was done earlier in the expectation that any write errors on the data file will be flushed(!) out thereby. Nevertheless, it is theoretically -possible for fclose() to fail - but what to do? What has happened to the lock -if this happens? We can at least log it; if it is observed on some platform -then we can think about properly declaring the message not-received. */ +possible for fclose() to fail - and this has been seen on obscure filesystems +(probably one that delayed the actual media write as long as possible) +but what to do? What has happened to the lock if this happens? +It's a mes because we already logged the acceptance. +We can at least log the issue, try to remove spoolfiles and respond with +a temp-reject. We do not want to close before logging acceptance because +we want to hold the lock until we know that logging worked. +Could we make this less likely by doing an fdatasync() just after the fflush()? +That seems like a good thing on data-security grounds, but how much will it hit +performance? */ goto TIDYUP; @@ -4404,8 +4410,27 @@ process_info[process_info_len] = 0; /* Remove message id */ if (spool_data_file && cutthrough_done == NOT_TRIED) { if (fclose(spool_data_file)) /* Frees the lock */ - log_write(0, LOG_MAIN|LOG_PANIC, - "spoolfile error on close: %s", strerror(errno)); + { + log_msg = string_sprintf("spoolfile error on close: %s", strerror(errno)); + log_write(0, LOG_MAIN|LOG_PANIC | + (LOGGING(received_recipients) ? LOG_RECIPIENTS : 0) | + (LOGGING(received_sender) ? LOG_SENDER : 0), + "%s", log_msg); + log_write(0, LOG_MAIN | + (LOGGING(received_recipients) ? LOG_RECIPIENTS : 0) | + (LOGGING(received_sender) ? LOG_SENDER : 0), + "rescind the above message-accept"); + + Uunlink(spool_name); + Uunlink(spool_fname(US"input", message_subdir, message_id, US"-H")); + Uunlink(spool_fname(US"msglog", message_subdir, message_id, US"")); + + /* Claim a data ACL temp-reject, just to get reject logging and resposponse */ + smtp_handle_acl_fail(ACL_WHERE_DATA, rc, NULL, log_msg); + smtp_reply = US""; /* Indicate reply already sent */ + + message_id[0] = 0; /* no message accepted */ + } spool_data_file = NULL; } @@ -4434,7 +4459,7 @@ if (smtp_input) { if (fake_response != OK) smtp_respond(fake_response == DEFER ? US"450" : US"550", - 3, TRUE, fake_response_text); + 3, SR_FINAL, fake_response_text); /* An OK response is required; use "message" text if present. */ @@ -4443,7 +4468,7 @@ if (smtp_input) uschar *code = US"250"; int len = 3; smtp_message_code(&code, &len, &user_msg, NULL, TRUE); - smtp_respond(code, len, TRUE, user_msg); + smtp_respond(code, len, SR_FINAL, user_msg); } /* Default OK response */ @@ -4471,10 +4496,10 @@ if (smtp_input) else if (smtp_reply[0] != 0) if (fake_response != OK && smtp_reply[0] == '2') - smtp_respond(fake_response == DEFER ? US"450" : US"550", 3, TRUE, - fake_response_text); + smtp_respond(fake_response == DEFER ? US"450" : US"550", + 3, SR_FINAL, fake_response_text); else - smtp_printf("%.1024s\r\n", FALSE, smtp_reply); + smtp_printf("%.1024s\r\n", SP_NO_MORE, smtp_reply); switch (cutthrough_done) { diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 8cc9f74bf..c565d522d 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -764,7 +764,7 @@ for(;;) return EOD; } - smtp_printf("250 %u byte chunk received\r\n", FALSE, chunking_datasize); + smtp_printf("250 %u byte chunk received\r\n", SP_NO_MORE, chunking_datasize); chunking_state = CHUNKING_OFFERED; DEBUG(D_receive) debug_printf("chunking state %d\n", (int)chunking_state); @@ -802,7 +802,7 @@ next_cmd: case NOOP_CMD: HAD(SCH_NOOP); - smtp_printf("250 OK\r\n", FALSE); + smtp_printf("250 OK\r\n", SP_NO_MORE); goto next_cmd; case BDAT_CMD: @@ -1298,7 +1298,7 @@ smtp_closedown(uschar * message) { if (!smtp_in || smtp_batched_input) return; receive_swallow_smtp(); -smtp_printf("421 %s\r\n", FALSE, message); +smtp_printf("421 %s\r\n", SP_NO_MORE, message); for (;;) switch(smtp_read_command(FALSE, GETC_BUFFER_UNLIMITED)) { @@ -1307,16 +1307,16 @@ for (;;) switch(smtp_read_command(FALSE, GETC_BUFFER_UNLIMITED)) case QUIT_CMD: f.smtp_in_quit = TRUE; - smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname); + smtp_printf("221 %s closing connection\r\n", SP_NO_MORE, smtp_active_hostname); mac_smtp_fflush(); return; case RSET_CMD: - smtp_printf("250 Reset OK\r\n", FALSE); + smtp_printf("250 Reset OK\r\n", SP_NO_MORE); break; default: - smtp_printf("421 %s\r\n", FALSE, message); + smtp_printf("421 %s\r\n", SP_NO_MORE, message); break; } } @@ -2266,7 +2266,7 @@ if (!f.sender_host_unknown) { log_write(0, LOG_MAIN, "getsockopt() failed from %s: %s", host_and_ident(FALSE), strerror(errno)); - smtp_printf("451 SMTP service not available\r\n", FALSE); + smtp_printf("451 SMTP service not available\r\n", SP_NO_MORE); return FALSE; } } @@ -2370,7 +2370,7 @@ if (!f.sender_host_unknown) log_write(0, LOG_MAIN|LOG_REJECT, "connection from %s refused (IP options)", host_and_ident(FALSE)); - smtp_printf("554 SMTP service not available\r\n", FALSE); + smtp_printf("554 SMTP service not available\r\n", SP_NO_MORE); return FALSE; } @@ -2425,7 +2425,7 @@ if (!f.sender_host_unknown) #ifndef DISABLE_TLS if (!tls_in.on_connect) #endif - smtp_printf("554 SMTP service not available\r\n", FALSE); + smtp_printf("554 SMTP service not available\r\n", SP_NO_MORE); return FALSE; } @@ -2456,7 +2456,7 @@ if (!f.sender_host_unknown) log_write(L_connection_reject, LOG_MAIN|LOG_REJECT, "refused connection from %s " "(tcp wrappers)", host_and_ident(FALSE)); - smtp_printf("554 SMTP service not available\r\n", FALSE); + smtp_printf("554 SMTP service not available\r\n", SP_NO_MORE); } else { @@ -2466,7 +2466,7 @@ if (!f.sender_host_unknown) log_write(L_connection_reject, LOG_MAIN|LOG_REJECT, "temporarily refused connection from %s " "(tcp wrappers errno=%d)", host_and_ident(FALSE), save_errno); - smtp_printf("451 Temporary local problem - please try later\r\n", FALSE); + smtp_printf("451 Temporary local problem - please try later\r\n", SP_NO_MORE); } return FALSE; } @@ -2486,7 +2486,7 @@ if (!f.sender_host_unknown) host_and_ident(FALSE), smtp_accept_count - 1, smtp_accept_max, smtp_accept_reserve, (rc == DEFER)? " (lookup deferred)" : ""); smtp_printf("421 %s: Too many concurrent SMTP connections; " - "please try again later\r\n", FALSE, smtp_active_hostname); + "please try again later\r\n", SP_NO_MORE, smtp_active_hostname); return FALSE; } reserved_host = TRUE; @@ -2507,7 +2507,7 @@ if (!f.sender_host_unknown) LOG_MAIN, "temporarily refused connection from %s: not in " "reserve list and load average = %.2f", host_and_ident(FALSE), (double)load_average/1000.0); - smtp_printf("421 %s: Too much load; please try again later\r\n", FALSE, + smtp_printf("421 %s: Too much load; please try again later\r\n", SP_NO_MORE, smtp_active_hostname); return FALSE; } @@ -2681,7 +2681,7 @@ if (!check_sync()) "synchronization error (input sent without waiting for greeting): " "rejected connection from %s input=\"%s\"", host_and_ident(TRUE), string_printing(string_copyn(smtp_inptr, n))); - smtp_printf("554 SMTP synchronization error\r\n", FALSE); + smtp_printf("554 SMTP synchronization error\r\n", SP_NO_MORE); return FALSE; } @@ -2692,7 +2692,7 @@ smtp_printf("%Y", #ifndef DISABLE_PIPE_CONNECT fl.pipe_connect_acceptable && pipeline_connect_sends(), #else - FALSE, + SP_NO_MORE, #endif ss); @@ -2751,10 +2751,10 @@ if (++synprot_error_count > smtp_max_synprot_errors) if (code > 0) { - smtp_printf("%d%c%s%s%s\r\n", FALSE, code, yield == 1 ? '-' : ' ', + smtp_printf("%d%c%s%s%s\r\n", SP_NO_MORE, code, yield == 1 ? '-' : ' ', data ? data : US"", data ? US": " : US"", errmess); if (yield == 1) - smtp_printf("%d Too many syntax or protocol errors\r\n", FALSE, code); + smtp_printf("%d Too many syntax or protocol errors\r\n", SP_NO_MORE, code); } return yield; @@ -2781,7 +2781,7 @@ Returns: nothing */ void -smtp_respond(uschar* code, int codelen, BOOL final, uschar *msg) +smtp_respond(uschar * code, int codelen, BOOL final, uschar * msg) { int esclen = 0; uschar *esc = US""; @@ -2830,7 +2830,7 @@ for (;;) } else { - smtp_printf("%.3s-%.*s%.*s\r\n", TRUE, code, esclen, esc, (int)(nl - msg), msg); + smtp_printf("%.3s-%.*s%.*s\r\n", SP_MORE, code, esclen, esc, (int)(nl - msg), msg); msg = nl + 1; Uskip_whitespace(&msg); } @@ -2936,7 +2936,7 @@ Returns: 0 in most cases */ int -smtp_handle_acl_fail(int where, int rc, uschar *user_msg, uschar *log_msg) +smtp_handle_acl_fail(int where, int rc, uschar * user_msg, uschar * log_msg) { BOOL drop = rc == FAIL_DROP; int codelen = 3; @@ -3022,7 +3022,7 @@ if (sender_verified_failed && string_sprintf(": %s", sender_verified_failed->message)); if (rc == FAIL && sender_verified_failed->user_message) - smtp_respond(smtp_code, codelen, FALSE, string_sprintf( + smtp_respond(smtp_code, codelen, SR_NOT_FINAL, string_sprintf( testflag(sender_verified_failed, af_verify_pmfail)? "Postmaster verification failed while checking <%s>\n%s\n" "Several RFCs state that you are required to have a postmaster\n" @@ -3054,7 +3054,7 @@ always a 5xx one - see comments at the start of this function. If the original rc was FAIL_DROP we drop the connection and yield 2. */ if (rc == FAIL) - smtp_respond(smtp_code, codelen, TRUE, + smtp_respond(smtp_code, codelen, SR_FINAL, user_msg ? user_msg : US"Administrative prohibition"); /* Send temporary failure response to the command. Don't give any details, @@ -3072,12 +3072,12 @@ else && sender_verified_failed && sender_verified_failed->message ) - smtp_respond(smtp_code, codelen, FALSE, sender_verified_failed->message); + smtp_respond(smtp_code, codelen, SR_NOT_FINAL, sender_verified_failed->message); - smtp_respond(smtp_code, codelen, TRUE, user_msg); + smtp_respond(smtp_code, codelen, SR_FINAL, user_msg); } else - smtp_respond(smtp_code, codelen, TRUE, + smtp_respond(smtp_code, codelen, SR_FINAL, US"Temporary local problem - please try later"); /* Log the incident to the logs that are specified by log_reject_target @@ -3194,7 +3194,7 @@ responses are all internal, they should be reasonable size. */ if (code && defaultrespond) { if (user_msg) - smtp_respond(code, 3, TRUE, user_msg); + smtp_respond(code, 3, SR_FINAL, user_msg); else { gstring * g; @@ -3203,7 +3203,7 @@ if (code && defaultrespond) va_start(ap, defaultrespond); g = string_vformat(NULL, SVFMT_EXTEND|SVFMT_REBUFFER, CS defaultrespond, ap); va_end(ap); - smtp_printf("%s %Y\r\n", FALSE, code, g); + smtp_printf("%s %Y\r\n", SP_NO_MORE, code, g); } mac_smtp_fflush(); } @@ -3360,7 +3360,7 @@ smtp_user_msg(uschar *code, uschar *user_msg) { int len = 3; smtp_message_code(&code, &len, &user_msg, NULL, TRUE); -smtp_respond(code, len, TRUE, user_msg); +smtp_respond(code, len, SR_FINAL, user_msg); } @@ -3495,7 +3495,7 @@ if (f.allow_unqualified_recipient || strcmpic(*recipient, US"postmaster") == 0) *recipient = US rewrite_address_qualify(*recipient, TRUE); return rd; } -smtp_printf("501 %s: recipient address must contain a domain\r\n", FALSE, +smtp_printf("501 %s: recipient address must contain a domain\r\n", SP_NO_MORE, smtp_cmd_data); log_write(L_smtp_syntax_error, LOG_MAIN|LOG_REJECT, "unqualified %s rejected: <%s> %s%s", @@ -3523,9 +3523,9 @@ if ( acl_smtp_quit #endif if (*user_msgp) - smtp_respond(US"221", 3, TRUE, *user_msgp); + smtp_respond(US"221", 3, SR_FINAL, *user_msgp); else - smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname); + smtp_printf("221 %s closing connection\r\n", SP_NO_MORE, smtp_active_hostname); #ifdef SERVERSIDE_CLOSE_NOWAIT # ifndef DISABLE_TLS @@ -3554,7 +3554,7 @@ smtp_rset_handler(void) { HAD(SCH_RSET); incomplete_transaction_log(US"RSET"); -smtp_printf("250 Reset OK\r\n", FALSE); +smtp_printf("250 Reset OK\r\n", SP_NO_MORE); cmd_list[CL_RSET].is_mail_cmd = FALSE; if (chunking_state > CHUNKING_OFFERED) chunking_state = CHUNKING_OFFERED; @@ -3809,7 +3809,7 @@ while (done <= 0) { int rc = smtp_in_auth(au, &smtp_resp, &errmsg); - smtp_printf("%s\r\n", FALSE, smtp_resp); + smtp_printf("%s\r\n", SP_NO_MORE, smtp_resp); if (rc != OK) { uschar * logmsg = NULL; @@ -3870,7 +3870,7 @@ while (done <= 0) if (!check_helo(smtp_cmd_data)) { - smtp_printf("501 Syntactically invalid %s argument(s)\r\n", FALSE, hello); + smtp_printf("501 Syntactically invalid %s argument(s)\r\n", SP_NO_MORE, hello); log_write(0, LOG_MAIN|LOG_REJECT, "rejected %s from %s: syntactically " "invalid argument(s): %s", hello, host_and_ident(FALSE), @@ -3935,7 +3935,7 @@ while (done <= 0) { if (fl.helo_verify_required) { - smtp_printf("%d %s argument does not match calling host\r\n", FALSE, + smtp_printf("%d %s argument does not match calling host\r\n", SP_NO_MORE, tempfail? 451 : 550, hello); log_write(0, LOG_MAIN|LOG_REJECT, "%srejected \"%s %s\" from %s", tempfail? "temporarily " : "", @@ -4288,7 +4288,7 @@ while (done <= 0) done = synprot_error(L_smtp_syntax_error, resp, NULL, errmsg); else { - smtp_printf("%d %s\r\n", FALSE, resp, errmsg); + smtp_printf("%d %s\r\n", SP_NO_MORE, resp, errmsg); log_write(0, LOG_MAIN|LOG_REJECT, "rejected XCLIENT from %s: %s", host_and_ident(FALSE), errmsg); } @@ -4302,7 +4302,7 @@ while (done <= 0) We require that we do; the following HELO/EHLO handling will set sender_helo_name as normal. */ - smtp_printf("%s XCLIENT success\r\n", FALSE, smtp_code); + smtp_printf("%s XCLIENT success\r\n", SP_NO_MORE, smtp_code); } break; /* XCLIENT */ } @@ -4326,7 +4326,7 @@ while (done <= 0) if ( fl.helo_verify_required || verify_check_host(&hosts_require_helo) == OK) { - smtp_printf("503 HELO or EHLO required\r\n", FALSE); + smtp_printf("503 HELO or EHLO required\r\n", SP_NO_MORE); log_write(0, LOG_MAIN|LOG_REJECT, "rejected MAIL from %s: no " "HELO/EHLO given", host_and_ident(FALSE)); break; @@ -4353,7 +4353,7 @@ while (done <= 0) if (smtp_mailcmd_max > 0 && smtp_mailcmd_count > smtp_mailcmd_max) { - smtp_printf("421 too many messages in this connection\r\n", FALSE); + smtp_printf("421 too many messages in this connection\r\n", SP_NO_MORE); log_write(0, LOG_MAIN|LOG_REJECT, "rejected MAIL command %s: too many " "messages in one connection", host_and_ident(TRUE)); break; @@ -4626,7 +4626,7 @@ while (done <= 0) if (thismessage_size_limit > 0 && message_size > thismessage_size_limit) { - smtp_printf("552 Message size exceeds maximum permitted\r\n", FALSE); + smtp_printf("552 Message size exceeds maximum permitted\r\n", SP_NO_MORE); log_write(L_size_reject, LOG_MAIN|LOG_REJECT, "rejected MAIL FROM:<%s> %s: " "message too big: size%s=%d max=%d", @@ -4651,7 +4651,7 @@ while (done <= 0) smtp_check_spool_space && message_size >= 0 ? message_size + 5000 : 0)) { - smtp_printf("452 Space shortage, please try later\r\n", FALSE); + smtp_printf("452 Space shortage, please try later\r\n", SP_NO_MORE); sender_address = NULL; break; } @@ -4673,7 +4673,7 @@ while (done <= 0) } else { - smtp_printf("501 %s: sender address must contain a domain\r\n", FALSE, + smtp_printf("501 %s: sender address must contain a domain\r\n", SP_NO_MORE, smtp_cmd_data); log_write(L_smtp_syntax_error, LOG_MAIN|LOG_REJECT, @@ -4753,7 +4753,7 @@ while (done <= 0) { if (f.smtp_in_pipelining_advertised && last_was_rej_mail) { - smtp_printf("503 sender not yet given\r\n", FALSE); + smtp_printf("503 sender not yet given\r\n", SP_NO_MORE); was_rej_mail = TRUE; } else @@ -4902,7 +4902,7 @@ while (done <= 0) if (recipients_max_reject) { rcpt_fail_count++; - smtp_printf("552 too many recipients\r\n", FALSE); + smtp_printf("552 too many recipients\r\n", SP_NO_MORE); if (!toomany) log_write(0, LOG_MAIN|LOG_REJECT, "too many recipients: message " "rejected: sender=<%s> %s", sender_address, host_and_ident(TRUE)); @@ -4910,7 +4910,7 @@ while (done <= 0) else { rcpt_defer_count++; - smtp_printf("452 too many recipients\r\n", FALSE); + smtp_printf("452 too many recipients\r\n", SP_NO_MORE); if (!toomany) log_write(0, LOG_MAIN|LOG_REJECT, "too many recipients: excess " "temporarily rejected: sender=<%s> %s", sender_address, @@ -4976,7 +4976,7 @@ while (done <= 0) if (user_msg) smtp_user_msg(US"250", user_msg); else - smtp_printf("250 Accepted\r\n", FALSE); + smtp_printf("250 Accepted\r\n", SP_NO_MORE); rcpt_fail_count++; discarded = TRUE; log_write(0, LOG_MAIN|LOG_REJECT, "%s F=<%s> RCPT %s: " @@ -5063,15 +5063,15 @@ while (done <= 0) { uschar *code = US"503"; int len = Ustrlen(rcpt_smtp_response); - smtp_respond(code, 3, FALSE, US"All RCPT commands were rejected with " + smtp_respond(code, 3, SR_NOT_FINAL, US"All RCPT commands were rejected with " "this error:"); /* Responses from smtp_printf() will have \r\n on the end */ if (len > 2 && rcpt_smtp_response[len-2] == '\r') rcpt_smtp_response[len-2] = 0; - smtp_respond(code, 3, FALSE, rcpt_smtp_response); + smtp_respond(code, 3, SR_NOT_FINAL, rcpt_smtp_response); } if (f.smtp_in_pipelining_advertised && last_was_rcpt) - smtp_printf("503 Valid RCPT command must precede %s\r\n", FALSE, + smtp_printf("503 Valid RCPT command must precede %s\r\n", SP_NO_MORE, smtp_names[smtp_connection_had[SMTP_HBUFF_PREV(smtp_ch_index)]]); else done = synprot_error(L_smtp_protocol_error, 503, NULL, @@ -5091,7 +5091,7 @@ while (done <= 0) { sender_address = NULL; /* This will allow a new MAIL without RSET */ sender_address_unrewritten = NULL; - smtp_printf("554 Too many recipients\r\n", FALSE); + smtp_printf("554 Too many recipients\r\n", SP_NO_MORE); if (chunking_state > CHUNKING_OFFERED) { @@ -5132,7 +5132,7 @@ while (done <= 0) smtp_user_msg(US"354", user_msg); else smtp_printf( - "354 Enter message, ending with \".\" on a line by itself\r\n", FALSE); + "354 Enter message, ending with \".\" on a line by itself\r\n", SP_NO_MORE); } if (f.bdat_readers_wanted) @@ -5158,7 +5158,7 @@ while (done <= 0) if (!(address = parse_extract_address(smtp_cmd_data, &errmess, &start, &end, &recipient_domain, FALSE))) { - smtp_printf("501 %s\r\n", FALSE, errmess); + smtp_printf("501 %s\r\n", SP_NO_MORE, errmess); break; } @@ -5197,7 +5197,7 @@ while (done <= 0) break; } - smtp_printf("%s\r\n", FALSE, s); + smtp_printf("%s\r\n", SP_NO_MORE, s); } break; } @@ -5325,7 +5325,7 @@ while (done <= 0) if (rc == DEFER) { - smtp_printf("454 TLS currently unavailable\r\n", FALSE); + smtp_printf("454 TLS currently unavailable\r\n", SP_NO_MORE); break; } @@ -5358,15 +5358,15 @@ while (done <= 0) log_write(0, LOG_MAIN|LOG_PANIC, "ACL for QUIT returned ERROR: %s", log_msg); if (user_msg) - smtp_respond(US"221", 3, TRUE, user_msg); + smtp_respond(US"221", 3, SR_FINAL, user_msg); else - smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname); + smtp_printf("221 %s closing connection\r\n", SP_NO_MORE, smtp_active_hostname); log_close_event(US"by QUIT"); done = 2; break; default: - smtp_printf("554 Security failure\r\n", FALSE); + smtp_printf("554 Security failure\r\n", SP_NO_MORE); break; } tls_close(NULL, TLS_SHUTDOWN_NOWAIT); @@ -5394,7 +5394,7 @@ while (done <= 0) case NOOP_CMD: HAD(SCH_NOOP); - smtp_printf("250 OK\r\n", FALSE); + smtp_printf("250 OK\r\n", SP_NO_MORE); break; @@ -5405,23 +5405,23 @@ while (done <= 0) case HELP_CMD: HAD(SCH_HELP); - smtp_printf("214-Commands supported:\r\n214", TRUE); - smtp_printf(" AUTH", TRUE); + smtp_printf("214-Commands supported:\r\n214", SP_MORE); + smtp_printf(" AUTH", SP_MORE); #ifndef DISABLE_TLS if (tls_in.active.sock < 0 && verify_check_host(&tls_advertise_hosts) != FAIL) - smtp_printf(" STARTTLS", TRUE); + smtp_printf(" STARTTLS", SP_MORE); #endif - smtp_printf(" HELO EHLO MAIL RCPT DATA BDAT", TRUE); - smtp_printf(" NOOP QUIT RSET HELP", TRUE); - if (acl_smtp_etrn) smtp_printf(" ETRN", TRUE); - if (acl_smtp_expn) smtp_printf(" EXPN", TRUE); - if (acl_smtp_vrfy) smtp_printf(" VRFY", TRUE); + smtp_printf(" HELO EHLO MAIL RCPT DATA BDAT", SP_MORE); + smtp_printf(" NOOP QUIT RSET HELP", SP_MORE); + if (acl_smtp_etrn) smtp_printf(" ETRN", SP_MORE); + if (acl_smtp_expn) smtp_printf(" EXPN", SP_MORE); + if (acl_smtp_vrfy) smtp_printf(" VRFY", SP_MORE); #ifdef EXPERIMENTAL_XCLIENT if (proxy_session || verify_check_host(&hosts_xclient) != FAIL) - smtp_printf(" XCLIENT", TRUE); + smtp_printf(" XCLIENT", SP_MORE); #endif - smtp_printf("\r\n", FALSE); + smtp_printf("\r\n", SP_NO_MORE); break; @@ -5500,7 +5500,7 @@ while (done <= 0) { log_write(0, LOG_MAIN|LOG_PANIC, "failed to set up ETRN command: %s", error); - smtp_printf("458 Internal failure\r\n", FALSE); + smtp_printf("458 Internal failure\r\n", SP_NO_MORE); break; } } @@ -5531,7 +5531,7 @@ while (done <= 0) debug_printf("ETRN command is: %s\n", etrn_command); debug_printf("ETRN command execution skipped\n"); } - if (user_msg == NULL) smtp_printf("250 OK\r\n", FALSE); + if (user_msg == NULL) smtp_printf("250 OK\r\n", SP_NO_MORE); else smtp_user_msg(US"250", user_msg); break; } @@ -5542,7 +5542,7 @@ while (done <= 0) if (smtp_etrn_serialize && !enq_start(etrn_serialize_key, 1)) { - smtp_printf("458 Already processing %s\r\n", FALSE, smtp_cmd_data); + smtp_printf("458 Already processing %s\r\n", SP_NO_MORE, smtp_cmd_data); break; } @@ -5607,12 +5607,12 @@ while (done <= 0) { log_write(0, LOG_MAIN|LOG_PANIC, "fork of process for ETRN failed: %s", strerror(errno)); - smtp_printf("458 Unable to fork process\r\n", FALSE); + smtp_printf("458 Unable to fork process\r\n", SP_NO_MORE); if (smtp_etrn_serialize) enq_end(etrn_serialize_key); } else if (!user_msg) - smtp_printf("250 OK\r\n", FALSE); + smtp_printf("250 OK\r\n", SP_NO_MORE); else smtp_user_msg(US"250", user_msg); @@ -5632,7 +5632,7 @@ while (done <= 0) done = synprot_error(L_smtp_syntax_error, 0, NULL, /* Just logs */ US"NUL character(s) present (shown as '?')"); smtp_printf("501 NUL characters are not allowed in SMTP commands\r\n", - FALSE); + SP_NO_MORE); break; @@ -5669,7 +5669,7 @@ while (done <= 0) #ifdef SUPPORT_PROXY case PROXY_FAIL_IGNORE_CMD: - smtp_printf("503 Command refused, required Proxy negotiation failed\r\n", FALSE); + smtp_printf("503 Command refused, required Proxy negotiation failed\r\n", SP_NO_MORE); break; #endif diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index efa6004a3..a17597e8b 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -3000,7 +3000,7 @@ exim_gnutls_state_st * state = NULL; if (tls_in.active.sock >= 0) { tls_error(US"STARTTLS received after TLS started", US "", NULL, errstr); - smtp_printf("554 Already in TLS\r\n", FALSE); + smtp_printf("554 Already in TLS\r\n", SP_NO_MORE); return FAIL; } @@ -3079,7 +3079,7 @@ mode, the fflush() happens when smtp_getc() is called. */ if (!state->tlsp->on_connect) { - smtp_printf("220 TLS go ahead\r\n", FALSE); + smtp_printf("220 TLS go ahead\r\n", SP_NO_MORE); fflush(smtp_out); } diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index b02605eae..ef11de593 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -3504,7 +3504,7 @@ static uschar peerdn[256]; if (tls_in.active.sock >= 0) { tls_error(US"STARTTLS received after TLS started", NULL, US"", errstr); - smtp_printf("554 Already in TLS\r\n", FALSE); + smtp_printf("554 Already in TLS\r\n", SP_NO_MORE); return FAIL; } @@ -3624,7 +3624,7 @@ mode, the fflush() happens when smtp_getc() is called. */ SSL_set_session_id_context(ssl, sid_ctx, Ustrlen(sid_ctx)); if (!tls_in.on_connect) { - smtp_printf("220 TLS go ahead\r\n", FALSE); + smtp_printf("220 TLS go ahead\r\n", SP_NO_MORE); fflush(smtp_out); } commit 44e35f5f592b4fb993a6a23ad9e0446af8a6d527 Author: Jeremy Harris Date: Sun Nov 5 21:29:53 2023 +0000 DANE: handle servefail for TLSA during Rverify. Bug 3030 diff --git a/src/src/verify.c b/src/src/verify.c index d8ebf5925..6361aa434 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -677,7 +677,7 @@ coding means skipping this whole loop and doing the append separately. */ sx->conn_args.interface = interface; sx->helo_data = tf->helo_data; sx->conn_args.tblock = addr->transport; - sx->conn_args.sock = -1; + sx->cctx.sock = sx->conn_args.sock = -1; sx->verify = TRUE; tls_retry_connection: @@ -1152,7 +1152,7 @@ no_conn: /* 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 (sx->send_quit && sx->cctx.sock >= 0) 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); commit 37b849dca4dfd855212a763662825e967a4d77b1 Author: Jeremy Harris Date: Tue Nov 7 15:02:18 2023 +0000 OpenSSL: fix non-DANE build diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index ef11de593..851ac77c5 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -2605,7 +2605,7 @@ if (!(bs = OCSP_response_get1_basic(rsp))) asking for certificate-status under DANE, so this callback won't run for that combination. It still will for non-DANE. */ -#ifdef EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_SIGNER +#if defined(EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_SIGNER) && defined(SUPPORT_DANE) X509 * signer; if ( tls_out.dane_verified commit eb09d0da1fb995ad88f9a1c2998766c3e530be68 Author: Jeremy Harris Date: Tue Nov 7 19:38:22 2023 +0000 Fix use of empty log_reject_target. Bug 3039 Broken-by: 4243a209fd94 diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index c565d522d..e19c86ff8 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -2051,16 +2051,19 @@ else DEBUG(D_receive) static void log_connect_tls_drop(const uschar * what, const uschar * log_msg) { -gstring * g = s_tlslog(NULL); -uschar * tls = string_from_gstring(g); - -log_write(L_connection_reject, - log_reject_target, "%s%s%s dropped by %s%s%s", - LOGGING(dnssec) && sender_host_dnssec ? US" DS" : US"", - host_and_ident(TRUE), - tls ? tls : US"", - what, - log_msg ? US": " : US"", log_msg); +if (log_reject_target) + { + gstring * g = s_tlslog(NULL); + uschar * tls = string_from_gstring(g); + + log_write(L_connection_reject, + log_reject_target, "%s%s%s dropped by %s%s%s", + LOGGING(dnssec) && sender_host_dnssec ? US" DS" : US"", + host_and_ident(TRUE), + tls ? tls : US"", + what, + log_msg ? US": " : US"", log_msg); + } } @@ -3085,7 +3088,7 @@ else the connection is not forcibly to be dropped, return 0. Otherwise, log why it is closing if required and return 2. */ -if (log_reject_target != 0) +if (log_reject_target) { #ifndef DISABLE_TLS gstring * g = s_tlslog(NULL); commit caf57fe7eb5018b8df196e6d9f99586232798eb3 Author: Jeremy Harris Date: Wed Nov 8 14:22:37 2023 +0000 typoes diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 851ac77c5..237303ba9 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -77,9 +77,9 @@ change this guard and punt the issue for a while longer. */ # define EXIM_HAVE_OPENSSL_KEYLOG # define EXIM_HAVE_OPENSSL_CIPHER_GET_ID # define EXIM_HAVE_SESSION_TICKET -# define EXIM_HAVE_OPESSL_TRACE -# define EXIM_HAVE_OPESSL_GET0_SERIAL -# define EXIM_HAVE_OPESSL_OCSP_RESP_GET0_CERTS +# define EXIM_HAVE_OPENSSL_TRACE +# define EXIM_HAVE_OPENSSL_GET0_SERIAL +# define EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_CERTS # define EXIM_HAVE_SSL_GET0_VERIFIED_CHAIN # ifndef DISABLE_OCSP # define EXIM_HAVE_OCSP @@ -1756,7 +1756,7 @@ level. */ DEBUG(D_tls) { SSL_CTX_set_info_callback(ctx, info_callback); -#if defined(EXIM_HAVE_OPESSL_TRACE) && !defined(OPENSSL_NO_SSL_TRACE) +#if defined(EXIM_HAVE_OPENSSL_TRACE) && !defined(OPENSSL_NO_SSL_TRACE) /* this needs a debug build of OpenSSL */ SSL_CTX_set_msg_callback(ctx, SSL_trace); #endif @@ -2442,7 +2442,7 @@ tls_in.ocsp = OCSP_NOT_RESP; if (!olist) return SSL_TLSEXT_ERR_NOACK; -#ifdef EXIM_HAVE_OPESSL_GET0_SERIAL +#ifdef EXIM_HAVE_OPENSSL_GET0_SERIAL { const X509 * cert_sent = SSL_get_certificate(s); const ASN1_INTEGER * cert_serial = X509_get0_serialNumber(cert_sent); @@ -2646,7 +2646,7 @@ if (!(bs = OCSP_response_get1_basic(rsp))) debug_printf("certs contained in basicresp:\n"); x509_stack_dump_cert_s_names( -#ifdef EXIM_HAVE_OPESSL_OCSP_RESP_GET0_CERTS +#ifdef EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_CERTS OCSP_resp_get0_certs(bs) #else bs->certs commit 4f780c09d2ab95bd9562ebe307a01043933adcd9 Author: Jeremy Harris Date: Fri Nov 10 14:37:23 2023 +0000 fix doubled logging of message_id diff --git a/src/src/receive.c b/src/src/receive.c index 8190c5941..041898a6b 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -3380,8 +3380,8 @@ if (extract_recip && (bad_addresses || recipients_count == 0)) } } - log_write(0, LOG_MAIN|LOG_PANIC, "%s %s found in headers", - message_id, bad_addresses ? "bad addresses" : "no recipients"); + log_write(0, LOG_MAIN|LOG_PANIC, "%s found in headers", + bad_addresses ? "bad addresses" : "no recipients"); fseek(spool_data_file, (long int)spool_data_start_offset(message_id), SEEK_SET); commit 5d5ad9fb16a2511ff2e0e7d4528d399f06f608da Author: Jeremy Harris Date: Mon Nov 13 18:12:31 2023 +0000 TLS: fix resumption for TLS-on-connect diff --git a/src/src/macros.h b/src/src/macros.h index 969393561..e2c1d0f94 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -1103,14 +1103,22 @@ should not be one active. */ #define RESUME_USED BIT(4) #define RESUME_DECODE_STRING \ - US"not requested or offered : 0x02 :client requested, no server ticket" \ - ": 0x04 : 0x05 : 0x06 :client offered session, no server action" \ - ": 0x08 :no client request: 0x0A :client requested new ticket, server provided" \ - ": 0x0C :client offered session, not used: 0x0E :client offered session, server only provided new ticket" \ - ": 0x10 :session resumed unasked: 0x12 :session resumed unasked" \ - ": 0x14 : 0x15 : 0x16 :session resumed" \ - ": 0x18 :session resumed unasked: 0x1A :session resumed unasked" \ - ": 0x1C :session resumed: 0x1E :session resumed, also new ticket" + US"not requested or offered" \ + ": 0x02 :client requested, no server ticket" \ + ": 0x04 : 0x05 " \ + ": 0x06 :client offered session, no server action" \ + ": 0x08 :no client request" \ + ": 0x0A :client requested new ticket, server provided" \ + ": 0x0C :client offered session, not used" \ + ": 0x0E :client offered session, server only provided new ticket" \ + ": 0x10 :session resumed unasked" \ + ": 0x12 :session resumed unasked" \ + ": 0x14 : 0x15" \ + ": 0x16 :session resumed" \ + ": 0x18 :session resumed unasked" \ + ": 0x1A :session resumed unasked" \ + ": 0x1C :session resumed" \ + ": 0x1E :session resumed, also new ticket" /* Flags for string_vformat */ #define SVFMT_EXTEND BIT(0) diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index a17597e8b..56ea93935 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -2851,7 +2851,7 @@ static int tls_server_ticket_cb(gnutls_session_t sess, u_int htype, unsigned when, unsigned incoming, const gnutls_datum_t * msg) { -DEBUG(D_tls) debug_printf("newticket cb\n"); +DEBUG(D_tls) debug_printf("newticket cb (on server)\n"); tls_in.resumption |= RESUME_CLIENT_REQUESTED; return 0; } @@ -2888,9 +2888,12 @@ tls_server_resume_posthandshake(exim_gnutls_state_st * state) { if (gnutls_session_resumption_requested(state->session)) { - /* This tells us the client sent a full ticket. We use a + /* This tells us the client sent a full (?) ticket. We use a callback on session-ticket request, elsewhere, to tell - if a client asked for a ticket. */ + if a client asked for a ticket. + XXX As of GnuTLS 3.0.1 it seems to be returning true even for + a pure ticket-req (a zero-length Session Ticket extension + in the Client Hello, for 1.2) which mucks up our logic. */ tls_in.resumption |= RESUME_CLIENT_SUGGESTED; DEBUG(D_tls) debug_printf("client requested resumption\n"); @@ -3319,7 +3322,8 @@ tls_retrieve_session(tls_support * tlsp, gnutls_session_t session, tlsp->resumption = RESUME_SUPPORTED; if (!conn_args->have_lbserver) - { DEBUG(D_tls) debug_printf("resumption not supported on continued-connection\n"); } + { DEBUG(D_tls) debug_printf( + "resumption not supported: no LB detection done (continued-conn?)\n"); } else if (verify_check_given_host(CUSS &ob->tls_resumption_hosts, conn_args->host) == OK) { dbdata_tls_session * dt; @@ -3347,6 +3351,7 @@ else if (verify_check_given_host(CUSS &ob->tls_resumption_hosts, conn_args->host dbfn_close(dbm_file); } } +else DEBUG(D_tls) debug_printf("no resumption for this host\n"); } @@ -3374,7 +3379,7 @@ if (gnutls_session_get_flags(session) & GNUTLS_SFLAGS_SESSION_TICKET) int dlen = sizeof(dbdata_tls_session) + tkt.size; dbdata_tls_session * dt = store_get(dlen, GET_TAINTED); - DEBUG(D_tls) debug_printf("session data size %u\n", (unsigned)tkt.size); + DEBUG(D_tls) debug_printf(" session data size %u\n", (unsigned)tkt.size); memcpy(dt->session, tkt.data, tkt.size); gnutls_free(tkt.data); @@ -3385,11 +3390,15 @@ if (gnutls_session_get_flags(session) & GNUTLS_SFLAGS_SESSION_TICKET) dbfn_close(dbm_file); DEBUG(D_tls) - debug_printf("wrote session db (len %u)\n", (unsigned)dlen); + debug_printf(" wrote session db (len %u)\n", (unsigned)dlen); } } - else DEBUG(D_tls) - debug_printf("extract session data: %s\n", US gnutls_strerror(rc)); + else + { DEBUG(D_tls) + debug_printf(" extract session data: %s\n", US gnutls_strerror(rc)); + } + else DEBUG(D_tls) + debug_printf(" host not resmable; not saving ticket\n"); } } @@ -3406,7 +3415,7 @@ tls_client_ticket_cb(gnutls_session_t sess, u_int htype, unsigned when, exim_gnutls_state_st * state = gnutls_session_get_ptr(sess); tls_support * tlsp = state->tlsp; -DEBUG(D_tls) debug_printf("newticket cb\n"); +DEBUG(D_tls) debug_printf("newticket cb (on client)\n"); if (!tlsp->ticket_received) tls_save_session(tlsp, sess, state->host); diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index af2e1f2dd..8c00a1ef2 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -203,9 +203,6 @@ smtp_transport_options_block smtp_transport_option_defaults = { .tls_tempfail_tryclear = TRUE, .tls_try_verify_hosts = US"*", .tls_verify_cert_hostnames = US"*", -# ifndef DISABLE_TLS_RESUME - .host_name_extract = US"${if and {{match{$host}{.outlook.com\\$}} {match{$item}{\\N^250-([\\w.]+)\\s\\N}}} {$1}}", -# endif #endif #ifdef SUPPORT_I18N .utf8_downconvert = US"-1", @@ -352,7 +349,7 @@ Returns: nothing void smtp_transport_init(transport_instance *tblock) { -smtp_transport_options_block *ob = SOB tblock->options_block; +smtp_transport_options_block * ob = SOB tblock->options_block; int old_pool = store_pool; /* Retry_use_local_part defaults FALSE if unset */ @@ -769,7 +766,7 @@ return TRUE; resumption when such servers do not share a session-cache */ static void -ehlo_response_lbserver(smtp_context * sx, smtp_transport_options_block * ob) +ehlo_response_lbserver(smtp_context * sx, const uschar * name_extract) { #if !defined(DISABLE_TLS) && !defined(DISABLE_TLS_RESUME) const uschar * s; @@ -778,7 +775,7 @@ uschar * save_item = iterate_item; if (sx->conn_args.have_lbserver) return; iterate_item = sx->buffer; -s = expand_cstring(ob->host_name_extract); +s = expand_cstring(name_extract); iterate_item = save_item; sx->conn_args.host_lbserver = s && !*s ? NULL : s; sx->conn_args.have_lbserver = TRUE; @@ -1067,6 +1064,8 @@ sx->pending_EHLO = FALSE; if (pending_BANNER) { + const uschar * s; + DEBUG(D_transport) debug_printf("%s expect banner\n", __FUNCTION__); (*countp)--; if (!smtp_reap_banner(sx)) @@ -1076,7 +1075,10 @@ if (pending_BANNER) goto fail; } /*XXX EXPERIMENTAL_ESMTP_LIMITS ? */ - ehlo_response_lbserver(sx, sx->conn_args.ob); + + s = ((smtp_transport_options_block *)sx->conn_args.ob)->host_name_extract; + if (!s) s = HNE_DEFAULT; + ehlo_response_lbserver(sx, s); } if (pending_EHLO) @@ -2474,10 +2476,20 @@ goto SEND_QUIT; #ifndef DISABLE_TLS if (sx->smtps) { + const uschar * s; + smtp_peer_options |= OPTION_TLS; suppress_tls = FALSE; ob->tls_tempfail_tryclear = FALSE; smtp_command = US"SSL-on-connect"; + + /* Having no EHLO response yet, cannot peek there for a servername to detect + an LB. Call this anyway, so that a dummy host_name_extract option value can + force resumption attempts. */ + + if (!(s = ob->host_name_extract)) s = US"never-LB"; + ehlo_response_lbserver(sx, s); + goto TLS_NEGOTIATE; } #endif @@ -2565,6 +2577,8 @@ goto SEND_QUIT; if (!sx->early_pipe_active) #endif { + const uschar * s; + sx->peer_offered = ehlo_response(sx->buffer, OPTION_TLS /* others checked later */ #ifndef DISABLE_PIPE_CONNECT @@ -2600,7 +2614,8 @@ goto SEND_QUIT; } } #endif - ehlo_response_lbserver(sx, ob); + if (!(s = ob->host_name_extract)) s = HNE_DEFAULT; + ehlo_response_lbserver(sx, s); } /* Set tls_offered if the response to EHLO specifies support for STARTTLS. */ diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h index cb1d72625..0d15b9626 100644 --- a/src/src/transports/smtp.h +++ b/src/src/transports/smtp.h @@ -109,6 +109,7 @@ typedef struct { uschar *tls_privatekey; uschar *tls_require_ciphers; # ifndef DISABLE_TLS_RESUME +# define HNE_DEFAULT US"${if and {{match{$host}{.outlook.com\\$}} {match{$item}{\\N^250-([\\w.]+)\\s\\N}}} {$1}}" uschar *host_name_extract; uschar *tls_resumption_hosts; # endif commit fba5586e6d47f55e024e97681c166e857c4f3d1c Author: Jeremy Harris Date: Tue Nov 14 14:08:36 2023 +0000 constification diff --git a/src/src/deliver.c b/src/src/deliver.c index fa624f9de..9d459167e 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -6388,7 +6388,7 @@ Returns: When the global variable mua_wrapper is FALSE: */ int -deliver_message(uschar * id, BOOL forced, BOOL give_up) +deliver_message(const uschar * id, BOOL forced, BOOL give_up) { int i, rc; int final_yield = DELIVER_ATTEMPTED_NORMAL; diff --git a/src/src/exim.c b/src/src/exim.c index 118fe5c7c..3cc2fa2fb 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -1537,7 +1537,7 @@ Returns: DOES NOT RETURN */ static void -exim_usage(uschar *progname) +exim_usage(const uschar * progname) { /* Handle specific program invocation variants */ @@ -1744,9 +1744,9 @@ Returns: EXIT_SUCCESS if terminated successfully */ int -main(int argc, char **cargv) +main(int argc, char ** cargv) { -uschar **argv = USS cargv; +const uschar ** argv = CUSS cargv; int arg_receive_timeout = -1; int arg_smtp_receive_timeout = -1; int arg_error_handling = error_handling; @@ -1795,18 +1795,18 @@ BOOL verify_address_mode = FALSE; BOOL verify_as_sender = FALSE; BOOL rcpt_verify_quota = FALSE; BOOL version_printed = FALSE; -uschar *alias_arg = NULL; -uschar *called_as = US""; -uschar *cmdline_syslog_name = NULL; -uschar *start_queue_run_id = NULL; -uschar *stop_queue_run_id = NULL; -uschar *expansion_test_message = NULL; +const uschar *alias_arg = NULL; +const uschar *called_as = US""; +const uschar *cmdline_syslog_name = NULL; +const uschar *start_queue_run_id = NULL; +const uschar *stop_queue_run_id = NULL; +const uschar *expansion_test_message = NULL; const uschar *ftest_domain = NULL; const uschar *ftest_localpart = NULL; const uschar *ftest_prefix = NULL; const uschar *ftest_suffix = NULL; uschar *log_oneline = NULL; -uschar *malware_test_file = NULL; +const uschar *malware_test_file = NULL; uschar *real_sender_address; uschar *originator_home = US"/"; size_t sz; @@ -2152,8 +2152,8 @@ on the second character (the one after '-'), to save some effort. */ for (i = 1; i < argc; i++) { BOOL badarg = FALSE; - uschar * arg = argv[i]; - uschar * argrest; + const uschar * arg = argv[i]; + const uschar * argrest; uschar switchchar; /* An argument not starting with '-' is the start of a recipients list; @@ -2347,7 +2347,7 @@ on the second character (the one after '-'), to save some effort. */ case 'I': if (Ustrlen(argrest) >= 1 && *argrest == ':') { - uschar *p = argrest+1; + const uschar * p = argrest+1; info_flag = CMDINFO_HELP; if (Ustrlen(p)) if (strcmpic(p, CUS"sieve") == 0) @@ -2674,9 +2674,9 @@ on the second character (the one after '-'), to save some effort. */ #else { int ptr = 0; - macro_item *m; + macro_item * m; uschar name[24]; - uschar *s = argrest; + const uschar * s = argrest; opt_D_used = TRUE; while (isspace(*s)) s++; @@ -2833,7 +2833,7 @@ on the second character (the one after '-'), to save some effort. */ *(sender_address = store_get(1, GET_UNTAINTED)) = '\0'; /* Ensure writeable memory */ else { - uschar * temp = argrest + Ustrlen(argrest) - 1; + const uschar * temp = argrest + Ustrlen(argrest) - 1; while (temp >= argrest && isspace(*temp)) temp--; if (temp >= argrest && *temp == '.') f_end_dot = TRUE; allow_domain_literals = TRUE; @@ -3272,7 +3272,7 @@ on the second character (the one after '-'), to save some effort. */ /* -oB: Set a connection message max value for remote deliveries */ case 'B': { - uschar * p = argrest; + const uschar * p = argrest; if (!*p) if (i+1 < argc && isdigit((argv[i+1][0]))) p = argv[++i]; @@ -4432,7 +4432,7 @@ if (bi_option) if (bi_command && *bi_command) { int i = 0; - uschar *argv[3]; + const uschar * argv[3]; argv[i++] = bi_command; /* nonexpanded option so assume untainted */ if (alias_arg) argv[i++] = alias_arg; argv[i++] = NULL; @@ -4761,7 +4761,7 @@ if (rcpt_verify_quota) exim_fail("exim: missing recipient for quota check\n"); else { - verify_quota(argv[recipients_arg]); + verify_quota(US argv[recipients_arg]); /*XXX we lose track of const here */ exim_exit(EXIT_SUCCESS); } @@ -5768,7 +5768,7 @@ for (BOOL more = TRUE; more; ) { int rcount = 0; int count = argc - recipients_arg; - uschar **list = argv + recipients_arg; + const uschar ** list = argv + recipients_arg; /* These options cannot be changed dynamically for non-SMTP messages */ diff --git a/src/src/filtertest.c b/src/src/filtertest.c index 2426f7feb..2af5f87c2 100644 --- a/src/src/filtertest.c +++ b/src/src/filtertest.c @@ -164,7 +164,7 @@ Returns: TRUE if no errors */ BOOL -filter_runtest(int fd, uschar *filename, BOOL is_system, BOOL dot_ended) +filter_runtest(int fd, const uschar * filename, BOOL is_system, BOOL dot_ended) { int rc, filter_type; BOOL yield; diff --git a/src/src/functions.h b/src/src/functions.h index 39119ca09..ba4fd215c 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -218,7 +218,7 @@ extern void delete_pid_file(void); extern void deliver_local(address_item *, BOOL); extern address_item *deliver_make_addr(uschar *, BOOL); extern void delivery_log(int, address_item *, int, uschar *); -extern int deliver_message(uschar *, BOOL, BOOL); +extern int deliver_message(const uschar *, BOOL, BOOL); extern void deliver_msglog(const char *, ...) PRINTF_FUNCTION(1,2); extern void deliver_set_expansions(address_item *); extern int deliver_split_address(address_item *); @@ -284,7 +284,7 @@ extern BOOL fd_ready(int, time_t); extern int filter_interpret(const uschar *, int, address_item **, uschar **); extern BOOL filter_personal(string_item *, BOOL); -extern BOOL filter_runtest(int, uschar *, BOOL, BOOL); +extern BOOL filter_runtest(int, const uschar *, BOOL, BOOL); extern BOOL filter_system_interpret(address_item **, uschar **); extern uschar * fn_hdrs_added(void); @@ -345,7 +345,7 @@ extern uschar *macros_expand(int, int *, BOOL *); extern void mainlog_close(void); #ifdef WITH_CONTENT_SCAN extern int malware(const uschar *, BOOL, int); -extern int malware_in_file(uschar *); +extern int malware_in_file(const uschar *); extern void malware_init(void); extern gstring * malware_show_supported(gstring *); #endif @@ -408,15 +408,15 @@ extern BOOL proxy_protocol_host(void); extern void proxy_protocol_setup(void); #endif -extern BOOL queue_action(uschar *, int, uschar **, int, int); +extern BOOL queue_action(const uschar *, int, const uschar **, int, int); extern void queue_check_only(void); extern unsigned queue_count(void); extern unsigned queue_count_cached(void); -extern void queue_list(int, uschar **, int); +extern void queue_list(int, const uschar **, int); #ifndef DISABLE_QUEUE_RAMP extern void queue_notify_daemon(const uschar * hostname); #endif -extern void queue_run(qrunner *, uschar *, uschar *, BOOL); +extern void queue_run(qrunner *, const uschar *, const uschar *, BOOL); extern int random_number(int); extern const uschar *rc_to_string(int); @@ -430,7 +430,7 @@ extern void readconf_driver_init(uschar *, driver_instance **, extern uschar *readconf_find_option(void *); extern void readconf_main(BOOL); extern void readconf_options_from_list(optionlist *, unsigned, const uschar *, uschar *); -extern BOOL readconf_print(const uschar *, uschar *, BOOL); +extern BOOL readconf_print(const uschar *, const uschar *, BOOL); extern uschar *readconf_printtime(int); extern const uschar *readconf_readname(uschar *, int, const uschar *); extern int readconf_readtime(const uschar *, int, BOOL); @@ -503,7 +503,7 @@ extern int sieve_interpret(const uschar *, int, const uschar *, const uschar *, const uschar *, const uschar *, address_item **, uschar **); extern void sigalrm_handler(int); -extern void single_queue_run(qrunner *, uschar *, uschar *); +extern void single_queue_run(qrunner *, const uschar *, const uschar *); extern int smtp_boundsock(smtp_connect_args *); extern void smtp_closedown(uschar *); extern void smtp_command_timeout_exit(void) NORETURN; @@ -544,12 +544,12 @@ extern int spam(const uschar **); extern FILE *spool_mbox(unsigned long *, const uschar *, uschar **); #endif extern void spool_clear_header_globals(void); -extern BOOL spool_move_message(uschar *, uschar *, uschar *, uschar *); -extern int spool_open_datafile(uschar *); +extern BOOL spool_move_message(const uschar *, const uschar *, const uschar *, const uschar *); +extern int spool_open_datafile(const uschar *); extern int spool_open_temp(uschar *); extern int spool_read_header(uschar *, BOOL, BOOL); extern uschar *spool_sender_from_msgid(const uschar *); -extern int spool_write_header(uschar *, int, uschar **); +extern int spool_write_header(const uschar *, int, uschar **); extern int stdin_getc(unsigned); extern int stdin_feof(void); extern int stdin_ferror(void); @@ -1144,7 +1144,7 @@ return string_sprintf("%s/%s/%s/%s", # endif static inline uschar * -spool_q_sname(const uschar * purpose, const uschar * q, uschar * subdir) +spool_q_sname(const uschar * purpose, const uschar * q, const uschar * subdir) { return string_sprintf("%s%s%s%s%s", q, *q ? "/" : "", @@ -1153,7 +1153,7 @@ return string_sprintf("%s%s%s%s%s", } static inline uschar * -spool_sname(const uschar * purpose, uschar * subdir) +spool_sname(const uschar * purpose, const uschar * subdir) { return spool_q_sname(purpose, queue_name, subdir); } diff --git a/src/src/globals.c b/src/src/globals.c index e94bef75c..46ca72f20 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -726,7 +726,7 @@ gid_t config_gid = CONFIGURE_GROUP; #else gid_t config_gid = 0; #endif -uschar *config_main_filelist = US CONFIGURE_FILE +const uschar * config_main_filelist = US CONFIGURE_FILE "\0<-----------Space to patch configure_filename->"; uschar *config_main_filename = NULL; uschar *config_main_directory = NULL; @@ -939,8 +939,8 @@ uschar *fake_response_text = US"Your message has been rejected but is " int filter_n[FILTER_VARIABLE_COUNT]; int filter_sn[FILTER_VARIABLE_COUNT]; int filter_test = FTEST_NONE; -uschar *filter_test_sfile = NULL; -uschar *filter_test_ufile = NULL; +const uschar * filter_test_sfile = NULL; +const uschar * filter_test_ufile = NULL; uschar *filter_thisaddress = NULL; int finduser_retries = 0; uid_t fixed_never_users[] = { FIXED_NEVER_USERS }; @@ -1185,7 +1185,7 @@ uschar *message_size_limit = US"50M"; int message_utf8_downconvert = 0; /* -1 ifneeded; 0 never; 1 always */ #endif uschar message_subdir[2] = { 0, 0 }; -uschar *message_reference = NULL; +const uschar *message_reference= NULL; /* MIME ACL expandables */ #ifdef WITH_CONTENT_SCAN @@ -1219,7 +1219,7 @@ uschar *originator_login = NULL; uschar *originator_name = NULL; uid_t originator_uid; uschar *override_local_interfaces = NULL; -uschar *override_pid_file_path = NULL; +const uschar *override_pid_file_path = NULL; BOOL panic_coredump = FALSE; pcre2_general_context * pcre_gen_ctx = NULL; @@ -1229,7 +1229,7 @@ pcre2_general_context * pcre_mlc_ctx = NULL; pcre2_compile_context * pcre_mlc_cmp_ctx = NULL; uschar *percent_hack_domains = NULL; -uschar *pid_file_path = US PID_FILE_PATH +const uschar *pid_file_path = US PID_FILE_PATH "\0<--------------Space to patch pid_file_path->"; #ifndef DISABLE_PIPE_CONNECT uschar *pipe_connect_advertise_hosts = US"*"; @@ -1485,7 +1485,7 @@ uschar *sending_ip_address = NULL; int sending_port = -1; SIGNAL_BOOL sigalrm_seen = FALSE; const uschar *sigalarm_setter = NULL; -uschar **sighup_argv = NULL; +const uschar **sighup_argv = NULL; int slow_lookup_log = 0; /* millisecs, zero disables */ int smtp_accept_count = 0; int smtp_accept_max = 20; @@ -1572,7 +1572,7 @@ int string_datestamp_type = -1; const uschar *submission_domain = NULL; const uschar *submission_name = NULL; int syslog_facility = LOG_MAIL; -uschar *syslog_processname = US"exim"; +const uschar *syslog_processname= US"exim"; uschar *system_filter = NULL; uschar *system_filter_directory_transport = NULL; diff --git a/src/src/globals.h b/src/src/globals.h index 6ec4e619d..9ec44487f 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -438,7 +438,7 @@ extern FILE *config_file; /* Configuration file */ extern const uschar *config_filename; /* Configuration file name */ extern gid_t config_gid; /* Additional group owner */ extern int config_lineno; /* Line number */ -extern uschar *config_main_filelist; /* List of possible config files */ +extern const uschar *config_main_filelist; /* List of possible config files */ extern uschar *config_main_filename; /* File name actually used */ extern uschar *config_main_directory; /* Directory where the main config file was found */ extern uid_t config_uid; /* Additional owner */ @@ -625,8 +625,8 @@ extern uschar *fake_response_text; /* User defined message for the above. De extern int filter_n[FILTER_VARIABLE_COUNT]; /* filter variables */ extern int filter_sn[FILTER_VARIABLE_COUNT]; /* variables set by system filter */ extern int filter_test; /* Filter test type */ -extern uschar *filter_test_sfile; /* System filter test file */ -extern uschar *filter_test_ufile; /* User filter test file */ +extern const uschar *filter_test_sfile;/* System filter test file */ +extern const uschar *filter_test_ufile;/* User filter test file */ extern uschar *filter_thisaddress; /* For address looping */ extern int finduser_retries; /* Retry count for getpwnam() */ extern uid_t fixed_never_users[]; /* Can't be overridden */ @@ -755,7 +755,7 @@ extern int message_utf8_downconvert; /* convert from utf8 */ const extern pcre2_code *regex_UTF8; /* For recognizing SMTPUTF8 settings */ #endif extern uschar message_subdir[]; /* Subdirectory for messages */ -extern uschar *message_reference; /* Reference for error messages */ +extern const uschar *message_reference;/* Reference for error messages */ /* MIME ACL expandables */ #ifdef WITH_CONTENT_SCAN @@ -798,7 +798,7 @@ extern uschar *originator_login; /* Login of same */ extern uschar *originator_name; /* Full name of same */ extern uid_t originator_uid; /* Uid of ditto */ extern uschar *override_local_interfaces; /* Value of -oX argument */ -extern uschar *override_pid_file_path; /* Value of -oP argument */ +extern const uschar *override_pid_file_path; /* Value of -oP argument */ extern BOOL panic_coredump; /* SEGV rather than exit, on LOG_PANIC_DIE */ extern pcre2_general_context * pcre_gen_ctx; /* pcre memory management */ @@ -808,7 +808,7 @@ extern pcre2_general_context * pcre_mlc_ctx; extern pcre2_compile_context * pcre_mlc_cmp_ctx; extern uschar *percent_hack_domains; /* Local domains for which '% operates */ -extern uschar *pid_file_path; /* For writing daemon pids */ +extern const uschar *pid_file_path; /* For writing daemon pids */ #ifndef DISABLE_PIPE_CONNECT extern uschar *pipe_connect_advertise_hosts; /* for banner/EHLO pipelining */ #endif @@ -970,7 +970,7 @@ extern uschar *sending_ip_address; /* Address of outgoing (SMTP) interface * extern int sending_port; /* Port of outgoing interface */ extern SIGNAL_BOOL sigalrm_seen; /* Flag for sigalrm_handler */ extern const uschar *sigalarm_setter; /* For debug, set to callpoint of alarm() */ -extern uschar **sighup_argv; /* Args for re-execing after SIGHUP */ +extern const uschar **sighup_argv; /* Args for re-execing after SIGHUP */ extern int slow_lookup_log; /* Log DNS lookups taking longer than N millisecs */ extern int smtp_accept_count; /* Count of connections */ extern BOOL smtp_accept_keepalive; /* Set keepalive on incoming */ @@ -1065,7 +1065,7 @@ extern const uschar *submission_name; /* User name set from ACL */ extern BOOL syslog_duplication; /* FALSE => no duplicate logging */ extern int syslog_facility; /* As defined by Syslog.h */ extern BOOL syslog_pid; /* TRUE if PID on syslogs */ -extern uschar *syslog_processname; /* 'ident' param to openlog() */ +extern const uschar *syslog_processname; /* 'ident' param to openlog() */ extern BOOL syslog_timestamp; /* TRUE if time on syslogs */ extern uschar *system_filter; /* Name of system filter file */ diff --git a/src/src/malware.c b/src/src/malware.c index 9455d4769..760278079 100644 --- a/src/src/malware.c +++ b/src/src/malware.c @@ -2238,7 +2238,7 @@ Returns: Exim message processing code (OK, FAIL, DEFER, ...) where true means malware was found (condition applies) */ int -malware_in_file(uschar *eml_filename) +malware_in_file(const uschar * eml_filename) { uschar message_id_buf[64]; int ret; diff --git a/src/src/queue.c b/src/src/queue.c index fa4fc0aec..cfe87e953 100644 --- a/src/src/queue.c +++ b/src/src/queue.c @@ -354,7 +354,7 @@ Returns: nothing */ void -queue_run(qrunner * q, uschar * start_id, uschar * stop_id, BOOL recurse) +queue_run(qrunner * q, const uschar * start_id, const uschar * stop_id, BOOL recurse) { BOOL force_delivery = q->queue_run_force || deliver_selectstring || deliver_selectstring_sender; @@ -813,7 +813,7 @@ if (!recurse) void -single_queue_run(qrunner * q, uschar * start_id, uschar * stop_id) +single_queue_run(qrunner * q, const uschar * start_id, const uschar * stop_id) { DEBUG(D_queue_run) debug_printf("Single queue run%s%s%s%s\n", start_id ? US" starting at " : US"", @@ -919,7 +919,7 @@ Returns: nothing */ void -queue_list(int option, uschar ** list, int count) +queue_list(int option, const uschar ** list, int count) { int subcount; int now = (int)time(NULL); @@ -1093,7 +1093,7 @@ Returns: FALSE if there was any problem */ BOOL -queue_action(uschar * id, int action, uschar ** argv, int argc, +queue_action(const uschar * id, int action, const uschar ** argv, int argc, int recipients_arg) { BOOL yield = TRUE; diff --git a/src/src/readconf.c b/src/src/readconf.c index e77458d68..bbcca87f7 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -2741,7 +2741,7 @@ Returns: Boolean success */ BOOL -readconf_print(const uschar *name, uschar *type, BOOL no_labels) +readconf_print(const uschar * name, const uschar * type, BOOL no_labels) { BOOL names_only = FALSE; optionlist *ol2 = NULL; @@ -3121,8 +3121,8 @@ readconf_main(BOOL nowarn) { int sep = 0; struct stat statbuf; -uschar *s, *filename; -const uschar *list = config_main_filelist; +uschar * s, * filename; +const uschar * list = config_main_filelist; /* Loop through the possible file names */ @@ -3459,7 +3459,7 @@ leading "log_". */ if (syslog_facility_str) { int i; - uschar *s = syslog_facility_str; + uschar * s = syslog_facility_str; if ((Ustrlen(syslog_facility_str) >= 4) && (strncmpic(syslog_facility_str, US"log_", 4) == 0)) @@ -3481,10 +3481,11 @@ if (syslog_facility_str) if (*pid_file_path) { - if (!(s = expand_string(pid_file_path))) + const uschar * t = expand_cstring(pid_file_path); + if (!t) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to expand pid_file_path " "\"%s\": %s", pid_file_path, expand_string_message); - pid_file_path = s; + pid_file_path = t; } /* Set default value of process_log_path */ diff --git a/src/src/spool_in.c b/src/src/spool_in.c index 737a01cdd..588effd2c 100644 --- a/src/src/spool_in.c +++ b/src/src/spool_in.c @@ -36,7 +36,7 @@ Side effect: message_subdir is set for the (possibly split) spool directory */ int -spool_open_datafile(uschar * id) +spool_open_datafile(const uschar * id) { struct stat statbuf; flock_t lock_data; diff --git a/src/src/spool_out.c b/src/src/spool_out.c index 9c5a683e3..f3373faed 100644 --- a/src/src/spool_out.c +++ b/src/src/spool_out.c @@ -152,7 +152,7 @@ Returns: the size of the header texts on success; */ int -spool_write_header(uschar * id, int where, uschar ** errmsg) +spool_write_header(const uschar * id, int where, uschar ** errmsg) { int fd, size_correction; FILE * fp; @@ -444,7 +444,7 @@ start-up time. Arguments: dir base directory name - dq destiinationqueue name + dq destination queue name subdir subdirectory name id message id suffix suffix to add to id @@ -457,8 +457,8 @@ Returns: TRUE if all went well */ static BOOL -make_link(uschar * dir, uschar * dq, uschar * subdir, uschar * id, uschar * suffix, - uschar * from, uschar * to, BOOL noentok) +make_link(const uschar * dir, const uschar * dq, const uschar * subdir, const uschar * id, + const uschar * suffix, const uschar * from, const uschar * to, BOOL noentok) { uschar * fname = spool_fname(string_sprintf("%s%s", from, dir), subdir, id, suffix); uschar * tname = spool_q_fname(string_sprintf("%s%s", to, dir), dq, subdir, id, suffix); @@ -494,8 +494,8 @@ Returns: TRUE if all went well */ static BOOL -break_link(uschar * dir, uschar * subdir, uschar * id, uschar * suffix, uschar * from, - BOOL noentok) +break_link(const uschar * dir, const uschar * subdir, const uschar * id, + const uschar * suffix, const uschar * from, BOOL noentok) { uschar * fname = spool_fname(string_sprintf("%s%s", from, dir), subdir, id, suffix); if (Uunlink(fname) < 0 && (!noentok || errno != ENOENT)) @@ -528,7 +528,8 @@ Returns: TRUE if all is well */ BOOL -spool_move_message(uschar * id, uschar * subdir, uschar * from, uschar * to) +spool_move_message(const uschar * id, const uschar * subdir, + const uschar * from, const uschar * to) { uschar * dest_qname = queue_name_dest ? queue_name_dest : queue_name; commit b8e078953178c757578f2e104d9d2c822ae0943c Author: Jeremy Harris Date: Tue Nov 14 12:10:36 2023 +0000 Check for missing commandline arg after options taking one. Bug 3049 diff --git a/src/src/exim.c b/src/src/exim.c index 3cc2fa2fb..14c057f40 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -847,7 +847,7 @@ exit(EXIT_FAILURE); /* fail if a length is too long */ static inline void -exim_len_fail_toolong(int itemlen, int maxlen, const char *description) +exim_len_fail_toolong(int itemlen, int maxlen, const char * description) { if (itemlen <= maxlen) return; @@ -858,8 +858,10 @@ exit(EXIT_FAILURE); /* only pass through the string item back to the caller if it's short enough */ static inline const uschar * -exim_str_fail_toolong(const uschar *item, int maxlen, const char *description) +exim_str_fail_toolong(const uschar * item, int maxlen, const char * description) { +if (!item) + exim_fail("exim: bad item for: %s\n", description); exim_len_fail_toolong(Ustrlen(item), maxlen, description); return item; } @@ -884,10 +886,10 @@ log_write(0, LOG_MAIN|LOG_PANIC, struct stat buf; if (0 == (fd < 0 ? stat(name, &buf) : fstat(fd, &buf))) -{ + { if (buf.st_uid == owner && buf.st_gid == group) return 0; log_write(0, LOG_MAIN|LOG_PANIC, "Wrong ownership on %s", name); -} + } else log_write(0, LOG_MAIN|LOG_PANIC, "Stat failed on %s: %s", name, strerror(errno)); #endif @@ -896,6 +898,18 @@ return -1; } +/* Bump the index for argv, checking for overflow, +and return the argument. */ + +static const uschar * +next_argv(const uschar ** argv, int * pi, int argc, const uschar * where) +{ +int i = *pi; +if (++i >= argc) exim_fail("exim: bad item for: %s\n", where); +return argv[*pi = i]; +} + + /************************************************* * Extract port from host address * *************************************************/ @@ -3151,7 +3165,8 @@ on the second character (the one after '-'), to save some effort. */ { msg_action = MSG_SETQUEUE; queue_name_dest = string_copy_taint( - exim_str_fail_toolong(argv[++i], EXIM_DRIVERNAME_MAX, "-MG"), + exim_str_fail_toolong(next_argv(argv, &i, argc, arg), + EXIM_DRIVERNAME_MAX, "-MG"), GET_TAINTED); } else if (Ustrcmp(argrest, "mad") == 0) msg_action = MSG_MARK_ALL_DELIVERED; @@ -3363,36 +3378,36 @@ on the second character (the one after '-'), to save some effort. */ if (Ustrcmp(argrest, "a") == 0) sender_host_address = string_copy_taint( - exim_str_fail_toolong(argv[++i], EXIM_IPADDR_MAX, "-oMa"), - GET_TAINTED); + exim_str_fail_toolong(next_argv(argv, &i, argc, arg), + EXIM_IPADDR_MAX, "-oMa"), GET_TAINTED); /* -oMaa: Set authenticator name */ else if (Ustrcmp(argrest, "aa") == 0) sender_host_authenticated = string_copy_taint( - exim_str_fail_toolong(argv[++i], EXIM_DRIVERNAME_MAX, "-oMaa"), - GET_TAINTED); + exim_str_fail_toolong(next_argv(argv, &i, argc, arg), + EXIM_DRIVERNAME_MAX, "-oMaa"), GET_TAINTED); /* -oMas: setting authenticated sender */ else if (Ustrcmp(argrest, "as") == 0) authenticated_sender = string_copy_taint( - exim_str_fail_toolong(argv[++i], EXIM_EMAILADDR_MAX, "-oMas"), - GET_TAINTED); + exim_str_fail_toolong(next_argv(argv, &i, argc, arg), + EXIM_EMAILADDR_MAX, "-oMas"), GET_TAINTED); /* -oMai: setting authenticated id */ else if (Ustrcmp(argrest, "ai") == 0) authenticated_id = string_copy_taint( - exim_str_fail_toolong(argv[++i], EXIM_EMAILADDR_MAX, "-oMai"), - GET_TAINTED); + exim_str_fail_toolong(next_argv(argv, &i, argc, arg), + EXIM_EMAILADDR_MAX, "-oMai"), GET_TAINTED); /* -oMi: Set incoming interface address */ else if (Ustrcmp(argrest, "i") == 0) interface_address = string_copy_taint( - exim_str_fail_toolong(argv[++i], EXIM_IPADDR_MAX, "-oMi"), - GET_TAINTED); + exim_str_fail_toolong(next_argv(argv, &i, argc, arg), + EXIM_IPADDR_MAX, "-oMi"), GET_TAINTED); /* -oMm: Message reference */ @@ -3402,7 +3417,7 @@ on the second character (the one after '-'), to save some effort. */ exim_fail("-oMm must be a valid message ID\n"); if (!f.trusted_config) exim_fail("-oMm must be called by a trusted user/config\n"); - message_reference = argv[++i]; + message_reference = next_argv(argv, &i, argc, arg); } /* -oMr: Received protocol */ @@ -3412,26 +3427,32 @@ on the second character (the one after '-'), to save some effort. */ if (received_protocol) exim_fail("received_protocol is set already\n"); else - received_protocol = string_copy_taint( - exim_str_fail_toolong(argv[++i], EXIM_DRIVERNAME_MAX, "-oMr"), - GET_TAINTED); + if (++i >= argc) badarg = TRUE; + else + received_protocol = string_copy_taint( + exim_str_fail_toolong(argv[i], EXIM_DRIVERNAME_MAX, "-oMr"), + GET_TAINTED); /* -oMs: Set sender host name */ else if (Ustrcmp(argrest, "s") == 0) - sender_host_name = string_copy_taint( - exim_str_fail_toolong(argv[++i], EXIM_HOSTNAME_MAX, "-oMs"), - GET_TAINTED); + if (++i >= argc) badarg = TRUE; + else + sender_host_name = string_copy_taint( + exim_str_fail_toolong(argv[i], EXIM_HOSTNAME_MAX, "-oMs"), + GET_TAINTED); /* -oMt: Set sender ident */ else if (Ustrcmp(argrest, "t") == 0) - { - sender_ident_set = TRUE; - sender_ident = string_copy_taint( - exim_str_fail_toolong(argv[++i], EXIM_IDENTUSER_MAX, "-oMt"), - GET_TAINTED); - } + if (++i >= argc) badarg = TRUE; + else + { + sender_ident_set = TRUE; + sender_ident = string_copy_taint( + exim_str_fail_toolong(argv[i], EXIM_IDENTUSER_MAX, "-oMt"), + GET_TAINTED); + } /* Else a bad argument */ @@ -3459,7 +3480,9 @@ on the second character (the one after '-'), to save some effort. */ exim_fail("exim: only uid=%d or uid=%d can use -oP and -oPX " "(uid=%d euid=%d | %d)\n", root_uid, exim_uid, getuid(), geteuid(), real_uid); - if (!*argrest) override_pid_file_path = argv[++i]; + if (!*argrest) + if (++i < argc) override_pid_file_path = argv[i]; + else badarg = TRUE; else if (Ustrcmp(argrest, "X") == 0) delete_pid_file(); else badarg = TRUE; break; @@ -3487,10 +3510,9 @@ on the second character (the one after '-'), to save some effort. */ /* Limits: Is there a real limit we want here? 1024 is very arbitrary. */ case 'X': - if (*argrest) badarg = TRUE; + if (*argrest || ++i >= argc) badarg = TRUE; else override_local_interfaces = string_copy_taint( - exim_str_fail_toolong(argv[++i], 1024, "-oX"), - GET_TAINTED); + exim_str_fail_toolong(argv[i], 1024, "-oX"), GET_TAINTED); break; /* -oY: Override creation of daemon notifier socket */ @@ -3528,7 +3550,7 @@ on the second character (the one after '-'), to save some effort. */ which sets the host protocol and host name */ if (!*argrest) - if (i+1 < argc) argrest = argv[++i]; else { badarg = TRUE; break; } + argrest = next_argv(argv, &i, argc, arg); if (*argrest) { @@ -3614,8 +3636,14 @@ on the second character (the one after '-'), to save some effort. */ else { - int intvl = readconf_readtime(*argrest ? argrest : argv[++i], 0, FALSE); - if (intvl <= 0) + int intvl; + const uschar * s; + + if (*argrest) s = argrest; + else if (++i < argc) { badarg = TRUE; break; } + else s = argv[i]; + + if ((intvl = readconf_readtime(s, 0, FALSE)) <= 0) exim_fail("exim: bad time value %s: abandoned\n", argv[i]); for (qrunner * qq = qrunners; qq; qq = qq->next) @@ -3706,8 +3734,8 @@ on the second character (the one after '-'), to save some effort. */ tested. Otherwise variability of clock ticks etc. cause problems. */ case 'T': - if (f.running_in_test_harness && Ustrcmp(argrest, "qt") == 0) - fudged_queue_times = string_copy_taint(argv[++i], GET_TAINTED); + if (f.running_in_test_harness && Ustrcmp(argrest, "qt") == 0 && ++i < argc) + fudged_queue_times = string_copy_taint(argv[i], GET_TAINTED); else badarg = TRUE; break; commit fa1c8faf169384bebaa8d172f491574c45ae2aa4 Author: Jeremy Harris Date: Wed Nov 15 02:14:02 2023 +0000 constification diff --git a/src/exim_monitor/em_globals.c b/src/exim_monitor/em_globals.c index cf9b1075e..3822746ba 100644 --- a/src/exim_monitor/em_globals.c +++ b/src/exim_monitor/em_globals.c @@ -204,7 +204,7 @@ recipient_item *recipients_list = NULL; int recipients_list_max = 0; BOOL running_in_test_harness=FALSE; -uschar *sender_address = NULL; +const uschar *sender_address = NULL; uschar *sender_fullhost = NULL; uschar *sender_helo_name = NULL; uschar *sender_host_address = NULL; diff --git a/src/exim_monitor/em_hdr.h b/src/exim_monitor/em_hdr.h index 61f390d2c..feffa6bfc 100644 --- a/src/exim_monitor/em_hdr.h +++ b/src/exim_monitor/em_hdr.h @@ -309,7 +309,7 @@ extern uschar *copystring(uschar *); extern void create_dialog(uschar *, uschar *); extern void create_stripchart(Widget, uschar *); extern void debug(char *, ...); -extern dest_item *find_dest(queue_item *, uschar *, int, BOOL); +extern dest_item *find_dest(queue_item *, const uschar *, int, BOOL); extern queue_item *find_queue(uschar *, int, int); extern void init(int, uschar **); extern void menu_create(Widget, XEvent *, String *, Cardinal *); diff --git a/src/exim_monitor/em_queue.c b/src/exim_monitor/em_queue.c index 4c3d3fd18..c7c61a652 100644 --- a/src/exim_monitor/em_queue.c +++ b/src/exim_monitor/em_queue.c @@ -66,18 +66,16 @@ address is lowercased to start with, unless it begins with "*", which it does for error messages. */ dest_item * -find_dest(queue_item *q, uschar *name, int action, BOOL caseless) +find_dest(queue_item * q, const uschar * name, int action, BOOL caseless) { -dest_item *dd; -dest_item **d = &(q->destinations); +dest_item * dd; +dest_item ** d = &q->destinations; -while (*d != NULL) +while (*d) { if ((caseless? strcmpic(name,(*d)->address) : Ustrcmp(name,(*d)->address)) == 0) { - dest_item *ddd; - if (action != dest_remove) return *d; dd = *d; *d = dd->next; @@ -85,14 +83,12 @@ while (*d != NULL) /* Unset any parent pointers that were to this address */ - for (ddd = q->destinations; ddd != NULL; ddd = ddd->next) - { + for (dest_item * ddd = q->destinations; ddd; ddd = ddd->next) if (ddd->parent == dd) ddd->parent = NULL; - } return NULL; } - d = &((*d)->next); + d = &(*d)->next; } if (action != dest_add) return NULL; @@ -207,8 +203,9 @@ if it's there. */ else { q->update_time = q->input_time = received_time.tv_sec; - if ((p = strstric(sender_address+1, qualify_domain, FALSE)) != NULL && - *(--p) == '@') *p = 0; + /* deconst ok; strstric is actually safe */ + if ((p = strstric(US sender_address+1, qualify_domain, FALSE)) != NULL && + *--p == '@') *p = 0; } /* If we didn't read the whole header successfully, generate an error @@ -279,10 +276,11 @@ been delivered, and removing visible names. */ if (recipients_list) for (i = 0; i < recipients_count; i++) { - uschar * r = recipients_list[i].address; + const uschar * r = recipients_list[i].address; if (tree_search(tree_nonrecipients, r) == NULL) { - if ((p = strstric(r+1, qualify_domain, FALSE)) != NULL && + /* deconst ok; strstric is actually safe */ + if ((p = strstric(US r+1, qualify_domain, FALSE)) != NULL && *(--p) == '@') *p = 0; (void)find_dest(q, r, dest_add, FALSE); } @@ -663,13 +661,14 @@ if (recipients_list) for (i = 0; i < recipients_count; i++) { uschar * pp; - uschar * r = recipients_list[i].address; + const uschar * r = recipients_list[i].address; tree_node * node; if (!(node = tree_search(tree_nonrecipients, r))) node = tree_search(tree_nonrecipients, string_copylc(r)); - if ((pp = strstric(r+1, qualify_domain, FALSE)) && *(--pp) == '@') + /* deconst ok; strstric is actually safe */ + if ((pp = strstric(US r+1, qualify_domain, FALSE)) && *(--pp) == '@') *pp = 0; if (!node) (void)find_dest(p, r, dest_add, FALSE); diff --git a/src/src/acl.c b/src/src/acl.c index cdf60bbb8..9223fcec8 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -1711,10 +1711,10 @@ BOOL no_details = FALSE; BOOL success_on_redirect = FALSE; BOOL quota = FALSE; int quota_pos_cache = QUOTA_POS_DEFAULT, quota_neg_cache = QUOTA_NEG_DEFAULT; -address_item *sender_vaddr = NULL; -uschar *verify_sender_address = NULL; -uschar *pm_mailfrom = NULL; -uschar *se_mailfrom = NULL; +address_item * sender_vaddr = NULL; +const uschar * verify_sender_address = NULL; +uschar * pm_mailfrom = NULL; +uschar * se_mailfrom = NULL; /* Some of the verify items have slash-separated options; some do not. Diagnose an error if options are given for items that don't expect them. @@ -4713,8 +4713,8 @@ Returns: OK access is granted by an ACCEPT verb int acl_where = ACL_WHERE_UNKNOWN; int -acl_check(int where, uschar *recipient, uschar *s, uschar **user_msgptr, - uschar **log_msgptr) +acl_check(int where, const uschar * recipient, uschar * s, + uschar ** user_msgptr, uschar ** log_msgptr) { int rc; address_item adb; diff --git a/src/src/deliver.c b/src/src/deliver.c index 9d459167e..e96733f73 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -27,7 +27,7 @@ typedef struct pardata { int transport_count; /* returned transport count value */ BOOL done; /* no more data needed */ uschar *msg; /* error message */ - uschar *return_path; /* return_path for these addresses */ + const uschar *return_path; /* return_path for these addresses */ } pardata; /* Values for the process_recipients variable */ @@ -77,7 +77,7 @@ static pardata *parlist = NULL; static struct pollfd *parpoll; static int return_count; static uschar *frozen_info = US""; -static uschar *used_return_path = NULL; +static const uschar * used_return_path = NULL; @@ -144,7 +144,7 @@ Returns: a pointer to an initialized address_item */ address_item * -deliver_make_addr(uschar *address, BOOL copy) +deliver_make_addr(const uschar * address, BOOL copy) { address_item * addr = store_get(sizeof(address_item), GET_UNTAINTED); *addr = address_defaults; @@ -575,7 +575,7 @@ Returns: TRUE or FALSE */ static BOOL -same_strings(uschar *one, uschar *two) +same_strings(const uschar * one, const uschar * two) { if (one == two) return TRUE; /* Includes the case where both NULL */ if (!one || !two) return FALSE; @@ -898,7 +898,7 @@ void msg_event_raise(const uschar * event, const address_item * addr) { const uschar * save_domain = deliver_domain; -uschar * save_local = deliver_localpart; +const uschar * save_local = deliver_localpart; const uschar * save_host = deliver_host; const uschar * save_address = deliver_host_address; const int save_port = deliver_host_port; @@ -950,13 +950,13 @@ router_name = transport_name = NULL; * Generate local part for logging * *************************************************/ -static uschar * -string_get_lpart_sub(const address_item * addr, uschar * s) +static const uschar * +string_get_lpart_sub(const address_item * addr, const uschar * s) { #ifdef SUPPORT_I18N if (testflag(addr, af_utf8_downcvt)) { - uschar * t = string_localpart_utf8_to_alabel(s, NULL); + const uschar * t = string_localpart_utf8_to_alabel(s, NULL); return t ? t : s; /* t is NULL on a failed conversion */ } #endif @@ -975,7 +975,7 @@ Returns: the new value of the buffer pointer static gstring * string_get_localpart(address_item * addr, gstring * yield) { -uschar * s; +const uschar * s; if (testflag(addr, af_include_affixes) && (s = addr->prefix)) yield = string_cat(yield, string_get_lpart_sub(addr, s)); @@ -5086,7 +5086,7 @@ Returns: OK int deliver_split_address(address_item * addr) { -uschar * address = addr->address; +const uschar * address = addr->address; uschar * domain; uschar * t; int len; @@ -5103,7 +5103,7 @@ where they are locally interpreted. [The new draft "821" is more explicit on this, Jan 1999.] We know the syntax is valid, so this can be done by simply removing quoting backslashes and any unquoted doublequotes. */ -t = addr->cc_local_part = store_get(len+1, address); +addr->cc_local_part = t = store_get(len+1, address); while(len-- > 0) { int c = *address++; @@ -5115,7 +5115,7 @@ while(len-- > 0) } else *t++ = c; } -*t = 0; +*t = '\0'; /* We do the percent hack only for those domains that are listed in percent_hack_domains. A loop is required, to copy with multiple %-hacks. */ @@ -5123,8 +5123,8 @@ percent_hack_domains. A loop is required, to copy with multiple %-hacks. */ if (percent_hack_domains) { int rc; - uschar *new_address = NULL; - uschar *local_part = addr->cc_local_part; + uschar * new_address = NULL; + const uschar * local_part = addr->cc_local_part; deliver_domain = addr->domain; /* set $domain */ @@ -5261,12 +5261,12 @@ Returns: TRUE if the address is not hidden */ static BOOL -print_address_information(address_item *addr, FILE *f, uschar *si, uschar *sc, - uschar *se) +print_address_information(address_item * addr, FILE * f, uschar * si, + uschar * sc, uschar * se) { BOOL yield = TRUE; -uschar *printed = US""; -address_item *ancestor = addr; +const uschar * printed = US""; +address_item * ancestor = addr; while (ancestor->parent) ancestor = ancestor->parent; fprintf(f, "%s", CS si); @@ -5281,8 +5281,8 @@ else if (!testflag(addr, af_pfr) || !addr->parent) else { - uschar *s = addr->address; - uschar *ss; + const uschar * s = addr->address; + const uschar * ss; if (addr->address[0] == '>') { ss = US"mail"; s++; } else if (addr->address[0] == '|') ss = US"pipe"; @@ -5296,7 +5296,7 @@ fprintf(f, "%s", CS string_printing(printed)); if (ancestor != addr) { - uschar *original = ancestor->onetime_parent; + const uschar * original = ancestor->onetime_parent; if (!original) original= ancestor->address; if (strcmpic(original, printed) != 0) fprintf(f, "%s(%sgenerated from %s)", sc, @@ -6973,7 +6973,7 @@ else if (system_filter && process_recipients != RECIP_FAIL_TIMEOUT) if (!p->transport) { - address_item *badp = p; + address_item * badp = p; p = p->next; if (!addr_last) addr_new = p; else addr_last->next = p; badp->local_part = badp->address; /* Needed for log line */ @@ -7114,9 +7114,10 @@ if (process_recipients != RECIP_IGNORE) #ifndef DISABLE_EVENT if (process_recipients != RECIP_ACCEPT && event_action) { - uschar * save_local = deliver_localpart; + const uschar * save_local = deliver_localpart; const uschar * save_domain = deliver_domain; - uschar * addr = new->address, * errmsg = NULL; + const uschar * addr = new->address; + uschar * errmsg = NULL; int start, end, dom; if (!parse_extract_address(addr, &errmsg, &start, &end, &dom, TRUE)) @@ -8365,7 +8366,7 @@ else if (addr_defer != (address_item *)(+1)) for (i = 0; i < recipients_count; i++) { - uschar *r = recipients_list[i].address; + const uschar * r = recipients_list[i].address; if (Ustrcmp(otaddr->onetime_parent, r) == 0) t = i; if (Ustrcmp(otaddr->address, r) == 0) break; } @@ -8393,7 +8394,7 @@ else if (addr_defer != (address_item *)(+1)) if (sender_address[0]) { - uschar * s = addr->prop.errors_address; + const uschar * s = addr->prop.errors_address; if (!s) s = sender_address; if (Ustrstr(recipients, s) == NULL) recipients = string_sprintf("%s%s%s", recipients, diff --git a/src/src/exim.c b/src/src/exim.c index 14c057f40..4473373b3 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -1379,17 +1379,15 @@ Argument: the local part Returns: the local part, quoted if necessary */ -uschar * -local_part_quote(uschar *lpart) +const uschar * +local_part_quote(const uschar * lpart) { BOOL needs_quote = FALSE; gstring * g; -for (uschar * t = lpart; !needs_quote && *t != 0; t++) - { +for (const uschar * t = lpart; !needs_quote && *t; t++) needs_quote = !isalnum(*t) && strchr("!#$%&'*+-/=?^_`{|}~", *t) == NULL && (*t != '.' || t == lpart || t[1] == 0); - } if (!needs_quote) return lpart; @@ -1397,8 +1395,8 @@ g = string_catn(NULL, US"\"", 1); for (;;) { - uschar *nq = US Ustrpbrk(lpart, "\\\""); - if (nq == NULL) + uschar * nq = US Ustrpbrk(lpart, "\\\""); + if (!nq) { g = string_cat(g, lpart); break; @@ -1809,20 +1807,20 @@ BOOL verify_address_mode = FALSE; BOOL verify_as_sender = FALSE; BOOL rcpt_verify_quota = FALSE; BOOL version_printed = FALSE; -const uschar *alias_arg = NULL; -const uschar *called_as = US""; -const uschar *cmdline_syslog_name = NULL; -const uschar *start_queue_run_id = NULL; -const uschar *stop_queue_run_id = NULL; -const uschar *expansion_test_message = NULL; -const uschar *ftest_domain = NULL; -const uschar *ftest_localpart = NULL; -const uschar *ftest_prefix = NULL; -const uschar *ftest_suffix = NULL; -uschar *log_oneline = NULL; -const uschar *malware_test_file = NULL; -uschar *real_sender_address; -uschar *originator_home = US"/"; +const uschar * alias_arg = NULL; +const uschar * called_as = US""; +const uschar * cmdline_syslog_name = NULL; +const uschar * start_queue_run_id = NULL; +const uschar * stop_queue_run_id = NULL; +const uschar * expansion_test_message = NULL; +const uschar * ftest_domain = NULL; +const uschar * ftest_localpart = NULL; +const uschar * ftest_prefix = NULL; +const uschar * ftest_suffix = NULL; +uschar * log_oneline = NULL; +const uschar * malware_test_file = NULL; +const uschar * real_sender_address; +uschar * originator_home = US"/"; size_t sz; struct passwd *pw; @@ -2844,7 +2842,11 @@ on the second character (the one after '-'), to save some effort. */ if (i+1 < argc) argrest = argv[++i]; else { badarg = TRUE; break; } (void) exim_str_fail_toolong(argrest, EXIM_DISPLAYMAIL_MAX, "-f"); if (!*argrest) - *(sender_address = store_get(1, GET_UNTAINTED)) = '\0'; /* Ensure writeable memory */ + { + uschar * s = store_get(1, GET_UNTAINTED); /* Ensure writeable memory */ + *s = '\0'; + sender_address = s; + } else { const uschar * temp = argrest + Ustrlen(argrest) - 1; diff --git a/src/src/expand.c b/src/src/expand.c index 40cc8d73a..9a88d38ca 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -1804,20 +1804,19 @@ return g; /* A recipients list is available only during system message filtering, during ACL processing after DATA, and while expanding pipe commands generated from a system filter, but not elsewhere. Note that this does -not check for comman in the elements, and uses comma-space as seperator - +not check for commas in the elements, and uses comma-space as seperator - so cannot be used as an exim list as-is. */ static uschar * fn_recipients(void) { -uschar * s; gstring * g = NULL; if (!f.enable_dollar_recipients) return NULL; for (int i = 0; i < recipients_count; i++) { - s = recipients_list[i].address; + const uschar * s = recipients_list[i].address; g = string_append2_listele_n(g, US", ", s, Ustrlen(s)); } gstring_release_unused(g); diff --git a/src/src/functions.h b/src/src/functions.h index ba4fd215c..74cd15424 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -100,7 +100,7 @@ extern int tlsa_lookup(const host_item *, dns_answer *, BOOL); /* Everything else... */ extern acl_block *acl_read(uschar *(*)(void), uschar **); -extern int acl_check(int, uschar *, uschar *, uschar **, uschar **); +extern int acl_check(int, const uschar *, uschar *, uschar **, uschar **); extern uschar *acl_current_verb(void); extern int acl_eval(int, uschar *, uschar **, uschar **); extern uschar *acl_standalone_setvar(const uschar *); @@ -216,7 +216,7 @@ extern void decode_bits(unsigned int *, size_t, int *, const uschar *, bit_table *, int, uschar *, int); extern void delete_pid_file(void); extern void deliver_local(address_item *, BOOL); -extern address_item *deliver_make_addr(uschar *, BOOL); +extern address_item *deliver_make_addr(const uschar *, BOOL); extern void delivery_log(int, address_item *, int, uschar *); extern int deliver_message(const uschar *, BOOL, BOOL); extern void deliver_msglog(const char *, ...) PRINTF_FUNCTION(1,2); @@ -335,7 +335,7 @@ extern int ip_streamsocket(const uschar *, uschar **, int, host_item *); extern int ipv6_nmtoa(int *, uschar *); -extern uschar *local_part_quote(uschar *); +extern const uschar *local_part_quote(const uschar *); extern int log_open_as_exim(const uschar * const); extern void log_close_all(void); @@ -375,11 +375,11 @@ extern ssize_t mime_decode_base64(FILE *, FILE *, uschar *); extern int mime_regex(const uschar **, BOOL); extern void mime_set_anomaly(int); #endif -extern uschar *moan_check_errorcopy(uschar *); +extern uschar *moan_check_errorcopy(const uschar *); extern BOOL moan_skipped_syntax_errors(uschar *, error_block *, uschar *, BOOL, uschar *); extern void moan_smtp_batch(uschar *, const char *, ...) PRINTF_FUNCTION(2,3); -extern BOOL moan_send_message(uschar *, int, error_block *eblock, +extern BOOL moan_send_message(const uschar *, int, error_block *eblock, header_line *, FILE *, uschar *); extern void moan_tell_someone(uschar *, address_item *, const uschar *, const char *, ...) PRINTF_FUNCTION(4,5); @@ -440,7 +440,7 @@ extern void readconf_save_config(const uschar *); extern void read_message_body(BOOL); extern void receive_bomb_out(uschar *, uschar *) NORETURN; extern BOOL receive_check_fs(int); -extern BOOL receive_check_set_sender(uschar *); +extern BOOL receive_check_set_sender(const uschar *); extern BOOL receive_msg(BOOL); extern int_eximarith_t receive_statvfs(BOOL, int *); extern void receive_swallow_smtp(void); @@ -636,7 +636,7 @@ extern BOOL transport_pass_socket(const uschar *, const uschar *, const uscha , unsigned, unsigned, unsigned #endif ); -extern uschar *transport_rcpt_address(address_item *, BOOL); +extern const uschar *transport_rcpt_address(address_item *, BOOL); extern BOOL transport_set_up_command(const uschar ***, const uschar *, unsigned, int, address_item *, const uschar *, uschar **); extern void transport_update_waiting(host_item *, uschar *); @@ -644,7 +644,7 @@ extern BOOL transport_write_block(transport_ctx *, uschar *, int, BOOL); extern void transport_write_reset(int); extern BOOL transport_write_string(int, const char *, ...); extern BOOL transport_headers_send(transport_ctx *, - BOOL (*)(transport_ctx *, uschar *, int)); + BOOL (*)(transport_ctx *, const uschar *, int)); extern gstring * transport_show_supported(gstring *); extern BOOL transport_write_message(transport_ctx *, int); extern void tree_add_duplicate(const uschar *, address_item *); @@ -679,7 +679,7 @@ extern int verify_check_notblind(BOOL); extern int verify_check_given_host(const uschar **, const host_item *); extern int verify_check_this_host(const uschar **, unsigned int *, const uschar*, const uschar *, const uschar **); -extern address_item *verify_checked_sender(uschar *); +extern address_item *verify_checked_sender(const uschar *); extern void verify_get_ident(int); extern void verify_quota(uschar *); extern int verify_quota_call(const uschar *, int, int, uschar **); @@ -687,7 +687,7 @@ extern BOOL verify_sender(int *, uschar **); extern BOOL verify_sender_preliminary(int *, uschar **); extern void version_init(void); -extern BOOL write_chunk(transport_ctx *, uschar *, int); +extern BOOL write_chunk(transport_ctx *, const uschar *, int); extern ssize_t write_to_fd_buf(int, const uschar *, size_t); extern uschar *wrap_header(const uschar *, unsigned, unsigned, const uschar *, unsigned); diff --git a/src/src/globals.c b/src/src/globals.c index 46ca72f20..bb5bd6b1a 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -610,8 +610,8 @@ address_item address_defaults = { } }; -uschar *address_file = NULL; -uschar *address_pipe = NULL; +const uschar *address_file = NULL; +const uschar *address_pipe = NULL; tree_node *addresslist_anchor = NULL; int addresslist_count = 0; gid_t *admin_groups = NULL; @@ -683,7 +683,7 @@ int body_linecount = 0; int body_zerocount = 0; uschar *bounce_message_file = NULL; uschar *bounce_message_text = NULL; -uschar *bounce_recipient = NULL; +const uschar *bounce_recipient = NULL; int bounce_return_linesize_limit = 998; int bounce_return_size_limit = 100*1024; uschar *bounce_sender_authentication = NULL; @@ -842,14 +842,14 @@ const uschar *deliver_host_address = NULL; int deliver_host_port = 0; uschar *deliver_in_buffer = NULL; ino_t deliver_inode = 0; -uschar *deliver_localpart = NULL; +const uschar *deliver_localpart= NULL; uschar *deliver_localpart_data = NULL; -uschar *deliver_localpart_orig = NULL; -uschar *deliver_localpart_parent = NULL; -uschar *deliver_localpart_prefix = NULL; -uschar *deliver_localpart_prefix_v = NULL; -uschar *deliver_localpart_suffix = NULL; -uschar *deliver_localpart_suffix_v = NULL; +const uschar *deliver_localpart_orig = NULL; +const uschar *deliver_localpart_parent = NULL; +const uschar *deliver_localpart_prefix = NULL; +const uschar *deliver_localpart_prefix_v = NULL; +const uschar *deliver_localpart_suffix = NULL; +const uschar *deliver_localpart_suffix_v = NULL; uschar *deliver_out_buffer = NULL; int deliver_queue_load_max = -1; address_item *deliver_recipients = NULL; @@ -1276,8 +1276,8 @@ tree_node *ratelimiters_cmd = NULL; tree_node *ratelimiters_conn = NULL; tree_node *ratelimiters_mail = NULL; uschar *raw_active_hostname = NULL; -uschar *raw_sender = NULL; -uschar **raw_recipients = NULL; +const uschar *raw_sender = NULL; +const uschar **raw_recipients = NULL; int raw_recipients_count = 0; int rcpt_count = 0; @@ -1289,7 +1289,7 @@ int receive_linecount = 0; int receive_messagecount = 0; int receive_timeout = 0; int received_count = 0; -uschar *received_for = NULL; +const uschar *received_for = NULL; /* This is the default text for Received headers generated by Exim. The date will be automatically added on the end. */ @@ -1348,7 +1348,7 @@ int retry_data_expire = 7*24*60*60; int retry_interval_max = 24*60*60; int retry_maximum_timeout = 0; /* set from retry config */ retry_config *retries = NULL; -uschar *return_path = NULL; +const uschar *return_path = NULL; int rewrite_existflags = 0; uschar *rfc1413_hosts = US"@[]"; int rfc1413_query_timeout = 0; @@ -1456,10 +1456,10 @@ int runrc = 0; uschar *search_error_message = NULL; uschar *self_hostname = NULL; -uschar *sender_address = NULL; +const uschar *sender_address = NULL; unsigned int sender_address_cache[(MAX_NAMED_LIST * 2)/32]; uschar *sender_address_data = NULL; -uschar *sender_address_unrewritten = NULL; +const uschar *sender_address_unrewritten = NULL; uschar *sender_data = NULL; unsigned int sender_domain_cache[(MAX_NAMED_LIST * 2)/32]; uschar *sender_fullhost = NULL; diff --git a/src/src/globals.h b/src/src/globals.h index 9ec44487f..dff3762a8 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -350,8 +350,8 @@ extern uschar *acl_wherecodes[]; /* Response codes for ACL fails */ extern uschar *acl_wherenames[]; /* Names for messages */ extern address_item *addr_duplicate; /* Duplicate address list */ extern address_item address_defaults; /* Default data for address item */ -extern uschar *address_file; /* Name of file when delivering to one */ -extern uschar *address_pipe; /* Pipe command when delivering to one */ +extern const uschar *address_file; /* Name of file when delivering to one */ +extern const uschar *address_pipe; /* Pipe command when delivering to one */ extern tree_node *addresslist_anchor; /* Tree of defined address lists */ extern int addresslist_count; /* Number defined */ extern gid_t *admin_groups; /* List of admin groups */ @@ -400,7 +400,7 @@ extern int bsmtp_transaction_linecount; /* Start of last transaction */ extern int body_8bitmime; /* sender declared BODY= ; 7=7BIT, 8=8BITMIME */ extern uschar *bounce_message_file; /* Template file */ extern uschar *bounce_message_text; /* One-liner */ -extern uschar *bounce_recipient; /* When writing an errmsg */ +extern const uschar *bounce_recipient; /* When writing an errmsg */ extern BOOL bounce_return_body; /* Include body in returned message */ extern int bounce_return_linesize_limit; /* Max line length in return */ extern BOOL bounce_return_message; /* Include message in bounce */ @@ -517,14 +517,14 @@ extern const uschar *deliver_host_address; /* Address for remote delivery filter extern int deliver_host_port; /* Address for remote delivery filter */ extern uschar *deliver_in_buffer; /* Buffer for copying file */ extern ino_t deliver_inode; /* Inode for appendfile */ -extern uschar *deliver_localpart; /* The local part for delivery */ +extern const uschar *deliver_localpart;/* The local part for delivery */ extern uschar *deliver_localpart_data; /* From local part lookup (de-tainted) */ -extern uschar *deliver_localpart_orig; /* The original local part for delivery */ -extern uschar *deliver_localpart_parent; /* The parent local part for delivery */ -extern uschar *deliver_localpart_prefix; /* The stripped prefix, if any */ -extern uschar *deliver_localpart_prefix_v; /* The stripped-prefix variable portion, if any */ -extern uschar *deliver_localpart_suffix; /* The stripped suffix, if any */ -extern uschar *deliver_localpart_suffix_v; /* The stripped-suffix variable portion, if any */ +extern const uschar *deliver_localpart_orig; /* The original local part for delivery */ +extern const uschar *deliver_localpart_parent; /* The parent local part for delivery */ +extern const uschar *deliver_localpart_prefix; /* The stripped prefix, if any */ +extern const uschar *deliver_localpart_prefix_v; /* The stripped-prefix variable portion, if any */ +extern const uschar *deliver_localpart_suffix; /* The stripped suffix, if any */ +extern const uschar *deliver_localpart_suffix_v; /* The stripped-suffix variable portion, if any */ extern uschar *deliver_out_buffer; /* Buffer for copying file */ extern int deliver_queue_load_max; /* Different value for queue running */ extern address_item *deliver_recipients; /* Current set of addresses */ @@ -872,8 +872,8 @@ extern tree_node *ratelimiters_cmd; /* Results of command ratelimit checks */ extern tree_node *ratelimiters_conn; /* Results of connection ratelimit checks */ extern tree_node *ratelimiters_mail; /* Results of per-mail ratelimit checks */ extern uschar *raw_active_hostname; /* Pre-expansion */ -extern uschar *raw_sender; /* Before rewriting */ -extern uschar **raw_recipients; /* Before rewriting */ +extern const uschar *raw_sender; /* Before rewriting */ +extern const uschar **raw_recipients; /* Before rewriting */ extern int raw_recipients_count; extern const uschar * rc_names[]; /* Mostly for debug output */ extern int rcpt_count; /* Count of RCPT commands in a message */ @@ -885,7 +885,7 @@ extern int receive_linecount; /* Mainly for BSMTP errors */ extern int receive_messagecount; /* Mainly for BSMTP errors */ extern int receive_timeout; /* For non-SMTP acceptance */ extern int received_count; /* Count of Received: headers */ -extern uschar *received_for; /* For "for" field */ +extern const uschar *received_for; /* For "for" field */ extern uschar *received_header_text; /* Definition of Received: header */ extern int received_headers_max; /* Max count of Received: headers */ extern struct timeval received_time; /* Time the message started to be received */ @@ -926,7 +926,7 @@ extern retry_config *retries; /* Chain of retry config information */ extern int retry_data_expire; /* When to expire retry data */ extern int retry_interval_max; /* Absolute maximum */ extern int retry_maximum_timeout; /* The maximum timeout */ -extern uschar *return_path; /* Return path for a message */ +extern const uschar *return_path; /* Return path for a message */ extern BOOL return_path_remove; /* Remove return-path headers */ extern int rewrite_existflags; /* Indicate which headers have rewrites */ extern uschar *rfc1413_hosts; /* RFC hosts */ @@ -947,7 +947,7 @@ extern uschar *search_error_message; /* Details of lookup problem */ extern uschar *self_hostname; /* Self host after routing->directors */ extern unsigned int sender_address_cache[(MAX_NAMED_LIST * 2)/32]; /* Cache bits for sender */ extern uschar *sender_address_data; /* address_data from sender verify */ -extern uschar *sender_address_unrewritten; /* Set if rewritten by verify */ +extern const uschar *sender_address_unrewritten; /* Set if rewritten by verify */ extern uschar *sender_data; /* lookup result for senders */ extern unsigned int sender_domain_cache[(MAX_NAMED_LIST * 2)/32]; /* Cache bits for sender domain */ extern uschar *sender_fullhost; /* Sender host name + address */ diff --git a/src/src/local_scan.h b/src/src/local_scan.h index 153b4cb3c..ed9f26d62 100644 --- a/src/src/local_scan.h +++ b/src/src/local_scan.h @@ -153,9 +153,9 @@ field is always NULL except for one_time aliases that had errors_to on the routers that generated them. */ typedef struct recipient_item { - uschar *address; /* the recipient address */ + const uschar *address; /* the recipient address */ int pno; /* parent number for "one_time" alias, or -1 */ - uschar *errors_to; /* the errors_to address or NULL */ + const uschar *errors_to; /* the errors_to address or NULL */ uschar *orcpt; /* DSN orcpt */ int dsn_flags; /* DSN flags */ #ifdef EXPERIMENTAL_BRIGHTMAIL @@ -181,7 +181,7 @@ extern uschar *message_id; /* Internal id of message being handled * extern uschar *received_protocol; /* Name of incoming protocol */ extern int recipients_count; /* Number of recipients */ extern recipient_item *recipients_list;/* List of recipient addresses */ -extern unsigned char *sender_address; /* Sender address */ +extern const unsigned char *sender_address; /* Sender address */ extern uschar *sender_host_address; /* IP address of sender, as chars */ extern uschar *sender_host_authenticated; /* Name of authentication mechanism */ extern uschar *sender_host_name; /* Host name from lookup */ @@ -207,8 +207,8 @@ extern int lss_match_domain(uschar *, uschar *); extern int lss_match_local_part(uschar *, uschar *, BOOL); extern int lss_match_address(uschar *, uschar *, BOOL); extern int lss_match_host(uschar *, uschar *, uschar *); -extern void receive_add_recipient(uschar *, int); -extern BOOL receive_remove_recipient(uschar *); +extern void receive_add_recipient(const uschar *, int); +extern BOOL receive_remove_recipient(const uschar *); extern uschar *rfc2047_decode(uschar *, BOOL, const uschar *, int, int *, uschar **); extern int smtp_fflush(void); diff --git a/src/src/log.c b/src/src/log.c index af6e8b01b..f85823520 100644 --- a/src/src/log.c +++ b/src/src/log.c @@ -1032,11 +1032,10 @@ if ( flags & LOG_RECIPIENTS && g->ptr < LOG_BUFFER_SIZE - 6 && raw_recipients_count > 0) { - int i; g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " for", NULL); - for (i = 0; i < raw_recipients_count; i++) + for (int i = 0; i < raw_recipients_count; i++) { - uschar * s = raw_recipients[i]; + const uschar * s = raw_recipients[i]; if (LOG_BUFFER_SIZE - g->ptr < Ustrlen(s) + 3) break; g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " %s", s); } diff --git a/src/src/moan.c b/src/src/moan.c index 25595816c..c2e38d4f7 100644 --- a/src/src/moan.c +++ b/src/src/moan.c @@ -158,8 +158,8 @@ Returns: TRUE if message successfully sent */ BOOL -moan_send_message(uschar *recipient, int ident, error_block *eblock, - header_line *headers, FILE *message_file, uschar *firstline) +moan_send_message(const uschar * recipient, int ident, error_block * eblock, + header_line * headers, FILE * message_file, uschar * firstline) { int written = 0; int fd; @@ -631,7 +631,7 @@ if (addr) fprintf(f, "\nThe following address(es) have yet to be delivered:\n"); for (; addr; addr = addr->next) { - uschar * parent = addr->parent ? addr->parent->address : NULL; + const uschar * parent = addr->parent ? addr->parent->address : NULL; fprintf(f, " %s", addr->address); if (parent) fprintf(f, " <%s>", parent); if (addr->basic_errno > 0) fprintf(f, ": %s", strerror(addr->basic_errno)); @@ -734,22 +734,23 @@ Returns: additional recipient list or NULL */ uschar * -moan_check_errorcopy(uschar *recipient) +moan_check_errorcopy(const uschar * recipient) { -uschar *item, *localpart, *domain; -const uschar *listptr = errors_copy; +uschar * item; +const uschar * localpart, * domain; +const uschar * listptr = errors_copy; uschar *yield = NULL; int sep = 0; int llen; -if (errors_copy == NULL) return NULL; +if (!errors_copy) return NULL; /* Set up pointer to the local part and domain, and compute the length of the local part. */ localpart = recipient; domain = Ustrrchr(recipient, '@'); -if (domain == NULL) return NULL; /* should not occur, but avoid crash */ +if (!domain) return NULL; /* should not occur, but avoid crash */ llen = domain++ - recipient; /* Scan through the configured items */ diff --git a/src/src/queue.c b/src/src/queue.c index cfe87e953..deedcde6f 100644 --- a/src/src/queue.c +++ b/src/src/queue.c @@ -585,7 +585,7 @@ for (int i = queue_run_in_order ? -1 : 0; else if ( deliver_selectstring_sender && !(f.deliver_selectstring_sender_regex ? regex_match(selectstring_regex_sender, sender_address, -1, NULL) - : (strstric(sender_address, deliver_selectstring_sender, FALSE) + : (strstric_c(sender_address, deliver_selectstring_sender, FALSE) != NULL) ) ) { @@ -601,10 +601,10 @@ for (int i = queue_run_in_order ? -1 : 0; int i; for (i = 0; i < recipients_count; i++) { - uschar *address = recipients_list[i].address; + const uschar * address = recipients_list[i].address; if ( (f.deliver_selectstring_regex ? regex_match(selectstring_regex, address, -1, NULL) - : (strstric(address, deliver_selectstring, FALSE) != NULL) + : (strstric_c(address, deliver_selectstring, FALSE) != NULL) ) && tree_search(tree_nonrecipients, address) == NULL ) @@ -1369,9 +1369,10 @@ switch(action) tree_search(tree_nonrecipients, recipients_list[i].address); if (!delivered) { - uschar * save_local = deliver_localpart; + const uschar * save_local = deliver_localpart; const uschar * save_domain = deliver_domain; - uschar * addr = recipients_list[i].address, * errmsg = NULL; + const uschar * addr = recipients_list[i].address; + uschar * errmsg = NULL; int start, end, dom; if (!parse_extract_address(addr, &errmsg, &start, &end, &dom, TRUE)) diff --git a/src/src/rda.c b/src/src/rda.c index 9c2aa5022..7d329c580 100644 --- a/src/src/rda.c +++ b/src/src/rda.c @@ -466,7 +466,7 @@ Returns: FALSE if data missing */ static BOOL -rda_read_string(int fd, uschar **sp) +rda_read_string(int fd, uschar ** sp) { int len; @@ -866,9 +866,9 @@ if (yield == FF_DELIVERED || yield == FF_NOTDELIVERED || for (;;) { int i, reply_options; - address_item *addr; - uschar *recipient; - uschar *expandn[EXPAND_MAXN + 2]; + address_item * addr; + uschar * recipient, * s; + uschar * expandn[EXPAND_MAXN + 2]; /* First string is the address; NULL => end of addresses */ @@ -885,10 +885,11 @@ if (yield == FF_DELIVERED || yield == FF_NOTDELIVERED || if ( read(fd, &addr->mode, sizeof(addr->mode)) != sizeof(addr->mode) || read(fd, &addr->flags, sizeof(addr->flags)) != sizeof(addr->flags) - || !rda_read_string(fd, &addr->prop.errors_address) + || !rda_read_string(fd, &s) || read(fd, &i, sizeof(i)) != sizeof(i) ) goto DISASTER; + addr->prop.errors_address = s; addr->prop.ignore_error = (i != 0); /* Next comes a possible setting for $thisaddress and any numerical diff --git a/src/src/receive.c b/src/src/receive.c index 041898a6b..a459061ef 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -137,9 +137,9 @@ Returns: TRUE for a trusted caller */ BOOL -receive_check_set_sender(uschar *newsender) +receive_check_set_sender(const uschar * newsender) { -uschar *qnewsender; +const uschar * qnewsender; if (f.trusted_caller) return TRUE; if (!newsender || !untrusted_set_sender) return FALSE; qnewsender = Ustrchr(newsender, '@') @@ -514,7 +514,7 @@ Returns: nothing */ void -receive_add_recipient(uschar * recipient, int pno) +receive_add_recipient(const uschar * recipient, int pno) { if (recipients_count >= recipients_list_max) { @@ -591,7 +591,7 @@ Returns: TRUE if it did remove something; FALSE otherwise */ BOOL -receive_remove_recipient(uschar *recipient) +receive_remove_recipient(const uschar * recipient) { DEBUG(D_receive) debug_printf("receive_remove_recipient(\"%s\") called\n", recipient); @@ -3600,7 +3600,7 @@ else /* Loop through recipients, responses must be in same order received */ for (unsigned int c = 0; recipients_count > c; c++) { - uschar * addr= recipients_list[c].address; + const uschar * addr = recipients_list[c].address; uschar * msg= US"PRDR R=<%s> %s"; uschar * code; DEBUG(D_receive) diff --git a/src/src/rewrite.c b/src/src/rewrite.c index ec5c2cffc..c0d173abf 100644 --- a/src/src/rewrite.c +++ b/src/src/rewrite.c @@ -119,9 +119,9 @@ for (rewrite_rule * rule = rewrite_rules; { int start, end, pdomain; int count = 0; - uschar *save_localpart; - const uschar *save_domain; - uschar *error, *new; + const uschar * save_localpart; + const uschar * save_domain; + uschar * error, * new; const uschar * newparsed; /* Come back here for a repeat after a successful rewrite. We do this diff --git a/src/src/route.c b/src/src/route.c index 82d51bc68..0bd87b837 100644 --- a/src/src/route.c +++ b/src/src/route.c @@ -859,12 +859,12 @@ Returns: OK if all the tests succeed */ static BOOL -check_router_conditions(router_instance *r, address_item *addr, int verify, - struct passwd **pw, uschar **perror) +check_router_conditions(router_instance * r, address_item * addr, int verify, + struct passwd ** pw, uschar ** perror) { int rc; -uschar *check_local_part; -unsigned int *localpart_cache; +uschar * check_local_part; +unsigned int * localpart_cache; /* Reset variables to hold a home directory and data from lookup of a domain or local part, and ensure search_find_defer is unset, in case there aren't any @@ -928,15 +928,12 @@ because that doesn't have the prefix or suffix stripped. A bit of massaging is required. Also, we only use the match cache for local parts that have not had a prefix or suffix stripped. */ +check_local_part = string_copy(addr->cc_local_part); if (!addr->prefix && !addr->suffix) - { localpart_cache = addr->localpart_cache; - check_local_part = addr->cc_local_part; - } else { localpart_cache = NULL; - check_local_part = string_copy(addr->cc_local_part); if (addr->prefix) check_local_part += Ustrlen(addr->prefix); if (addr->suffix) diff --git a/src/src/routers/accept.c b/src/src/routers/accept.c index 63c8c22e4..8b72bfac1 100644 --- a/src/src/routers/accept.c +++ b/src/src/routers/accept.c @@ -104,9 +104,9 @@ accept_router_options_block *ob = (accept_router_options_block *)(rblock->options_block); */ int rc; -uschar *errors_to; -uschar *remove_headers; -header_line *extra_headers; +const uschar * errors_to; +uschar * remove_headers; +header_line * extra_headers; DEBUG(D_route) debug_printf("%s router called for %s\n domain = %s\n", rblock->name, addr->address, addr->domain); diff --git a/src/src/routers/redirect.c b/src/src/routers/redirect.c index 6a17c2f8d..4c8e30563 100644 --- a/src/src/routers/redirect.c +++ b/src/src/routers/redirect.c @@ -306,9 +306,8 @@ redirect_router_options_block *ob = while (generated) { - address_item *parent; - address_item *next = generated; - uschar *errors_address = next->prop.errors_address; + address_item * next = generated, * parent; + const uschar * errors_address = next->prop.errors_address; generated = next->next; next->parent = addr; diff --git a/src/src/routers/rf_functions.h b/src/src/routers/rf_functions.h index 91ccfb132..40d4cde1c 100644 --- a/src/src/routers/rf_functions.h +++ b/src/src/routers/rf_functions.h @@ -15,7 +15,7 @@ extern void rf_add_generated(router_instance *, address_item **, extern void rf_change_domain(address_item *, const uschar *, BOOL, address_item **); extern uschar *rf_expand_data(address_item *, uschar *, int *); extern int rf_get_errors_address(address_item *, router_instance *, - int, uschar **); + int, const uschar **); extern int rf_get_munge_headers(address_item *, router_instance *, header_line **, uschar **); extern BOOL rf_get_transport(uschar *, transport_instance **, address_item *, diff --git a/src/src/routers/rf_get_errors_address.c b/src/src/routers/rf_get_errors_address.c index f70bdf25e..222d157d8 100644 --- a/src/src/routers/rf_get_errors_address.c +++ b/src/src/routers/rf_get_errors_address.c @@ -35,13 +35,13 @@ Returns: OK if no problem */ int -rf_get_errors_address(address_item *addr, router_instance *rblock, - int verify, uschar **errors_to) +rf_get_errors_address(address_item * addr, router_instance * rblock, + int verify, const uschar ** errors_to) { uschar *s; *errors_to = addr->prop.errors_address; -if (rblock->errors_to == NULL) return OK; +if (!rblock->errors_to) return OK; s = expand_string(rblock->errors_to); @@ -84,17 +84,14 @@ if (verify != v_none) else { BOOL save_address_test_mode = f.address_test_mode; - int save1 = 0; + const uschar * save_sender = sender_address; int i; const uschar ***p; const uschar *address_expansions_save[ADDRESS_EXPANSIONS_COUNT]; address_item *snew = deliver_make_addr(s, FALSE); if (sender_address) - { - save1 = sender_address[0]; - sender_address[0] = 0; - } + sender_address = US""; for (i = 0, p = address_expansions; *p;) address_expansions_save[i++] = **p++; @@ -124,7 +121,7 @@ else for (i = 0, p = address_expansions; *p; ) **p++ = address_expansions_save[i++]; - if (sender_address) sender_address[0] = save1; + sender_address = save_sender; } return OK; diff --git a/src/src/spf.c b/src/src/spf.c index e72051708..2e3f861eb 100644 --- a/src/src/spf.c +++ b/src/src/spf.c @@ -341,7 +341,8 @@ else for (int i = 0; i < SPF_response_messages(spf_response); i++) Return: OK/FAIL */ int -spf_process(const uschar **listptr, uschar *spf_envelope_sender, int action) +spf_process(const uschar ** listptr, const uschar * spf_envelope_sender, + int action) { int sep = 0; const uschar *list = *listptr; diff --git a/src/src/spf.h b/src/src/spf.h index 76c7522bd..afee742ed 100644 --- a/src/src/spf.h +++ b/src/src/spf.h @@ -29,7 +29,7 @@ typedef struct spf_result_id { gstring * spf_lib_version_report(gstring *); BOOL spf_init(void); BOOL spf_conn_init(uschar *, uschar *); -int spf_process(const uschar **, uschar *, int); +int spf_process(const uschar **, const uschar *, int); void spf_response_debug(SPF_response_t *); #define SPF_PROCESS_NORMAL 0 diff --git a/src/src/spool_in.c b/src/src/spool_in.c index 588effd2c..3e3b246ab 100644 --- a/src/src/spool_in.c +++ b/src/src/spool_in.c @@ -456,9 +456,12 @@ n = Ustrlen(big_buffer); if (n < 3 || big_buffer[0] != '<' || big_buffer[n-2] != '>') goto SPOOL_FORMAT_ERROR; -sender_address = store_get(n-2, GET_TAINTED); -Ustrncpy(sender_address, big_buffer+1, n-3); -sender_address[n-3] = 0; + { + uschar * s = store_get(n-2, GET_TAINTED); + Ustrncpy(s, big_buffer+1, n-3); + s[n-3] = '\0'; + sender_address = s; + } where = US"time"; if (Ufgets(big_buffer, big_buffer_size, fp) == NULL) goto SPOOL_READ_ERROR; diff --git a/src/src/structs.h b/src/src/structs.h index 214819482..209d657c6 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -526,7 +526,7 @@ typedef struct address_item_propagated { uschar *address_data; /* arbitrary data to keep with the address */ uschar *domain_data; /* from "domains" lookup */ uschar *localpart_data; /* from "local_parts" lookup */ - uschar *errors_address; /* where to send errors (NULL => sender) */ + const uschar *errors_address; /* where to send errors (NULL => sender) */ header_line *extra_headers; /* additional headers */ uschar *remove_headers; /* list of those to remove */ void *variables; /* router-vasriables */ @@ -561,15 +561,15 @@ typedef struct address_item { reply_item *reply; /* data for autoreply */ retry_item *retries; /* chain of retry information */ - uschar *address; /* address being delivered or routed */ + const uschar *address; /* address being delivered or routed */ uschar *unique; /* used for disambiguating */ - uschar *cc_local_part; /* caseful local part */ - uschar *lc_local_part; /* lowercased local part */ - uschar *local_part; /* points to cc or lc version */ - uschar *prefix; /* stripped prefix of local part */ - uschar *prefix_v; /* variable part of above */ - uschar *suffix; /* stripped suffix of local part */ - uschar *suffix_v; /* variable part of above */ + const uschar *cc_local_part; /* caseful local part */ + const uschar *lc_local_part; /* lowercased local part */ + const uschar *local_part; /* points to cc or lc version */ + const uschar *prefix; /* stripped prefix of local part */ + const uschar *prefix_v; /* variable part of above */ + const uschar *suffix; /* stripped suffix of local part */ + const uschar *suffix_v; /* variable part of above */ const uschar *domain; /* working domain (lower cased) */ uschar *address_retry_key; /* retry key including full address */ @@ -580,7 +580,7 @@ typedef struct address_item { uschar *message; /* error message */ uschar *user_message; /* error message that can be sent over SMTP or quoted in bounce message */ - uschar *onetime_parent; /* saved original parent for onetime */ + const uschar *onetime_parent; /* saved original parent for onetime */ uschar **pipe_expandn; /* numeric expansions for pipe from filter */ uschar *return_filename; /* name of return file */ uschar *self_hostname; /* after self=pass */ diff --git a/src/src/transport.c b/src/src/transport.c index b3b05c0a3..a0f097579 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -434,10 +434,10 @@ Returns: TRUE on success, FALSE on failure (with errno preserved) */ BOOL -write_chunk(transport_ctx * tctx, uschar *chunk, int len) +write_chunk(transport_ctx * tctx, const uschar * chunk, int len) { -uschar *start = chunk; -uschar *end = chunk + len; +const uschar * start = chunk; +const uschar * end = chunk + len; int mlen = DELIVER_OUT_BUFFER_SIZE - nl_escape_length - 2; /* The assumption is made that the check string will never stretch over move @@ -474,7 +474,7 @@ if (nl_partial_match >= 0) for possible escaping. The code for the non-NL route should be as fast as possible. */ -for (uschar * ptr = start; ptr < end; ptr++) +for (const uschar * ptr = start; ptr < end; ptr++) { int ch, len; @@ -580,7 +580,7 @@ Arguments: Returns: a string */ -uschar * +const uschar * transport_rcpt_address(address_item *addr, BOOL include_affixes) { uschar *at; @@ -704,7 +704,7 @@ Returns: TRUE on success; FALSE on failure. */ BOOL transport_headers_send(transport_ctx * tctx, - BOOL (*sendfn)(transport_ctx * tctx, uschar * s, int len)) + BOOL (*sendfn)(transport_ctx * tctx, const uschar * s, int len)) { const uschar * list; transport_instance * tblock = tctx ? tctx->tblock : NULL; @@ -2302,7 +2302,7 @@ if (flags & TSUC_EXPAND_ARGS) address_pipe_argv = store_get((address_pipe_max_args+1)*sizeof(uschar *), GET_UNTAINTED); /* +1 because addr->local_part[0] == '|' since af_force_command is set */ - s = expand_string(addr->local_part + 1); + s = expand_cstring(addr->local_part + 1); if (!s || !*s) { diff --git a/src/src/transports/appendfile.c b/src/src/transports/appendfile.c index 2c7bea11d..ec41ca035 100644 --- a/src/src/transports/appendfile.c +++ b/src/src/transports/appendfile.c @@ -502,7 +502,7 @@ Returns: nothing */ static void -notify_comsat(uschar *user, off_t offset) +notify_comsat(const uschar * user, off_t offset) { struct servent *sp; host_item host; diff --git a/src/src/transports/pipe.c b/src/src/transports/pipe.c index b9cc32df1..64c70e719 100644 --- a/src/src/transports/pipe.c +++ b/src/src/transports/pipe.c @@ -292,7 +292,7 @@ Returns: TRUE if all went well; otherwise an error will be */ static BOOL -set_up_direct_command(const uschar *** argvptr, uschar * cmd, +set_up_direct_command(const uschar *** argvptr, const uschar * cmd, BOOL expand_arguments, int expand_fail, address_item * addr, uschar * tname, pipe_transport_options_block * ob) { @@ -414,8 +414,8 @@ Returns: TRUE if all went well; otherwise an error will be */ static BOOL -set_up_shell_command(const uschar ***argvptr, uschar *cmd, - BOOL expand_arguments, int expand_fail, address_item *addr, uschar *tname) +set_up_shell_command(const uschar *** argvptr, const uschar * cmd, + BOOL expand_arguments, int expand_fail, address_item * addr, uschar * tname) { const uschar **argv; @@ -462,10 +462,10 @@ if (expand_arguments) } g = string_cat(g, q); - argv[2] = (cmd = string_from_gstring(g)) ? expand_string(cmd) : NULL; + argv[2] = (cmd = string_from_gstring(g)) ? expand_cstring(cmd) : NULL; } else - argv[2] = expand_string(cmd); + argv[2] = expand_cstring(cmd); f.enable_dollar_recipients = FALSE; @@ -518,11 +518,12 @@ pipe_transport_options_block *ob = int timeout = ob->timeout; BOOL written_ok = FALSE; BOOL expand_arguments; -const uschar **argv; -uschar *envp[50]; -const uschar *envlist = ob->environment; -uschar *cmd, *ss; -uschar *eol = ob->use_crlf ? US"\r\n" : US"\n"; +const uschar ** argv; +uschar * envp[50]; +const uschar * envlist = ob->environment; +const uschar * cmd; +uschar * ss; +uschar * eol = ob->use_crlf ? US"\r\n" : US"\n"; transport_ctx tctx = { .tblock = tblock, .addr = addr, diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 8c00a1ef2..c53e1000f 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -656,8 +656,7 @@ static void deferred_event_raise(address_item * addr, host_item * host, uschar * evstr) { uschar * action = addr->transport->event_action; -const uschar * save_domain; -uschar * save_local; +const uschar * save_domain, * save_local; if (!action) return; @@ -1793,7 +1792,7 @@ return FALSE; typedef struct smtp_compare_s { - uschar * current_sender_address; + const uschar * current_sender_address; struct transport_instance * tblock; } smtp_compare_t; @@ -1803,7 +1802,7 @@ sender_address, helo_data and tls_certificate if enabled. */ static uschar * -smtp_local_identity(uschar * sender, struct transport_instance * tblock) +smtp_local_identity(const uschar * sender, struct transport_instance * tblock) { address_item * addr1; uschar * if1 = US""; @@ -1811,7 +1810,7 @@ uschar * helo1 = US""; #ifndef DISABLE_TLS uschar * tlsc1 = US""; #endif -uschar * save_sender_address = sender_address; +const uschar * save_sender_address = sender_address; uschar * local_identity = NULL; smtp_transport_options_block * ob = SOB tblock->options_block; @@ -3434,7 +3433,7 @@ buffer. */ sx->pending_MAIL = TRUE; /* The block starts with MAIL */ { - uschar * s = sx->from_addr; + const uschar * s = sx->from_addr; #ifdef SUPPORT_I18N uschar * errstr = NULL; @@ -3505,7 +3504,7 @@ for (addr = sx->first_addr, address_count = 0, pipe_limit = 100; { int cmds_sent; BOOL no_flush; - uschar * rcpt_addr; + const uschar * rcpt_addr; #ifdef EXPERIMENTAL_ESMTP_LIMITS if ( sx->single_rcpt_domain /* restriction on domains */ diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h index 0d15b9626..1f888ee0b 100644 --- a/src/src/transports/smtp.h +++ b/src/src/transports/smtp.h @@ -140,7 +140,7 @@ typedef struct { /* smtp connect context */ typedef struct { - uschar * from_addr; + const uschar * from_addr; address_item * addrlist; smtp_connect_args conn_args; diff --git a/src/src/verify.c b/src/src/verify.c index 6361aa434..bc3666285 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -105,8 +105,8 @@ Return: TRUE if result found */ static BOOL -cached_callout_lookup(address_item * addr, uschar * address_key, - uschar * from_address, int * opt_ptr, uschar ** pm_ptr, +cached_callout_lookup(address_item * addr, const uschar * address_key, + const uschar * from_address, int * opt_ptr, uschar ** pm_ptr, int * yield, uschar ** failure_ptr, dbdata_callout_cache * new_domain_record, int * old_domain_res) { @@ -278,10 +278,10 @@ return FALSE; */ static void cache_callout_write(dbdata_callout_cache * dom_rec, const uschar * domain, - int done, dbdata_callout_cache_address * addr_rec, uschar * address_key) + int done, dbdata_callout_cache_address * addr_rec, const uschar * address_key) { open_db dbblock; -open_db *dbm_file = NULL; +open_db * dbm_file = NULL; /* If we get here with done == TRUE, a successful callout happened, and yield will be set OK or FAIL according to the response to the RCPT command. @@ -502,11 +502,11 @@ do_callout(address_item *addr, host_item *host_list, transport_feedback *tf, int yield = OK; int old_domain_cache_result = ccache_accept; BOOL done = FALSE; -uschar *address_key; -uschar *from_address; -uschar *random_local_part = NULL; -const uschar *save_deliver_domain = deliver_domain; -uschar **failure_ptr = options & vopt_is_recipient +const uschar * address_key; +const uschar * from_address; +uschar * random_local_part = NULL; +const uschar * save_deliver_domain = deliver_domain; +uschar ** failure_ptr = options & vopt_is_recipient ? &recipient_verify_failure : &sender_verify_failure; dbdata_callout_cache new_domain_record; dbdata_callout_cache_address new_address_record; @@ -771,7 +771,7 @@ tls_retry_connection: if (random_local_part) { - uschar * main_address = addr->address; + const uschar * main_address = addr->address; const uschar * rcpt_domain = addr->domain; #ifdef SUPPORT_I18N @@ -942,7 +942,7 @@ tls_retry_connection: if (done) { - uschar * main_address = addr->address; + const uschar * main_address = addr->address; /*XXX oops, affixes */ addr->address = string_sprintf("postmaster@%.1000s", addr->domain); @@ -1286,7 +1286,7 @@ return FALSE; static BOOL -_cutthrough_puts(uschar * cp, int n) +_cutthrough_puts(const uschar * cp, int n) { while(n--) { @@ -1301,7 +1301,7 @@ return TRUE; /* Buffered output of counted data block. Return boolean success */ static BOOL -cutthrough_puts(uschar * cp, int n) +cutthrough_puts(const uschar * cp, int n) { if (cutthrough.cctx.sock < 0) return TRUE; if (_cutthrough_puts(cp, n)) return TRUE; @@ -1407,9 +1407,9 @@ return cutthrough_response(&cutthrough.cctx, '3', NULL, CUTTHROUGH_DATA_TIMEOUT) /* tctx arg only to match write_chunk() */ static BOOL -cutthrough_write_chunk(transport_ctx * tctx, uschar * s, int len) +cutthrough_write_chunk(transport_ctx * tctx, const uschar * s, int len) { -uschar * s2; +const uschar * s2; while(s && (s2 = Ustrchr(s, '\n'))) { if(!cutthrough_puts(s, s2-s) || !cutthrough_put_nl()) @@ -1697,16 +1697,16 @@ int yield = OK; int verify_type = expn ? v_expn : f.address_test_mode ? v_none : options & vopt_is_recipient ? v_recipient : v_sender; -address_item *addr_list; -address_item *addr_new = NULL; -address_item *addr_remote = NULL; -address_item *addr_local = NULL; -address_item *addr_succeed = NULL; -uschar **failure_ptr = options & vopt_is_recipient +address_item * addr_list; +address_item * addr_new = NULL; +address_item * addr_remote = NULL; +address_item * addr_local = NULL; +address_item * addr_succeed = NULL; +uschar ** failure_ptr = options & vopt_is_recipient ? &recipient_verify_failure : &sender_verify_failure; -uschar *ko_prefix, *cr; -uschar *address = vaddr->address; -uschar *save_sender; +uschar * ko_prefix, * cr; +const uschar * address = vaddr->address; +const uschar * save_sender; uschar null_sender[] = { 0 }; /* Ensure writeable memory */ /* Clear, just in case */ @@ -1751,9 +1751,8 @@ may have been set by domains and local part tests during an ACL. */ if (global_rewrite_rules) { - uschar *old = address; - /* deconst ok as address was not const */ - address = US rewrite_address(address, options & vopt_is_recipient, FALSE, + const uschar * old = address; + address = rewrite_address(address, options & vopt_is_recipient, FALSE, global_rewrite_rules, rewrite_existflags); if (address != old) { @@ -1919,8 +1918,8 @@ while (addr_new) if (tf.hosts && (!host_list || tf.hosts_override)) { uschar *s; - const uschar *save_deliver_domain = deliver_domain; - uschar *save_deliver_localpart = deliver_localpart; + const uschar * save_deliver_domain = deliver_domain; + const uschar * save_deliver_localpart = deliver_localpart; host_list = NULL; /* Ignore the router's hosts */ @@ -2455,11 +2454,11 @@ verify_check_notblind(BOOL case_sensitive) for (int i = 0; i < recipients_count; i++) { BOOL found = FALSE; - uschar *address = recipients_list[i].address; + const uschar * address = recipients_list[i].address; for (header_line * h = header_list; !found && h; h = h->next) { - uschar *colon, *s; + uschar * colon, * s; if (h->type != htype_to && h->type != htype_cc) continue; @@ -2533,7 +2532,7 @@ Returns: pointer to an address item, or NULL */ address_item * -verify_checked_sender(uschar *sender) +verify_checked_sender(const uschar * sender) { for (address_item * addr = sender_verified_list; addr; addr = addr->next) if (Ustrcmp(sender, addr->address) == 0) return addr; commit 9643095d7cae6866b716d361a45c9a95f605040c Author: Andrew Aitchison Date: Thu Nov 16 17:09:57 2023 +0000 Compiler quiteneing diff --git a/src/src/globals.h b/src/src/globals.h index dff3762a8..fe4a1610a 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -323,7 +323,7 @@ extern uschar *acl_smtp_connect; /* ACL run on SMTP connection */ extern uschar *acl_smtp_data; /* ACL run after DATA received */ #ifndef DISABLE_PRDR extern uschar *acl_smtp_data_prdr; /* ACL run after DATA received if in PRDR mode*/ -const extern pcre2_code *regex_PRDR; /* For recognizing PRDR settings */ +extern const pcre2_code *regex_PRDR; /* For recognizing PRDR settings */ #endif #ifndef DISABLE_DKIM extern uschar *acl_smtp_dkim; /* ACL run for DKIM signatures / domains */ @@ -752,7 +752,7 @@ extern uschar *message_size_limit; /* As it says */ #ifdef SUPPORT_I18N extern BOOL message_smtputf8; /* Internationalized mail handling */ extern int message_utf8_downconvert; /* convert from utf8 */ -const extern pcre2_code *regex_UTF8; /* For recognizing SMTPUTF8 settings */ +extern const pcre2_code *regex_UTF8; /* For recognizing SMTPUTF8 settings */ #endif extern uschar message_subdir[]; /* Subdirectory for messages */ extern const uschar *message_reference;/* Reference for error messages */ commit 79670d3c32ccb37fe06f25d8192943b58606a32a Author: Jeremy Harris Date: Fri Nov 17 16:55:17 2023 +0000 Lookups: Fix dnsdb lookup of multi-chunk TXT. Bug 3054 Broken=by: f6b1f8e7d642 diff --git a/src/src/dns.c b/src/src/dns.c index a652dcd31..74c5a58c5 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -431,7 +431,9 @@ namelen = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, dnss->aptr, if (namelen < 0) goto null_return; /* Move the pointer past the name and fill in the rest of the data structure -from the following bytes. */ +from the following bytes. We seem to be assuming here that the RR blob passed +to us by the resolver library is the same as that defined for an RR by RFC 1035 +section 3.2.1 */ TRACE trace = "R-name"; if (dnss_inc_aptr(dnsa, dnss, namelen)) goto null_return; diff --git a/src/src/lookups/dnsdb.c b/src/src/lookups/dnsdb.c index ff51dec23..8288896ca 100644 --- a/src/src/lookups/dnsdb.c +++ b/src/src/lookups/dnsdb.c @@ -387,38 +387,31 @@ while ((domain = string_nextinlist(&keystring, &sep, NULL, 0))) } /* Other kinds of record just have one piece of data each, but there may be - several of them, of course. */ + several of them, of course. TXT & SPF can have data in multiple chunks. */ if (yield->ptr) yield = string_catn(yield, outsep, 1); if (type == T_TXT || type == T_SPF) - { - if (!outsep2) /* output only the first item of data */ + for (unsigned data_offset = 0; data_offset + 1 < rr->size; ) { - uschar n = (rr->data)[0]; - /* size byte + data bytes must not excced the RRs length */ - if (n + 1 <= rr->size) - yield = string_catn(yield, US (rr->data+1), n); + uschar chunk_len = (rr->data)[data_offset]; + int remain; + + if (outsep2 && *outsep2 && data_offset != 0) + yield = string_catn(yield, outsep2, 1); + + /* Apparently there are resolvers that do not check RRs before passing + them on, and glibc fails to do so. So every application must... + Check for chunk len exceeding RR */ + + remain = rr->size - ++data_offset; + if (chunk_len > remain) + chunk_len = remain; + yield = string_catn(yield, US ((rr->data) + data_offset), chunk_len); + data_offset += chunk_len; + + if (!outsep2) break; /* output only the first chunk of the RR */ } - else - for (unsigned data_offset = 0; data_offset < rr->size; ) - { - uschar chunk_len = (rr->data)[data_offset]; - int remain = rr->size - data_offset; - - /* Apparently there are resolvers that do not check RRs before passing - them on, and glibc fails to do so. So every application must... - Check for chunk len exceeding RR */ - - if (chunk_len > remain) - chunk_len = remain; - - if (*outsep2 && data_offset != 0) - yield = string_catn(yield, outsep2, 1); - yield = string_catn(yield, US ((rr->data) + ++data_offset), --chunk_len); - data_offset += chunk_len; - } - } else if (type == T_TLSA) if (rr->size < 3) continue; commit 1d19531abc45f656eff8f3dd5ba6986d919fee11 Author: Jeremy Harris Date: Sun Nov 19 11:31:45 2023 +0000 Support old-format message_id spoolfiles for mailq / -bp. Bug 3050 Broken-by: 46a36afae41f diff --git a/src/src/queue.c b/src/src/queue.c index deedcde6f..2c3d014f5 100644 --- a/src/src/queue.c +++ b/src/src/queue.c @@ -961,7 +961,9 @@ each time. */ if (option == QL_MSGID_ONLY) /* Print only the message IDs from the chain */ for (; qf; qf = qf->next) - fprintf(stdout, "%.*s\n", MESSAGE_ID_LENGTH, qf->text); + fprintf(stdout, "%.*s\n", + is_old_message_id(qf->text) ? MESSAGE_ID_LENGTH_OLD : MESSAGE_ID_LENGTH, + qf->text); else for (; qf && (reset_point = store_mark()); @@ -1021,7 +1023,9 @@ else for (; } fprintf(stdout, "%s %.*s", - string_format_size(size, big_buffer), MESSAGE_ID_LENGTH, qf->text); + string_format_size(size, big_buffer), + is_old_message_id(qf->text) ? MESSAGE_ID_LENGTH_OLD : MESSAGE_ID_LENGTH, + qf->text); if (env_read && sender_address) { commit 8213f508a8c66e7ed8e00a9123f72763ff99010b Author: Jeremy Harris Date: Sat Nov 25 15:46:12 2023 +0000 DMARC: fix reporting, no-ARC-support case diff --git a/src/src/dmarc.c b/src/src/dmarc.c index 042ebe982..f684e08ea 100644 --- a/src/src/dmarc.c +++ b/src/src/dmarc.c @@ -311,7 +311,7 @@ g = string_fmt_append(g, "align_dkim %d\nalign_spf %d\naction %d\n", # ifdef EXPERIMENTAL_ARC g = arc_dmarc_hist_append(g); # else -g = string_fmt_append(g, "arc %d\narc_policy $d json:[]\n", +g = string_fmt_append(g, "arc %d\narc_policy %d json:[]\n", ARES_RESULT_UNKNOWN, DMARC_ARC_POLICY_RESULT_UNUSED); # endif #endif commit 33c9e43ec040c0a29394d8e3b745180ddddc0ed8 Author: Jeremy Harris Date: Sat Nov 25 16:47:35 2023 +0000 DMARC: fix reporting, ARC-support but not in message diff --git a/src/src/arc.c b/src/src/arc.c index defe9b171..bd2fa3bae 100644 --- a/src/src/arc.c +++ b/src/src/arc.c @@ -2011,7 +2011,7 @@ if (arc_state) g = string_catn(g, US" ]\n", 3); } else - g = string_fmt_append(g, "arc %d\narc_policy $d json:[]\n", + g = string_fmt_append(g, "arc %d\narc_policy %d json:[]\n", ARES_RESULT_UNKNOWN, DMARC_ARC_POLICY_RESULT_UNUSED); return g; } commit 5d0d124a8b49b484cda869bcb0d3e5224716d276 Author: Jeremy Harris Date: Sun Nov 26 20:28:07 2023 +0000 Unbreak DISABLE_TLS_RESUME build Broken-by: 5d5ad9fb16a2 diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index c53e1000f..4cf58436d 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -761,13 +761,14 @@ return TRUE; } +#if !defined(DISABLE_TLS) && !defined(DISABLE_TLS_RESUME) + /* Grab a string differentiating server behind a loadbalancer, for TLS resumption when such servers do not share a session-cache */ static void ehlo_response_lbserver(smtp_context * sx, const uschar * name_extract) { -#if !defined(DISABLE_TLS) && !defined(DISABLE_TLS_RESUME) const uschar * s; uschar * save_item = iterate_item; @@ -778,8 +779,8 @@ s = expand_cstring(name_extract); iterate_item = save_item; sx->conn_args.host_lbserver = s && !*s ? NULL : s; sx->conn_args.have_lbserver = TRUE; -#endif } +#endif @@ -1075,9 +1076,11 @@ if (pending_BANNER) } /*XXX EXPERIMENTAL_ESMTP_LIMITS ? */ +# ifndef DISABLE_TLS_RESUME s = ((smtp_transport_options_block *)sx->conn_args.ob)->host_name_extract; if (!s) s = HNE_DEFAULT; ehlo_response_lbserver(sx, s); +# endif } if (pending_EHLO) @@ -2482,13 +2485,14 @@ goto SEND_QUIT; ob->tls_tempfail_tryclear = FALSE; smtp_command = US"SSL-on-connect"; +# ifndef DISABLE_TLS_RESUME /* Having no EHLO response yet, cannot peek there for a servername to detect an LB. Call this anyway, so that a dummy host_name_extract option value can force resumption attempts. */ if (!(s = ob->host_name_extract)) s = US"never-LB"; ehlo_response_lbserver(sx, s); - +# endif goto TLS_NEGOTIATE; } #endif @@ -2613,8 +2617,10 @@ goto SEND_QUIT; } } #endif +#ifndef DISABLE_TLS_RESUME if (!(s = ob->host_name_extract)) s = HNE_DEFAULT; ehlo_response_lbserver(sx, s); +#endif } /* Set tls_offered if the response to EHLO specifies support for STARTTLS. */ commit 5bca25510e7313419c4ed0107ae153177bee938c Author: Jeremy Harris Date: Mon Nov 27 17:51:25 2023 +0000 Build: fix for Solaris 11 diff --git a/src/src/lookups/dnsdb.c b/src/src/lookups/dnsdb.c index 8288896ca..050baa3b7 100644 --- a/src/src/lookups/dnsdb.c +++ b/src/src/lookups/dnsdb.c @@ -445,20 +445,20 @@ while ((domain = string_nextinlist(&keystring, &sep, NULL, 0))) switch (type) { case T_MXH: - if (rr_bad_size(rr, sizeof(u_int16_t))) continue; + if (rr_bad_size(rr, sizeof(uint16_t))) continue; /* mxh ignores the priority number and includes only the hostnames */ GETSHORT(priority, p); break; case T_MX: - if (rr_bad_size(rr, sizeof(u_int16_t))) continue; + if (rr_bad_size(rr, sizeof(uint16_t))) continue; GETSHORT(priority, p); sprintf(CS s, "%d%c", priority, *outsep2); yield = string_cat(yield, s); break; case T_SRV: - if (rr_bad_size(rr, 3*sizeof(u_int16_t))) continue; + if (rr_bad_size(rr, 3*sizeof(uint16_t))) continue; GETSHORT(priority, p); GETSHORT(weight, p); GETSHORT(port, p); @@ -468,7 +468,7 @@ while ((domain = string_nextinlist(&keystring, &sep, NULL, 0))) break; case T_CSA: - if (rr_bad_size(rr, 3*sizeof(u_int16_t))) continue; + if (rr_bad_size(rr, 3*sizeof(uint16_t))) continue; /* See acl_verify_csa() for more comments about CSA. */ GETSHORT(priority, p); GETSHORT(weight, p); commit e4520c7817b76d6a5453237d94b1ed10fa6d2ef3 Author: Jeremy Harris Date: Fri Dec 1 15:44:57 2023 +0000 Avoid trying to send smtp repoonse for non-smtp input, on datafile close error Broken-by: f70940c9489d diff --git a/src/src/receive.c b/src/src/receive.c index a459061ef..c4b80c236 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -4425,8 +4425,8 @@ if (spool_data_file && cutthrough_done == NOT_TRIED) Uunlink(spool_fname(US"input", message_subdir, message_id, US"-H")); Uunlink(spool_fname(US"msglog", message_subdir, message_id, US"")); - /* Claim a data ACL temp-reject, just to get reject logging and resposponse */ - smtp_handle_acl_fail(ACL_WHERE_DATA, rc, NULL, log_msg); + /* Claim a data ACL temp-reject, just to get reject logging and response */ + if (smtp_input) smtp_handle_acl_fail(ACL_WHERE_DATA, rc, NULL, log_msg); smtp_reply = US""; /* Indicate reply already sent */ message_id[0] = 0; /* no message accepted */ commit 87229a18d1fbe959d38f82bde661b57fed11d220 Author: Jeremy Harris Date: Tue Dec 5 17:49:06 2023 +0000 Compiler quietening diff --git a/src/src/verify.c b/src/src/verify.c index bc3666285..d080ddd63 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -3005,6 +3005,8 @@ if ((semicolon = Ustrchr(ss, ';'))) endname = semicolon; opts = NULL; } +else + opts = NULL; /* If we are doing an IP address only match, then all lookups must be IP address lookups, even if there is no "net-". */ commit 2658a023286fcce529031e9f071c5579650687b8 Author: Jeremy Harris Date: Tue Dec 5 21:23:46 2023 +0000 DKIM: tighten up parsing for DKIM DNS and header records. Bug 3056 diff --git a/src/src/functions.h b/src/src/functions.h index 74cd15424..b201467b2 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -760,7 +760,7 @@ return US strncpy(CS dst, CCS src, n); /* Advance the string pointer given over any whitespace. -Return the next char as there's enought places using it to be useful. */ +Return the next char as there's enough places using it to be useful. */ #define Uskip_whitespace(sp) skip_whitespace(CUSS sp) diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index c723ae6c8..21e17c61e 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -512,10 +512,6 @@ for (uschar * p = raw_hdr; ; p++) } if (where == PDKIM_HDR_TAG) - { - if (c >= 'a' && c <= 'z') - cur_tag = string_catn(cur_tag, p, 1); - if (c == '=') { if (Ustrcmp(string_from_gstring(cur_tag), "b") == 0) @@ -526,7 +522,8 @@ for (uschar * p = raw_hdr; ; p++) where = PDKIM_HDR_VALUE; goto NEXT_CHAR; } - } + else if (!isspace(c)) + cur_tag = string_catn(cur_tag, p, 1); if (where == PDKIM_HDR_VALUE) { @@ -553,17 +550,17 @@ for (uschar * p = raw_hdr; ; p++) switch (cur_tag->s[1]) { case '\0': pdkim_decode_base64(cur_val->s, &sig->sighash); break; - case 'h': if (cur_tag->ptr == 2) - pdkim_decode_base64(cur_val->s, &sig->bodyhash); + case 'h': if (cur_tag->ptr != 2) goto bad_tag; + pdkim_decode_base64(cur_val->s, &sig->bodyhash); break; - default: break; + default: goto bad_tag; } break; case 'v': /* version */ /* We only support version 1, and that is currently the only version there is. */ sig->version = - Ustrcmp(cur_val->s, PDKIM_SIGNATURE_VERSION) == 0 ? 1 : -1; + Ustrcmp(cur_val->s, PDKIM_SIGNATURE_VERSION) == 0 ? 1 : -1; break; case 'a': /* algorithm */ { @@ -578,6 +575,7 @@ for (uschar * p = raw_hdr; ; p++) if (Ustrcmp(elem, pdkim_hashes[i].dkim_hashname) == 0) { sig->hashtype = i; break; } } + break; case 'c': /* canonicalization */ pdkim_cstring_to_canons(cur_val->s, 0, @@ -586,15 +584,15 @@ for (uschar * p = raw_hdr; ; p++) case 'q': /* Query method (for pubkey)*/ for (int i = 0; pdkim_querymethods[i]; i++) if (Ustrcmp(cur_val->s, pdkim_querymethods[i]) == 0) - { + { sig->querymethod = i; /* we never actually use this */ break; } break; case 's': /* Selector */ - sig->selector = string_copyn(cur_val->s, cur_val->ptr); break; + sig->selector = string_copy_from_gstring(cur_val); break; case 'd': /* SDID */ - sig->domain = string_copyn(cur_val->s, cur_val->ptr); break; + sig->domain = string_copy_from_gstring(cur_val); break; case 'i': /* AUID */ sig->identity = pdkim_decode_qp(cur_val->s); break; case 't': /* Timestamp */ @@ -604,16 +602,18 @@ for (uschar * p = raw_hdr; ; p++) case 'l': /* Body length count */ sig->bodylength = strtol(CS cur_val->s, NULL, 10); break; case 'h': /* signed header fields */ - sig->headernames = string_copyn(cur_val->s, cur_val->ptr); break; + sig->headernames = string_copy_from_gstring(cur_val); break; case 'z': /* Copied headfields */ sig->copiedheaders = pdkim_decode_qp(cur_val->s); break; /*XXX draft-ietf-dcrup-dkim-crypto-05 would need 'p' tag support for rsafp signatures. But later discussion is dropping those. */ default: - DEBUG(D_acl) debug_printf(" Unknown tag encountered\n"); - break; + goto bad_tag; } } + else +bad_tag: DEBUG(D_acl) debug_printf(" Unknown tag encountered: %Y\n", cur_tag); + cur_tag = cur_val = NULL; in_b_val = FALSE; where = PDKIM_HDR_LIMBO; @@ -659,25 +659,37 @@ return sig; /* -------------------------------------------------------------------------- */ pdkim_pubkey * -pdkim_parse_pubkey_record(const uschar *raw_record) +pdkim_parse_pubkey_record(const uschar * raw_record) { -const uschar * ele; -int sep = ';'; -pdkim_pubkey * pub; +pdkim_pubkey * pub = store_get(sizeof(pdkim_pubkey), GET_TAINTED); -pub = store_get(sizeof(pdkim_pubkey), GET_TAINTED); memset(pub, 0, sizeof(pdkim_pubkey)); -while ((ele = string_nextinlist(&raw_record, &sep, NULL, 0))) +for (const uschar * ele = raw_record, * tspec, * end, * val; *ele; ele = end) { - const uschar * val; + Uskip_whitespace(&ele); + end = US strchrnul(CS ele, ';'); + tspec = string_copyn(ele, end - ele); + if (*end) end++; /* skip the ; */ - if ((val = Ustrchr(ele, '='))) + if ((val = Ustrchr(tspec, '='))) { - int taglen = val++ - ele; + int taglen = val++ - tspec; + + DEBUG(D_acl) debug_printf(" %.*s=%s\n", taglen, tspec, val); + while (taglen > 1 && isspace(tspec[taglen-1])) + taglen--; /* Ignore whitespace before = */ + while (isspace(*val)) + val++; /* Ignore whitespace after = */ + if (isspace(val[ Ustrlen(val)-1 ])) + { /* Ignore whitespace after value */ + gstring * g = string_cat(NULL, val); + while (isspace(gstring_last_char(g))) + gstring_trim(g, 1); + val = string_from_gstring(g); + } - DEBUG(D_acl) debug_printf(" %.*s=%s\n", taglen, ele, val); - switch (ele[0]) + if (taglen == 1) switch (tspec[0]) { case 'v': pub->version = val; break; case 'h': pub->hashes = val; break; @@ -689,8 +701,11 @@ while ((ele = string_nextinlist(&raw_record, &sep, NULL, 0))) case 't': if (Ustrchr(val, 'y')) pub->testing = 1; if (Ustrchr(val, 's')) pub->no_subdomaining = 1; break; - default: DEBUG(D_acl) debug_printf(" Unknown tag encountered\n"); break; + default: goto bad_tag; } + else +bad_tag: + DEBUG(D_acl) debug_printf(" Unknown tag encountered\n"); } } commit 5930166b443390a522f210c2e60ec447a7dbd080 Author: Jeremy Harris Date: Wed Dec 6 12:25:14 2023 +0000 Supply strchrnul() for platforms apparently missing it Broken-by: 2658a023286f diff --git a/src/OS/os.h-FreeBSD b/src/OS/os.h-FreeBSD index 6756d42f6..11f8b1ae7 100644 --- a/src/OS/os.h-FreeBSD +++ b/src/OS/os.h-FreeBSD @@ -78,5 +78,7 @@ extern ssize_t os_sendfile(int, int, off_t *, size_t); /*******************/ #define EXIM_HAVE_KEVENT +#define EXIM_HAVE_STRCHRNUL + /* End */ diff --git a/src/OS/os.h-Linux b/src/OS/os.h-Linux index 25a12862b..cebda3367 100644 --- a/src/OS/os.h-Linux +++ b/src/OS/os.h-Linux @@ -102,4 +102,6 @@ then change the 0 to 1 in the next block. */ # define NS_MAXMSG 65535 #endif +#define EXIM_HAVE_STRCHRNUL + /* End */ diff --git a/src/src/os.c b/src/src/os.c index 5c52c6d4d..5e5899eeb 100644 --- a/src/src/os.c +++ b/src/src/os.c @@ -893,6 +893,21 @@ return buffer ? buffer : realloc(b, strlen(b) + 1); #endif /* ----------------------------------------------------------------------- */ +/*********************************************************** +* strchrnul() * +***********************************************************/ + +#if !defined(EXIM_HAVE_STRCHRNUL) +char * +strchrnul(const char * s, int c) +{ +while (*s != c && *s) s++ +return CS s; +} +#endif + + +/* ----------------------------------------------------------------------- */ commit b0e63c7efdc2133c61545b051042d3617ecd2bbd Author: Jeremy Harris Date: Wed Dec 6 19:54:40 2023 +0000 Logging: ensure that an error for a mistyped IPv6 address in a search list is available for logging. Bug 3057 diff --git a/src/src/match.c b/src/src/match.c index df440108e..8b1a3bef6 100644 --- a/src/src/match.c +++ b/src/src/match.c @@ -886,14 +886,19 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) log_write(0, LOG_MAIN, "%s: accepted by +include_defer", error); goto OK_RETURN; - case ERROR: /* host name lookup failed - this can only */ - if (ignore_unknown) /* be for an incoming host (not outgoing) */ + /* The ERROR return occurs when checking hosts, when either a forward + or reverse lookup has failed. It can also occur in a match_ip list if a + non-IP address item is encountered. The error string gives details of + which it was. */ + + case ERROR: + if (ignore_unknown) { HDEBUG(D_lists) debug_printf_indent("%s: item ignored by +ignore_unknown\n", error); } else - { + { HDEBUG(D_lists) debug_printf_indent("%s %s (%s)\n", ot, include_unknown? "yes":"no", error); (void)fclose(f); diff --git a/src/src/string.c b/src/src/string.c index dfe0f2451..3bf2f1df7 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -57,6 +57,7 @@ union { /* we do not need this, but inet_pton() needs a place for storage */ we return failure, as we do if the mask isn't a pure numerical value, or if it is negative. The actual length is checked later, once we know the address family. */ + if (slash = Ustrchr(ip_addr, '/')) { uschar * rest; @@ -74,10 +75,11 @@ if (slash = Ustrchr(ip_addr, '/')) return 0; } - *maskptr = slash - ip_addr; /* offset of the slash */ + *maskptr = slash - ip_addr; /* offset of the slash */ endp = slash; } -else if (maskptr) *maskptr = 0; /* no slash found */ +else if (maskptr) + *maskptr = 0; /* no slash found */ /* The interface-ID suffix (%) is optional (for IPv6). If it exists, we check it syntactically. Later, if we know the address @@ -160,7 +162,7 @@ switch (af) int string_is_ip_address(const uschar * ip_addr, int * maskptr) { -return string_is_ip_addressX(ip_addr, maskptr, 0); +return string_is_ip_addressX(ip_addr, maskptr, NULL); } #endif /* COMPILE_UTILITY */ diff --git a/src/src/verify.c b/src/src/verify.c index d080ddd63..194e9a76a 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -2971,7 +2971,7 @@ if (*ss == '@') a (possibly masked) comparison with the current IP address. */ if (string_is_ip_address(ss, &maskoffset) != 0) - return (host_is_in_net(cb->host_address, ss, maskoffset)? OK : FAIL); + return host_is_in_net(cb->host_address, ss, maskoffset) ? OK : FAIL; /* The pattern is not an IP address. A common error that people make is to omit one component of an IPv4 address, either by accident, or believing that, for @@ -2982,13 +2982,25 @@ ancient specification.) To aid in debugging these cases, we give a specific error if the pattern contains only digits and dots or contains a slash preceded only by digits and dots (a slash at the start indicates a file name and of course slashes may be present in lookups, but not preceded only by digits and -dots). */ +dots). Then the equivalent for IPv6 (roughly). */ -for (t = ss; isdigit(*t) || *t == '.'; ) t++; -if (!*t || (*t == '/' && t != ss)) +if (Ustrchr(ss, ':')) { - *error = string_sprintf("malformed IPv4 address or address mask: %.*s", (int)(t - ss), ss); - return ERROR; + for (t = ss; isxdigit(*t) || *t == ':' || *t == '.'; ) t++; + if (!*t || (*t == '/' || *t == '%') && t != ss) + { + *error = string_sprintf("malformed IPv6 address or address mask: %.*s", (int)(t - ss), ss); + return ERROR; + } + } +else + { + for (t = ss; isdigit(*t) || *t == '.'; ) t++; + if (!*t || (*t == '/' && t != ss)) + { + *error = string_sprintf("malformed IPv4 address or address mask: %.*s", (int)(t - ss), ss); + return ERROR; + } } /* See if there is a semicolon in the pattern, separating a searchtype commit b037dd69615b2ce9e084932e8a61477afc67345e Author: Jeremy Harris Date: Wed Dec 6 22:08:08 2023 +0000 typo Broken-by: 5930166b4433 diff --git a/src/src/os.c b/src/src/os.c index 5e5899eeb..c6d1e06c2 100644 --- a/src/src/os.c +++ b/src/src/os.c @@ -901,7 +901,7 @@ return buffer ? buffer : realloc(b, strlen(b) + 1); char * strchrnul(const char * s, int c) { -while (*s != c && *s) s++ +while (*s != c && *s) s++; return CS s; } #endif commit ce336ea1afd8d5f6b79d36fc7ec4313f3d80146b Author: Jeremy Harris Date: Thu Dec 7 19:59:35 2023 +0000 Handle expansion fails in router "set" options. Bug 3058 diff --git a/src/src/route.c b/src/src/route.c index 0bd87b837..3401f15b4 100644 --- a/src/src/route.c +++ b/src/src/route.c @@ -1492,7 +1492,11 @@ for (uschar * ele; (ele = string_nextinlist(&varlist, &sep, NULL, 0)); ) { addr->message = string_sprintf("expansion of \"%s\" failed " "in %s router: %s", ele, r->name, expand_string_message); - return DEFER; + /* Caller will replace that for logging, if a DB lookup, to avoid exposing + passwords */ + DEBUG(D_route) debug_printf("%s\n", addr->message); + if (!f.search_find_defer) + return f.search_find_defer ? DEFER : FAIL; } if (!(node = tree_search(*root, name))) @@ -1546,8 +1550,8 @@ route_address(address_item *addr, address_item **paddr_local, { int yield = OK; BOOL unseen; -router_instance *r, *nextr; -const uschar *old_domain = addr->domain; +router_instance * r, * nextr; +const uschar * old_domain = addr->domain; HDEBUG(D_route) { @@ -1561,8 +1565,8 @@ instead of at the first router. */ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) { - uschar *error; - struct passwd *pw = NULL; + uschar * error; + struct passwd * pw = NULL; struct passwd pwcopy; BOOL loop_detected = FALSE; BOOL more; @@ -1736,11 +1740,11 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) router traversal. On the addr string they are held as a variable tree, so as to maintain the post-expansion taints separate. */ - switch (set_router_vars(addr, r)) + switch (rc = set_router_vars(addr, r)) { case OK: break; case PASS: continue; /* with next router */ - default: goto ROUTE_EXIT; + default: yield = rc; goto ROUTE_EXIT; } /* Finally, expand the address_data field in the router. Forced failure commit ced0bbf43860a2e4ddc88e3ba20ae731324586c5 Author: Jeremy Harris Date: Fri Dec 15 21:05:32 2023 +0000 Fix periodic queue runs. Bug 3046 Broken-by: 7d5055276a22 diff --git a/src/src/daemon.c b/src/src/daemon.c index f2183c735..aff05120a 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -1258,10 +1258,9 @@ static const uschar * queuerun_msg_qname; /* The notifier socket has something to read. Pull the message from it, decode and do the action. +*/ -Return TRUE if a sigalrm should be emulated */ - -static BOOL +static void daemon_notification(void) { uschar buf[256], cbuf[256]; @@ -1277,8 +1276,8 @@ struct msghdr msg = { .msg_name = &sa_un, ssize_t sz; buf[sizeof(buf)-1] = 0; -if ((sz = recvmsg(daemon_notifier_fd, &msg, 0)) <= 0) return FALSE; -if (sz >= sizeof(buf)) return FALSE; +if ((sz = recvmsg(daemon_notifier_fd, &msg, 0)) <= 0) return; +if (sz >= sizeof(buf)) return; #ifdef notdef debug_printf("addrlen %d\n", msg.msg_namelen); @@ -1351,7 +1350,7 @@ switch (buf[0]) : !buf[1+MESSAGE_ID_LENGTH+1] ) { queuerun_msg_qname = q->name; break; } - return TRUE; + return; #endif case NOTIFY_QUEUE_SIZE_REQ: @@ -1373,7 +1372,7 @@ switch (buf[0]) regex_at_daemon(buf); break; } -return FALSE; +return; } @@ -1432,7 +1431,7 @@ for (qrunner * q = qrunners, * next; q; q = next) if (sorted) { qrunner ** p = &sorted; - for (qrunner * qq; qq = *p; p = &(qq->next)) + for (qrunner * qq; qq = *p; p = &qq->next) if ( q->next_tick < qq->next_tick || q->next_tick == qq->next_tick && q->interval < qq->interval ) @@ -1451,6 +1450,13 @@ qrunners = sorted; return qrunners ? qrunners->next_tick - time(NULL) : 0; } +/* See if we can do a queue run. If policy limit permit, kick one off. +If both notification and timer events are present, handle the former +and leave the timer outstanding. + +Return the number of seconds until the next due runner. +*/ + static int daemon_qrun(int local_queue_run_max, struct pollfd * fd_polls, int listen_socket_count) { @@ -1464,13 +1470,16 @@ DEBUG(D_any) debug_printf("%s received\n", enough queue runners on the go. If we are not running as root, a re-exec is required. In the calling process, restart the alamr timer for the next run. */ -if (is_multiple_qrun()) +if (is_multiple_qrun()) /* we are managing periodic runs */ if (local_queue_run_max <= 0 || queue_run_count < local_queue_run_max) { qrunner * q = NULL; #ifndef DISABLE_QUEUE_RAMP - if (*queuerun_msgid) /* See if we can start another runner for this queue */ + /* If this is a triggered run for a specific message, see if we can start + another runner for this queue. */ + + if (*queuerun_msgid) { for (qrunner * qq = qrunners; qq; qq = qq->next) if (qq->name == queuerun_msg_qname) @@ -1481,13 +1490,13 @@ if (is_multiple_qrun()) } else #endif - /* In order of run priority, find the first queue for which we can start - a runner */ + /* Normal periodic runL in order of run priority, find the first queue + for which we can start a runner */ for (q = qrunners; q; q = q->next) if (q->run_count < q->run_max) break; - if (q) + if (q) /* found a queue to run */ { pid_t pid; @@ -1619,19 +1628,23 @@ if (is_multiple_qrun()) } } -sigalrm_seen = FALSE; +/* The queue run has been initiated (unless we were already running enough) */ + #ifndef DISABLE_QUEUE_RAMP -if (*queuerun_msgid) /* it was a fast-ramp kick */ +if (*queuerun_msgid) /* it was a fast-ramp kick; dealt with */ *queuerun_msgid = 0; else /* periodic or one-time queue run */ #endif - { /* Impose a minimum 1s tick, even when a run was outstanding */ + /* Set up next timer callback. Impose a minimum 1s tick, + even when a run was outstanding */ + { int interval = next_qrunner_interval(); if (interval <= 0) interval = 1; + sigalrm_seen = FALSE; if (qrunners) /* there are still periodic qrunners */ { - ALARM(interval); + ALARM(interval); /* set up next qrun tick */ return interval; } } @@ -2612,7 +2625,7 @@ for (;;) The other option is that we have an inetd wait timeout specified to -bw. */ - if (sigalrm_seen) + if (sigalrm_seen || *queuerun_msgid) if (inetd_wait_timeout > 0) daemon_inetd_wtimeout(last_connection_time); /* Might not return */ else @@ -2641,7 +2654,9 @@ for (;;) select() was interrupted so that we reap the child. This might still leave a small window when a SIGCHLD could get lost. However, since we use SIGCHLD only to do the reaping more quickly, it shouldn't result in anything other - than a delay until something else causes a wake-up. */ + than a delay until something else causes a wake-up. + For the normal case, wait for either a pollable fd (eg. new connection) or + or a SIGALRM (for a periodic queue run) */ if (sigchld_seen) { @@ -2706,10 +2721,13 @@ for (;;) break; /* to top of daemon loop */ } #endif + /* Handle the daemon-notifier socket. If it was a fast-ramp + notification then queuerun_msgid will have a nonzerolength string. */ + if (dnotify_poll && dnotify_poll->revents & POLLIN) { dnotify_poll->revents = 0; - sigalrm_seen = daemon_notification(); + daemon_notification(); break; /* to top of daemon loop */ } for (struct pollfd * p = fd_polls; p < fd_polls + listen_socket_count; diff --git a/src/src/structs.h b/src/src/structs.h index 209d657c6..256560ef8 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -964,7 +964,7 @@ typedef struct qrunner { struct qrunner * next; /* list sorted by next tick */ uschar * name; /* NULL for the default queue */ - unsigned interval; /* tick rate, seconds */ + unsigned interval; /* tick rate, seconds. Zero for a one-time run */ time_t next_tick; /* next run should, or should have, start(ed) */ unsigned run_max; /* concurrent queue runner limit */ unsigned run_count; /* current runners */ commit 5ce637f7a74d392ddafb1d38e3cefb744996b287 Author: Jeremy Harris Date: Sat Dec 16 16:31:06 2023 +0000 tidying diff --git a/src/src/EDITME b/src/src/EDITME index f68b3f104..85d24f73f 100644 --- a/src/src/EDITME +++ b/src/src/EDITME @@ -52,6 +52,7 @@ # they are correctly installed, via their compatibility interfaces. However, # Exim can also be configured to use the native calls for Berkeley DB (obsolete # versions 1.85, 2.x, 3.x, or the current 4.x version) and also for gdbm. +# See definitions for DBMLIB below. # For some operating systems, a default DBM library (other than ndbm) is # selected by a setting in the OS-specific Makefile. Most modern OS now have diff --git a/src/src/receive.c b/src/src/receive.c index c4b80c236..e35400aec 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -4391,7 +4391,7 @@ data file will be flushed(!) out thereby. Nevertheless, it is theoretically possible for fclose() to fail - and this has been seen on obscure filesystems (probably one that delayed the actual media write as long as possible) but what to do? What has happened to the lock if this happens? -It's a mes because we already logged the acceptance. +It's a mess because we already logged the acceptance. We can at least log the issue, try to remove spoolfiles and respond with a temp-reject. We do not want to close before logging acceptance because we want to hold the lock until we know that logging worked. diff --git a/src/src/store.c b/src/src/store.c index 684f83cf0..03322a097 100644 --- a/src/src/store.c +++ b/src/src/store.c @@ -1145,11 +1145,11 @@ Returns: pointer to gotten store (panic on failure) */ static void * -internal_store_malloc(size_t size, const char *func, int line) +internal_store_malloc(size_t size, const char * func, int line) { void * yield; -/* Check specifically for a possibly result of conversion from +/* Check specifically for a possible result of conversion from a negative int, to the (unsigned, wider) size_t */ if (size >= INT_MAX/2) commit 322ae953e144d9e1fdc06d993d2e4551f5fb1322 Author: Jeremy Harris Date: Thu Dec 21 23:33:10 2023 +0000 tweak observability diff --git a/src/src/functions.h b/src/src/functions.h index b201467b2..ef84e3d7c 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -1400,6 +1400,11 @@ HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP%c> %s\n", client_cmd_log = string_catn(client_cmd_log, US"|", 1); (void) string_from_gstring(client_cmd_log); } + else if (mode == SCMD_MORE) + { + client_cmd_log = string_catn(client_cmd_log, US"+", 1); + (void) string_from_gstring(client_cmd_log); + } store_pool = old_pool; } # endif diff --git a/src/src/smtp_out.c b/src/src/smtp_out.c index 90a0eb23e..62557072e 100644 --- a/src/src/smtp_out.c +++ b/src/src/smtp_out.c @@ -649,7 +649,7 @@ Arguments: sx SMTP connection, contains buffer for pipelining, and socket mode buffer, write-with-more-likely, write format a format, starting with one of - of HELO, MAIL FROM, RCPT TO, DATA, ".", or QUIT. + of HELO, MAIL FROM, RCPT TO, DATA, BDAT, ".", or QUIT. If NULL, flush pipeline buffer only. ... data for the format diff --git a/src/src/transport.c b/src/src/transport.c index a0f097579..79a69f941 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -1658,6 +1658,10 @@ another message waiting for the same host. However, it doesn't do this if the current continue sequence is greater than the maximum supplied as an argument, or greater than the global connection_max_messages, which, if set, overrides. +It is also called if conditions are otherwise right for pipelining a QUIT after +the message data, since if there is another message waiting we do not want to +send that QUIT. + Arguments: transport_name name of the transport hostname name of the host commit cf1376206284f2a4f11e32d931d4aade34c206c5 Author: Jeremy Harris Date: Fri Dec 22 23:57:05 2023 +0000 Reject "dot, LF" as ending data phase. Bug 3063 diff --git a/src/src/receive.c b/src/src/receive.c index e35400aec..c6f612832 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -1960,8 +1960,10 @@ for (;;) if (ch == '\n') { - if (first_line_ended_crlf == TRUE_UNSET) first_line_ended_crlf = FALSE; - else if (first_line_ended_crlf) receive_ungetc(' '); + if (first_line_ended_crlf == TRUE_UNSET) + first_line_ended_crlf = FALSE; + else if (first_line_ended_crlf) + receive_ungetc(' '); goto EOL; } @@ -1970,6 +1972,7 @@ for (;;) This implements the dot-doubling rule, though header lines starting with dots aren't exactly common. They are legal in RFC 822, though. If the following is CRLF or LF, this is the line that that terminates the + entire message. We set message_ended to indicate this has happened (to prevent further reading), and break out of the loop, having freed the empty header, and set next = NULL to indicate no data line. */ @@ -1977,7 +1980,11 @@ for (;;) if (f.dot_ends && ptr == 0 && ch == '.') { ch = (receive_getc)(GETC_BUFFER_UNLIMITED); - if (ch == '\r') + if (ch == '\n' && first_line_ended_crlf == TRUE /* and not TRUE_UNSET */ ) + /* dot, LF but we are in CRLF mode. Attack? */ + ch = ' '; /* replace the LF with a space */ + + else if (ch == '\r') { ch = (receive_getc)(GETC_BUFFER_UNLIMITED); if (ch != '\n') @@ -2013,7 +2020,8 @@ for (;;) ch = (receive_getc)(GETC_BUFFER_UNLIMITED); if (ch == '\n') { - if (first_line_ended_crlf == TRUE_UNSET) first_line_ended_crlf = TRUE; + if (first_line_ended_crlf == TRUE_UNSET) + first_line_ended_crlf = TRUE; goto EOL; } diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index e19c86ff8..aeaffeb37 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -5105,15 +5105,18 @@ while (done <= 0) } if (chunking_state > CHUNKING_OFFERED) - rc = OK; /* No predata ACL or go-ahead output for BDAT */ + rc = OK; /* There is no predata ACL or go-ahead output for BDAT */ else { - /* If there is an ACL, re-check the synchronization afterwards, since the - ACL may have delayed. To handle cutthrough delivery enforce a dummy call - to get the DATA command sent. */ + /* If there is a predata-ACL, re-check the synchronization afterwards, + since the ACL may have delayed. To handle cutthrough delivery enforce a + dummy call to get the DATA command sent. */ if (!acl_smtp_predata && cutthrough.cctx.sock < 0) + { + if (!check_sync()) goto SYNC_FAILURE; rc = OK; + } else { uschar * acl = acl_smtp_predata ? acl_smtp_predata : US"accept"; commit 4596719398f6f2365bed563aafd757a6433ce7b4 Author: Jeremy Harris Date: Sat Dec 23 15:59:53 2023 +0000 Use enum for body data input state-machine diff --git a/src/src/receive.c b/src/src/receive.c index c6f612832..e8bd80596 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -836,93 +836,95 @@ Returns: One of the END_xxx values indicating why it stopped reading */ static int -read_message_data_smtp(FILE *fout) +read_message_data_smtp(FILE * fout) { -int ch_state = 0; -int ch; -int linelength = 0; +enum { s_linestart, s_normal, s_had_cr, s_had_nl_dot, s_had_dot_cr } ch_state = + s_linestart; +int linelength = 0, ch; while ((ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF) { if (ch == 0) body_zerocount++; switch (ch_state) { - case 0: /* After LF or CRLF */ - if (ch == '.') - { - ch_state = 3; - continue; /* Don't ever write . after LF */ - } - ch_state = 1; + case s_linestart: /* After LF or CRLF */ + if (ch == '.') + { + ch_state = s_had_nl_dot; + continue; /* Don't ever write . after LF */ + } + ch_state = s_normal; - /* Else fall through to handle as normal uschar. */ + /* Else fall through to handle as normal uschar. */ - case 1: /* Normal state */ - if (ch == '\n') - { - ch_state = 0; - body_linecount++; + case s_normal: /* Normal state */ + if (ch == '\r') + { + ch_state = s_had_cr; + continue; /* Don't write the CR */ + } + if (ch == '\n') /* Bare NL ends line */ + { + ch_state = s_linestart; + body_linecount++; + if (linelength > max_received_linelength) + max_received_linelength = linelength; + linelength = -1; + } + break; + + case s_had_cr: /* After (unwritten) CR */ + body_linecount++; /* Any char ends line */ if (linelength > max_received_linelength) - max_received_linelength = linelength; + max_received_linelength = linelength; linelength = -1; - } - else if (ch == '\r') - { - ch_state = 2; - continue; - } - break; + if (ch == '\n') /* proper CRLF */ + ch_state = s_linestart; + else + { + message_size++; /* convert the dropped CR to a stored NL */ + if (fout && fputc('\n', fout) == EOF) return END_WERROR; + cutthrough_data_put_nl(); + if (ch == '\r') /* CR; do not write */ + continue; + ch_state = s_normal; /* not LF or CR; process as standard */ + } + break; - case 2: /* After (unwritten) CR */ - body_linecount++; - if (linelength > max_received_linelength) - max_received_linelength = linelength; - linelength = -1; - if (ch == '\n') - { - ch_state = 0; - } - else - { - message_size++; - if (fout != NULL && fputc('\n', fout) == EOF) return END_WERROR; - cutthrough_data_put_nl(); - if (ch != '\r') ch_state = 1; else continue; - } - break; + case s_had_nl_dot: /* After [CR] LF . */ + if (ch == '\n') /* [CR] LF . LF */ + return END_DOT; + if (ch == '\r') /* [CR] LF . CR */ + { + ch_state = s_had_dot_cr; + continue; /* Don't write the CR */ + } + /* The dot was removed on reaching s_had_nl_dot. For a doubled dot, here, + reinstate it to cutthrough. The current ch, dot or not, is passed both to + cutthrough and to file below. */ + if (ch == '.') + { + uschar c = ch; + cutthrough_data_puts(&c, 1); + } + ch_state = s_normal; + break; - case 3: /* After [CR] LF . */ - if (ch == '\n') - return END_DOT; - if (ch == '\r') - { - ch_state = 4; - continue; - } - /* The dot was removed at state 3. For a doubled dot, here, reinstate - it to cutthrough. The current ch, dot or not, is passed both to cutthrough - and to file below. */ - if (ch == '.') - { - uschar c= ch; - cutthrough_data_puts(&c, 1); - } - ch_state = 1; - break; + case s_had_dot_cr: /* After [CR] LF . CR */ + if (ch == '\n') + return END_DOT; /* Preferred termination */ - case 4: /* After [CR] LF . CR */ - if (ch == '\n') return END_DOT; - message_size++; - body_linecount++; - if (fout != NULL && fputc('\n', fout) == EOF) return END_WERROR; - cutthrough_data_put_nl(); - if (ch == '\r') - { - ch_state = 2; - continue; - } - ch_state = 1; - break; + message_size++; /* convert the dropped CR to a stored NL */ + body_linecount++; + if (fout && fputc('\n', fout) == EOF) return END_WERROR; + cutthrough_data_put_nl(); + if (ch == '\r') + { + ch_state = s_had_cr; + continue; /* CR; do not write */ + } + ch_state = s_normal; + break; } /* Add the character to the spool file, unless skipping; then loop for the @@ -1979,6 +1981,7 @@ for (;;) if (f.dot_ends && ptr == 0 && ch == '.') { + /* leading dot while in headers-read mode */ ch = (receive_getc)(GETC_BUFFER_UNLIMITED); if (ch == '\n' && first_line_ended_crlf == TRUE /* and not TRUE_UNSET */ ) /* dot, LF but we are in CRLF mode. Attack? */ @@ -3169,7 +3172,7 @@ if (cutthrough.cctx.sock >= 0 && cutthrough.delivery) /* Open a new spool file for the data portion of the message. We need -to access it both via a file descriptor and a stream. Try to make the +to access it both via a file descriptor and a stdio stream. Try to make the directory if it isn't there. */ spool_name = spool_fname(US"input", message_subdir, message_id, US"-D"); commit 5bb786d5ad568a88d50d15452aacc8404047e5ca Author: Jeremy Harris Date: Sat Dec 23 17:42:57 2023 +0000 Reject "dot, LF" as ending data phase (pt. 2). Bug 3063 diff --git a/src/src/receive.c b/src/src/receive.c index e8bd80596..ae7045068 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -829,14 +829,20 @@ July 2003: Bare CRs cause trouble. We now treat them as line terminators as well, so that there are no CRs in spooled messages. However, the message terminating dot is not recognized between two bare CRs. +Dec 2023: getting a site to send a body including an "LF . LF" sequence +followed by SMTP commands is a possible "smtp smuggling" attack. If +the first (header) line for the message has a proper CRLF then enforce +that for the body: convert bare LF to a space. + Arguments: - fout a FILE to which to write the message; NULL if skipping + fout a FILE to which to write the message; NULL if skipping + strict_crlf require full CRLF sequence as a line ending Returns: One of the END_xxx values indicating why it stopped reading */ static int -read_message_data_smtp(FILE * fout) +read_message_data_smtp(FILE * fout, BOOL strict_crlf) { enum { s_linestart, s_normal, s_had_cr, s_had_nl_dot, s_had_dot_cr } ch_state = s_linestart; @@ -863,14 +869,17 @@ while ((ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF) ch_state = s_had_cr; continue; /* Don't write the CR */ } - if (ch == '\n') /* Bare NL ends line */ - { - ch_state = s_linestart; - body_linecount++; - if (linelength > max_received_linelength) - max_received_linelength = linelength; - linelength = -1; - } + if (ch == '\n') /* Bare LF at end of line */ + if (strict_crlf) + ch = ' '; /* replace LF with space */ + else + { /* treat as line ending */ + ch_state = s_linestart; + body_linecount++; + if (linelength > max_received_linelength) + max_received_linelength = linelength; + linelength = -1; + } break; case s_had_cr: /* After (unwritten) CR */ @@ -893,8 +902,11 @@ while ((ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF) case s_had_nl_dot: /* After [CR] LF . */ if (ch == '\n') /* [CR] LF . LF */ - return END_DOT; - if (ch == '\r') /* [CR] LF . CR */ + if (strict_crlf) + ch = ' '; /* replace LF with space */ + else + return END_DOT; + else if (ch == '\r') /* [CR] LF . CR */ { ch_state = s_had_dot_cr; continue; /* Don't write the CR */ @@ -902,7 +914,7 @@ while ((ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF) /* The dot was removed on reaching s_had_nl_dot. For a doubled dot, here, reinstate it to cutthrough. The current ch, dot or not, is passed both to cutthrough and to file below. */ - if (ch == '.') + else if (ch == '.') { uschar c = ch; cutthrough_data_puts(&c, 1); @@ -1140,7 +1152,7 @@ receive_swallow_smtp(void) { if (message_ended >= END_NOTENDED) message_ended = chunking_state <= CHUNKING_OFFERED - ? read_message_data_smtp(NULL) + ? read_message_data_smtp(NULL, FALSE) : read_message_bdat_smtp_wire(NULL); } @@ -3241,7 +3253,7 @@ if (!ferror(spool_data_file) && !(receive_feof)() && message_ended != END_DOT) if (smtp_input) { message_ended = chunking_state <= CHUNKING_OFFERED - ? read_message_data_smtp(spool_data_file) + ? read_message_data_smtp(spool_data_file, first_line_ended_crlf) : spool_wireformat ? read_message_bdat_smtp_wire(spool_data_file) : read_message_bdat_smtp(spool_data_file); commit f78e417c3c2e7348ce58bb03efc7343bb6d32383 Author: Jeremy Harris Date: Mon Dec 25 16:50:23 2023 +0000 Testsuite: testcase for "smtp smuggling". Bug 3063 Also remove the unneeded sync point added in cf1376206284 diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index aeaffeb37..541288301 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -5113,10 +5113,7 @@ while (done <= 0) dummy call to get the DATA command sent. */ if (!acl_smtp_predata && cutthrough.cctx.sock < 0) - { - if (!check_sync()) goto SYNC_FAILURE; rc = OK; - } else { uschar * acl = acl_smtp_predata ? acl_smtp_predata : US"accept"; commit 80bacd22c5aaacd7fca966af7384ac6f2679cb21 Author: Jeremy Harris Date: Fri Dec 29 12:40:22 2023 +0000 DKIM: logging for signing in transport. Bug 3062 diff --git a/src/src/deliver.c b/src/src/deliver.c index e96733f73..2cef3d731 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -1245,6 +1245,14 @@ else g = string_catn(g, US" K", 2); } +#ifndef DISABLE_DKIM + if (addr->dkim_used && LOGGING(dkim_verbose)) + { + g = string_catn(g, US" DKIM=", 6); + g = string_cat(g, addr->dkim_used); + } +#endif + /* confirmation message (SMTP (host_used) and LMTP (driver_name)) */ if ( LOGGING(smtp_confirmation) @@ -3571,7 +3579,14 @@ while (!done) switch (*subid) { - case 3: /* explicit notification of continued-connection (non)use; +#ifndef DISABLE_DKIM + case '4': /* DKIM information */ + addr->dkim_used = string_copy(ptr); + while(*ptr++); + break; +#endif + + case '3': /* explicit notification of continued-connection (non)use; overrides caller's knowlege. */ if (*ptr & BIT(1)) setflag(addr, af_new_conn); else if (*ptr & BIT(2)) setflag(addr, af_cont_conn); @@ -4886,6 +4901,15 @@ all pipes, so I do not see a reason to use non-blocking IO here rmt_dlv_checked_write(fd, 'R', '0', big_buffer, ptr - big_buffer); } +#ifndef DISABLE_DKIM + if (addr->dkim_used && LOGGING(dkim_verbose)) + { + DEBUG(D_deliver) debug_printf("dkim used: %s\n", addr->dkim_used); + ptr = big_buffer + sprintf(CS big_buffer, "%.128s", addr->dkim_used) + 1; + rmt_dlv_checked_write(fd, 'A', '4', big_buffer, ptr - big_buffer); + } +#endif + if (testflag(addr, af_new_conn) || testflag(addr, af_cont_conn)) { DEBUG(D_deliver) debug_printf("%scontinued-connection\n", diff --git a/src/src/dkim.c b/src/src/dkim.c index ea82f34ea..59025608f 100644 --- a/src/src/dkim.c +++ b/src/src/dkim.c @@ -741,6 +741,9 @@ if (dkim_domain) if (!pdkim_set_sig_bodyhash(&dkim_sign_ctx, sig)) goto bad; + dkim_signing_record = string_append_listele(dkim_signing_record, ':', dkim_signing_domain); + dkim_signing_record = string_append_listele(dkim_signing_record, ':', dkim_signing_selector); + if (!dkim_sign_ctx.sig) /* link sig to context chain */ dkim_sign_ctx.sig = sig; else diff --git a/src/src/dkim_transport.c b/src/src/dkim_transport.c index fae2f4891..38f849af6 100644 --- a/src/src/dkim_transport.c +++ b/src/src/dkim_transport.c @@ -384,6 +384,8 @@ BOOL dkim_transport_write_message(transport_ctx * tctx, struct ob_dkim * dkim, const uschar ** err) { +BOOL yield; + /* If we can't sign, just call the original function. */ if ( !(dkim->dkim_private_key && dkim->dkim_domain && dkim->dkim_selector) @@ -398,12 +400,16 @@ if ( !transport_filter_argv || !*transport_filter_argv || !**transport_filter_argv ) - return dkt_direct(tctx, dkim, err); + yield = dkt_direct(tctx, dkim, err); + +else + /* Use the transport path to write a file, calculate a dkim signature, + send the signature and then send the file. */ -/* Use the transport path to write a file, calculate a dkim signature, -send the signature and then send the file. */ + yield = dkt_via_kfile(tctx, dkim, err); -return dkt_via_kfile(tctx, dkim, err); +tctx->addr->dkim_used = string_from_gstring(dkim_signing_record); +return yield; } #endif /* whole file */ diff --git a/src/src/globals.c b/src/src/globals.c index bb5bd6b1a..c535ea121 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -864,6 +864,7 @@ void *dkim_signatures = NULL; uschar *dkim_signers = NULL; uschar *dkim_signing_domain = NULL; uschar *dkim_signing_selector = NULL; +gstring *dkim_signing_record = NULL; uschar *dkim_verify_hashes = US"sha256:sha512"; uschar *dkim_verify_keytypes = US"ed25519:rsa"; uschar *dkim_verify_min_keysizes = US"rsa=1024 ed25519=250"; diff --git a/src/src/globals.h b/src/src/globals.h index fe4a1610a..ffc7150f2 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -541,6 +541,7 @@ extern uschar *dkim_cur_signer; /* Expansion variable, holds the current extern int dkim_key_length; /* Expansion variable, length of signing key in bits */ extern void *dkim_signatures; /* Actually a (pdkim_signature *) but most files do not need to know */ extern uschar *dkim_signers; /* Expansion variable, holds colon-separated list of domains and identities that have signed a message */ +extern gstring *dkim_signing_record; /* domains+selectors used */ extern uschar *dkim_signing_domain; /* Expansion variable, domain used for signing a message. */ extern uschar *dkim_signing_selector; /* Expansion variable, selector used for signing a message. */ extern uschar *dkim_verify_hashes; /* Preference order for signatures */ diff --git a/src/src/structs.h b/src/src/structs.h index 256560ef8..b16a8a3f2 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -586,6 +586,9 @@ typedef struct address_item { uschar *self_hostname; /* after self=pass */ uschar *shadow_message; /* info about shadow transporting */ + uid_t uid; /* uid for transporting */ + gid_t gid; /* gid for transporting */ + #ifndef DISABLE_TLS const uschar *tlsver; /* version used for transport */ uschar *cipher; /* Cipher used for transport */ @@ -608,9 +611,9 @@ typedef struct address_item { int dsn_flags; /* DSN flags */ int dsn_aware; /* DSN aware flag */ - uid_t uid; /* uid for transporting */ - gid_t gid; /* gid for transporting */ - +#ifndef DISABLE_DKIM + const uschar * dkim_used; /* DKIM info, or NULL */ +#endif /* flags */ struct { BOOL af_allow_file:1; /* allow file in generated address */ commit 582f61444fa806694c2caae7c156830dbf8531d7 Author: Jeremy Harris Date: Fri Dec 29 15:29:27 2023 +0000 Tidying: defines for sync_responses() retcodes diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 4cf58436d..194671272 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -235,6 +235,17 @@ static BOOL pipelining_active; /* current transaction is in pipe mode */ static unsigned ehlo_response(uschar * buf, unsigned checks); +/* sync_responses() return codes */ + +#define RESP_BIT_HAD_5XX BIT(1) +#define RESP_BIT_HAD_2XX BIT(0) +#define RESP_HAD_2_AND_5 (RESP_BIT_HAD_2XX | RESP_BIT_HAD_5XX) +#define RESP_NOERROR 0 +#define RESP_RCPT_TIMEO -1 +#define RESP_RCPT_ERROR -2 +#define RESP_MAIL_OR_DATA_ERROR -3 +#define RESP_EPIPE_EHLO_ERR -4 +#define RESP_EHLO_ERR_TLS -5 /******************************************************************************/ @@ -1225,7 +1236,7 @@ int yield = 0; #ifndef DISABLE_PIPE_CONNECT int rc; if ((rc = smtp_reap_early_pipe(sx, &count)) != OK) - return rc == FAIL ? -4 : -5; + return rc == FAIL ? RESP_EPIPE_EHLO_ERR : RESP_EHLO_ERR_TLS; #endif /* Handle the response for a MAIL command. On error, reinstate the original @@ -1243,7 +1254,7 @@ if (sx->pending_MAIL) DEBUG(D_transport) debug_printf("bad response for MAIL\n"); Ustrcpy(big_buffer, mail_command); /* Fits, because it came from there! */ if (errno == ERRNO_TLSFAILURE) - return -5; + return RESP_EHLO_ERR_TLS; if (errno == 0 && sx->buffer[0] != 0) { int save_errno = 0; @@ -1263,7 +1274,7 @@ if (sx->pending_MAIL) addr->host_used = sx->conn_args.host; addr = addr->next; } - return -3; + return RESP_MAIL_OR_DATA_ERROR; } } @@ -1277,7 +1288,7 @@ while (count-- > 0) { while (addr->transport_return != PENDING_DEFER) if (!(addr = addr->next)) - return -2; + return RESP_RCPT_ERROR; /* The address was accepted */ addr->host_used = sx->conn_args.host; @@ -1286,7 +1297,7 @@ while (count-- > 0) if (smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2', ob->command_timeout)) { - yield |= 1; + yield |= RESP_BIT_HAD_2XX; addr->transport_return = PENDING_OK; /* If af_dr_retry_exists is set, there was a routing delay on this address; @@ -1305,7 +1316,7 @@ while (count-- > 0) /* Error on first TLS read */ else if (errno == ERRNO_TLSFAILURE) - return -5; + return RESP_EHLO_ERR_TLS; /* Timeout while reading the response */ @@ -1316,7 +1327,7 @@ while (count-- > 0) set_errno_nohost(sx->first_addr, ETIMEDOUT, message, DEFER, FALSE, &sx->delivery_start); retry_add_item(addr, addr->address_retry_key, 0); update_waiting = FALSE; - return -1; + return RESP_RCPT_TIMEO; } /* Handle other errors in obtaining an SMTP response by returning -1. This @@ -1334,7 +1345,7 @@ while (count-- > 0) g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, "RCPT TO:<%s>", transport_rcpt_address(addr, sx->conn_args.tblock->rcpt_include_affixes)); string_from_gstring(g); - return -2; + return RESP_RCPT_ERROR; } /* Handle SMTP permanent and temporary response codes. */ @@ -1354,7 +1365,7 @@ while (count-- > 0) if (sx->buffer[0] == '5') { addr->transport_return = FAIL; - yield |= 2; + yield |= RESP_BIT_HAD_5XX; } /* The response was 4xx */ @@ -1374,7 +1385,7 @@ while (count-- > 0) /* If a 452 and we've had at least one 2xx or 5xx, set next_addr to the start point for another MAIL command. */ - if (addr->more_errno >> 8 == 52 && yield & 3) + if (addr->more_errno >> 8 == 52 && yield > 0) { if (!sx->RCPT_452) /* initialised at MAIL-ack above */ { @@ -1424,7 +1435,7 @@ while (count-- > 0) } } if (count && !(addr = addr->next)) - return -2; + return RESP_RCPT_ERROR; } /* Loop for next RCPT response */ /* Update where to start at for the next block of responses, unless we @@ -1446,16 +1457,16 @@ if (pending_DATA != 0) BOOL pass_message; if (errno == ERRNO_TLSFAILURE) /* Error on first TLS read */ - return -5; + return RESP_EHLO_ERR_TLS; - if (pending_DATA > 0 || (yield & 1) != 0) + if (pending_DATA > 0 || yield & RESP_BIT_HAD_2XX) { if (errno == 0 && sx->buffer[0] == '4') { errno = ERRNO_DATA4XX; sx->first_addr->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8; } - return -3; + return RESP_MAIL_OR_DATA_ERROR; } (void)check_response(sx->conn_args.host, &errno, 0, sx->buffer, &code, &msg, &pass_message); DEBUG(D_transport) debug_printf("%s\nerror for DATA ignored: pipelining " @@ -2008,18 +2019,18 @@ if (flags & tc_reap_prev && prev_cmd_count > 0) switch(sync_responses(sx, prev_cmd_count, 0)) { - case 1: /* 2xx (only) => OK */ - case 3: sx->good_RCPT = TRUE; /* 2xx & 5xx => OK & progress made */ - case 2: sx->completed_addr = TRUE; /* 5xx (only) => progress made */ - case 0: break; /* No 2xx or 5xx, but no probs */ + case RESP_BIT_HAD_2XX: /* OK */ + case RESP_HAD_2_AND_5: sx->good_RCPT = TRUE; /* OK & progress made */ + case RESP_BIT_HAD_5XX: sx->completed_addr = TRUE; /* progress made */ + case RESP_NOERROR: break; /* No 2xx or 5xx, but no probs */ - case -5: errno = ERRNO_TLSFAILURE; - return DEFER; + case RESP_EHLO_ERR_TLS:errno = ERRNO_TLSFAILURE; + return DEFER; #ifndef DISABLE_PIPE_CONNECT - case -4: /* non-2xx for pipelined banner or EHLO */ + case RESP_EPIPE_EHLO_ERR: /* non-2xx for pipelined banner or EHLO */ #endif - case -1: /* Timeout on RCPT */ - default: return ERROR; /* I/O error, or any MAIL/DATA error */ + case RESP_RCPT_TIMEO: /* Timeout on RCPT */ + default: return ERROR;/* I/O error, or any MAIL/DATA error */ } cmd_count = 1; if (!sx->pending_BDAT) @@ -2523,7 +2534,7 @@ goto SEND_QUIT; DEBUG(D_transport) debug_printf("may need to auth, so pipeline no further\n"); if (smtp_write_command(sx, SCMD_FLUSH, NULL) < 0) goto SEND_FAILED; - if (sync_responses(sx, 2, 0) != 0) + if (sync_responses(sx, 2, 0) != RESP_NOERROR) { HDEBUG(D_transport) debug_printf("failed reaping pipelined cmd responses\n"); @@ -2714,7 +2725,7 @@ if ( smtp_peer_options & OPTION_TLS if (sx->early_pipe_active) { - if (sync_responses(sx, 2, 0) != 0) + if (sync_responses(sx, 2, 0) != RESP_NOERROR) { HDEBUG(D_transport) debug_printf("failed reaping pipelined cmd responses\n"); @@ -3568,14 +3579,14 @@ for (addr = sx->first_addr, address_count = 0, pipe_limit = 100; { switch(sync_responses(sx, cmds_sent, 0)) { - case 3: sx->ok = TRUE; /* 2xx & 5xx => OK & progress made */ - case 2: sx->completed_addr = TRUE; /* 5xx (only) => progress made */ + case RESP_HAD_2_AND_5: sx->ok = TRUE; /* OK & progress made */ + case RESP_BIT_HAD_5XX: sx->completed_addr = TRUE; /* progress made */ break; - case 1: sx->ok = TRUE; /* 2xx (only) => OK, but if LMTP, */ + case RESP_BIT_HAD_2XX: sx->ok = TRUE; /* OK, but if LMTP, */ if (!sx->lmtp) /* can't tell about progress yet */ sx->completed_addr = TRUE; - case 0: /* No 2xx or 5xx, but no probs */ + case RESP_NOERROR: /* No 2xx or 5xx, but no probs */ /* If any RCPT got a 452 response then next_addr has been updated for restarting with a new MAIL on the same connection. Send no more RCPTs for this MAIL. */ @@ -3589,13 +3600,13 @@ for (addr = sx->first_addr, address_count = 0, pipe_limit = 100; } break; - case -1: return -3; /* Timeout on RCPT */ - case -2: return -2; /* non-MAIL read i/o error */ - default: return -1; /* any MAIL error */ + case RESP_RCPT_TIMEO: return -3; /* Timeout on RCPT */ + case RESP_RCPT_ERROR: return -2; /* non-MAIL read i/o error */ + default: return -1; /* any MAIL error */ #ifndef DISABLE_PIPE_CONNECT - case -4: return -1; /* non-2xx for pipelined banner or EHLO */ - case -5: return -1; /* TLS first-read error */ + case RESP_EPIPE_EHLO_ERR: return -1; /* non-2xx for pipelined banner or EHLO */ + case RESP_EHLO_ERR_TLS: return -1; /* TLS first-read error */ #endif } } @@ -3956,17 +3967,19 @@ if ( !(smtp_peer_options & OPTION_CHUNKING) 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)) { - case 3: sx->ok = TRUE; /* 2xx & 5xx => OK & progress made */ - case 2: sx->completed_addr = TRUE; /* 5xx (only) => progress made */ - break; + case RESP_HAD_2_AND_5: sx->ok = TRUE; /* OK & progress made */ + case RESP_BIT_HAD_5XX: sx->completed_addr = TRUE; /* 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 0: break; /* No 2xx or 5xx, but no probs */ + case RESP_BIT_HAD_2XX: sx->ok = TRUE; /* OK, but if LMTP, */ + if (!sx->lmtp) /* can't tell about progress yet */ + sx->completed_addr = TRUE; + case RESP_NOERROR: break; /* No 2xx or 5xx, but no probs */ - case -1: goto END_OFF; /* Timeout on RCPT */ + case RESP_RCPT_TIMEO: goto END_OFF; #ifndef DISABLE_PIPE_CONNECT case -5: /* TLS first-read error */ @@ -4196,22 +4209,23 @@ else /* Reap any outstanding MAIL & RCPT commands, but not a DATA-go-ahead */ 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 */ - break; + case RESP_HAD_2_AND_5: sx->ok = TRUE; /* OK & progress made */ + case RESP_BIT_HAD_5XX: sx->completed_addr = TRUE; /* 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 0: break; /* No 2xx or 5xx, but no probs */ + case RESP_BIT_HAD_2XX: sx->ok = TRUE; /* OK, but if LMTP, */ + if (!sx->lmtp) /* can't tell about progress yet */ + sx->completed_addr = TRUE; + case RESP_NOERROR: break; /* No 2xx or 5xx, but no probs */ - case -1: goto END_OFF; /* Timeout on RCPT */ + case RESP_RCPT_TIMEO: goto END_OFF; /* Timeout on RCPT */ #ifndef DISABLE_PIPE_CONNECT - case -5: /* TLS first-read error */ - case -4: HDEBUG(D_transport) + case RESP_EHLO_ERR_TLS: /* TLS first-read error */ + case RESP_EPIPE_EHLO_ERR: HDEBUG(D_transport) debug_printf("failed reaping pipelined cmd responses\n"); #endif - default: goto RESPONSE_FAILED; /* I/O error, or any MAIL/DATA error */ + default: goto RESPONSE_FAILED; /* I/O error, or any MAIL/DATA error */ } } commit f0ce0ecaa054ad9773a2078bc8f011a59518120a Author: Jeremy Harris Date: Fri Dec 29 22:12:58 2023 +0000 Tidying: enum for smtp_write_mail_and_rcpt_cmds() retcodes diff --git a/src/src/macros.h b/src/src/macros.h index e2c1d0f94..5279f70d0 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -1175,4 +1175,14 @@ typedef unsigned mcs_flags; #define SR_FINAL TRUE #define SR_NOT_FINAL FALSE +/* Return codes for smtp_write_mail_and_rcpt_cmds() */ +typedef enum { + sw_mrc_ok, /* good, rcpt results in addr->transport_return (PENDING_OK, DEFER, FAIL) */ + sw_mrc_bad_mail, /* MAIL response error */ + sw_mrc_bad_read, /* any non-MAIL read i/o error */ + sw_mrc_nonmail_read_timeo, /* non-MAIL response timeout */ + sw_mrc_bad_internal, /* internal error; channel still usable */ + sw_mrc_tx_fail, /* transmit failed */ +} sw_mrc_t; + /* End of macros.h */ diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 194671272..817d729a7 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -3415,17 +3415,11 @@ if (sx->peer_offered & OPTION_DSN && !(addr->dsn_flags & rf_dsnlasthop)) -/* -Return: - 0 good, rcpt results in addr->transport_return (PENDING_OK, DEFER, FAIL) - -1 MAIL response error - -2 any non-MAIL read i/o error - -3 non-MAIL response timeout - -4 internal error; channel still usable - -5 transmit failed +/* Send MAIL FROM and RCPT TO commands. +See sw_mrc_t definition for return codes. */ -int +sw_mrc_t smtp_write_mail_and_rcpt_cmds(smtp_context * sx, int * yield) { address_item * addr; @@ -3438,7 +3432,7 @@ int rc; if (build_mailcmd_options(sx, sx->first_addr) != OK) { *yield = ERROR; - return -4; + return sw_mrc_bad_internal; } /* From here until we send the DATA command, we can make use of PIPELINING @@ -3466,7 +3460,7 @@ sx->pending_MAIL = TRUE; /* The block starts with MAIL */ { set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE, &sx->delivery_start); *yield = ERROR; - return -4; + return sw_mrc_bad_internal; } setflag(sx->addrlist, af_utf8_downcvt); } @@ -3481,7 +3475,7 @@ mail_command = string_copy(big_buffer); /* Save for later error message */ switch(rc) { case -1: /* Transmission error */ - return -5; + return sw_mrc_bad_mail; case +1: /* Cmd was sent */ if (!smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2', @@ -3492,7 +3486,7 @@ switch(rc) errno = ERRNO_MAIL4XX; sx->addrlist->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8; } - return -1; + return sw_mrc_bad_mail; } sx->pending_MAIL = FALSE; break; @@ -3567,14 +3561,14 @@ for (addr = sx->first_addr, address_count = 0, pipe_limit = 100; { /*XXX could we use a per-address errstr here? Not fail the whole send? */ errno = ERRNO_EXPANDFAIL; - return -5; /*XXX too harsh? */ + return sw_mrc_tx_fail; /*XXX too harsh? */ } #endif cmds_sent = smtp_write_command(sx, no_flush ? SCMD_BUFFER : SCMD_FLUSH, "RCPT TO:<%s>%s%s\r\n", rcpt_addr, sx->igquotstr, sx->buffer); - if (cmds_sent < 0) return -5; + if (cmds_sent < 0) return sw_mrc_tx_fail; if (cmds_sent > 0) { switch(sync_responses(sx, cmds_sent, 0)) @@ -3596,17 +3590,17 @@ for (addr = sx->first_addr, address_count = 0, pipe_limit = 100; DEBUG(D_transport) debug_printf("seen 452 too-many-rcpts\n"); sx->RCPT_452 = FALSE; /* sx->next_addr has been reset for fast_retry */ - return 0; + return sw_mrc_ok; } break; - case RESP_RCPT_TIMEO: return -3; /* Timeout on RCPT */ - case RESP_RCPT_ERROR: return -2; /* non-MAIL read i/o error */ - default: return -1; /* any MAIL error */ + case RESP_RCPT_TIMEO: return sw_mrc_nonmail_read_timeo; + case RESP_RCPT_ERROR: return sw_mrc_bad_read; + default: return sw_mrc_bad_mail; /* any MAIL error */ #ifndef DISABLE_PIPE_CONNECT - case RESP_EPIPE_EHLO_ERR: return -1; /* non-2xx for pipelined banner or EHLO */ - case RESP_EHLO_ERR_TLS: return -1; /* TLS first-read error */ + case RESP_EPIPE_EHLO_ERR: return sw_mrc_bad_mail; /* non-2xx for pipelined banner or EHLO */ + case RESP_EHLO_ERR_TLS: return sw_mrc_bad_mail; /* TLS first-read error */ #endif } } @@ -3617,7 +3611,7 @@ sx->next_addr = restart_addr ? restart_addr : addr; #else sx->next_addr = addr; #endif -return 0; +return sw_mrc_ok; } @@ -3925,11 +3919,12 @@ else 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; + case sw_mrc_ok: break; + case sw_mrc_bad_mail: goto RESPONSE_FAILED; + case sw_mrc_bad_read: goto RESPONSE_FAILED; + case sw_mrc_nonmail_read_timeo: goto END_OFF; + case sw_mrc_bad_internal: goto SEND_QUIT; + default: goto SEND_FAILED; } /* If we are an MUA wrapper, abort if any RCPTs were rejected, either diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h index 1f888ee0b..ef9135838 100644 --- a/src/src/transports/smtp.h +++ b/src/src/transports/smtp.h @@ -225,7 +225,7 @@ typedef struct { } smtp_context; extern int smtp_setup_conn(smtp_context *, BOOL); -extern int smtp_write_mail_and_rcpt_cmds(smtp_context *, int *); +extern sw_mrc_t smtp_write_mail_and_rcpt_cmds(smtp_context *, int *); extern int smtp_reap_early_pipe(smtp_context *, int *); diff --git a/src/src/verify.c b/src/src/verify.c index 194e9a76a..bde660063 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -823,7 +823,7 @@ tls_retry_connection: /* 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) == sw_mrc_ok) switch(addr->transport_return) { case PENDING_OK: /* random was accepted, unfortunately */ @@ -891,33 +891,34 @@ tls_retry_connection: done = FALSE; switch(smtp_write_mail_and_rcpt_cmds(sx, &yield)) { - case 0: switch(addr->transport_return) /* ok so far */ - { - case PENDING_OK: done = TRUE; - new_address_record.result = ccache_accept; - break; - case FAIL: done = TRUE; - yield = FAIL; - *failure_ptr = US"recipient"; - new_address_record.result = ccache_reject; - break; - default: break; - } - break; + case sw_mrc_ok: + switch(addr->transport_return) /* ok so far */ + { + case PENDING_OK: done = TRUE; + new_address_record.result = ccache_accept; + break; + case FAIL: done = TRUE; + yield = FAIL; + *failure_ptr = US"recipient"; + new_address_record.result = ccache_reject; + break; + default: break; + } + break; - case -1: /* MAIL response error */ - *failure_ptr = US"mail"; - if (errno == 0 && sx->buffer[0] == '5') - { - setflag(addr, af_verify_nsfail); - if (from_address[0] == 0) - new_domain_record.result = ccache_reject_mfnull; - } - break; - /* non-MAIL read i/o error */ - /* non-MAIL response timeout */ - /* internal error; channel still usable */ - default: break; /* transmit failed */ + case sw_mrc_bad_mail: /* MAIL response error */ + *failure_ptr = US"mail"; + if (errno == 0 && sx->buffer[0] == '5') + { + setflag(addr, af_verify_nsfail); + if (from_address[0] == 0) + new_domain_record.result = ccache_reject_mfnull; + } + break; + /* non-MAIL read i/o error */ + /* non-MAIL response timeout */ + /* internal error; channel still usable */ + default: break; /* transmit failed */ } } @@ -955,7 +956,7 @@ tls_retry_connection: 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) == sw_mrc_ok && addr->transport_return == PENDING_OK ) done = TRUE; commit 690bc2ce8c7f0a76f01d03b5b158b0d64abbc86a Author: Jeremy Harris Date: Sat Oct 14 19:35:34 2023 +0100 fdatasync the spool data file diff --git a/src/src/exim.h b/src/src/exim.h index ccf14f0fd..699b39165 100644 --- a/src/src/exim.h +++ b/src/src/exim.h @@ -569,7 +569,7 @@ requires various things that are set therein. */ #endif #ifdef ENABLE_DISABLE_FSYNC -# define EXIMfsync(f) (disable_fsync? 0 : fsync(f)) +# define EXIMfsync(f) (disable_fsync ? 0 : fsync(f)) #else # define EXIMfsync(f) fsync(f) #endif diff --git a/src/src/receive.c b/src/src/receive.c index ae7045068..63aded09f 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -4052,7 +4052,15 @@ else receive_messagecount++; -if (fflush(spool_data_file)) +if ( fflush(spool_data_file) +#if _POSIX_C_SOURCE >= 199309L || _XOPEN_SOURCE >= 500 +# ifdef ENABLE_DISABLE_FSYNC + || !disable_fsync && fdatasync(data_fd) +# else + || fdatasync(data_fd) +# endif +#endif + ) { errmsg = string_sprintf("Spool write error: %s", strerror(errno)); log_write(0, LOG_MAIN, "%s\n", errmsg); commit 17648b558fc29a488d1e0bc12d2960f892d2838a Author: Jeremy Harris Date: Sun Dec 31 19:59:09 2023 +0000 Rewrites: avoid a split log line when rewiting hits a malformed address. Bug 3160 diff --git a/src/src/parse.c b/src/src/parse.c index 29f98e1e2..ecdbe9f8d 100644 --- a/src/src/parse.c +++ b/src/src/parse.c @@ -69,30 +69,31 @@ Returns: pointer past the end of the address */ uschar * -parse_find_address_end(const uschar *s, BOOL nl_ends) +parse_find_address_end(const uschar * s, BOOL nl_ends) { BOOL source_routing = *s == '@'; -int no_term = source_routing? 1 : 0; +int no_term = source_routing ? 1 : 0; -while (*s != 0 && (*s != ',' || no_term > 0) && (*s != '\n' || !nl_ends)) +while (*s && (*s != ',' || no_term > 0) && (*s != '\n' || !nl_ends)) { /* Skip single quoted characters. Strictly these should not occur outside quoted strings in RFC 822 addresses, but they can in RFC 821 addresses. Pity about the lack of consistency, isn't it? */ - if (*s == '\\' && s[1] != 0) s += 2; + if (*s == '\\' && s[1]) + s += 2; /* Skip quoted items that are not inside brackets. Note that quoted pairs are allowed inside quoted strings. */ else if (*s == '\"') - { - while (*(++s) != 0 && (*s != '\n' || !nl_ends)) + while (*++s && (*s != '\n' || !nl_ends)) { - if (*s == '\\' && s[1] != 0) s++; - else if (*s == '\"') { s++; break; } + if (*s == '\\' && s[1]) + s++; + else if (*s == '\"') + { s++; break; } } - } /* Skip comments, which may include nested brackets, but quotes are not recognized inside comments, though quoted pairs are. */ @@ -100,12 +101,13 @@ while (*s != 0 && (*s != ',' || no_term > 0) && (*s != '\n' || !nl_ends)) else if (*s == '(') { int level = 1; - while (*(++s) != 0 && (*s != '\n' || !nl_ends)) - { - if (*s == '\\' && s[1] != 0) s++; - else if (*s == '(') level++; - else if (*s == ')' && --level <= 0) { s++; break; } - } + while (*++s && (*s != '\n' || !nl_ends)) + if (*s == '\\' && s[1]) + s++; + else if (*s == '(') + level++; + else if (*s == ')' && --level <= 0) + { s++; break; } } /* Non-special character; just advance. Passing the colon in a source @@ -117,10 +119,12 @@ while (*s != 0 && (*s != ',' || no_term > 0) && (*s != '\n' || !nl_ends)) if (*s == '<') { source_routing = s[1] == '@'; - no_term = source_routing? 2 : 1; + no_term = source_routing ? 2 : 1; } - else if (*s == '>') no_term--; - else if (source_routing && *s == ':') no_term--; + else if (*s == '>') + no_term--; + else if (source_routing && *s == ':') + no_term--; s++; } } diff --git a/src/src/receive.c b/src/src/receive.c index 63aded09f..ae4203e7f 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -3069,7 +3069,7 @@ if ( from_header it has already been rewritten as part of verification for SMTP input. */ DEBUG(D_rewrite) - { debug_printf("global rewrite rules\n"); acl_level++; } + { debug_printf("rewrite rules on sender address\n"); acl_level++; } if (global_rewrite_rules && !sender_address_unrewritten && *sender_address) { /* deconst ok as src was not const */ @@ -3096,7 +3096,7 @@ documented as happening *after* recipient addresses are taken from the headers by the -t command line option. An added Sender: gets rewritten here. */ DEBUG(D_rewrite) - { debug_printf("rewrite headers\n"); acl_level++; } + { debug_printf("qualify and rewrite headers\n"); acl_level++; } for (header_line * h = header_list->next, * newh; h; h = h->next) if ((newh = rewrite_header(h, NULL, NULL, global_rewrite_rules, rewrite_existflags, TRUE))) @@ -3135,9 +3135,11 @@ new Received:) has not yet been set. */ DEBUG(D_receive) { debug_printf(">>Headers after rewriting and local additions:\n"); + acl_level++; for (header_line * h = header_list->next; h; h = h->next) - debug_printf("%c %s", h->type, h->text); + debug_printf_indent("%c %s", h->type, h->text); debug_printf("\n"); + acl_level--; } /* The headers are now complete in store. If we are running in filter diff --git a/src/src/rewrite.c b/src/src/rewrite.c index c0d173abf..318cda215 100644 --- a/src/src/rewrite.c +++ b/src/src/rewrite.c @@ -450,13 +450,13 @@ rewrite_one_header(header_line *h, int flag, rewrite_rule *rewrite_rules, int existflags, BOOL replace) { int lastnewline = 0; -header_line *newh = NULL; +header_line * newh = NULL; rmark function_reset_point = store_mark(); -uschar *s = Ustrchr(h->text, ':') + 1; +uschar * s = Ustrchr(h->text, ':') + 1; while (isspace(*s)) s++; -DEBUG(D_rewrite) +DEBUG(D_rewrite) /* The header text includes the trailing newline */ debug_printf_indent("rewrite_one_header: type=%c:\n %s", h->type, h->text); f.parse_allow_group = TRUE; /* Allow group syntax */ @@ -470,24 +470,31 @@ We want to avoid keeping store for any intermediate versions. */ while (*s) { - uschar *sprev; - uschar *ss = parse_find_address_end(s, FALSE); - uschar *recipient, *new; + uschar * sprev = s; + uschar * ss = parse_find_address_end(s, FALSE), * ss1 = ss; + uschar * recipient, * new; rmark loop_reset_point = store_mark(); - uschar *errmess = NULL; + uschar * errmess = NULL; BOOL changed = FALSE; - int terminator = *ss; + uschar terminator = *ss; int start, end, domain; + /* If we hit the end of the header, trim trailing newline and whitespace */ + + if (!terminator) + { + while (ss1 > s && isspace(ss1[-1])) ss1--; + terminator = *ss1; + } + /* Temporarily terminate the string at this point, and extract the operative address within. Then put back the terminator and prepare for the next address, saving the start of the old one. */ - *ss = 0; + *ss1 = '\0'; recipient = parse_extract_address(s, &errmess, &start, &end, &domain, FALSE); - *ss = terminator; - sprev = s; - s = ss + (terminator ? 1 : 0); + *ss1 = terminator; + s = ss + (*ss ? 1 : 0); while (isspace(*s)) s++; /* There isn't much we can do for syntactic disasters at this stage. @@ -587,7 +594,8 @@ while (*s) point, because we may have a rewritten line from a previous time round the loop. */ - if (!changed) loop_reset_point = store_reset(loop_reset_point); + if (!changed) + loop_reset_point = store_reset(loop_reset_point); /* If the address has changed, create a new header containing the rewritten address. We do not need to set the chain pointers at this commit 12fab4b0af476cf9d4bd389a2c4ebf7ff76e8252 Author: Jeremy Harris Date: Sun Dec 31 22:12:17 2023 +0000 Address qualification: ensure maformed addresses are logged. Bug 3061 diff --git a/src/src/rewrite.c b/src/src/rewrite.c index 318cda215..70bd79e26 100644 --- a/src/src/rewrite.c +++ b/src/src/rewrite.c @@ -510,8 +510,8 @@ while (*s) address, "To: undisclosed recpients:;" being the classic case. Ignore this one and carry on. */ - if ((rewrite_rules || routed_old) && Ustrcmp(errmess, "empty address") != 0) - log_write(0, LOG_MAIN, "rewrite: %s", errmess); + if (Ustrcmp(errmess, "empty address") != 0) + log_write(0, LOG_MAIN, "qualify/rewrite: %s", errmess); loop_reset_point = store_reset(loop_reset_point); continue; commit 4f1d6cb9b60f4d26c0654eba8889c04079d046d2 Author: Jeremy Harris Date: Thu Jan 4 22:29:59 2024 +0000 Report two-phase queue run in daemon startup log line diff --git a/src/src/daemon.c b/src/src/daemon.c index aff05120a..704ede24e 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -1654,7 +1654,7 @@ return 0; -const uschar * +static const uschar * describe_queue_runners(void) { gstring * g = NULL; @@ -1664,6 +1664,7 @@ if (!is_multiple_qrun()) return US"no queue runs"; for (qrunner * q = qrunners; q; q = q->next) { g = string_catn(g, US"-q", 2); + if (q->queue_2stage) g = string_catn(g, US"q", 1); if (q->name) g = string_append(g, 3, US"G", q->name, US"/"); g = string_cat(g, readconf_printtime(q->interval)); g = string_catn(g, US" ", 1); commit 77da14136b689daefe37608b4fd77417cb8c17b3 Author: Simon Arlott Date: Sun Jan 7 18:04:55 2024 +0000 DKIM: signing with t but no x timestamp. Bug 2851 diff --git a/src/src/dkim.c b/src/src/dkim.c index 59025608f..63b16af39 100644 --- a/src/src/dkim.c +++ b/src/src/dkim.c @@ -720,8 +720,12 @@ if (dkim_domain) if (!(dkim_timestamps_expanded = expand_string(dkim->dkim_timestamps))) { errwhen = US"dkim_timestamps"; goto expand_bad; } else - xval = (tval = (unsigned long) time(NULL)) - + strtoul(CCS dkim_timestamps_expanded, NULL, 10); + { + tval = (unsigned long) time(NULL); + xval = strtoul(CCS dkim_timestamps_expanded, NULL, 10); + if (xval > 0) + xval += tval; + } if (!(sig = pdkim_init_sign(&dkim_sign_ctx, dkim_signing_domain, dkim_signing_selector, commit 15851896ef1507c0901a4150fd869cbce6c17e3d Author: Jeremy Harris Date: Mon Jan 8 16:01:55 2024 +0000 Fix recipient (or source) selection in combination with periodic queue run. Bug 3064 Broken-by: 1e835086d159 diff --git a/src/src/exim.c b/src/src/exim.c index 4473373b3..c53b98186 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -3688,7 +3688,7 @@ on the second character (the one after '-'), to save some effort. */ in all cases provided there are no further characters in this argument. */ - alloc_onetime_qrunner(); + if (!qrunners) alloc_onetime_qrunner(); qrunners->queue_2stage = f.queue_2stage; if (*argrest) for (int i = 0; i < nelem(rsopts); i++) commit 4da37662868dcfd8ec23ad9d7f643ef014b1a42b Author: Heiko Schlichting Date: Fri Jan 12 21:43:54 2024 +0000 Eximon: handle new-format message IDs Broken-by: 46a36afae4 diff --git a/src/exim_monitor/em_hdr.h b/src/exim_monitor/em_hdr.h index feffa6bfc..f79686a80 100644 --- a/src/exim_monitor/em_hdr.h +++ b/src/exim_monitor/em_hdr.h @@ -190,7 +190,7 @@ typedef struct queue_item { int update_time; int size; uschar *sender; - uschar name[17]; + uschar name[MESSAGE_ID_LENGTH + 1]; uschar seen; uschar frozen; uschar dir_char; diff --git a/src/exim_monitor/em_log.c b/src/exim_monitor/em_log.c index 55925d786..fbcb6b1c0 100644 --- a/src/exim_monitor/em_log.c +++ b/src/exim_monitor/em_log.c @@ -108,7 +108,9 @@ length = Ustrlen(buffer); #ifdef ANONYMIZE { uschar *p = buffer + 9; - if (p[6] == '-' && p[13] == '-') p += 17; + if ( p[MESSAGE_ID_TIME_LEN] == '-' + && p[MESSAGE_ID_TIME_LEN + MESSAGE_ID_PID_LEN + 1] == '-') + p += MESSAGE_ID_LENGTH + 1; while (p < buffer + length) { commit 06b43f1b7f7319445d4a71b9a60e3f2f144cf7a2 Author: Jeremy Harris Date: Sat Jan 13 15:24:12 2024 +0000 Lookups: log warning for deprecated syntax. Bug 3068 diff --git a/src/src/lookups/lf_sqlperform.c b/src/src/lookups/lf_sqlperform.c index ecb0a3221..d5bb8d297 100644 --- a/src/src/lookups/lf_sqlperform.c +++ b/src/src/lookups/lf_sqlperform.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ @@ -56,6 +56,8 @@ if (Ustrncmp(query, "servers", 7) == 0) const uschar *qserverlist; uschar *qserver; + log_write(0, LOG_MAIN|LOG_CONFIG_IN, "WARNING: obslete syntax used for lookup\n"); + s = query + 7; skip_whitespace(&s); if (*s++ != '=') commit 17061038519b42b6919af1d3d82d346f2259a114 Author: Jeremy Harris Date: Mon Jan 15 12:54:41 2024 +0000 Exinext: handle new-format message IDs Broken-by: 46a36afae4 diff --git a/src/src/exinext.src b/src/src/exinext.src index 1a1d88f6b..882ad5f60 100644 --- a/src/src/exinext.src +++ b/src/src/exinext.src @@ -163,6 +163,7 @@ perl - $exim_path "$eximmacdef" $argone $spool_directory $qualify_domain $config if (scalar(@list) == 0) { + push(@list, $subject) if $subject =~ /^\w{6}-\w{11}-\w{4}$/; push(@list, $subject) if $subject =~ /^\w{6}-\w{6}-\w{2}$/; if ($subject !~ /\@/ && $subject !~ /\./) commit fe105877d57ac7e05a4333e0d072f232d212b9fe Author: Jeremy Harris Date: Sat Jan 20 12:38:15 2024 +0000 TLS: fix startup after forced-fail Broken-by: cf0c61644d7d, ce93c6d840d5, 520ef00f56ce diff --git a/src/src/expand.c b/src/src/expand.c index 9a88d38ca..f9813133d 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -8558,13 +8558,12 @@ Returns: the expanded string, or NULL if expansion failed; if failure was const uschar * expand_string_2(const uschar * string, BOOL * textonly_p) { +f.expand_string_forcedfail = f.search_find_defer = malformed_header = FALSE; if (Ustrpbrk(string, "$\\") != NULL) { int old_pool = store_pool; uschar * s; - f.search_find_defer = FALSE; - malformed_header = FALSE; store_pool = POOL_MAIN; s = expand_string_internal(string, ESI_HONOR_DOLLAR, NULL, NULL, textonly_p); store_pool = old_pool; diff --git a/src/src/tls.c b/src/src/tls.c index b6c5d70e1..e6203b768 100644 --- a/src/src/tls.c +++ b/src/src/tls.c @@ -105,7 +105,10 @@ expand_check(const uschar * s, const uschar * name, uschar ** result, uschar ** errstr) { if (!s) + { + f.expand_string_forcedfail = FALSE; *result = NULL; + } else if ( !(*result = expand_string(US s)) /* need to clean up const more */ && !f.expand_string_forcedfail ) commit b4e7527561f1c68b821d5cf25efe29ae63d1d434 Author: Jeremy Harris Date: Thu Jan 25 17:48:43 2024 +0000 Appendfile: release regex-match store every thousand files. Bug 3047 diff --git a/src/src/exim.c b/src/src/exim.c index c53b98186..e5b72f11a 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -49,6 +49,8 @@ optimize out the tail recursion and so not make them too expensive. */ static void * function_store_malloc(PCRE2_SIZE size, void * tag) { +if (size > INT_MAX) + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "excessive memory alloc request"); return store_malloc((int)size); } @@ -63,12 +65,15 @@ if (block) store_free(block); static void * function_store_get(PCRE2_SIZE size, void * tag) { +if (size > INT_MAX) + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "excessive memory alloc request"); return store_get((int)size, GET_UNTAINTED); /* loses track of taint */ } static void function_store_nullfree(void * block, void * tag) { +/* We cannot free memory allocated using store_get() */ } diff --git a/src/src/transports/appendfile.c b/src/src/transports/appendfile.c index ec41ca035..91b353079 100644 --- a/src/src/transports/appendfile.c +++ b/src/src/transports/appendfile.c @@ -153,6 +153,10 @@ static const char *mailbox_formats[] = { (!ob->quota_warn_threshold_is_percent || ob->quota_value > 0)) +/* Free memory allocated by PCRE2 every so often, because a recent version +is now using 20kB for every match call */ + +#define RESET_STORE_FILECNT 1000 /************************************************* * Setup entry point * @@ -661,13 +665,14 @@ Returns: the sum of the sizes of the stattable files off_t check_dir_size(const uschar * dirname, int * countptr, const pcre2_code * re) { -DIR *dir; +DIR * dir; off_t sum = 0; -int count = *countptr; +int count = *countptr, lcount = RESET_STORE_FILECNT; +rmark reset_point = store_mark(); if (!(dir = exim_opendir(dirname))) return 0; -for (struct dirent *ent; ent = readdir(dir); ) +for (struct dirent * ent; ent = readdir(dir); ) { uschar * path, * name = US ent->d_name; struct stat statbuf; @@ -675,6 +680,11 @@ for (struct dirent *ent; ent = readdir(dir); ) if (Ustrcmp(name, ".") == 0 || Ustrcmp(name, "..") == 0) continue; count++; + if (--lcount == 0) + { + store_reset(reset_point); reset_point = store_mark(); + lcount = RESET_STORE_FILECNT; + } /* If there's a regex, try to find the size using it */ @@ -726,6 +736,7 @@ DEBUG(D_transport) debug_printf("check_dir_size: dir=%s sum=" OFF_T_FMT " count=%d\n", dirname, sum, count); +store_reset(reset_point); *countptr = count; return sum; } commit 35aacb69f5c839a4b77158464e401d86eb422ed6 Author: Jeremy Harris Date: Fri Jan 26 21:58:59 2024 +0000 ACL: in "regex" condition, release store every thousand lines. Bug 3047 diff --git a/src/src/acl.c b/src/src/acl.c index 9223fcec8..ecba07b25 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -3955,11 +3955,11 @@ for (; cb; cb = cb->next) CUSS &recipient_data); break; - #ifdef WITH_CONTENT_SCAN +#ifdef WITH_CONTENT_SCAN case ACLC_REGEX: rc = regex(&arg, textonly); break; - #endif +#endif case ACLC_REMOVE_HEADER: setup_remove_header(arg); diff --git a/src/src/macros.h b/src/src/macros.h index 5279f70d0..8aed335b5 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -1185,4 +1185,9 @@ typedef enum { sw_mrc_tx_fail, /* transmit failed */ } sw_mrc_t; +/* Recent versions of PCRE2 are allocating 20kB per match, rather than the previous 112 B. +When doing en extended loop of matching, release store periodically. */ + +#define REGEX_LOOPCOUNT_STORE_RESET 1000 + /* End of macros.h */ diff --git a/src/src/regex.c b/src/src/regex.c index af425db2d..cda807716 100644 --- a/src/src/regex.c +++ b/src/src/regex.c @@ -31,12 +31,11 @@ extern uschar *mime_current_boundary; static pcre_list * -compile(const uschar * list, BOOL cacheable) +compile(const uschar * list, BOOL cacheable, int * cntp) { -int sep = 0; +int sep = 0, cnt = 0; uschar * regex_string; -pcre_list * re_list_head = NULL; -pcre_list * ri; +pcre_list * re_list_head = NULL, * ri; /* precompile our regexes */ while ((regex_string = string_nextinlist(&list, &sep, NULL, 0))) @@ -58,7 +57,9 @@ while ((regex_string = string_nextinlist(&list, &sep, NULL, 0))) ri->pcre_text = regex_string; ri->next = re_list_head; re_list_head = ri; + cnt++; } +if (cntp) *cntp = cnt; return re_list_head; } @@ -112,7 +113,8 @@ FILE * mbox_file; pcre_list * re_list_head; uschar * linebuffer; long f_pos = 0; -int ret = FAIL; +int ret = FAIL, cnt, lcount = REGEX_LOOPCOUNT_STORE_RESET; +rmark reset_point; regex_vars_clear(); @@ -136,26 +138,34 @@ else mbox_file = mime_stream; } -/* precompile our regexes */ -if (!(re_list_head = compile(*listptr, cacheable))) - return FAIL; /* no regexes -> nothing to do */ - -/* match each line against all regexes */ -linebuffer = store_get(32767, GET_TAINTED); -while (fgets(CS linebuffer, 32767, mbox_file)) +reset_point = store_mark(); { - if ( mime_stream && mime_current_boundary /* check boundary */ - && Ustrncmp(linebuffer, "--", 2) == 0 - && Ustrncmp((linebuffer+2), mime_current_boundary, - Ustrlen(mime_current_boundary)) == 0) - break; /* found boundary */ - - if ((ret = matcher(re_list_head, linebuffer, (int)Ustrlen(linebuffer))) == OK) - goto done; + /* precompile our regexes */ + if ((re_list_head = compile(*listptr, cacheable, &cnt))) + { + /* match each line against all regexes */ + linebuffer = store_get(32767, GET_TAINTED); + while (fgets(CS linebuffer, 32767, mbox_file)) + { + if ( mime_stream && mime_current_boundary /* check boundary */ + && Ustrncmp(linebuffer, "--", 2) == 0 + && Ustrncmp((linebuffer+2), mime_current_boundary, + Ustrlen(mime_current_boundary)) == 0) + break; /* found boundary */ + + if ((ret = matcher(re_list_head, linebuffer, (int)Ustrlen(linebuffer))) == OK) + break; + + if ((lcount -= cnt) <= 0) + { + store_reset(reset_point); reset_point = store_mark(); + lcount = REGEX_LOOPCOUNT_STORE_RESET; + } + } + } } -/* no matches ... */ +store_reset(reset_point); -done: if (!mime_stream) (void)fclose(mbox_file); else @@ -180,14 +190,11 @@ pcre_list * re_list_head = NULL; FILE * f; uschar * mime_subject = NULL; int mime_subject_len = 0; -int ret; +int ret = FAIL; +rmark reset_point; regex_vars_clear(); -/* precompile our regexes */ -if (!(re_list_head = compile(*listptr, cacheable))) - return FAIL; /* no regexes -> nothing to do */ - /* check if the file is already decoded */ if (!mime_decoded_filename) { /* no, decode it first */ @@ -210,12 +217,20 @@ if (!(f = fopen(CS mime_decoded_filename, "rb"))) return DEFER; } -/* get 32k memory, tainted */ -mime_subject = store_get(32767, GET_TAINTED); +reset_point = store_mark(); + { + /* precompile our regexes */ + if ((re_list_head = compile(*listptr, cacheable, NULL))) + { + /* get 32k memory, tainted */ + mime_subject = store_get(32767, GET_TAINTED); -mime_subject_len = fread(mime_subject, 1, 32766, f); + mime_subject_len = fread(mime_subject, 1, 32766, f); -ret = matcher(re_list_head, mime_subject, mime_subject_len); + ret = matcher(re_list_head, mime_subject, mime_subject_len); + } + } +store_reset(reset_point); (void)fclose(f); return ret; } diff --git a/src/src/transports/appendfile.c b/src/src/transports/appendfile.c index 91b353079..ce52cc6ff 100644 --- a/src/src/transports/appendfile.c +++ b/src/src/transports/appendfile.c @@ -153,11 +153,6 @@ static const char *mailbox_formats[] = { (!ob->quota_warn_threshold_is_percent || ob->quota_value > 0)) -/* Free memory allocated by PCRE2 every so often, because a recent version -is now using 20kB for every match call */ - -#define RESET_STORE_FILECNT 1000 - /************************************************* * Setup entry point * *************************************************/ @@ -667,7 +662,7 @@ check_dir_size(const uschar * dirname, int * countptr, const pcre2_code * re) { DIR * dir; off_t sum = 0; -int count = *countptr, lcount = RESET_STORE_FILECNT; +int count = *countptr, lcount = REGEX_LOOPCOUNT_STORE_RESET; rmark reset_point = store_mark(); if (!(dir = exim_opendir(dirname))) return 0; @@ -683,7 +678,7 @@ for (struct dirent * ent; ent = readdir(dir); ) if (--lcount == 0) { store_reset(reset_point); reset_point = store_mark(); - lcount = RESET_STORE_FILECNT; + lcount = REGEX_LOOPCOUNT_STORE_RESET; } /* If there's a regex, try to find the size using it */ commit d0e4bb183c038fe38ee249c1bd096f49120e908a Author: Jeremy Harris Date: Fri Jan 26 21:18:01 2024 +0000 Fix smtp transport response to close after all rcpt fates determined. Bug 3059 diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 817d729a7..c205145a7 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -4518,7 +4518,26 @@ if (!sx->ok) break; case ERRNO_SMTPCLOSED: - message_error = Ustrncmp(smtp_command,"end ",4) == 0; + /* If the peer closed the TCP connection after end-of-data, but before + we could send QUIT, do TLS close, etc - call it a message error. + Otherwise, if all the recipients have been dealt with, call a close no + error at all; each address_item should have a suitable result already + (2xx: PENDING_OK, 4xx: DEFER, 5xx: FAIL) */ + + if (!(message_error = Ustrncmp(smtp_command,"end ",4) == 0)) + { + address_item * addr; + for (addr = sx->addrlist; addr; addr = addr->next) + if (addr->transport_return == PENDING_DEFER) + break; + if (!addr) /* all rcpts fates determined */ + { + log_write(0, LOG_MAIN, "peer close after all rcpt responses;" + " converting i/o-error to no-error"); + sx->ok = TRUE; + goto happy; + } + } break; #ifndef DISABLE_DKIM @@ -4542,7 +4561,8 @@ if (!sx->ok) break; } - /* Handle the cases that are treated as message errors. These are: + /* Handle the cases that are treated as message errors (as opposed to + host-errors). These are: (a) negative response or timeout after MAIL (b) negative response after DATA @@ -4646,6 +4666,8 @@ connection to a new process. However, not all servers can handle this (Exim can), so we do not pass such a connection on if the host matches hosts_nopass_tls. */ +happy: + 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, @@ -5063,16 +5085,16 @@ Returns: the first address for this delivery */ static address_item * -prepare_addresses(address_item *addrlist, host_item *host) +prepare_addresses(address_item * addrlist, host_item * host) { -address_item *first_addr = NULL; +address_item * first_addr = NULL; for (address_item * addr = addrlist; addr; addr = addr->next) if (addr->transport_return == DEFER) { if (!first_addr) first_addr = addr; addr->transport_return = PENDING_DEFER; addr->basic_errno = 0; - addr->more_errno = (host->mx >= 0)? 'M' : 'A'; + addr->more_errno = host->mx >= 0 ? 'M' : 'A'; addr->message = NULL; #ifndef DISABLE_TLS addr->cipher = NULL; @@ -5104,24 +5126,17 @@ FALSE. */ BOOL smtp_transport_entry( - transport_instance *tblock, /* data for this instantiation */ - address_item *addrlist) /* addresses we are working on */ + transport_instance * tblock, /* data for this instantiation */ + address_item * addrlist) /* addresses we are working on */ { int defport; -int hosts_defer = 0; -int hosts_fail = 0; -int hosts_looked_up = 0; -int hosts_retry = 0; -int hosts_serial = 0; -int hosts_total = 0; -int total_hosts_tried = 0; +int hosts_defer = 0, hosts_fail = 0, hosts_looked_up = 0; +int hosts_retry = 0, hosts_serial = 0, hosts_total = 0, total_hosts_tried = 0; BOOL expired = TRUE; -uschar *expanded_hosts = NULL; -uschar *pistring; -uschar *tid = string_sprintf("%s transport", tblock->name); -smtp_transport_options_block *ob = SOB tblock->options_block; -host_item *hostlist = addrlist->host_list; -host_item *host = NULL; +uschar * expanded_hosts = NULL, * pistring; +uschar * tid = string_sprintf("%s transport", tblock->name); +smtp_transport_options_block * ob = SOB tblock->options_block; +host_item * hostlist = addrlist->host_list, * host = NULL; DEBUG(D_transport) { @@ -5161,7 +5176,7 @@ database if the delivery fails temporarily or if we are running with queue_smtp or a 2-stage queue run. This gets unset for certain kinds of error, typically those that are specific to the message. */ -update_waiting = TRUE; +update_waiting = TRUE; /* If a host list is not defined for the addresses - they must all have the same one in order to be passed to a single transport - or if the transport has @@ -5349,16 +5364,12 @@ retry_non_continued: && total_hosts_tried < ob->hosts_max_try_hardlimit; host = nexthost) { - int rc; - int host_af; - BOOL host_is_expired = FALSE; - BOOL message_defer = FALSE; - BOOL some_deferred = FALSE; - address_item *first_addr = NULL; - uschar *interface = NULL; - uschar *retry_host_key = NULL; - uschar *retry_message_key = NULL; - uschar *serialize_key = NULL; + int rc, host_af; + BOOL host_is_expired = FALSE, message_defer = FALSE, some_deferred = FALSE; + address_item * first_addr = NULL; + uschar * interface = NULL; + uschar * retry_host_key = NULL, * retry_message_key = NULL; + uschar * serialize_key = NULL; /* Deal slightly better with a possible Linux kernel bug that results in intermittent TFO-conn fails deep into the TCP flow. Bug 2907 tracks. @@ -5665,7 +5676,14 @@ retry_non_continued: out the result of previous attempts, and finding the first address that is still to be delivered. */ - first_addr = prepare_addresses(addrlist, host); + if (!(first_addr = prepare_addresses(addrlist, host))) + { + /* Obscure situation; at least one case (bug 3059, fixed) where + a previous host try returned DEFER, but having moved all + recipients away from DEFER (the waiting-to-be-done state). */ + DEBUG(D_transport) debug_printf("no pending recipients\n"); + goto END_TRANSPORT; + } DEBUG(D_transport) debug_printf("delivering %s to %s [%s] (%s%s)\n", message_id, host->name, host->address, addrlist->address, @@ -5717,7 +5735,7 @@ retry_non_continued: { host_item * thost; /* Make a copy of the host if it is local to this invocation - of the transport. */ + of the transport. */ if (expanded_hosts) { @@ -5911,20 +5929,14 @@ retry_non_continued: if (rc == OK) for (address_item * addr = addrlist; addr; addr = addr->next) if (addr->transport_return == DEFER) - { - some_deferred = TRUE; - break; - } + { some_deferred = TRUE; break; } /* If no addresses deferred or the result was ERROR, return. We do this for ERROR because a failing filter set-up or add_headers expansion is likely to fail for any host we try. */ if (rc == ERROR || (rc == OK && !some_deferred)) - { - DEBUG(D_transport) debug_printf("Leaving %s transport\n", tblock->name); - return TRUE; /* Each address has its status */ - } + goto END_TRANSPORT; /* If the result was DEFER or some individual addresses deferred, let the loop run to try other hosts with the deferred addresses, except for the @@ -5944,7 +5956,7 @@ retry_non_continued: if ((rc == DEFER || some_deferred) && nexthost) { BOOL timedout; - retry_config *retry = retry_find_config(host->name, NULL, 0, 0); + retry_config * retry = retry_find_config(host->name, NULL, 0, 0); if (retry && retry->rules) { commit cd8cb71deb2d653228cc037cf91ecab980acdcd0 Author: Jeremy Harris Date: Sat Feb 10 16:44:54 2024 +0000 Move ESMPT LIMITS extension from experimental to mainline diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults index 28d4f13f7..c08f1874d 100644 --- a/src/src/config.h.defaults +++ b/src/src/config.h.defaults @@ -51,6 +51,7 @@ Do not put spaces between # and the 'define'. #define DISABLE_D_OPTION #define DISABLE_DNSSEC #define DISABLE_DKIM +#define DISABLE_ESMTP_LIMITS #define DISABLE_EVENT #define DISABLE_OCSP #define DISABLE_PIPE_CONNECT @@ -209,7 +210,6 @@ Do not put spaces between # and the 'define'. #define EXPERIMENTAL_BRIGHTMAIL #define EXPERIMENTAL_DCC #define EXPERIMENTAL_DSN_INFO -#define EXPERIMENTAL_ESMTP_LIMITS #define EXPERIMENTAL_QUEUEFILE #define EXPERIMENTAL_XCLIENT diff --git a/src/src/exim.c b/src/src/exim.c index e5b72f11a..1f76f8f14 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -1105,6 +1105,9 @@ g = string_cat(g, US"Support for:"); #ifndef DISABLE_DNSSEC g = string_cat(g, US" DNSSEC"); #endif +#ifndef DISABLE_ESMTP_LIMITS + g = string_cat(g, US" ESMTP_Limits"); +#endif #ifndef DISABLE_EVENT g = string_cat(g, US" Event"); #endif @@ -1151,9 +1154,6 @@ g = string_cat(g, US"Support for:"); #ifdef EXPERIMENTAL_DSN_INFO g = string_cat(g, US" Experimental_DSN_info"); #endif -#ifdef EXPERIMENTAL_ESMTP_LIMITS - g = string_cat(g, US" Experimental_ESMTP_Limits"); -#endif #ifdef EXPERIMENTAL_QUEUEFILE g = string_cat(g, US" Experimental_QUEUEFILE"); #endif @@ -3019,7 +3019,7 @@ on the second character (the one after '-'), to save some effort. */ case 'K': smtp_peer_options |= OPTION_CHUNKING; break; -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS /* -MCL: peer used LIMITS RCPTMAX and/or RCPTDOMAINMAX */ case 'L': if (++i < argc) continue_limit_mail = Uatoi(argv[i]); else badarg = TRUE; diff --git a/src/src/functions.h b/src/src/functions.h index ef84e3d7c..77323a5c1 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -632,7 +632,7 @@ extern void transport_do_pass_socket(const uschar *, const uschar *, const uschar *, uschar *, int); extern void transport_init(void); extern BOOL transport_pass_socket(const uschar *, const uschar *, const uschar *, uschar *, int -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS , unsigned, unsigned, unsigned #endif ); diff --git a/src/src/globals.c b/src/src/globals.c index c535ea121..c483ab182 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -706,7 +706,7 @@ unsigned chunking_data_left = 0; chunking_state_t chunking_state= CHUNKING_NOT_OFFERED; const pcre2_code *regex_CHUNKING = NULL; -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS const pcre2_code *regex_LIMITS = NULL; #endif @@ -746,7 +746,7 @@ uschar *continue_hostname = NULL; uschar *continue_host_address = NULL; int continue_sequence = 1; uschar *continue_transport = NULL; -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS unsigned continue_limit_mail = 0; unsigned continue_limit_rcpt = 0; unsigned continue_limit_rcptdom= 0; @@ -1029,7 +1029,7 @@ const uschar *letter_digit_hyphen_dot = US"abcdefghijklmnopqrstuvwxyz" ".-0123456789" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS uschar *limits_advertise_hosts = US"*"; #endif int load_average = -2; diff --git a/src/src/globals.h b/src/src/globals.h index ffc7150f2..b5c3a520e 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -449,7 +449,7 @@ extern uschar *continue_hostname; /* Host for continued delivery */ extern uschar *continue_host_address; /* IP address for ditto */ extern int continue_sequence; /* Sequence num for continued delivery */ extern uschar *continue_transport; /* Transport for continued delivery */ -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS extern unsigned continue_limit_mail; /* Peer advertised limit */ extern unsigned continue_limit_rcpt; extern unsigned continue_limit_rcptdom; @@ -688,7 +688,7 @@ extern int keep_malformed; /* Time to keep malformed messages */ extern uschar *eldap_dn; /* Where LDAP DNs are left */ extern const uschar *letter_digit_hyphen_dot; /* Legitimate DNS host name chars */ -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS extern uschar *limits_advertise_hosts; /* for banner/EHLO pipelining */ #endif extern int load_average; /* Most recently read load average */ @@ -902,7 +902,7 @@ extern const pcre2_code *regex_check_dns_names; /* For DNS name checking */ extern const pcre2_code *regex_From; /* For recognizing "From_" lines */ extern const pcre2_code *regex_CHUNKING; /* For recognizing CHUNKING (RFC 3030) */ extern const pcre2_code *regex_IGNOREQUOTA; /* For recognizing IGNOREQUOTA (LMTP) */ -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS extern const pcre2_code *regex_LIMITS; /* For recognizing LIMITS */ #endif extern const pcre2_code *regex_PIPELINING; /* For recognizing PIPELINING */ diff --git a/src/src/hintsdb_structs.h b/src/src/hintsdb_structs.h index 0e5853b2b..5da311c2b 100644 --- a/src/src/hintsdb_structs.h +++ b/src/src/hintsdb_structs.h @@ -163,7 +163,7 @@ typedef struct { unsigned short cleartext_auths; unsigned short crypted_auths; -# ifdef EXPERIMENTAL_ESMTP_LIMITS +# ifndef DISABLE_ESMTP_LIMITS unsigned int limit_mail; unsigned int limit_rcpt; unsigned int limit_rcptdom; diff --git a/src/src/macro_predef.c b/src/src/macro_predef.c index bdc587502..9b354d345 100644 --- a/src/src/macro_predef.c +++ b/src/src/macro_predef.c @@ -160,6 +160,9 @@ due to conflicts with other common macros. */ #ifndef DISABLE_DNSSEC builtin_macro_create(US"_HAVE_DNSSEC"); #endif +#ifndef DISABLE_ESMTP_LIMITS + builtin_macro_create(US"_HAVE_ESMTP_LIMITS"); +#endif #ifndef DISABLE_EVENT builtin_macro_create(US"_HAVE_EVENT"); #endif @@ -309,9 +312,6 @@ exp_features(void) #ifdef EXPERIMENTAL_DSN_INFO builtin_macro_create(US"_EXP_DSNI"); #endif -#ifdef EXPERIMENTAL_ESMTP_LIMITS - builtin_macro_create(US"_EXP_LIMITS"); -#endif #ifdef EXPERIMENTAL_QUEUEFILE builtin_macro_create(US"_EXP_QUEUEFILE"); #endif diff --git a/src/src/readconf.c b/src/src/readconf.c index bbcca87f7..8ee6e9fe0 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -209,7 +209,7 @@ static optionlist optionlist_config[] = { { "ldap_start_tls", opt_bool, {&eldap_start_tls} }, { "ldap_version", opt_int, {&eldap_version} }, #endif -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS { "limits_advertise_hosts", opt_stringptr, {&limits_advertise_hosts} }, #endif { "local_from_check", opt_bool, {&local_from_check} }, diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 541288301..a57c9821e 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -4054,7 +4054,7 @@ while (done <= 0) g = string_catn(g, US"-SIZE\r\n", 7); } -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS if ( (smtp_mailcmd_max > 0 || recipients_max) && verify_check_host(&limits_advertise_hosts) == OK) { diff --git a/src/src/transport.c b/src/src/transport.c index 79a69f941..5729f8e75 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -1941,7 +1941,7 @@ const uschar **argv; #ifndef DISABLE_TLS if (smtp_peer_options & OPTION_TLS) i += 6; #endif -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS if (continue_limit_mail || continue_limit_rcpt || continue_limit_rcptdom) i += 4; #endif @@ -1983,7 +1983,7 @@ if (smtp_peer_options & OPTION_TLS) argv[i++] = US"-MCT"; #endif -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS if (continue_limit_rcpt || continue_limit_rcptdom) { argv[i++] = US"-MCL"; @@ -2055,7 +2055,7 @@ Returns: FALSE if fork fails; TRUE otherwise BOOL transport_pass_socket(const uschar *transport_name, const uschar *hostname, const uschar *hostaddress, uschar *id, int socket_fd -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS , unsigned peer_limit_mail, unsigned peer_limit_rcpt, unsigned peer_limit_rcptdom #endif ) @@ -2065,7 +2065,7 @@ int status; DEBUG(D_transport) debug_printf("transport_pass_socket entered\n"); -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS continue_limit_mail = peer_limit_mail; continue_limit_rcpt = peer_limit_rcpt; continue_limit_rcptdom = peer_limit_rcptdom; diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index c205145a7..596a328c3 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -277,7 +277,7 @@ struct list #ifndef DISABLE_PIPE_CONNECT { ®ex_EARLY_PIPE, US"\\n250[\\s\\-]" EARLY_PIPE_FEATURE_NAME "(\\s|\\n|$)" }, #endif -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS { ®ex_LIMITS, US"\\n250[\\s\\-]LIMITS\\s" }, #endif }; @@ -797,7 +797,7 @@ sx->conn_args.have_lbserver = TRUE; /******************************************************************************/ -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS /* If TLS, or TLS not offered, called with the EHLO response in the buffer. Check it for a LIMITS keyword and parse values into the smtp context structure. @@ -900,7 +900,7 @@ write_ehlo_cache_entry(smtp_context * sx) { open_db dbblock, * dbm_file; -# ifdef EXPERIMENTAL_ESMTP_LIMITS +# ifndef DISABLE_ESMTP_LIMITS sx->ehlo_resp.limit_mail = sx->peer_limit_mail; sx->ehlo_resp.limit_rcpt = sx->peer_limit_rcpt; sx->ehlo_resp.limit_rcptdom = sx->peer_limit_rcptdom; @@ -912,7 +912,7 @@ if ((dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE))) dbdata_ehlo_resp er = { .data = sx->ehlo_resp }; HDEBUG(D_transport) -# ifdef EXPERIMENTAL_ESMTP_LIMITS +# ifndef DISABLE_ESMTP_LIMITS if (sx->ehlo_resp.limit_mail || sx->ehlo_resp.limit_rcpt || sx->ehlo_resp.limit_rcptdom) debug_printf("writing clr %04x/%04x cry %04x/%04x lim %05d/%05d/%05d\n", sx->ehlo_resp.cleartext_features, sx->ehlo_resp.cleartext_auths, @@ -979,7 +979,7 @@ else else { DEBUG(D_transport) -# ifdef EXPERIMENTAL_ESMTP_LIMITS +# ifndef DISABLE_ESMTP_LIMITS if (er->data.limit_mail || er->data.limit_rcpt || er->data.limit_rcptdom) debug_printf("EHLO response bits from cache:" " cleartext 0x%04x/0x%04x crypted 0x%04x/0x%04x lim %05d/%05d/%05d\n", @@ -994,7 +994,7 @@ else er->data.crypted_features, er->data.crypted_auths); sx->ehlo_resp = er->data; -# ifdef EXPERIMENTAL_ESMTP_LIMITS +# ifndef DISABLE_ESMTP_LIMITS ehlo_cache_limits_apply(sx); # endif dbfn_close(dbm_file); @@ -1120,7 +1120,7 @@ if (pending_EHLO) | OPTION_CHUNKING | OPTION_PRDR | OPTION_DSN | OPTION_PIPE | OPTION_SIZE | OPTION_UTF8 | OPTION_EARLY_PIPE ); -# ifdef EXPERIMENTAL_ESMTP_LIMITS +# ifndef DISABLE_ESMTP_LIMITS if (tls_out.active.sock >= 0 || !(peer_offered & OPTION_TLS)) ehlo_response_limits_read(sx); # endif @@ -1147,7 +1147,7 @@ if (pending_EHLO) return OK; /* just carry on */ } -# ifdef EXPERIMENTAL_ESMTP_LIMITS +# ifndef DISABLE_ESMTP_LIMITS /* If we are handling LIMITS, compare the actual EHLO LIMITS values with the cached values and invalidate cache if different. OK to carry on with connect since values are advisory. */ @@ -2305,7 +2305,7 @@ if (!continue_hostname) sx->cctx.tls_ctx = NULL; sx->inblock.cctx = sx->outblock.cctx = &sx->cctx; -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS sx->peer_limit_mail = sx->peer_limit_rcpt = sx->peer_limit_rcptdom = #endif sx->avoid_option = sx->peer_offered = smtp_peer_options = 0; @@ -2607,7 +2607,7 @@ goto SEND_QUIT; ) #endif ); -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS if (tls_out.active.sock >= 0 || !(sx->peer_offered & OPTION_TLS)) { ehlo_response_limits_read(sx); @@ -2672,7 +2672,7 @@ else sx->inblock.cctx = sx->outblock.cctx = &sx->cctx; smtp_command = big_buffer; sx->peer_offered = smtp_peer_options; -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS /* Limits passed by cmdline over exec. */ ehlo_limits_apply(sx, sx->peer_limit_mail = continue_limit_mail, @@ -3000,7 +3000,7 @@ if ( !continue_hostname sx->ehlo_resp.crypted_features = sx->peer_offered; #endif -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS if (tls_out.active.sock >= 0 || !(sx->peer_offered & OPTION_TLS)) { ehlo_response_limits_read(sx); @@ -3423,7 +3423,7 @@ sw_mrc_t smtp_write_mail_and_rcpt_cmds(smtp_context * sx, int * yield) { address_item * addr; -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS address_item * restart_addr = NULL; #endif int address_count, pipe_limit; @@ -3517,7 +3517,7 @@ for (addr = sx->first_addr, address_count = 0, pipe_limit = 100; BOOL no_flush; const uschar * rcpt_addr; -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS if ( sx->single_rcpt_domain /* restriction on domains */ && address_count > 0 /* not first being sent */ && Ustrcmp(addr->domain, sx->first_addr->domain) != 0 /* dom diff from first */ @@ -3606,7 +3606,7 @@ for (addr = sx->first_addr, address_count = 0, pipe_limit = 100; } } /* Loop for next address */ -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS sx->next_addr = restart_addr ? restart_addr : addr; #else sx->next_addr = addr; @@ -3790,7 +3790,7 @@ uschar *message = NULL; uschar new_message_id[MESSAGE_ID_LENGTH + 1]; smtp_context * sx = store_get(sizeof(*sx), GET_TAINTED); /* tainted, for the data buffers */ BOOL pass_message = FALSE; -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS BOOL mail_limit = FALSE; #endif #ifdef SUPPORT_DANE @@ -4674,7 +4674,7 @@ DEBUG(D_transport) sx->send_rset, f.continue_more, yield, sx->first_addr ? "not " : ""); if (sx->completed_addr && sx->ok && sx->send_quit) -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS if (mail_limit = continue_sequence >= sx->max_mail) { DEBUG(D_transport) @@ -4735,7 +4735,7 @@ if (sx->completed_addr && sx->ok && sx->send_quit) if (sx->first_addr) /* More addresses still to be sent */ { /* for this message */ -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS /* Any that we marked as skipped, reset to do now */ for (address_item * a = sx->first_addr; a; a = a->next) if (a->transport_return == SKIP) @@ -4810,7 +4810,7 @@ if (sx->completed_addr && sx->ok && sx->send_quit) */ if (sx->ok && transport_pass_socket(tblock->name, host->name, host->address, new_message_id, socket_fd -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS , sx->peer_limit_mail, sx->peer_limit_rcpt, sx->peer_limit_rcptdom #endif )) @@ -4987,7 +4987,7 @@ if (dane_held) } #endif -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS if (mail_limit && sx->first_addr) { /* Reset the sequence count since we closed the connection. This is flagged diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h index ef9135838..fc8a6b126 100644 --- a/src/src/transports/smtp.h +++ b/src/src/transports/smtp.h @@ -176,7 +176,7 @@ typedef struct { BOOL pending_BDAT:1; BOOL RCPT_452:1; BOOL good_RCPT:1; -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS BOOL single_rcpt_domain:1; #endif BOOL completed_addr:1; @@ -185,7 +185,7 @@ typedef struct { BOOL send_tlsclose:1; unsigned peer_offered; -#ifdef EXPERIMENTAL_ESMTP_LIMITS +#ifndef DISABLE_ESMTP_LIMITS unsigned peer_limit_mail; unsigned peer_limit_rcpt; unsigned peer_limit_rcptdom; commit 84add256b3467cf6e0d4a2edf72cf5bf16cd9306 Author: Jeremy Harris Date: Sun Feb 11 13:57:18 2024 +0000 Use non-releaseable memory for regex match strings. Bug 3047 Broken-by: 35aacb69f5c8 diff --git a/src/src/regex.c b/src/src/regex.c index cda807716..bb34e5b23 100644 --- a/src/src/regex.c +++ b/src/src/regex.c @@ -63,6 +63,13 @@ if (cntp) *cntp = cnt; return re_list_head; } + +/* Check list of REs against buffer, returning OK for (first) match, +else FAIL. On match return allocated result strings in regex_vars[]. + +We use the perm-pool for that, so that our caller can release +other allocations. +*/ static int matcher(pcre_list * re_list_head, uschar * linebuffer, int len) { @@ -75,6 +82,9 @@ for (pcre_list * ri = re_list_head; ri; ri = ri->next) /* try matcher on the line */ if ((n = pcre2_match(ri->re, (PCRE2_SPTR)linebuffer, len, 0, 0, md, pcre_gen_mtc_ctx)) > 0) { + int save_pool = store_pool; + store_pool = POOL_PERM; + Ustrncpy(regex_match_string_buffer, ri->pcre_text, sizeof(regex_match_string_buffer)-1); regex_match_string = regex_match_string_buffer; @@ -87,6 +97,7 @@ for (pcre_list * ri = re_list_head; ri; ri = ri->next) regex_vars[nn-1] = string_copyn(linebuffer + ovec[off], len); } + store_pool = save_pool; return OK; } } commit 6fcb3173d64ef8a9d70f8adf19f134a0cd9cf6e8 Author: Jeremy Harris Date: Sun Feb 11 15:04:58 2024 +0000 use dynamic mem for regex_match_string diff --git a/src/src/regex.c b/src/src/regex.c index bb34e5b23..d5dc10258 100644 --- a/src/src/regex.c +++ b/src/src/regex.c @@ -24,8 +24,6 @@ typedef struct pcre_list { struct pcre_list * next; } pcre_list; -uschar regex_match_string_buffer[1024]; - extern FILE *mime_stream; extern uschar *mime_current_boundary; @@ -85,9 +83,7 @@ for (pcre_list * ri = re_list_head; ri; ri = ri->next) int save_pool = store_pool; store_pool = POOL_PERM; - Ustrncpy(regex_match_string_buffer, ri->pcre_text, - sizeof(regex_match_string_buffer)-1); - regex_match_string = regex_match_string_buffer; + regex_match_string = string_copy(ri->pcre_text); for (int nn = 1; nn < n; nn++) { commit a173a4376d168edbf3fe2494dff998c4060bf425 Author: Jeremy Harris Date: Tue Feb 13 17:34:19 2024 +0000 Use non-releasable memory for regex line-buffer Broken-by: 5aacb69f5c8 diff --git a/src/src/regex.c b/src/src/regex.c index d5dc10258..642b9cf93 100644 --- a/src/src/regex.c +++ b/src/src/regex.c @@ -118,7 +118,6 @@ regex(const uschar ** listptr, BOOL cacheable) unsigned long mbox_size; FILE * mbox_file; pcre_list * re_list_head; -uschar * linebuffer; long f_pos = 0; int ret = FAIL, cnt, lcount = REGEX_LOOPCOUNT_STORE_RESET; rmark reset_point; @@ -151,16 +150,15 @@ reset_point = store_mark(); if ((re_list_head = compile(*listptr, cacheable, &cnt))) { /* match each line against all regexes */ - linebuffer = store_get(32767, GET_TAINTED); - while (fgets(CS linebuffer, 32767, mbox_file)) + while (fgets(CS big_buffer, big_buffer_size, mbox_file)) { if ( mime_stream && mime_current_boundary /* check boundary */ - && Ustrncmp(linebuffer, "--", 2) == 0 - && Ustrncmp((linebuffer+2), mime_current_boundary, + && Ustrncmp(big_buffer, "--", 2) == 0 + && Ustrncmp((big_buffer+2), mime_current_boundary, Ustrlen(mime_current_boundary)) == 0) break; /* found boundary */ - if ((ret = matcher(re_list_head, linebuffer, (int)Ustrlen(linebuffer))) == OK) + if ((ret = matcher(re_list_head, big_buffer, (int)Ustrlen(big_buffer))) == OK) break; if ((lcount -= cnt) <= 0) commit cd19f9a79c20f9c0c9d650a8aa21d9cc54a66620 Author: Jeremy Harris Date: Sun Feb 11 23:32:34 2024 +0000 Expand recipients_max diff --git a/src/src/exim.c b/src/src/exim.c index 1f76f8f14..10fc98963 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -5339,6 +5339,7 @@ if (expansion_test) else if (expansion_test_message) { + uschar * rme = expand_string(recipients_max); int save_stdin = dup(0); int fd = Uopen(expansion_test_message, O_RDONLY, 0); if (fd < 0) @@ -5347,6 +5348,7 @@ if (expansion_test) (void) dup2(fd, 0); filter_test = FTEST_USER; /* Fudge to make it look like filter test */ message_ended = END_NOTENDED; + recipients_max_expanded = atoi(CCS rme); read_message_body(receive_msg(extract_recipients)); message_linecount += body_linecount; (void)dup2(save_stdin, 0); @@ -5753,8 +5755,8 @@ for (BOOL more = TRUE; more; ) int rc; if ((rc = smtp_setup_msg()) > 0) { - if (real_sender_address != NULL && - !receive_check_set_sender(sender_address)) + if ( real_sender_address + && !receive_check_set_sender(sender_address)) { sender_address = raw_sender = real_sender_address; sender_address_unrewritten = NULL; @@ -5801,10 +5803,12 @@ for (BOOL more = TRUE; more; ) else { - int rcount = 0; - int count = argc - recipients_arg; + uschar * rme = expand_string(recipients_max); + int rcount = 0, count = argc - recipients_arg; const uschar ** list = argv + recipients_arg; + recipients_max_expanded = atoi(CCS rme); + /* These options cannot be changed dynamically for non-SMTP messages */ f.active_local_sender_retain = local_sender_retain; @@ -5831,15 +5835,19 @@ for (BOOL more = TRUE; more; ) while (*s) { BOOL finished = FALSE; - uschar *recipient; - uschar *ss = parse_find_address_end(s, FALSE); + uschar * recipient; + uschar * ss = parse_find_address_end(s, FALSE); if (*ss == ',') *ss = 0; else finished = TRUE; /* Check max recipients - if -t was used, these aren't recipients */ - if (recipients_max > 0 && ++rcount > recipients_max && - !extract_recipients) + if ( recipients_max_expanded > 0 && ++rcount > recipients_max_expanded + && !extract_recipients) + { + DEBUG(D_all) debug_printf("excess reipients (max %d)\n", + recipients_max_expanded); + if (error_handling == ERRORS_STDERR) { fprintf(stderr, "exim: too many recipients\n"); @@ -5849,6 +5857,7 @@ for (BOOL more = TRUE; more; ) return moan_to_sender(ERRMESS_TOOMANYRECIP, NULL, NULL, stdin, TRUE)? errors_sender_rc : EXIT_FAILURE; + } #ifdef SUPPORT_I18N { @@ -5873,6 +5882,10 @@ for (BOOL more = TRUE; more; ) } if (!recipient) + { + DEBUG(D_all) debug_printf("bad recipient address \"%s\": %s\n", + string_printing(list[i]), errmess); + if (error_handling == ERRORS_STDERR) { fprintf(stderr, "exim: bad recipient address \"%s\": %s\n", @@ -5889,6 +5902,7 @@ for (BOOL more = TRUE; more; ) moan_to_sender(ERRMESS_BADARGADDRESS, &eblock, NULL, stdin, TRUE)? errors_sender_rc : EXIT_FAILURE; } + } receive_add_recipient(string_copy_taint(recipient, GET_TAINTED), -1); s = ss; diff --git a/src/src/globals.c b/src/src/globals.c index c483ab182..4af8668f0 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -1322,7 +1322,8 @@ uschar *recipient_verify_failure = NULL; int recipients_count = 0; recipient_item *recipients_list = NULL; int recipients_list_max = 0; -int recipients_max = 50000; +uschar *recipients_max = US"50000"; +int recipients_max_expanded= 0; const pcre2_code *regex_AUTH = NULL; const pcre2_code *regex_check_dns_names = NULL; const pcre2_code *regex_From = NULL; diff --git a/src/src/globals.h b/src/src/globals.h index b5c3a520e..427590050 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -895,7 +895,8 @@ extern uschar *recipient_data; /* lookup data for recipients */ extern uschar *recipient_unqualified_hosts; /* Permitted unqualified recipients */ extern uschar *recipient_verify_failure; /* What went wrong */ extern int recipients_list_max; /* Maximum number fitting in list */ -extern int recipients_max; /* Max permitted */ +extern uschar *recipients_max; /* Max permitted */ +extern int recipients_max_expanded; extern BOOL recipients_max_reject; /* If TRUE, reject whole message */ extern const pcre2_code *regex_AUTH; /* For recognizing AUTH settings */ extern const pcre2_code *regex_check_dns_names; /* For DNS name checking */ diff --git a/src/src/moan.c b/src/src/moan.c index c2e38d4f7..a3c8e0aba 100644 --- a/src/src/moan.c +++ b/src/src/moan.c @@ -561,7 +561,7 @@ switch(ident) case ERRMESS_TOOMANYRECIP: log_write(0, LOG_MAIN, "%s: too many recipients (max set to %d)", msg, - recipients_max); + recipients_max_expanded); break; case ERRMESS_LOCAL_SCAN: diff --git a/src/src/readconf.c b/src/src/readconf.c index 8ee6e9fe0..5b486d0b6 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -293,7 +293,7 @@ static optionlist optionlist_config[] = { { "received_header_text", opt_stringptr, {&received_header_text} }, { "received_headers_max", opt_int, {&received_headers_max} }, { "recipient_unqualified_hosts", opt_stringptr, {&recipient_unqualified_hosts} }, - { "recipients_max", opt_int, {&recipients_max} }, + { "recipients_max", opt_stringptr, {&recipients_max} }, { "recipients_max_reject", opt_bool, {&recipients_max_reject} }, #ifdef LOOKUP_REDIS { "redis_servers", opt_stringptr, {&redis_servers} }, diff --git a/src/src/receive.c b/src/src/receive.c index ae4203e7f..990b9c22e 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -1204,6 +1204,8 @@ static void give_local_error(int errcode, uschar *text1, uschar *text2, int error_rc, FILE *f, header_line *hptr) { +DEBUG(D_all) debug_printf("%s%s\n", text2, text1); + if (error_handling == ERRORS_SENDER) { error_block eblock; @@ -2622,12 +2624,12 @@ if (extract_recip) if ((h->type == htype_to || h->type == htype_cc || h->type == htype_bcc) && (!contains_resent_headers || strncmpic(h->text, US"resent-", 7) == 0)) { - uschar *s = Ustrchr(h->text, ':') + 1; + uschar * s = Ustrchr(h->text, ':') + 1; while (isspace(*s)) s++; f.parse_allow_group = TRUE; /* Allow address group syntax */ - while (*s != 0) + while (*s) { uschar *ss = parse_find_address_end(s, FALSE); uschar *recipient, *errmess, *pp; @@ -2635,7 +2637,7 @@ if (extract_recip) /* Check on maximum */ - if (recipients_max > 0 && ++rcount > recipients_max) + if (recipients_max_expanded > 0 && ++rcount > recipients_max_expanded) give_local_error(ERRMESS_TOOMANYRECIP, US"too many recipients", US"message rejected: ", error_rc, stdin, NULL); /* Does not return */ diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index a57c9821e..1d4c232a5 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -1879,7 +1879,7 @@ while (done <= 0) if (recipients_max > 0 && recipients_count + 1 > recipients_max) /* The function moan_smtp_batch() does not return. */ moan_smtp_batch(smtp_cmd_buffer, "%s too many recipients", - recipients_max_reject? "552": "452"); + recipients_max_reject ? "552": "452"); /* Apply SMTP rewrite, then extract address. Don't allow "<>" as a recipient address */ @@ -2540,6 +2540,11 @@ if (!f.sender_host_unknown) fl.helo_accept_junk = verify_check_host(&helo_accept_junk_hosts) == OK; } +/* Expand recipients_max, if needed */ + { + uschar * rme = expand_string(recipients_max); + recipients_max_expanded = atoi(CCS rme); + } /* For batch SMTP input we are now done. */ if (smtp_batched_input) return TRUE; @@ -4055,14 +4060,14 @@ while (done <= 0) } #ifndef DISABLE_ESMTP_LIMITS - if ( (smtp_mailcmd_max > 0 || recipients_max) + if ( (smtp_mailcmd_max > 0 || recipients_max_expanded > 0) && verify_check_host(&limits_advertise_hosts) == OK) { g = string_fmt_append(g, "%.3s-LIMITS", smtp_code); if (smtp_mailcmd_max > 0) g = string_fmt_append(g, " MAILMAX=%d", smtp_mailcmd_max); - if (recipients_max) - g = string_fmt_append(g, " RCPTMAX=%d", recipients_max); + if (recipients_max > 9) + g = string_fmt_append(g, " RCPTMAX=%d", recipients_max_expanded); g = string_catn(g, US"\r\n", 2); } #endif @@ -4900,7 +4905,8 @@ while (done <= 0) /* Check maximum allowed */ - if (rcpt_count+1 < 0 || rcpt_count > recipients_max && recipients_max > 0) + if ( rcpt_count+1 < 0 + || rcpt_count > recipients_max_expanded && recipients_max_expanded > 0) { if (recipients_max_reject) { commit 7502f0cbd3753de473a2279acaae15f9bbdfb5ee Author: Jeremy Harris Date: Wed Feb 14 11:25:52 2024 +0000 Expand recipients_max, smtp-batch input diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 1d4c232a5..2d977b975 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -1876,7 +1876,8 @@ while (done <= 0) /* Check maximum number allowed */ - if (recipients_max > 0 && recipients_count + 1 > recipients_max) + if ( recipients_max_expanded > 0 + && recipients_count + 1 > recipients_max_expanded) /* The function moan_smtp_batch() does not return. */ moan_smtp_batch(smtp_cmd_buffer, "%s too many recipients", recipients_max_reject ? "552": "452"); @@ -4066,7 +4067,7 @@ while (done <= 0) g = string_fmt_append(g, "%.3s-LIMITS", smtp_code); if (smtp_mailcmd_max > 0) g = string_fmt_append(g, " MAILMAX=%d", smtp_mailcmd_max); - if (recipients_max > 9) + if (recipients_max_expanded > 0) g = string_fmt_append(g, " RCPTMAX=%d", recipients_max_expanded); g = string_catn(g, US"\r\n", 2); } commit 44b3172e369435c2c1baa4e9c837252f729d2905 Author: Jeremy Harris Date: Thu Feb 15 19:56:40 2024 +0000 regex: avoid releasing built RE midloop diff --git a/src/src/regex.c b/src/src/regex.c index 642b9cf93..285ab8dbd 100644 --- a/src/src/regex.c +++ b/src/src/regex.c @@ -120,7 +120,6 @@ FILE * mbox_file; pcre_list * re_list_head; long f_pos = 0; int ret = FAIL, cnt, lcount = REGEX_LOOPCOUNT_STORE_RESET; -rmark reset_point; regex_vars_clear(); @@ -144,11 +143,11 @@ else mbox_file = mime_stream; } -reset_point = store_mark(); - { /* precompile our regexes */ if ((re_list_head = compile(*listptr, cacheable, &cnt))) { + rmark reset_point = store_mark(); + /* match each line against all regexes */ while (fgets(CS big_buffer, big_buffer_size, mbox_file)) { @@ -167,9 +166,9 @@ reset_point = store_mark(); lcount = REGEX_LOOPCOUNT_STORE_RESET; } } + + store_reset(reset_point); } - } -store_reset(reset_point); if (!mime_stream) (void)fclose(mbox_file); commit 90bd3832bc0ff090ac5e37dfc66b30cabb9cfc1a Author: Jeremy Harris Date: Fri Feb 16 23:16:29 2024 +0000 Debug: option access for expansion diff --git a/src/src/daemon.c b/src/src/daemon.c index 704ede24e..3c63c6368 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -298,9 +298,10 @@ to provide host-specific limits according to $sender_host address, but because this is in the daemon mainline, only fast expansions (such as inline address checks) should be used. The documentation is full of warnings. */ +GET_OPTION("smtp_accept_max_per_host"); if (smtp_accept_max_per_host) { - uschar *expanded = expand_string(smtp_accept_max_per_host); + uschar * expanded = expand_string(smtp_accept_max_per_host); if (!expanded) { if (!f.expand_string_forcedfail) @@ -434,6 +435,7 @@ if (pid == 0) likely what it depends on.) */ smtp_active_hostname = primary_hostname; + GET_OPTION("smtp_active_hostname"); if (raw_active_hostname) { uschar * nah = expand_string(raw_active_hostname); @@ -1165,6 +1167,7 @@ return offsetof(struct sockaddr_un, sun_path) ssize_t daemon_notifier_sockname(struct sockaddr_un * sup) { +GET_OPTION("notifier_socket"); #ifdef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS sup->sun_path[0] = 0; /* Abstract local socket addr - Linux-specific? */ return offsetof(struct sockaddr_un, sun_path) + 1 @@ -1709,12 +1712,13 @@ time_t last_connection_time = (time_t)0; int local_queue_run_max = 0; if (is_multiple_qrun()) - + { /* Nuber of runner-tracking structs needed: If the option queue_run_max has no expandable elements then it is the overall maximum; else we assume it depends on the queue name, and add them up to get the maximum. Evaluate both that and the individual limits. */ + GET_OPTION("queue_run_max"); if (Ustrchr(queue_run_max, '$') != NULL) { for (qrunner * q = qrunners; q; q = q->next) @@ -1731,6 +1735,7 @@ if (is_multiple_qrun()) for (qrunner * q = qrunners; q; q = q->next) q->run_max = local_queue_run_max; } + } process_purpose = US"daemon"; diff --git a/src/src/deliver.c b/src/src/deliver.c index 2cef3d731..70f44535f 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -1858,8 +1858,9 @@ if (tp->gid_set) } else if (tp->expand_gid) { + GET_OPTION("group"); if (!route_find_expanded_group(tp->expand_gid, tp->name, US"transport", gidp, - &(addr->message))) + &addr->message)) { common_error(FALSE, addr, ERRNO_GIDFAIL, NULL); return FALSE; @@ -1885,6 +1886,7 @@ it does not provide a passwd value from which a gid can be taken. */ else if (tp->expand_uid) { struct passwd *pw; + GET_OPTION("user"); if (!route_find_expanded_user(tp->expand_uid, tp->name, US"transport", &pw, uidp, &(addr->message))) { @@ -1988,6 +1990,7 @@ check_message_size(transport_instance *tp, address_item *addr) int rc = OK; int size_limit; +GET_OPTION("message_size_limit"); deliver_set_expansions(addr); size_limit = expand_string_integer(tp->message_size_limit, TRUE); deliver_set_expansions(NULL); @@ -2150,6 +2153,7 @@ if(addr->prop.errors_address) else return_path = sender_address; +GET_OPTION("return_path"); if (tp->return_path) { uschar * new_return_path = expand_string(tp->return_path); @@ -2179,6 +2183,7 @@ if (!findugid(addr, tp, &uid, &gid, &use_initgroups)) return; home directory set in the address may already be expanded; a flag is set to indicate that. In other cases we must expand it. */ +GET_OPTION("home_directory"); if ( (deliver_home = tp->home_dir) /* Set in transport, or */ || ( (deliver_home = addr->home_dir) /* Set in address and */ && !testflag(addr, af_home_expanded) /* not expanded */ @@ -2208,6 +2213,7 @@ all users have access. It is necessary to be in a visible directory for some operating systems when running pipes, as some commands (e.g. "rm" under Solaris 2.5) require this. */ +GET_OPTION("current_directory"); working_directory = tp->current_dir ? tp->current_dir : addr->current_dir; if (working_directory) { @@ -2605,36 +2611,40 @@ if ((status & 0xffff) != 0) /* If SPECIAL_WARN is set in the top address, send a warning message. */ -if (addr->special_action == SPECIAL_WARN && addr->transport->warn_message) +if (addr->special_action == SPECIAL_WARN) { - int fd; - uschar *warn_message; - pid_t pid; + uschar * warn_message = addr->transport->warn_message; + GET_OPTION("quota_warn_message"); + if (warn_message) + { + int fd; + pid_t pid; - DEBUG(D_deliver) debug_printf("Warning message requested by transport\n"); + DEBUG(D_deliver) debug_printf("Warning message requested by transport\n"); - if (!(warn_message = expand_string(addr->transport->warn_message))) - log_write(0, LOG_MAIN|LOG_PANIC, "Failed to expand \"%s\" (warning " - "message for %s transport): %s", addr->transport->warn_message, - addr->transport->name, expand_string_message); + if (!(warn_message = expand_string(warn_message))) + log_write(0, LOG_MAIN|LOG_PANIC, "Failed to expand \"%s\" (warning " + "message for %s transport): %s", addr->transport->warn_message, + addr->transport->name, expand_string_message); - else if ((pid = child_open_exim(&fd, US"tpt-warning-message")) > 0) - { - FILE *f = fdopen(fd, "wb"); - if (errors_reply_to && !contains_header(US"Reply-To", warn_message)) - fprintf(f, "Reply-To: %s\n", errors_reply_to); - fprintf(f, "Auto-Submitted: auto-replied\n"); - if (!contains_header(US"From", warn_message)) - moan_write_from(f); - fprintf(f, "%s", CS warn_message); + else if ((pid = child_open_exim(&fd, US"tpt-warning-message")) > 0) + { + FILE * f = fdopen(fd, "wb"); + if (errors_reply_to && !contains_header(US"Reply-To", warn_message)) + fprintf(f, "Reply-To: %s\n", errors_reply_to); + fprintf(f, "Auto-Submitted: auto-replied\n"); + if (!contains_header(US"From", warn_message)) + moan_write_from(f); + fprintf(f, "%s", CS warn_message); - /* Close and wait for child process to complete, without a timeout. */ + /* Close and wait for child process to complete, without a timeout. */ - (void)fclose(f); - (void)child_close(pid, 0); - } + (void)fclose(f); + (void)child_close(pid, 0); + } - addr->special_action = SPECIAL_NONE; + addr->special_action = SPECIAL_NONE; + } } } @@ -2650,6 +2660,7 @@ tpt_parallel_check(transport_instance * tp, address_item * addr, uschar ** key) { unsigned max_parallel; +GET_OPTION("max_parallel"); if (!tp->max_parallel) return FALSE; max_parallel = (unsigned) expand_string_integer(tp->max_parallel, TRUE); @@ -2773,6 +2784,7 @@ while (addr_local) /* Expand the batch_id string for comparison with other addresses. Expansion failure suppresses batching. */ + GET_OPTION("batch_id"); if (tp->batch_id) { deliver_set_expansions(addr); @@ -2827,11 +2839,12 @@ while (addr_local) if (ok && batch_id) { - uschar *bid; - address_item *save_nextnext = next->next; + uschar * bid; + address_item * save_nextnext = next->next; next->next = NULL; /* Expansion for a single address */ deliver_set_expansions(next); next->next = save_nextnext; + GET_OPTION("batch_id"); bid = expand_string(tp->batch_id); deliver_set_expansions(NULL); if (!bid) @@ -4454,9 +4467,10 @@ nonmatch domains else return_path = sender_address; + GET_OPTION("return_path"); if (tp->return_path) { - uschar *new_return_path = expand_string(tp->return_path); + uschar * new_return_path = expand_string(tp->return_path); if (new_return_path) return_path = new_return_path; else if (!f.expand_string_forcedfail) @@ -5557,18 +5571,18 @@ return actual_time; static FILE * expand_open(const uschar * filename, - const uschar * varname, const uschar * reason) + const uschar * optname, const uschar * reason) { const uschar * s = expand_cstring(filename); FILE * fp = NULL; if (!s || !*s) log_write(0, LOG_MAIN|LOG_PANIC, - "Failed to expand %s: '%s'\n", varname, filename); + "Failed to expand %s: '%s'\n", optname, filename); else if (*s != '/' || is_tainted(s)) log_write(0, LOG_MAIN|LOG_PANIC, "%s is not %s after expansion: '%s'\n", - varname, *s == '/' ? "untainted" : "absolute", s); + optname, *s == '/' ? "untainted" : "absolute", s); else if (!(fp = Ufopen(s, "rb"))) log_write(0, LOG_MAIN|LOG_PANIC, "Failed to open %s for %s " "message texts: %s", s, reason, strerror(errno)); @@ -5699,6 +5713,7 @@ else /* Open a template file if one is provided. Log failure to open, but carry on - default texts will be used. */ + GET_OPTION("bounce_message_file"); if (bounce_message_file) emf = expand_open(bounce_message_file, US"bounce_message_file", US"error"); @@ -6058,6 +6073,7 @@ transport_ctx tctx = {{0}}; if (pid <= 0) return FALSE; +GET_OPTION("warn_message_file"); if (warn_message_file) wmf = expand_open(warn_message_file, US"warn_message_file", US"warning"); @@ -6757,6 +6773,7 @@ else if (system_filter && process_recipients != RECIP_FAIL_TIMEOUT) /* Any error in the filter file causes a delivery to be abandoned. */ + GET_OPTION("system_filter"); redirect.string = system_filter; redirect.isfile = TRUE; redirect.check_owner = redirect.check_group = FALSE; @@ -6936,12 +6953,14 @@ else if (system_filter && process_recipients != RECIP_FAIL_TIMEOUT) if (p->address[0] == '|') { type = US"pipe"; + GET_OPTION("system_filter_pipe_transport"); tpname = system_filter_pipe_transport; address_pipe = p->address; } else if (p->address[0] == '>') { type = US"reply"; + GET_OPTION("system_filter_reply_transport"); tpname = system_filter_reply_transport; } else @@ -6949,11 +6968,13 @@ else if (system_filter && process_recipients != RECIP_FAIL_TIMEOUT) if (p->address[Ustrlen(p->address)-1] == '/') { type = US"directory"; + GET_OPTION("system_filter_directory_transport"); tpname = system_filter_directory_transport; } else { type = US"file"; + GET_OPTION("system_filter_file_transport"); tpname = system_filter_file_transport; } address_file = p->address; @@ -8437,54 +8458,57 @@ else if (addr_defer != (address_item *)(+1)) || addr_defer->dsn_flags & rf_notify_delay ) && delay_warning[1] > 0 - && sender_address[0] != 0 - && ( !delay_warning_condition - || expand_check_condition(delay_warning_condition, - US"delay_warning", US"option") - ) - ) + && sender_address[0] != 0) { - int count; - int show_time; - int queue_time = time(NULL) - received_time.tv_sec; - - queue_time = test_harness_fudged_queue_time(queue_time); - - /* See how many warnings we should have sent by now */ + GET_OPTION("delay_warning_condition"); + if ( ( !delay_warning_condition + || expand_check_condition(delay_warning_condition, + US"delay_warning", US"option") + ) + ) + { + int count; + int show_time; + int queue_time = time(NULL) - received_time.tv_sec; - for (count = 0; count < delay_warning[1]; count++) - if (queue_time < delay_warning[count+2]) break; + queue_time = test_harness_fudged_queue_time(queue_time); - show_time = delay_warning[count+1]; + /* See how many warnings we should have sent by now */ - if (count >= delay_warning[1]) - { - int extra; - int last_gap = show_time; - if (count > 1) last_gap -= delay_warning[count]; - extra = (queue_time - delay_warning[count+1])/last_gap; - show_time += last_gap * extra; - count += extra; - } + for (count = 0; count < delay_warning[1]; count++) + if (queue_time < delay_warning[count+2]) break; - DEBUG(D_deliver) - { - debug_printf("time on queue = %s id %s addr %s\n", - readconf_printtime(queue_time), message_id, addr_defer->address); - debug_printf("warning counts: required %d done %d\n", count, - warning_count); - } + show_time = delay_warning[count+1]; - /* We have computed the number of warnings there should have been by now. - If there haven't been enough, send one, and up the count to what it should - have been. */ + if (count >= delay_warning[1]) + { + int extra; + int last_gap = show_time; + if (count > 1) last_gap -= delay_warning[count]; + extra = (queue_time - delay_warning[count+1])/last_gap; + show_time += last_gap * extra; + count += extra; + } - if (warning_count < count) - if (send_warning_message(recipients, queue_time, show_time)) + DEBUG(D_deliver) { - warning_count = count; - update_spool = TRUE; /* Ensure spool rewritten */ + debug_printf("time on queue = %s id %s addr %s\n", + readconf_printtime(queue_time), message_id, addr_defer->address); + debug_printf("warning counts: required %d done %d\n", count, + warning_count); } + + /* We have computed the number of warnings there should have been by now. + If there haven't been enough, send one, and up the count to what it should + have been. */ + + if (warning_count < count) + if (send_warning_message(recipients, queue_time, show_time)) + { + warning_count = count; + update_spool = TRUE; /* Ensure spool rewritten */ + } + } } /* Clear deliver_domain */ diff --git a/src/src/dkim.c b/src/src/dkim.c index 63b16af39..9e76c51a0 100644 --- a/src/src/dkim.c +++ b/src/src/dkim.c @@ -624,6 +624,7 @@ if (dkim->dot_stuffed) store_pool = POOL_MAIN; +GET_OPTION("dkim_domain"); if ((s = dkim->dkim_domain) && !(dkim_domain = expand_cstring(s))) /* expansion error, do not send message. */ { errwhen = US"dkim_domain"; goto expand_bad; } @@ -652,6 +653,7 @@ if (dkim_domain) /* Set $dkim_selector expansion variable to each selector in list, for this domain. */ + GET_OPTION("dkim_selector"); if (!(dkim_sel = expand_string(dkim->dkim_selector))) { errwhen = US"dkim_selector"; goto expand_bad; } @@ -669,6 +671,7 @@ if (dkim_domain) /* Get canonicalization to use */ + GET_OPTION("dkim_canon"); dkim_canon_expanded = dkim->dkim_canon ? expand_string(dkim->dkim_canon) : US"relaxed"; if (!dkim_canon_expanded) /* expansion error, do not send message. */ @@ -686,6 +689,7 @@ if (dkim_domain) pdkim_canon = PDKIM_CANON_RELAXED; } + GET_OPTION("dkim_sign_headers"); if ( dkim->dkim_sign_headers && !(dkim_sign_headers_expanded = expand_string(dkim->dkim_sign_headers))) { errwhen = US"dkim_sign_header"; goto expand_bad; } @@ -693,6 +697,7 @@ if (dkim_domain) /* Get private key to use. */ + GET_OPTION("dkim_private_key"); if (!(dkim_private_key_expanded = expand_string(dkim->dkim_private_key))) { errwhen = US"dkim_private_key"; goto expand_bad; } @@ -707,15 +712,18 @@ if (dkim_domain) expand_file_big_buffer(dkim_private_key_expanded))) goto bad; + GET_OPTION("dkim_hash"); if (!(dkim_hash_expanded = expand_string(dkim->dkim_hash))) { errwhen = US"dkim_hash"; goto expand_bad; } + GET_OPTION("dkim_identity"); if (dkim->dkim_identity) if (!(dkim_identity_expanded = expand_string(dkim->dkim_identity))) { errwhen = US"dkim_identity"; goto expand_bad; } else if (!*dkim_identity_expanded) dkim_identity_expanded = NULL; + GET_OPTION("dkim_timestamps"); if (dkim->dkim_timestamps) if (!(dkim_timestamps_expanded = expand_string(dkim->dkim_timestamps))) { errwhen = US"dkim_timestamps"; goto expand_bad; } diff --git a/src/src/dkim_transport.c b/src/src/dkim_transport.c index 38f849af6..8c8d9b41a 100644 --- a/src/src/dkim_transport.c +++ b/src/src/dkim_transport.c @@ -18,6 +18,7 @@ static BOOL dkt_sign_fail(struct ob_dkim * dkim, int * errp) { +GET_OPTION("dkim_strict"); if (dkim->dkim_strict) { uschar * dkim_strict_result = expand_string(dkim->dkim_strict); diff --git a/src/src/exim.c b/src/src/exim.c index 10fc98963..c591ee60c 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -5063,6 +5063,8 @@ for (i = 0;;) /* If a pattern for matching the gecos field was supplied, apply it and then expand the name string. */ + GET_OPTION("gecos_pattern"); + GET_OPTION("gecos_name"); if (gecos_pattern && gecos_name) { const pcre2_code *re; @@ -5108,6 +5110,7 @@ any setting of unknown_login overrides the actual name. */ if (!originator_login || f.running_in_test_harness) { + GET_OPTION("unknown_login"); if (unknown_login) { originator_login = expand_string(unknown_login); @@ -5407,17 +5410,18 @@ for hosts that want to play several parts at once. We need to ensure that it is set for host checking, and for receiving messages. */ smtp_active_hostname = primary_hostname; -if (raw_active_hostname != NULL) +GET_OPTION("smtp_active_hostname"); +if (raw_active_hostname) { - uschar *nah = expand_string(raw_active_hostname); - if (nah == NULL) + uschar * nah = expand_string(raw_active_hostname); + if (!nah) { if (!f.expand_string_forcedfail) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to expand \"%s\" " "(smtp_active_hostname): %s", raw_active_hostname, expand_string_message); } - else if (nah[0] != 0) smtp_active_hostname = nah; + else if (nah[0]) smtp_active_hostname = nah; } /* Handle host checking: this facility mocks up an incoming SMTP call from a @@ -5675,6 +5679,7 @@ if (smtp_input) else { + GET_OPTION("message_size_limit"); thismessage_size_limit = expand_string_integer(message_size_limit, TRUE); if (expand_string_message) if (thismessage_size_limit == -1) @@ -5767,14 +5772,18 @@ for (BOOL more = TRUE; more; ) the very end. The result of the ACL is ignored (as for other non-SMTP messages). It is run for its potential side effects. */ - if (smtp_batched_input && acl_not_smtp_start != NULL) - { - uschar *user_msg, *log_msg; - f.enable_dollar_recipients = TRUE; - (void)acl_check(ACL_WHERE_NOTSMTP_START, NULL, acl_not_smtp_start, - &user_msg, &log_msg); - f.enable_dollar_recipients = FALSE; - } + if (smtp_batched_input) + { + GET_OPTION("acl_not_smtp_start"); + if (acl_not_smtp_start) + { + uschar * user_msg, * log_msg; + f.enable_dollar_recipients = TRUE; + (void)acl_check(ACL_WHERE_NOTSMTP_START, NULL, acl_not_smtp_start, + &user_msg, &log_msg); + f.enable_dollar_recipients = FALSE; + } + } /* Now get the data for the message */ @@ -5928,9 +5937,10 @@ for (BOOL more = TRUE; more; ) ignored; rejecting here would just add complication, and it can just as well be done later. Allow $recipients to be visible in the ACL. */ + GET_OPTION("acl_not_smtp_start"); if (acl_not_smtp_start) { - uschar *user_msg, *log_msg; + uschar * user_msg, * log_msg; f.enable_dollar_recipients = TRUE; (void)acl_check(ACL_WHERE_NOTSMTP_START, NULL, acl_not_smtp_start, &user_msg, &log_msg); diff --git a/src/src/expand.c b/src/src/expand.c index f9813133d..6be5cca0e 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -8737,12 +8737,14 @@ Returns: OK value placed in rvalue */ int -exp_bool(address_item *addr, - uschar *mtype, uschar *mname, unsigned dbg_opt, - uschar *oname, BOOL bvalue, - uschar *svalue, BOOL *rvalue) +exp_bool(address_item * addr, + uschar * mtype, uschar * mname, unsigned dbg_opt, + uschar * oname, BOOL bvalue, + uschar * svalue, BOOL * rvalue) { -uschar *expanded; +uschar * expanded; + +DEBUG(D_expand) debug_printf("try option %s\n", oname); if (!svalue) { *rvalue = bvalue; return OK; } if (!(expanded = expand_string(svalue))) diff --git a/src/src/macros.h b/src/src/macros.h index 8aed335b5..902cdd165 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -1190,4 +1190,8 @@ When doing en extended loop of matching, release store periodically. */ #define REGEX_LOOPCOUNT_STORE_RESET 1000 +/* Debug an option access. Use for non-list ones about to be expanded. */ +#define GET_OPTION(name) \ + DEBUG(D_expand) debug_printf("try option " name "\n"); + /* End of macros.h */ diff --git a/src/src/moan.c b/src/src/moan.c index a3c8e0aba..6fe82b29c 100644 --- a/src/src/moan.c +++ b/src/src/moan.c @@ -30,8 +30,9 @@ Returns: nothing void moan_write_from(FILE *f) { -uschar * s = expand_string(dsn_from); -if (!s) +uschar * s; +GET_OPTION("dsn_from"); +if (!(s = expand_string(dsn_from))) { log_write(0, LOG_MAIN|LOG_PANIC, "Failed to expand dsn_from (using default): %s", expand_string_message); @@ -175,6 +176,7 @@ uschar * s, * s2; /* For DMARC if there is a specific sender set, expand the variable for the header From: and grab the address from that for the envelope FROM. */ +GET_OPTION("dmarc_forensic_sender"); if ( ident == ERRMESS_DMARC_FORENSIC && dmarc_forensic_sender && (s = expand_string(dmarc_forensic_sender)) @@ -513,9 +515,12 @@ if (check_sender && message_file && f.trusted_caller && { uschar *new_sender = NULL; if (regex_match_and_setup(regex_From, big_buffer, 0, -1)) + { + GET_OPTION("uucp_from_sender"); new_sender = expand_string(uucp_from_sender); + } if (new_sender) sender_address = new_sender; - else firstline = big_buffer; + else firstline = big_buffer; } /* If viable sender address, send a message */ diff --git a/src/src/receive.c b/src/src/receive.c index 990b9c22e..abac05c56 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -1574,6 +1574,7 @@ uschar * timestamp = expand_string(US"${tod_full}"); header_line * received_header= header_list; if (recipients_count == 1) received_for = recipients_list[0].address; +GET_OPTION("received_header_text"); received = expand_string(received_header_text); received_for = NULL; @@ -2201,8 +2202,9 @@ OVERSIZE: { if (!f.sender_address_forced) { - uschar *uucp_sender = expand_string(uucp_from_sender); - if (!uucp_sender) + uschar * uucp_sender; + GET_OPTION("uucp_from_sender"); + if (!(uucp_sender = expand_string(uucp_from_sender))) log_write(0, LOG_MAIN|LOG_PANIC, "expansion of \"%s\" failed after matching " "\"From \" line: %s", uucp_from_sender, expand_string_message); @@ -2821,6 +2823,7 @@ if ( !msgid_header /* Permit only letters, digits, dots, and hyphens in the domain */ + GET_OPTION("message_id_header_domain"); if (message_id_domain) { uschar *new_id_domain = expand_string(message_id_domain); @@ -2842,6 +2845,7 @@ if ( !msgid_header /* Permit all characters except controls and RFC 2822 specials in the additional text part. */ + GET_OPTION("message_id_header_text"); if (message_id_text) { uschar *new_id_text = expand_string(message_id_text); @@ -3517,6 +3521,7 @@ else dkim_exim_verify_finish(); /* Check if we must run the DKIM ACL */ + GET_OPTION("acl_smtp_dkim"); if (acl_smtp_dkim && dkim_verify_signers && *dkim_verify_signers) { uschar * dkim_verify_signers_expanded = @@ -3606,11 +3611,14 @@ else #endif /* DISABLE_DKIM */ #ifdef WITH_CONTENT_SCAN - if ( recipients_count > 0 - && acl_smtp_mime - && !run_mime_acl(acl_smtp_mime, &smtp_yield, &smtp_reply, &blackholed_by) - ) - goto TIDYUP; + if (recipients_count > 0) + { + GET_OPTION("acl_smtp_mime"); + if (acl_smtp_mime + && !run_mime_acl(acl_smtp_mime, &smtp_yield, &smtp_reply, &blackholed_by) + ) + goto TIDYUP; + } #endif /* WITH_CONTENT_SCAN */ #ifdef SUPPORT_DMARC @@ -3618,67 +3626,73 @@ else #endif #ifndef DISABLE_PRDR - if (prdr_requested && recipients_count > 1 && acl_smtp_data_prdr) + if (prdr_requested && recipients_count > 1) { - int all_pass = OK; - int all_fail = FAIL; + GET_OPTION("acl_smtp_data_prdr"); + if (acl_smtp_data_prdr) + { + int all_pass = OK; + int all_fail = FAIL; - smtp_printf("353 PRDR content analysis beginning\r\n", SP_MORE); - /* Loop through recipients, responses must be in same order received */ - for (unsigned int c = 0; recipients_count > c; c++) - { - const uschar * addr = recipients_list[c].address; - uschar * msg= US"PRDR R=<%s> %s"; - uschar * code; - DEBUG(D_receive) - debug_printf("PRDR processing recipient %s (%d of %d)\n", - addr, c+1, recipients_count); - rc = acl_check(ACL_WHERE_PRDR, addr, - acl_smtp_data_prdr, &user_msg, &log_msg); - - /* If any recipient rejected content, indicate it in final message */ - all_pass |= rc; - /* If all recipients rejected, indicate in final message */ - all_fail &= rc; - - switch (rc) - { - case OK: case DISCARD: code = US"250"; break; - case DEFER: code = US"450"; break; - default: code = US"550"; break; - } - if (user_msg != NULL) - smtp_user_msg(code, user_msg); - else + smtp_printf("353 PRDR content analysis beginning\r\n", SP_MORE); + /* Loop through recipients, responses must be in same order received */ + for (unsigned int c = 0; recipients_count > c; c++) { + const uschar * addr = recipients_list[c].address; + uschar * msg= US"PRDR R=<%s> %s"; + uschar * code; + DEBUG(D_receive) + debug_printf("PRDR processing recipient %s (%d of %d)\n", + addr, c+1, recipients_count); + rc = acl_check(ACL_WHERE_PRDR, addr, + acl_smtp_data_prdr, &user_msg, &log_msg); + + /* If any recipient rejected content, indicate it in final message */ + all_pass |= rc; + /* If all recipients rejected, indicate in final message */ + all_fail &= rc; + switch (rc) - { - case OK: case DISCARD: - msg = string_sprintf(CS msg, addr, "acceptance"); break; - case DEFER: - msg = string_sprintf(CS msg, addr, "temporary refusal"); break; - default: - msg = string_sprintf(CS msg, addr, "refusal"); break; - } - smtp_user_msg(code, msg); - } - if (log_msg) log_write(0, LOG_MAIN, "PRDR %s %s", addr, log_msg); - else if (user_msg) log_write(0, LOG_MAIN, "PRDR %s %s", addr, user_msg); - else log_write(0, LOG_MAIN, "%s", CS msg); + { + case OK: case DISCARD: code = US"250"; break; + case DEFER: code = US"450"; break; + default: code = US"550"; break; + } + if (user_msg != NULL) + smtp_user_msg(code, user_msg); + else + { + switch (rc) + { + case OK: case DISCARD: + msg = string_sprintf(CS msg, addr, "acceptance"); break; + case DEFER: + msg = string_sprintf(CS msg, addr, "temporary refusal"); break; + default: + msg = string_sprintf(CS msg, addr, "refusal"); break; + } + smtp_user_msg(code, msg); + } + if (log_msg) log_write(0, LOG_MAIN, "PRDR %s %s", addr, log_msg); + else if (user_msg) log_write(0, LOG_MAIN, "PRDR %s %s", addr, user_msg); + else log_write(0, LOG_MAIN, "%s", CS msg); - if (rc != OK) { receive_remove_recipient(addr); c--; } - } - /* Set up final message, used if data acl gives OK */ - smtp_reply = string_sprintf("%s id=%s message %s", - all_fail == FAIL ? US"550" : US"250", - message_id, - all_fail == FAIL - ? US"rejected for all recipients" - : all_pass == OK - ? US"accepted" - : US"accepted for some recipients"); - if (recipients_count == 0) - goto NOT_ACCEPTED; + if (rc != OK) { receive_remove_recipient(addr); c--; } + } + /* Set up final message, used if data acl gives OK */ + smtp_reply = string_sprintf("%s id=%s message %s", + all_fail == FAIL ? US"550" : US"250", + message_id, + all_fail == FAIL + ? US"rejected for all recipients" + : all_pass == OK + ? US"accepted" + : US"accepted for some recipients"); + if (recipients_count == 0) + goto NOT_ACCEPTED; + } + else + prdr_requested = FALSE; } else prdr_requested = FALSE; @@ -3687,6 +3701,7 @@ else /* Check the recipients count again, as the MIME ACL might have changed them. */ + GET_OPTION("acl_smtp_data"); if (acl_smtp_data && recipients_count > 0) { rc = acl_check(ACL_WHERE_DATA, NULL, acl_smtp_data, &user_msg, &log_msg); @@ -3724,6 +3739,7 @@ else { #ifdef WITH_CONTENT_SCAN + GET_OPTION("acl_not_smtp_mime"); if ( acl_not_smtp_mime && !run_mime_acl(acl_not_smtp_mime, &smtp_yield, &smtp_reply, &blackholed_by) @@ -3731,9 +3747,10 @@ else goto TIDYUP; #endif /* WITH_CONTENT_SCAN */ + GET_OPTION("acl_not_smtp"); if (acl_not_smtp) { - uschar *user_msg, *log_msg; + uschar * user_msg, * log_msg; f.authentication_local = TRUE; rc = acl_check(ACL_WHERE_NOTSMTP, NULL, acl_not_smtp, &user_msg, &log_msg); if (rc == DISCARD) diff --git a/src/src/route.c b/src/src/route.c index 3401f15b4..0fdcdf2ed 100644 --- a/src/src/route.c +++ b/src/src/route.c @@ -616,7 +616,7 @@ uschar *check; if (!s) return OK; -DEBUG(D_route) debug_printf("checking require_files\n"); +DEBUG(D_route|D_expand) debug_printf("checking require_files\n"); listptr = s; while ((check = string_nextinlist(&listptr, &sep, NULL, 0))) @@ -637,7 +637,7 @@ while ((check = string_nextinlist(&listptr, &sep, NULL, 0))) /* Empty items are just skipped */ - if (*ss == 0) continue; + if (!*ss) continue; /* If there are no slashes in the string, we have a user name or uid, with optional group/gid. */ @@ -651,9 +651,9 @@ while ((check = string_nextinlist(&listptr, &sep, NULL, 0))) /* If there's a comma, temporarily terminate the user name/number at that point. Then set the uid. */ - if (comma != NULL) *comma = 0; + if (comma) *comma = 0; ok = route_finduser(ss, &pw, &uid); - if (comma != NULL) *comma = ','; + if (comma) *comma = ','; if (!ok) { @@ -663,24 +663,22 @@ while ((check = string_nextinlist(&listptr, &sep, NULL, 0))) /* If there was no comma, the gid is that associated with the user. */ - if (comma == NULL) - { - if (pw != NULL) gid = pw->pw_gid; else + if (!comma) + if (pw) + gid = pw->pw_gid; + else { *perror = string_sprintf("group missing after numerical uid %d for " "require_files", (int)uid); goto RETURN_DEFER; } - } else - { if (!route_findgroup(comma + 1, &gid)) { *perror = string_sprintf("group \"%s\" for require_files not found\n", comma + 1); goto RETURN_DEFER; } - } /* Note that we have values set, and proceed to next item */ @@ -974,6 +972,7 @@ check_local_user before any subsequent expansions are done. Otherwise, $home could mean different things for different options, which would be extremely confusing. */ +GET_OPTION("router_home_directory"); if (r->router_home_directory) { uschar * router_home = expand_string(r->router_home_directory); @@ -1018,7 +1017,8 @@ if ((rc = check_files(r->require_files, perror)) != OK) if (r->condition) { - DEBUG(D_route) debug_printf("checking \"condition\" \"%.80s\"...\n", r->condition); + DEBUG(D_route|D_expand) + debug_printf("checking \"condition\" \"%.80s\"...\n", r->condition); if (!expand_check_condition(r->condition, r->name, US"router")) { if (f.search_find_defer) @@ -1440,6 +1440,7 @@ const uschar * varlist = r->set; tree_node ** root = (tree_node **) &addr->prop.variables; int sep = ';'; +GET_OPTION("set"); if (!varlist) return OK; /* Walk the varlist, creating variables */ @@ -1474,6 +1475,7 @@ for (uschar * ele; (ele = string_nextinlist(&varlist, &sep, NULL, 0)); ) /* Expand "more" if necessary; DEFER => an expansion failed */ + GET_OPTION("more"); yield = exp_bool(addr, US"router", r->name, D_route, US"more", r->more, r->expand_more, &more); if (yield != OK) return yield; @@ -1754,7 +1756,7 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) if (r->address_data) { - DEBUG(D_route) debug_printf("processing address_data\n"); + DEBUG(D_route|D_expand) debug_printf("processing address_data\n"); if (!(deliver_address_data = expand_string(r->address_data))) { if (f.expand_string_forcedfail) @@ -1764,6 +1766,7 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) /* Expand "more" if necessary; DEFER => an expansion failed */ + GET_OPTION("more"); yield = exp_bool(addr, US"router", r->name, D_route, US"more", r->more, r->expand_more, &more); if (yield != OK) goto ROUTE_EXIT; @@ -1872,6 +1875,7 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) { /* Expand "more" if necessary */ + GET_OPTION("more"); yield = exp_bool(addr, US"router", r->name, D_route, US"more", r->more, r->expand_more, &more); if (yield != OK) goto ROUTE_EXIT; @@ -1897,18 +1901,21 @@ if (!r) HDEBUG(D_route) debug_printf("no more routers\n"); if (!addr->message) { - uschar *message = US"Unrouteable address"; - if (addr->router && addr->router->cannot_route_message) + uschar * message = US"Unrouteable address"; + if (addr->router) { - uschar *expmessage = expand_string(addr->router->cannot_route_message); - if (!expmessage) - { - if (!f.expand_string_forcedfail) - log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand " - "cannot_route_message in %s router: %s", addr->router->name, - expand_string_message); - } - else message = expmessage; + uschar * s = addr->router->cannot_route_message; + GET_OPTION("cannot_route_message"); + if (s) + { + if ((s = expand_string(s))) + message = s; + else + if (!f.expand_string_forcedfail) + log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand " + "cannot_route_message in %s router: %s", addr->router->name, + expand_string_message); + } } addr->user_message = addr->message = message; } @@ -1950,6 +1957,7 @@ networking, so it is included in the binary only if requested. */ #ifdef SUPPORT_TRANSLATE_IP_ADDRESS +GET_OPTION("translate_ip_address"); if (r->translate_ip_address) { int rc; @@ -2008,6 +2016,7 @@ if (r->translate_ip_address) /* See if this is an unseen routing; first expand the option if necessary. DEFER can be given if the expansion fails */ +GET_OPTION("unseen"); yield = exp_bool(addr, US"router", r->name, D_route, US"unseen", r->unseen, r->expand_unseen, &unseen); if (yield != OK) goto ROUTE_EXIT; diff --git a/src/src/routers/dnslookup.c b/src/src/routers/dnslookup.c index d27757c7e..671e8c93f 100644 --- a/src/src/routers/dnslookup.c +++ b/src/src/routers/dnslookup.c @@ -151,14 +151,11 @@ host_item h; int rc; int widen_sep = 0; int whichrrs = HOST_FIND_BY_MX | HOST_FIND_BY_A | HOST_FIND_BY_AAAA; -dnslookup_router_options_block *ob = +dnslookup_router_options_block * ob = (dnslookup_router_options_block *)(rblock->options_block); -uschar *srv_service = NULL; -uschar *widen = NULL; -const uschar *pre_widen = addr->domain; -const uschar *post_widen = NULL; -const uschar *fully_qualified_name; -const uschar *listptr; +uschar * srv_service = NULL, * widen = NULL; +const uschar * pre_widen = addr->domain, * post_widen = NULL; +const uschar * fully_qualified_name, * listptr; uschar widen_buffer[256]; DEBUG(D_route) @@ -167,8 +164,8 @@ DEBUG(D_route) /* If an SRV check is required, expand the service name */ +GET_OPTION("check_srv"); if (ob->check_srv) - { if ( !(srv_service = expand_string(ob->check_srv)) && !f.expand_string_forcedfail) { @@ -176,8 +173,8 @@ if (ob->check_srv) rblock->name, ob->check_srv, expand_string_message); return DEFER; } - else whichrrs |= HOST_FIND_BY_SRV; - } + else + whichrrs |= HOST_FIND_BY_SRV; /* Set up the first of any widening domains. The code further down copes with either pre- or post-widening, but at present there is no way to turn on diff --git a/src/src/routers/iplookup.c b/src/src/routers/iplookup.c index a6dc0fc87..f9966bcc0 100644 --- a/src/src/routers/iplookup.c +++ b/src/src/routers/iplookup.c @@ -178,19 +178,17 @@ reply = store_get(256, GET_TAINTED); /* Build the query string to send. If not explicitly given, a default of "user@domain user@domain" is used. */ -if (ob->query == NULL) +GET_OPTION("query"); +if (!ob->query) query = string_sprintf("%s@%s %s@%s", addr->local_part, addr->domain, addr->local_part, addr->domain); else - { - query = expand_string(ob->query); - if (query == NULL) + if (!(query = expand_string(ob->query))) { addr->message = string_sprintf("%s router: failed to expand %s: %s", rblock->name, ob->query, expand_string_message); return DEFER; } - } query_len = Ustrlen(query); DEBUG(D_route) debug_printf("%s router query is \"%s\"\n", rblock->name, @@ -362,23 +360,24 @@ else /* If an explicit rerouting string is specified, expand it. Otherwise, use what was sent back verbatim. */ -if (ob->reroute != NULL) +GET_OPTION("reroute"); +if (ob->reroute) { reroute = expand_string(ob->reroute); expand_nmax = -1; - if (reroute == NULL) + if (!reroute) { addr->message = string_sprintf("%s router: failed to expand %s: %s", rblock->name, ob->reroute, expand_string_message); return DEFER; } } -else reroute = reply; +else + reroute = reply; /* We should now have a new address in the form user@domain. */ -domain = Ustrchr(reroute, '@'); -if (domain == NULL) +if (!(domain = Ustrchr(reroute, '@'))) { log_write(0, LOG_MAIN, "%s router: reroute string %s is not of the form " "user@domain", rblock->name, reroute); diff --git a/src/src/routers/manualroute.c b/src/src/routers/manualroute.c index 160c866b9..200e0206e 100644 --- a/src/src/routers/manualroute.c +++ b/src/src/routers/manualroute.c @@ -118,8 +118,8 @@ if (ob->hai_code < 0) /* One of route_list or route_data must be specified */ -if ((ob->route_list == NULL && ob->route_data == NULL) || - (ob->route_list != NULL && ob->route_data != NULL)) +if ( !ob->route_list && !ob->route_data + || ob->route_list && ob->route_data) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n " "route_list or route_data (but not both) must be specified", rblock->name); @@ -291,6 +291,7 @@ string, decline. */ else { + GET_OPTION("route_data"); if (!(route_item = rf_expand_data(addr, ob->route_data, &rc))) return rc; (void) parse_route_item(route_item, NULL, &hostlist, &options); diff --git a/src/src/routers/queryprogram.c b/src/src/routers/queryprogram.c index 2a6a73eb6..dd5e24b93 100644 --- a/src/src/routers/queryprogram.c +++ b/src/src/routers/queryprogram.c @@ -287,6 +287,7 @@ if (curr_uid != root_uid && (uid != curr_uid || gid != curr_gid)) /* Set up the command to run */ +GET_OPTION("command"); if (!transport_set_up_command(&argvptr, /* anchor for arg list */ ob->command, /* raw command */ TSUC_EXPAND_ARGS, /* arguments expanded but must not be tainted */ diff --git a/src/src/routers/redirect.c b/src/src/routers/redirect.c index 4c8e30563..6b27c82b0 100644 --- a/src/src/routers/redirect.c +++ b/src/src/routers/redirect.c @@ -370,12 +370,11 @@ while (generated) in \N...\N to avoid expansion later. In Cygwin, home directories can contain $ characters. */ - if (rblock->home_directory != NULL) + if (rblock->home_directory) next->home_dir = rblock->home_directory; else if (rblock->check_local_user) next->home_dir = string_sprintf("\\N%s\\N", pw->pw_dir); - else if (rblock->router_home_directory != NULL && - testflag(addr, af_home_expanded)) + else if (rblock->router_home_directory && testflag(addr, af_home_expanded)) { next->home_dir = deliver_home; setflag(next, af_home_expanded); @@ -395,6 +394,7 @@ while (generated) if (next->address[0] == '|') { address_pipe = next->address; + GET_OPTION("pipe_transport"); if (rf_get_transport(ob->pipe_transport_name, &ob->pipe_transport, next, rblock->name, US"pipe_transport")) next->transport = ob->pipe_transport; @@ -402,6 +402,7 @@ while (generated) } else if (next->address[0] == '>') { + GET_OPTION("reply_transport"); if (rf_get_transport(ob->reply_transport_name, &ob->reply_transport, next, rblock->name, US"reply_transport")) next->transport = ob->reply_transport; @@ -412,15 +413,19 @@ while (generated) address_file = next->address; if (next->address[len-1] == '/') { + GET_OPTION("directory_transport"); if (rf_get_transport(ob->directory_transport_name, &(ob->directory_transport), next, rblock->name, US"directory_transport")) next->transport = ob->directory_transport; } else + { + GET_OPTION("file_transport"); if (rf_get_transport(ob->file_transport_name, &ob->file_transport, next, rblock->name, US"file_transport")) next->transport = ob->file_transport; + } address_file = NULL; } @@ -566,11 +571,15 @@ address. Otherwise, if a local qualify_domain is provided, set that up. */ if (ob->qualify_preserve_domain) qualify_domain_recipient = addr->domain; -else if (ob->qualify_domain) +else { - uschar *new_qdr = rf_expand_data(addr, ob->qualify_domain, &xrc); - if (!new_qdr) return xrc; - qualify_domain_recipient = new_qdr; + GET_OPTION("qualify_domain"); + if (ob->qualify_domain) + { + uschar *new_qdr = rf_expand_data(addr, ob->qualify_domain, &xrc); + if (!new_qdr) return xrc; + qualify_domain_recipient = new_qdr; + } } redirect.owners = ob->owners; diff --git a/src/src/routers/rf_expand_data.c b/src/src/routers/rf_expand_data.c index 9892567f0..6ba613215 100644 --- a/src/src/routers/rf_expand_data.c +++ b/src/src/routers/rf_expand_data.c @@ -31,7 +31,7 @@ uschar * rf_expand_data(address_item *addr, uschar *s, int *prc) { uschar *yield = expand_string(s); -if (yield != NULL) return yield; +if (yield) return yield; if (f.expand_string_forcedfail) { DEBUG(D_route) debug_printf("forced failure for expansion of \"%s\"\n", s); diff --git a/src/src/routers/rf_get_errors_address.c b/src/src/routers/rf_get_errors_address.c index 222d157d8..792b9b5f3 100644 --- a/src/src/routers/rf_get_errors_address.c +++ b/src/src/routers/rf_get_errors_address.c @@ -38,14 +38,13 @@ int rf_get_errors_address(address_item * addr, router_instance * rblock, int verify, const uschar ** errors_to) { -uschar *s; +uschar * s; *errors_to = addr->prop.errors_address; if (!rblock->errors_to) return OK; -s = expand_string(rblock->errors_to); - -if (s == NULL) +GET_OPTION("errors_to"); +if (!(s = expand_string(rblock->errors_to))) { if (f.expand_string_forcedfail) { @@ -60,7 +59,7 @@ if (s == NULL) /* If the errors_to address is empty, it means "ignore errors" */ -if (*s == 0) +if (!*s) { addr->prop.ignore_error = TRUE; /* For locally detected errors */ *errors_to = US""; /* Return path for SMTP */ diff --git a/src/src/routers/rf_get_transport.c b/src/src/routers/rf_get_transport.c index d54e3c296..d37966865 100644 --- a/src/src/routers/rf_get_transport.c +++ b/src/src/routers/rf_get_transport.c @@ -47,6 +47,7 @@ rf_get_transport(uschar *tpname, transport_instance **tpptr, address_item *addr, uschar *ss; BOOL expandable; +GET_OPTION("transport"); if (!tpname) { if (!require_name) return TRUE; diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 2d977b975..ff768e94c 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -2172,6 +2172,7 @@ lwr_receive_ungetc = NULL; /* Set up the message size limit; this may be host-specific */ +GET_OPTION("message_size_limit"); thismessage_size_limit = expand_string_integer(message_size_limit, TRUE); if (expand_string_message) { @@ -2569,6 +2570,7 @@ if (proxy_protocol_host()) /* Run the connect ACL if it exists */ user_msg = NULL; +GET_OPTION("acl_smtp_connect"); if (acl_smtp_connect) { int rc; @@ -2615,16 +2617,20 @@ if (user_msg) esclen = codelen - 4; } } -else if (!(s = expand_string(smtp_banner))) +else { - log_write(0, f.expand_string_forcedfail ? LOG_MAIN : LOG_MAIN|LOG_PANIC_DIE, - "Expansion of \"%s\" (smtp_banner) failed: %s", - smtp_banner, expand_string_message); - /* for force-fail */ -#ifndef DISABLE_TLS - if (tls_in.on_connect) tls_close(NULL, TLS_SHUTDOWN_WAIT); -#endif - return FALSE; + GET_OPTION("smtp_banner"); + if (!(s = expand_string(smtp_banner))) + { + log_write(0, f.expand_string_forcedfail ? LOG_MAIN : LOG_MAIN|LOG_PANIC_DIE, + "Expansion of \"%s\" (smtp_banner) failed: %s", + smtp_banner, expand_string_message); + /* for force-fail */ + #ifndef DISABLE_TLS + if (tls_in.on_connect) tls_close(NULL, TLS_SHUTDOWN_WAIT); + #endif + return FALSE; + } } /* Remove any terminating newlines; might as well remove trailing space too */ @@ -3185,6 +3191,7 @@ fl.smtp_exit_function_called = TRUE; /* Call the not-QUIT ACL, if there is one, unless no reason is given. */ +GET_OPTION("acl_smtp_notquit"); if (acl_smtp_notquit && reason) { smtp_notquit_reason = reason; @@ -3521,6 +3528,7 @@ smtp_quit_handler(uschar ** user_msgp, uschar ** log_msgp) HAD(SCH_QUIT); f.smtp_in_quit = TRUE; incomplete_transaction_log(US"QUIT"); +GET_OPTION("acl_smtp_quit"); if ( acl_smtp_quit && acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, user_msgp, log_msgp) == ERROR) @@ -3694,6 +3702,7 @@ while (done <= 0) for (auth_instance * au = auths; au; au = au->next) if (strcmpic(US"tls", au->driver_name) == 0) { + GET_OPTION("acl_smtp_auth"); if ( acl_smtp_auth && (rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth, &user_msg, &log_msg)) != OK @@ -3772,6 +3781,7 @@ while (done <= 0) /* Check the ACL */ + GET_OPTION("acl_smtp_auth"); if ( acl_smtp_auth && (rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth, &user_msg, &log_msg)) != OK @@ -3966,6 +3976,7 @@ while (done <= 0) /* Apply an ACL check if one is defined; afterwards, recheck synchronization in case the client started sending in a delay. */ + GET_OPTION("acl_smtp_helo"); if (acl_smtp_helo) if ((rc = acl_check(ACL_WHERE_HELO, NULL, acl_smtp_helo, &user_msg, &log_msg)) != OK) @@ -4097,16 +4108,19 @@ while (done <= 0) /* Advertise ETRN/VRFY/EXPN if there's are ACL checking whether a host is permitted to issue them; a check is made when any host actually tries. */ + GET_OPTION("acl_smtp_etrn"); if (acl_smtp_etrn) { g = string_catn(g, smtp_code, 3); g = string_catn(g, US"-ETRN\r\n", 7); } + GET_OPTION("acl_smtp_vrfy"); if (acl_smtp_vrfy) { g = string_catn(g, smtp_code, 3); g = string_catn(g, US"-VRFY\r\n", 7); } + GET_OPTION("acl_smtp_expn"); if (acl_smtp_expn) { g = string_catn(g, smtp_code, 3); @@ -4502,6 +4516,7 @@ while (done <= 0) US"invalid data for AUTH"); goto COMMAND_LOOP; } + GET_OPTION("acl_smtp_mailauth"); if (!acl_smtp_mailauth) { ignore_msg = US"client not authenticated"; @@ -4698,6 +4713,7 @@ while (done <= 0) when pipelining is not advertised, do another sync check in case the ACL delayed and the client started sending in the meantime. */ + GET_OPTION("acl_smtp_mail"); if (acl_smtp_mail) { rc = acl_check(ACL_WHERE_MAIL, NULL, acl_smtp_mail, &user_msg, &log_msg); @@ -4953,10 +4969,13 @@ while (done <= 0) if (f.recipients_discarded) rc = DISCARD; else + { + GET_OPTION("acl_smtp_rcpt"); if ( (rc = acl_check(ACL_WHERE_RCPT, recipient, acl_smtp_rcpt, &user_msg, &log_msg)) == OK && !f.smtp_in_pipelining_advertised && !check_sync()) goto SYNC_FAILURE; + } /* The ACL was happy */ @@ -5119,6 +5138,7 @@ while (done <= 0) since the ACL may have delayed. To handle cutthrough delivery enforce a dummy call to get the DATA command sent. */ + GET_OPTION("acl_smtp_predata"); if (!acl_smtp_predata && cutthrough.cctx.sock < 0) rc = OK; else @@ -5177,6 +5197,7 @@ while (done <= 0) US"verify"))) break; + GET_OPTION("acl_smtp_vrfy"); if ((rc = acl_check(ACL_WHERE_VRFY, address, acl_smtp_vrfy, &user_msg, &log_msg)) != OK) done = smtp_handle_acl_fail(ACL_WHERE_VRFY, rc, user_msg, log_msg); @@ -5215,6 +5236,7 @@ while (done <= 0) case EXPN_CMD: HAD(SCH_EXPN); + GET_OPTION("acl_smtp_expn"); rc = acl_check(ACL_WHERE_EXPN, NULL, acl_smtp_expn, &user_msg, &log_msg); if (rc != OK) done = smtp_handle_acl_fail(ACL_WHERE_EXPN, rc, user_msg, log_msg); @@ -5244,6 +5266,7 @@ while (done <= 0) /* Apply an ACL check if one is defined */ + GET_OPTION("acl_smtp_starttls"); if ( acl_smtp_starttls && (rc = acl_check(ACL_WHERE_STARTTLS, NULL, acl_smtp_starttls, &user_msg, &log_msg)) != OK @@ -5362,6 +5385,7 @@ while (done <= 0) case QUIT_CMD: f.smtp_in_quit = TRUE; user_msg = NULL; + GET_OPTION("acl_smtp_quit"); if ( acl_smtp_quit && ((rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, &user_msg, &log_msg)) == ERROR)) @@ -5481,6 +5505,7 @@ while (done <= 0) log_write(L_etrn, LOG_MAIN, "ETRN %s received from %s", smtp_cmd_argument, host_and_ident(FALSE)); + GET_OPTION("acl_smtp_etrn"); if ((rc = acl_check(ACL_WHERE_ETRN, NULL, acl_smtp_etrn, &user_msg, &log_msg)) != OK) { @@ -5497,6 +5522,7 @@ while (done <= 0) since that is strictly the only kind of ETRN that can be implemented according to the RFC. */ + GET_OPTION("smtp_etrn_command"); if (smtp_etrn_command) { uschar *error; diff --git a/src/src/smtp_out.c b/src/src/smtp_out.c index 62557072e..895b594fc 100644 --- a/src/src/smtp_out.c +++ b/src/src/smtp_out.c @@ -278,6 +278,7 @@ if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, US &on, sizeof(on))) /* Set DSCP value, if we can. For now, if we fail to set the value, we don't bomb out, just log it and continue in default traffic class. */ +GET_OPTION("dscp"); if (dscp && dscp_lookup(dscp, sc->host_af, &dscp_level, &dscp_option, &dscp_value)) { HDEBUG(D_transport|D_acl|D_v) diff --git a/src/src/spf.c b/src/src/spf.c index 2e3f861eb..cc36463e4 100644 --- a/src/src/spf.c +++ b/src/src/spf.c @@ -253,10 +253,13 @@ if (!(spf_server = SPF_server_new_dns(dc, debug))) DEBUG(D_receive) debug_printf("spf: SPF_server_new() failed.\n"); return FALSE; } - /* Override the outdated explanation URL. - See https://www.mail-archive.com/mailop@mailop.org/msg08019.html - Used to work as "Please%_see%_http://www.open-spf.org/Why?id=%{S}&ip=%{C}&receiver=%{R}", - but is broken now (May 18th, 2020) */ + +/* Override the outdated explanation URL. +See https://www.mail-archive.com/mailop@mailop.org/msg08019.html +Used to work as "Please%_see%_http://www.open-spf.org/Why?id=%{S}&ip=%{C}&receiver=%{R}", +but is broken now (May 18th, 2020) */ + +GET_OPTION("spf_smtp_comment_template"); if (!(s = expand_string(spf_smtp_comment_template))) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "expansion of spf_smtp_comment_template failed"); diff --git a/src/src/transports/appendfile.c b/src/src/transports/appendfile.c index ce52cc6ff..72c96f0d0 100644 --- a/src/src/transports/appendfile.c +++ b/src/src/transports/appendfile.c @@ -178,18 +178,24 @@ static int appendfile_transport_setup(transport_instance *tblock, address_item *addrlist, transport_feedback *dummy, uid_t uid, gid_t gid, uschar **errmsg) { -appendfile_transport_options_block *ob = +appendfile_transport_options_block * ob = (appendfile_transport_options_block *)(tblock->options_block); -uschar *q = ob->quota; +uschar * q; double default_value = 0.0; if (ob->expand_maildir_use_size_file) - ob->maildir_use_size_file = expand_check_condition(ob->expand_maildir_use_size_file, + { + GET_OPTION("maildir_use_size_file"); + ob->maildir_use_size_file = + expand_check_condition(ob->expand_maildir_use_size_file, US"`maildir_use_size_file` in transport", tblock->name); + } /* Loop for quota, quota_filecount, quota_warn_threshold, mailbox_size, mailbox_filecount */ +GET_OPTION("quota"); +q = ob->quota; for (int i = 0; i < 5; i++) { double d = default_value; @@ -258,6 +264,7 @@ for (int i = 0; i < 5; i++) which = US"quota"; ob->quota_value = (off_t)d; ob->quota_no_check = no_check; + GET_OPTION("quota_filecount"); q = ob->quota_filecount; break; @@ -266,6 +273,7 @@ for (int i = 0; i < 5; i++) which = US"quota_filecount"; ob->quota_filecount_value = (int)d; ob->quota_filecount_no_check = no_check; + GET_OPTION("quota_warn_threshold"); q = ob->quota_warn_threshold; break; @@ -273,6 +281,7 @@ for (int i = 0; i < 5; i++) if (d >= 2.0*1024.0*1024.0*1024.0 && sizeof(off_t) <= 4) which = US"quota_warn_threshold"; ob->quota_warn_threshold_value = (off_t)d; + GET_OPTION("mailbox_size"); q = ob->mailbox_size_string; default_value = -1.0; break; @@ -281,6 +290,7 @@ for (int i = 0; i < 5; i++) if (d >= 2.0*1024.0*1024.0*1024.0 && sizeof(off_t) <= 4) which = US"mailbox_size";; ob->mailbox_size_value = (off_t)d; + GET_OPTION("mailbox_filecount"); q = ob->mailbox_filecount_string; break; @@ -399,10 +409,10 @@ if (ob->dirname) if (ob->maildir_format && ob->mailstore_format) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s transport:\n " "only one of maildir and mailstore may be specified", tblock->name); - if (ob->quota_filecount != NULL && ob->quota == NULL) + if (ob->quota_filecount != NULL && !ob->quota) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s transport:\n " "quota must be set if quota_filecount is set", tblock->name); - if (ob->quota_directory != NULL && ob->quota == NULL) + if (ob->quota_directory != NULL && !ob->quota) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s transport:\n " "quota must be set if quota_directory is set", tblock->name); } @@ -1221,6 +1231,7 @@ if (!fdname) { if (!(fdname = ob->filename)) { + GET_OPTION("directory"); fdname = ob->dirname; isdirectory = TRUE; } @@ -2234,6 +2245,7 @@ else /* Use an explicitly configured directory if set */ + GET_OPTION("quota_directory"); if (ob->quota_directory) { if (!(check_path = expand_string(ob->quota_directory))) @@ -2431,6 +2443,7 @@ else DEBUG(D_transport) debug_printf("delivering in maildir format in %s\n", path); + GET_OPTION("maildir_tag"); nametag = ob->maildir_tag; /* Check that nametag expands successfully; a hard failure causes a panic @@ -2567,9 +2580,10 @@ else /* Write the envelope file, then close it. */ + GET_OPTION("mailstore_prefix"); if (ob->mailstore_prefix) { - uschar *s = expand_string(ob->mailstore_prefix); + uschar * s = expand_string(ob->mailstore_prefix); if (!s) { if (!f.expand_string_forcedfail) @@ -2595,9 +2609,10 @@ else for (address_item * taddr = addr; taddr; taddr = taddr->next) fprintf(env_file, "%s@%s\n", taddr->local_part, taddr->domain); + GET_OPTION("mailstore_suffix"); if (ob->mailstore_suffix) { - uschar *s = expand_string(ob->mailstore_suffix); + uschar * s = expand_string(ob->mailstore_suffix); if (!s) { if (!f.expand_string_forcedfail) @@ -2757,18 +2772,21 @@ transport_newlines = 0; /* Write any configured prefix text first */ -if (yield == OK && ob->message_prefix && *ob->message_prefix) +if (yield == OK) { - uschar *prefix = expand_string(ob->message_prefix); - if (!prefix) - { - errno = ERRNO_EXPANDFAIL; - addr->transport_return = PANIC; - addr->message = string_sprintf("Expansion of \"%s\" (prefix for %s " - "transport) failed", ob->message_prefix, tblock->name); - yield = DEFER; - } - else if (!transport_write_string(fd, "%s", prefix)) yield = DEFER; + uschar * prefix = ob->message_prefix; + GET_OPTION("message_prefix"); + if (prefix && *prefix) + if (!(prefix = expand_string(prefix))) + { + errno = ERRNO_EXPANDFAIL; + addr->transport_return = PANIC; + addr->message = string_sprintf("Expansion of \"%s\" (prefix for %s " + "transport) failed", ob->message_prefix, tblock->name); + yield = DEFER; + } + else if (!transport_write_string(fd, "%s", prefix)) + yield = DEFER; } /* If the use_bsmtp option is on, we need to write SMTP prefix information. The @@ -2820,18 +2838,21 @@ if (yield == OK) /* Now a configured suffix. */ -if (yield == OK && ob->message_suffix && *ob->message_suffix) +if (yield == OK) { - uschar *suffix = expand_string(ob->message_suffix); - if (!suffix) - { - errno = ERRNO_EXPANDFAIL; - addr->transport_return = PANIC; - addr->message = string_sprintf("Expansion of \"%s\" (suffix for %s " - "transport) failed", ob->message_suffix, tblock->name); - yield = DEFER; - } - else if (!transport_write_string(fd, "%s", suffix)) yield = DEFER; + uschar * suffix = ob->message_suffix; + GET_OPTION("message_suffix"); + if (suffix && *suffix) + if (!(suffix = expand_string(suffix))) + { + errno = ERRNO_EXPANDFAIL; + addr->transport_return = PANIC; + addr->message = string_sprintf("Expansion of \"%s\" (suffix for %s " + "transport) failed", ob->message_suffix, tblock->name); + yield = DEFER; + } + else if (!transport_write_string(fd, "%s", suffix)) + yield = DEFER; } /* If batch smtp, write the terminating dot. */ diff --git a/src/src/transports/autoreply.c b/src/src/transports/autoreply.c index fa884cec4..3755ba284 100644 --- a/src/src/transports/autoreply.c +++ b/src/src/transports/autoreply.c @@ -314,20 +314,21 @@ if (addr->reply) } else { - uschar *oncerepeat = ob->once_repeat; + uschar * oncerepeat; DEBUG(D_transport) debug_printf("taking data from transport\n"); - from = ob->from; - reply_to = ob->reply_to; - to = ob->to; - cc = ob->cc; - bcc = ob->bcc; - subject = ob->subject; - headers = ob->headers; - text = ob->text; - file = ob->file; - logfile = ob->logfile; - oncelog = ob->oncelog; + GET_OPTION("once_repeat"); oncerepeat = ob->once_repeat; + GET_OPTION("from"); from = ob->from; + GET_OPTION("reply_to"); reply_to = ob->reply_to; + GET_OPTION("to"); to = ob->to; + GET_OPTION("cc"); cc = ob->cc; + GET_OPTION("bcc"); bcc = ob->bcc; + GET_OPTION("subject"); subject = ob->subject; + GET_OPTION("headers"); headers = ob->headers; + GET_OPTION("text"); text = ob->text; + GET_OPTION("file"); file = ob->file; + GET_OPTION("log"); logfile = ob->logfile; + GET_OPTION("once"); oncelog = ob->oncelog; file_expand = ob->file_expand; return_message = ob->return_message; diff --git a/src/src/transports/pipe.c b/src/src/transports/pipe.c index 64c70e719..157d2368f 100644 --- a/src/src/transports/pipe.c +++ b/src/src/transports/pipe.c @@ -315,6 +315,7 @@ argv = *argvptr; /* If allow_commands is set, see if the command is in the permitted list. */ +GET_OPTION("allow_commands"); if (ob->allow_commands) { int sep = 0; @@ -368,10 +369,11 @@ for it. */ if (argv[0][0] != '/') { int sep = 0; - uschar *p; - const uschar *listptr = expand_string(ob->path); + uschar * p; - while ((p = string_nextinlist(&listptr, &sep, NULL, 0))) + GET_OPTION("path"); + for (const uschar * listptr = expand_string(ob->path); + p = string_nextinlist(&listptr, &sep, NULL, 0); ) { struct stat statbuf; sprintf(CS big_buffer, "%.256s/%.256s", p, argv[0]); @@ -550,6 +552,7 @@ if (testflag(addr, af_pfr) && addr->local_part[0] == '|') { /* Enables expansion of $address_pipe into separate arguments */ setflag(addr, af_force_command); + GET_OPTION("commsnd"); cmd = ob->cmd; expand_arguments = TRUE; expand_fail = PANIC; @@ -563,6 +566,7 @@ if (testflag(addr, af_pfr) && addr->local_part[0] == '|') } else { + GET_OPTION("commsnd"); cmd = ob->cmd; expand_arguments = TRUE; expand_fail = PANIC; @@ -650,6 +654,7 @@ else if (timezone_string && timezone_string[0]) /* Add any requested items */ +GET_OPTION("environment"); if (envlist) if (!(envlist = expand_cstring(envlist))) { @@ -796,9 +801,10 @@ transport_count = 0; /* First write any configured prefix information */ +GET_OPTION("message_prefix"); if (ob->message_prefix) { - uschar *prefix = expand_string(ob->message_prefix); + uschar * prefix = expand_string(ob->message_prefix); if (!prefix) { addr->transport_return = f.search_find_defer? DEFER : PANIC; @@ -838,9 +844,10 @@ if (!transport_write_message(&tctx, 0)) /* Now any configured suffix */ +GET_OPTION("message_suffix"); if (ob->message_suffix) { - uschar *suffix = expand_string(ob->message_suffix); + uschar * suffix = expand_string(ob->message_suffix); if (!suffix) { addr->transport_return = f.search_find_defer? DEFER : PANIC; diff --git a/src/src/transports/queuefile.c b/src/src/transports/queuefile.c index b6511133c..21c98eeef 100644 --- a/src/src/transports/queuefile.c +++ b/src/src/transports/queuefile.c @@ -195,6 +195,7 @@ DEBUG(D_transport) # define O_NOFOLLOW 0 #endif +GET_OPTION("directory"); if (!(dstdir = expand_string(ob->dirname))) { addr->message = string_sprintf("%s transport: failed to expand dirname option", diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 596a328c3..736592352 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -1088,6 +1088,7 @@ if (pending_BANNER) /*XXX EXPERIMENTAL_ESMTP_LIMITS ? */ # ifndef DISABLE_TLS_RESUME + GET_OPTION("host_name_extract"); s = ((smtp_transport_options_block *)sx->conn_args.ob)->host_name_extract; if (!s) s = HNE_DEFAULT; ehlo_response_lbserver(sx, s); @@ -1769,6 +1770,7 @@ uschar * local_authenticated_sender = authenticated_sender; authenticated_sender, ob->authenticated_sender, f.smtp_authenticated?"Y":"N"); #endif +GET_OPTION("authenticated_sender"); if (ob->authenticated_sender) { uschar * new = expand_string(ob->authenticated_sender); @@ -2374,6 +2376,7 @@ PIPE_CONNECT_RETRY: For early-pipe, we are ok if binding to a local interface; otherwise (if $sending_ip_address is seen in helo_data) we disabled early-pipe above. */ + GET_OPTION("helo_data"); if (sx->helo_data) if (!(sx->helo_data = expand_string(sx->helo_data))) if (sx->verify) @@ -2501,6 +2504,7 @@ goto SEND_QUIT; an LB. Call this anyway, so that a dummy host_name_extract option value can force resumption attempts. */ + GET_OPTION("host_name_extract"); if (!(s = ob->host_name_extract)) s = US"never-LB"; ehlo_response_lbserver(sx, s); # endif @@ -2629,6 +2633,7 @@ goto SEND_QUIT; } #endif #ifndef DISABLE_TLS_RESUME + GET_OPTION("host_name_extract"); if (!(s = ob->host_name_extract)) s = HNE_DEFAULT; ehlo_response_lbserver(sx, s); #endif @@ -2837,13 +2842,17 @@ if (tls_out.active.sock >= 0) { uschar * greeting_cmd; - if (!sx->helo_data && !(sx->helo_data = expand_string(ob->helo_data))) + if (!sx->helo_data) { - uschar *message = string_sprintf("failed to expand helo_data: %s", - expand_string_message); - set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, &sx->delivery_start); - yield = DEFER; - goto SEND_QUIT; + GET_OPTION("helo_data"); + if (!(sx->helo_data = expand_string(ob->helo_data))) + { + uschar *message = string_sprintf("failed to expand helo_data: %s", + expand_string_message); + set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, &sx->delivery_start); + yield = DEFER; + goto SEND_QUIT; + } } #ifndef DISABLE_PIPE_CONNECT @@ -3094,6 +3103,7 @@ if (sx->addrlist->prop.utf8_msg) /* If the transport sets a downconversion mode it overrides any set by ACL for the message. */ + GET_OPTION("utf8_downconvert"); if ((s = ob->utf8_downconvert)) { if (!(s = expand_string(s))) @@ -5565,8 +5575,9 @@ retry_non_continued: host_af = Ustrchr(host->address, ':') ? AF_INET6 : AF_INET; { - uschar * s = ob->interface; - if (s && *s) + uschar * s; + GET_OPTION("interface"); + if ((s = ob->interface) && *s) { if (!smtp_get_interface(s, host_af, addrlist, &interface, tid)) return FALSE; diff --git a/src/src/transports/smtp_socks.c b/src/src/transports/smtp_socks.c index 22ee74bd8..ac8d0f2da 100644 --- a/src/src/transports/smtp_socks.c +++ b/src/src/transports/smtp_socks.c @@ -229,6 +229,7 @@ blob early_data; if (!timeout) timeout = 24*60*60; /* use 1 day for "indefinite" */ tmo = time(NULL) + timeout; +GET_OPTION("socks_proxy"); if (!(proxy_list = expand_string(ob->socks_proxy))) { log_write(0, LOG_MAIN|LOG_PANIC, "Bad expansion for socks_proxy in %s", diff --git a/src/src/verify.c b/src/src/verify.c index bde660063..78923651c 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -368,6 +368,7 @@ if (addr->transport == cutthrough.addr.transport) host_af = Ustrchr(host->address, ':') ? AF_INET6 : AF_INET; + GET_OPTION("interface"); if ( !smtp_get_interface(tf->interface, host_af, addr, &interface, US"callout") || !smtp_get_port(tf->port, addr, &port, US"callout") @@ -579,10 +580,14 @@ else with a random local part, ensure that such a local part is available. If not, log the fact, but carry on without randomising. */ - if (options & vopt_callout_random && callout_random_local_part) - if (!(random_local_part = expand_string(callout_random_local_part))) + if (options & vopt_callout_random) + { + GET_OPTION("callout_random_local_part"); + if ( callout_random_local_part + && !(random_local_part = expand_string(callout_random_local_part))) log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand " "callout_random_local_part: %s", expand_string_message); + } /* Compile regex' used by client-side smtp */ @@ -660,6 +665,7 @@ coding means skipping this whole loop and doing the append separately. */ deliver_domain = addr->domain; transport_name = addr->transport->name; + GET_OPTION("interface"); if ( !smtp_get_interface(tf->interface, host_af, addr, &interface, US"callout") || !smtp_get_port(tf->port, addr, &port, US"callout") commit 38965d80d70cacd0c944d32f7107549a3a272b35 Author: Jeremy Harris Date: Thu Sep 28 11:10:13 2023 +0100 Build: avoid compiling code for unused transports, routers, authenticators The smtp transport is not covered here; there are dependencies in other bits of the exim body. diff --git a/src/src/auths/cram_md5.c b/src/src/auths/cram_md5.c index 0761b7c6a..8f00ee498 100644 --- a/src/src/auths/cram_md5.c +++ b/src/src/auths/cram_md5.c @@ -13,15 +13,17 @@ in the MD5 computation functions, without their own stand-alone main program. */ #ifdef STAND_ALONE -#define CRAM_STAND_ALONE -#include "md5.c" +# define CRAM_STAND_ALONE +# include "md5.c" /* This is the normal, non-stand-alone case */ #else -#include "../exim.h" -#include "cram_md5.h" +# include "../exim.h" + +# ifdef AUTH_CRAM_MD5 +# include "cram_md5.h" /* Options specific to the cram_md5 authentication mechanism. */ @@ -49,7 +51,7 @@ auth_cram_md5_options_block auth_cram_md5_option_defaults = { }; -#ifdef MACRO_PREDEF +# ifdef MACRO_PREDEF /* Dummy values */ void auth_cram_md5_init(auth_instance *ablock) {} @@ -57,7 +59,7 @@ int auth_cram_md5_server(auth_instance *ablock, uschar *data) {return 0;} int auth_cram_md5_client(auth_instance *ablock, void *sx, int timeout, uschar *buffer, int buffsize) {return 0;} -#else /*!MACRO_PREDEF*/ +# else /*!MACRO_PREDEF*/ /************************************************* @@ -81,8 +83,9 @@ if (ob->client_secret != NULL) } } -#endif /*!MACRO_PREDEF*/ -#endif /* STAND_ALONE */ +# endif /*!MACRO_PREDEF*/ +# endif /*AUTH_CRAM_MD5*/ +#endif /*!STAND_ALONE*/ @@ -154,7 +157,8 @@ md5_end(&base, md5secret, 16, digestptr); } -#ifndef STAND_ALONE +# ifndef STAND_ALONE +# ifdef AUTH_CRAM_MD5 /************************************************* * Server entry point * @@ -329,7 +333,8 @@ if (smtp_write_command(sx, SCMD_FLUSH, "%s\r\n", b64encode(CUS big_buffer, return smtp_read_response(sx, US buffer, buffsize, '2', timeout) ? OK : FAIL; } -#endif /* STAND_ALONE */ +# endif /*AUTH_CRAM_MD5*/ +# endif /*!STAND_ALONE*/ /************************************************* @@ -338,7 +343,7 @@ return smtp_read_response(sx, US buffer, buffsize, '2', timeout) ************************************************** *************************************************/ -#ifdef STAND_ALONE +# ifdef STAND_ALONE int main(int argc, char **argv) { @@ -355,7 +360,7 @@ printf("\n"); return 0; } -#endif +# endif /*STAND_ALONE*/ #endif /*!MACRO_PREDEF*/ /* End of cram_md5.c */ diff --git a/src/src/auths/dovecot.c b/src/src/auths/dovecot.c index 377c09205..7f437fa0d 100644 --- a/src/src/auths/dovecot.c +++ b/src/src/auths/dovecot.c @@ -23,6 +23,8 @@ because using C buffered I/O gives problems on some operating systems. PH */ */ #include "../exim.h" + +#ifdef AUTH_DOVECOT /* Remainder of file */ #include "dovecot.h" #define VERSION_MAJOR 1 @@ -533,4 +535,6 @@ return ret; } -#endif /*!MACRO_PREDEF*/ +#endif /*!MACRO_PREDEF*/ +#endif /*AUTH_DOVECOT*/ +/* end of auths/dovecot.c */ diff --git a/src/src/auths/external.c b/src/src/auths/external.c index a89a50486..6ae5833af 100644 --- a/src/src/auths/external.c +++ b/src/src/auths/external.c @@ -14,6 +14,8 @@ method defined in RFC 4422 Appendix A. #include "../exim.h" + +#ifdef AUTH_EXTERNAL /* Remainder of file */ #include "external.h" /* Options specific to the external authentication mechanism. */ @@ -153,5 +155,6 @@ return OK; -#endif /*!MACRO_PREDEF*/ +#endif /*!MACRO_PREDEF*/ +#endif /*AUTH_EXTERNAL*/ /* End of external.c */ diff --git a/src/src/auths/plaintext.c b/src/src/auths/plaintext.c index 391e629f9..814896047 100644 --- a/src/src/auths/plaintext.c +++ b/src/src/auths/plaintext.c @@ -8,6 +8,8 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" + +#ifdef AUTH_PLAINTEXT /* Remainder of file */ #include "plaintext.h" @@ -176,5 +178,6 @@ while ((s = string_nextinlist(&text, &sep, NULL, 0))) return FAIL; } -#endif /*!MACRO_PREDEF*/ +#endif /*!MACRO_PREDEF*/ +#endif /*AUTH_PLAINTEST*/ /* End of plaintext.c */ diff --git a/src/src/auths/spa.c b/src/src/auths/spa.c index 0d4262ec5..51418c4ed 100644 --- a/src/src/auths/spa.c +++ b/src/src/auths/spa.c @@ -34,6 +34,8 @@ References: #include "../exim.h" + +#ifdef AUTH_SPA /* Remainder of file */ #include "spa.h" /* #define DEBUG_SPA */ @@ -372,5 +374,6 @@ if (errno != 0 || buffer[0] != '3') return FAIL; } -#endif /*!MACRO_PREDEF*/ +#endif /*!MACRO_PREDEF*/ +#endif /*AUTH_SPA*/ /* End of spa.c */ diff --git a/src/src/auths/tls.c b/src/src/auths/tls.c index 72ad56c4e..005032f57 100644 --- a/src/src/auths/tls.c +++ b/src/src/auths/tls.c @@ -12,6 +12,8 @@ a server to verify a client SSL certificate #include "../exim.h" + +#ifdef AUTH_TLS /* Remainder of file */ #include "tls.h" /* Options specific to the tls authentication mechanism. */ @@ -91,5 +93,6 @@ return auth_check_serv_cond(ablock); } -#endif /*!MACRO_PREDEF*/ +#endif /*!MACRO_PREDEF*/ +#endif /*AUTH_TLS*/ /* End of tls.c */ diff --git a/src/src/routers/accept.c b/src/src/routers/accept.c index 8b72bfac1..9766e5e10 100644 --- a/src/src/routers/accept.c +++ b/src/src/routers/accept.c @@ -9,6 +9,8 @@ #include "../exim.h" + +#ifdef ROUTER_ACCEPT /* Remainder of file */ #include "rf_functions.h" #include "accept.h" @@ -137,4 +139,5 @@ return rf_queue_add(addr, addr_local, addr_remote, rblock, pw)? OK : DEFER; } #endif /*!MACRO_PREDEF*/ +#endif /*ROUTER_ACCEPT*/ /* End of routers/accept.c */ diff --git a/src/src/routers/dnslookup.c b/src/src/routers/dnslookup.c index 671e8c93f..33382671f 100644 --- a/src/src/routers/dnslookup.c +++ b/src/src/routers/dnslookup.c @@ -8,6 +8,8 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" + +#ifdef ROUTER_DNSLOOKUP /* Remainder of file */ #include "rf_functions.h" #include "dnslookup.h" @@ -469,7 +471,8 @@ return rf_queue_add(addr, addr_local, addr_remote, rblock, pw)? OK : DEFER; } -#endif /*!MACRO_PREDEF*/ +#endif /*!MACRO_PREDEF*/ +#endif /*ROUTER_DNSLOOKUP /* End of routers/dnslookup.c */ /* vi: aw ai sw=2 */ diff --git a/src/src/routers/ipliteral.c b/src/src/routers/ipliteral.c index 1297b97f2..16e096b40 100644 --- a/src/src/routers/ipliteral.c +++ b/src/src/routers/ipliteral.c @@ -9,6 +9,8 @@ #include "../exim.h" + +#ifdef ROUTER_IPLITERAL /* Remainder of file */ #include "rf_functions.h" #include "ipliteral.h" @@ -199,5 +201,6 @@ return rf_queue_add(addr, addr_local, addr_remote, rblock, pw)? OK : DEFER; } -#endif /*!MACRO_PREDEF*/ +#endif /*!MACRO_PREDEF*/ +#endif /*ROUTER_IPLITERAL*/ /* End of routers/ipliteral.c */ diff --git a/src/src/routers/iplookup.c b/src/src/routers/iplookup.c index f9966bcc0..b88212e5c 100644 --- a/src/src/routers/iplookup.c +++ b/src/src/routers/iplookup.c @@ -9,6 +9,8 @@ #include "../exim.h" + +#ifdef ROUTER_IPLOOKUP /* Remainder of file */ #include "rf_functions.h" #include "iplookup.h" @@ -414,5 +416,6 @@ if (rc != OK) return rc; return OK; } -#endif /*!MACRO_PREDEF*/ +#endif /*!MACRO_PREDEF*/ +#endif /*ROUTER_IPLOOKUP*/ /* End of routers/iplookup.c */ diff --git a/src/src/routers/manualroute.c b/src/src/routers/manualroute.c index 200e0206e..45378ce3d 100644 --- a/src/src/routers/manualroute.c +++ b/src/src/routers/manualroute.c @@ -9,6 +9,8 @@ #include "../exim.h" + +#ifdef ROUTER_MANUALROUTE #include "rf_functions.h" #include "manualroute.h" @@ -488,5 +490,6 @@ addr->transport = transport; return OK; } -#endif /*!MACRO_PREDEF*/ +#endif /*!MACRO_PREDEF*/ +#endif /*ROUTER_MANUALROUTE*/ /* End of routers/manualroute.c */ diff --git a/src/src/routers/queryprogram.c b/src/src/routers/queryprogram.c index dd5e24b93..b1d736f94 100644 --- a/src/src/routers/queryprogram.c +++ b/src/src/routers/queryprogram.c @@ -8,6 +8,8 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" + +#ifdef ROUTER_QUERYPROGRAM /* Remainder of file */ #include "rf_functions.h" #include "queryprogram.h" @@ -535,5 +537,6 @@ addr->prop = addr_prop; return rf_queue_add(addr, addr_local, addr_remote, rblock, pw) ? OK : DEFER; } -#endif /*!MACRO_PREDEF*/ +#endif /*!MACRO_PREDEF*/ +#endif /*ROUTER_QUERYPROGRAM*/ /* End of routers/queryprogram.c */ diff --git a/src/src/routers/redirect.c b/src/src/routers/redirect.c index 6b27c82b0..37b5f5e24 100644 --- a/src/src/routers/redirect.c +++ b/src/src/routers/redirect.c @@ -9,6 +9,8 @@ #include "../exim.h" + +#ifdef ROUTER_REDIRECT /* Remainder of file */ #include "rf_functions.h" #include "redirect.h" @@ -807,4 +809,5 @@ return yield; } #endif /*!MACRO_PREDEF*/ +#endif /*ROUTER_REDIRECT*/ /* End of routers/redirect.c */ diff --git a/src/src/transports/appendfile.c b/src/src/transports/appendfile.c index 72c96f0d0..0279659f2 100644 --- a/src/src/transports/appendfile.c +++ b/src/src/transports/appendfile.c @@ -9,6 +9,8 @@ #include "../exim.h" + +#ifdef TRANSPORT_APPENDFILE /* Remainder of file */ #include "appendfile.h" #ifdef SUPPORT_MAILDIR @@ -3326,4 +3328,5 @@ ret_panic: } #endif /*!MACRO_PREDEF*/ +#endif /*TRANSPORT_APPENDFILE*/ /* End of transport/appendfile.c */ diff --git a/src/src/transports/autoreply.c b/src/src/transports/autoreply.c index 3755ba284..2f2fe1e16 100644 --- a/src/src/transports/autoreply.c +++ b/src/src/transports/autoreply.c @@ -9,6 +9,8 @@ #include "../exim.h" + +#ifdef TRANSPORT_AUTOREPLY /* Remainder of file */ #include "autoreply.h" @@ -820,4 +822,5 @@ return FALSE; } #endif /*!MACRO_PREDEF*/ +#endif /*TRANSPORT_AUTOREPOL*/ /* End of transport/autoreply.c */ diff --git a/src/src/transports/lmtp.c b/src/src/transports/lmtp.c index f3f908836..eff0dc79d 100644 --- a/src/src/transports/lmtp.c +++ b/src/src/transports/lmtp.c @@ -9,6 +9,8 @@ #include "../exim.h" +#ifdef TRANSPORT_LMTP /* Remainder of file */ + #include "lmtp.h" #define PENDING_OK 256 @@ -807,4 +809,5 @@ MINUS_N: } #endif /*!MACRO_PREDEF*/ +#endif /*TRANSPORT_LMTP*/ /* End of transport/lmtp.c */ diff --git a/src/src/transports/pipe.c b/src/src/transports/pipe.c index 157d2368f..60a20c8fe 100644 --- a/src/src/transports/pipe.c +++ b/src/src/transports/pipe.c @@ -9,6 +9,8 @@ #include "../exim.h" + +#ifdef TRANSPORT_PIPE /* Remainder of file */ #include "pipe.h" #ifdef HAVE_SETCLASSRESOURCES @@ -1131,4 +1133,5 @@ return FALSE; } #endif /*!MACRO_PREDEF*/ +#endif /*TRASPORT_PIPE*/ /* End of transport/pipe.c */ commit 46ad960434683b3e15eb17edf3b2d748c191fbc4 Author: Jeremy Harris Date: Sat Dec 23 17:44:31 2023 +0000 tidying diff --git a/src/src/daemon.c b/src/src/daemon.c index 3c63c6368..88114d648 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -1493,7 +1493,7 @@ if (is_multiple_qrun()) /* we are managing periodic runs */ } else #endif - /* Normal periodic runL in order of run priority, find the first queue + /* Normal periodic run: in order of run priority, find the first queue for which we can start a runner */ for (q = qrunners; q; q = q->next) commit f2738aab2d72569b6d47b788099f6ebab701b2b2 Author: Jeremy Harris Date: Wed Feb 28 13:46:56 2024 +0000 Lookups: avoid leaking user/passwd from server spec to log. Bug 3066 diff --git a/src/src/acl.c b/src/src/acl.c index ecba07b25..f4d65464e 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -1146,9 +1146,9 @@ Returns: nothing */ static void -acl_warn(int where, uschar *user_message, uschar *log_message) +acl_warn(int where, uschar * user_message, uschar * log_message) { -if (log_message != NULL && log_message != user_message) +if (log_message && log_message != user_message) { uschar *text; string_item *logged; @@ -1159,9 +1159,9 @@ if (log_message != NULL && log_message != user_message) /* If a sender verification has failed, and the log message is "sender verify failed", add the failure message. */ - if (sender_verified_failed != NULL && - sender_verified_failed->message != NULL && - strcmpic(log_message, US"sender verify failed") == 0) + if ( sender_verified_failed + && sender_verified_failed->message + && strcmpic(log_message, US"sender verify failed") == 0) text = string_sprintf("%s: %s", text, sender_verified_failed->message); /* Search previously logged warnings. They are kept in malloc diff --git a/src/src/lookups/lf_sqlperform.c b/src/src/lookups/lf_sqlperform.c index d5bb8d297..0f3984ba7 100644 --- a/src/src/lookups/lf_sqlperform.c +++ b/src/src/lookups/lf_sqlperform.c @@ -13,6 +13,15 @@ +static int +server_len_for_logging(const uschar * server) +{ +const uschar * s = Ustrchr(server, '/'); +if (!s) return 64; +if (!(s = Ustrchr(s+1, '/'))) return 64; +return (int) (s - server); +} + /************************************************* * Call SQL server(s) to run an actual query * *************************************************/ @@ -30,6 +39,7 @@ Arguments: result where to pass back the result errmsg where to pass back an error message do_cache to be set zero if data is changed + opts options (which suffixed the lookup name, minus cache-control ones) or NULL func the lookup function to call Returns: the return from the lookup function, or DEFER @@ -42,21 +52,19 @@ lf_sqlperform(const uschar *name, const uschar *optionname, int(*fn)(const uschar *, uschar *, uschar **, uschar **, BOOL *, uint *, const uschar *)) { int rc; -uschar *server; +uschar * server; BOOL defer_break = FALSE; DEBUG(D_lookup) debug_printf_indent("%s query: \"%s\" opts '%s'\n", name, query, opts); -/* Handle queries that do have server information at the start. */ +/* Handle queries that do have server information at the start (old style). */ if (Ustrncmp(query, "servers", 7) == 0) { int qsep = 0; - const uschar *s, *ss; - const uschar *qserverlist; - uschar *qserver; + const uschar * s, * ss, * qserverlist; - log_write(0, LOG_MAIN|LOG_CONFIG_IN, "WARNING: obslete syntax used for lookup\n"); + log_write(0, LOG_MAIN|LOG_CONFIG_IN, "WARNING: obsolete syntax used for lookup"); s = query + 7; skip_whitespace(&s); @@ -83,35 +91,37 @@ if (Ustrncmp(query, "servers", 7) == 0) } qserverlist = string_sprintf("%.*s", (int)(ss - s), s); + query = ss + 1; - while ((qserver = string_nextinlist(&qserverlist, &qsep, NULL, 0))) + for (uschar * qsrv; qsrv = string_nextinlist(&qserverlist, &qsep, NULL, 0); ) { - if (Ustrchr(qserver, '/')) - server = qserver; + if (Ustrchr(qsrv, '/')) + server = qsrv; /* full server spec */ else - { - int len = Ustrlen(qserver); + { /* only name; search in option list */ + int len = Ustrlen(qsrv); const uschar * serverlist = optserverlist; for (int sep = 0; server = string_nextinlist(&serverlist, &sep, NULL, 0);) - if (Ustrncmp(server, qserver, len) == 0 && server[len] == '/') + if (Ustrncmp(server, qsrv, len) == 0 && server[len] == '/') break; if (!server) { - *errmsg = string_sprintf("%s server \"%s\" not found in %s", name, - qserver, optionname); + *errmsg = string_sprintf("%s server \"%.*s\" not found in %s", + name, server_len_for_logging(qsrv), qsrv, optionname); return DEFER; } } if (is_tainted(server)) { - *errmsg = string_sprintf("%s server \"%s\" is tainted", name, server); + *errmsg = string_sprintf("%s server \"%.*s\" is tainted", + name, server_len_for_logging(server), server); return DEFER; } - rc = (*fn)(ss+1, server, result, errmsg, &defer_break, do_cache, opts); + rc = (*fn)(query, server, result, errmsg, &defer_break, do_cache, opts); if (rc != DEFER || defer_break) return rc; } } @@ -139,7 +149,7 @@ else *errmsg = string_sprintf("no %s servers defined (%s option)", name, optionname); else - for (int d = 0; (server = string_nextinlist(&serverlist, &d, NULL, 0)); ) + for (int d = 0; server = string_nextinlist(&serverlist, &d, NULL, 0); ) { /* If not a full spec assume from options; scan main list for matching hostname */ @@ -163,7 +173,8 @@ else if (is_tainted(server)) { - *errmsg = string_sprintf("%s server \"%s\" is tainted", name, server); + *errmsg = string_sprintf("%s server \"%.*s\" is tainted", + name, server_len_for_logging(server), server); return DEFER; } @@ -176,3 +187,5 @@ return DEFER; } /* End of lf_sqlperform.c */ +/* vi: aw ai sw=2 +*/ diff --git a/src/src/receive.c b/src/src/receive.c index abac05c56..a56ff473e 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -290,12 +290,11 @@ Returns: FALSE if there isn't enough space, or if the information cannot BOOL receive_check_fs(int msg_size) { -int_eximarith_t space; int inodes; if (check_spool_space > 0 || msg_size > 0 || check_spool_inodes > 0) { - space = receive_statvfs(TRUE, &inodes); + int_eximarith_t space = receive_statvfs(TRUE, &inodes); DEBUG(D_receive) debug_printf("spool directory space = " PR_EXIM_ARITH "K inodes = %d " @@ -313,7 +312,7 @@ if (check_spool_space > 0 || msg_size > 0 || check_spool_inodes > 0) if (check_log_space > 0 || check_log_inodes > 0) { - space = receive_statvfs(FALSE, &inodes); + int_eximarith_t space = receive_statvfs(FALSE, &inodes); DEBUG(D_receive) debug_printf("log directory space = " PR_EXIM_ARITH "K inodes = %d " diff --git a/src/src/search.c b/src/src/search.c index 52dcab2b7..62416da76 100644 --- a/src/src/search.c +++ b/src/src/search.c @@ -590,35 +590,48 @@ else is either untainted or properly quoted for the lookup type. XXX Should we this move into lf_sqlperform() ? The server-taint check is there. + Also it already knows about looking for a "servers" spec in the query string. + Passing search_type down that far is an issue. */ if ( !filename && lookup_list[search_type]->quote && is_tainted(keystring) && !is_quoted_like(keystring, search_type)) { - uschar * s = acl_current_verb(); - if (!s) s = authenticator_current_name(); /* must be before transport */ - if (!s) s = transport_current_name(); /* must be before router */ - if (!s) s = router_current_name(); /* GCC ?: would be good, but not in clang */ - if (!s) s = US""; + const uschar * ks = keystring; + uschar * loc = acl_current_verb(); + if (!loc) loc = authenticator_current_name(); /* must be before transport */ + if (!loc) loc = transport_current_name(); /* must be before router */ + if (!loc) loc = router_current_name(); /* GCC ?: would be good, but not in clang */ + if (!loc) loc = US""; + + if (Ustrncmp(ks, "servers", 7) == 0) /* Avoid logging server/password */ + if ((ks = Ustrchr(keystring, ';'))) + while (isspace(*++ks)) + ; + else + ks = US""; + #ifdef enforce_quote_protection_notyet search_error_message = string_sprintf( "tainted search query is not properly quoted%s: %s%s", - s, keystring); + loc, ks); f.search_find_defer = TRUE; + goto out; #else - { - int q = quoter_for_address(keystring); - /* If we're called from a transport, no privs to open the paniclog; - the logging punts to using stderr - and that seems to stop the debug - stream. */ - log_write(0, - transport_name ? LOG_MAIN : LOG_MAIN|LOG_PANIC, - "tainted search query is not properly quoted%s: %s", s, keystring); - - DEBUG(D_lookup) debug_printf_indent("search_type %d (%s) quoting %d (%s)\n", + /* If we're called from a transport, no privs to open the paniclog; + the logging punts to using stderr - and that seems to stop the debug + stream. */ + log_write(0, + transport_name ? LOG_MAIN : LOG_MAIN|LOG_PANIC, + "tainted search query is not properly quoted%s: %s", loc, ks); + + DEBUG(D_lookup) + { + int q = quoter_for_address(ks); + debug_printf_indent("search_type %d (%s) quoting %d (%s)\n", search_type, lookup_list[search_type]->name, q, is_real_quoter(q) ? lookup_list[q]->name : US"none"); - } + } #endif } @@ -669,6 +682,7 @@ pointer to NULL here, because we cannot release the store at this stage. */ } } +out: DEBUG(D_lookup) { if (data) commit 443245384239776d29f5386050f56247c7404c1e Author: Fabrice Fontaine Date: Thu Feb 29 19:01:49 2024 +0000 LibreSSL: fix build for 3.5.0+ Bug 3074 diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 237303ba9..e6a1c6fa4 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -97,6 +97,9 @@ change this guard and punt the issue for a while longer. */ #if LIBRESSL_VERSION_NUMBER >= 0x3040000fL # define EXIM_HAVE_OPENSSL_CIPHER_GET_ID #endif +#if LIBRESSL_VERSION_NUMBER >= 0x3050000fL +# define EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_CERTS +#endif #if !defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x030000000L) # define EXIM_HAVE_EXPORT_CHNL_BNGNG commit f9609304cd7ffe553e3da325287973a0b2a4446c Author: Heiko Schlichting Date: Thu Feb 29 19:26:48 2024 +0000 Fix crash on empty -oMt argument. Bug 3070 diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index ff768e94c..f62d6fbf2 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -1352,7 +1352,7 @@ if (host_checking) g = string_cat(g, hostname); else if (f.sender_host_unknown || f.sender_host_notsocket) - g = string_cat(g, sender_ident); + g = string_cat(g, sender_ident ? sender_ident : US"NULL"); else if (f.is_inetd) g = string_append(g, 2, hostname, US" (via inetd)"); commit d945e180cbe2b26392fb21f7ae4dd2ccc603a81b Author: Jeremy Harris Date: Fri Mar 8 01:59:34 2024 +0000 Debug: expansions: refactor ascii-art/UTF8; mark up space & nl diff --git a/src/src/expand.c b/src/src/expand.c index 6be5cca0e..a916eee64 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -4496,30 +4496,17 @@ return yield; /************************************************/ static void debug_expansion_interim(const uschar * what, const uschar * value, int nchar, - BOOL skipping) + esi_flags flags) { -DEBUG(D_noutf8) - debug_printf_indent("|"); -else - debug_printf_indent(UTF8_VERT_RIGHT); +debug_printf_indent("%V", "K"); for (int fill = 11 - Ustrlen(what); fill > 0; fill--) - DEBUG(D_noutf8) - debug_printf("-"); - else - debug_printf(UTF8_HORIZ); + debug_printf("%V", "-"); -debug_printf("%s: %.*s\n", what, nchar, value); +debug_printf("%s: %.*W\n", what, nchar, value); if (is_tainted(value)) - { - DEBUG(D_noutf8) - debug_printf_indent("%s \\__", skipping ? "| " : " "); - else - debug_printf_indent("%s", - skipping - ? UTF8_VERT " " : " " UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ); - debug_printf("(tainted)\n"); - } + debug_printf_indent("%V %V(tainted)\n", + flags & ESI_SKIPPING ? "|" : " ", "\\__"); } @@ -4618,17 +4605,10 @@ while (*s) DEBUG(D_expand) { - DEBUG(D_noutf8) - debug_printf_indent("%c%s: %s\n", - first ? '/' : '|', - flags & ESI_SKIPPING ? "---scanning" : "considering", s); - else - debug_printf_indent("%s%s: %s\n", - first ? UTF8_DOWN_RIGHT : UTF8_VERT_RIGHT, - flags & ESI_SKIPPING - ? UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ "scanning" - : "considering", - s); + debug_printf_indent("%V%V%s: %W\n", + first ? "/" : "K", + flags & ESI_SKIPPING ? "---" : "", + flags & ESI_SKIPPING ? "scanning" : "considering", s); first = FALSE; } @@ -4651,21 +4631,20 @@ while (*s) for (s = t; *s ; s++) if (*s == '\\' && s[1] == 'N') break; DEBUG(D_expand) - debug_expansion_interim(US"protected", t, (int)(s - t), !!(flags & ESI_SKIPPING)); - yield = string_catn(yield, t, s - t); + debug_expansion_interim(US"protected", t, (int)(s - t), flags); + if (!(flags & ESI_SKIPPING)) + yield = string_catn(yield, t, s - t); if (*s) s += 2; } else { uschar ch[1]; DEBUG(D_expand) - DEBUG(D_noutf8) - debug_printf_indent("|backslashed: '\\%c'\n", s[1]); - else - debug_printf_indent(UTF8_VERT_RIGHT "backslashed: '\\%c'\n", s[1]); + debug_printf_indent("%Vbackslashed: '\\%c'\n", "K", s[1]); ch[0] = string_interpret_escape(&s); + if (!(flags & ESI_SKIPPING)) + yield = string_catn(yield, ch, 1); s++; - yield = string_catn(yield, ch, 1); } continue; } @@ -4682,9 +4661,10 @@ while (*s) for (const uschar * t = s+1; *t && *t != '$' && *t != '}' && *t != '\\'; t++) i++; - DEBUG(D_expand) debug_expansion_interim(US"text", s, i, !!(flags & ESI_SKIPPING)); + DEBUG(D_expand) debug_expansion_interim(US"text", s, i, flags); - yield = string_catn(yield, s, i); + if (!(flags & ESI_SKIPPING)) + yield = string_catn(yield, s, i); s += i; continue; } @@ -4710,15 +4690,16 @@ while (*s) /* If this is the first thing to be expanded, release the pre-allocated buffer. */ - if (!yield) - g = store_get(sizeof(gstring), GET_UNTAINTED); - else if (yield->ptr == 0) - { - if (resetok) reset_point = store_reset(reset_point); - yield = NULL; - reset_point = store_mark(); - g = store_get(sizeof(gstring), GET_UNTAINTED); /* alloc _before_ calling find_variable() */ - } + if (!(flags & ESI_SKIPPING)) + if (!yield) + g = store_get(sizeof(gstring), GET_UNTAINTED); + else if (yield->ptr == 0) + { + if (resetok) reset_point = store_reset(reset_point); + yield = NULL; + reset_point = store_mark(); + g = store_get(sizeof(gstring), GET_UNTAINTED); /* alloc _before_ calling find_variable() */ + } /* Header */ @@ -4767,16 +4748,17 @@ while (*s) reset in the middle of the buffer will make it inaccessible. */ len = Ustrlen(value); - DEBUG(D_expand) debug_expansion_interim(US"value", value, len, !!(flags & ESI_SKIPPING)); - if (!yield && newsize != 0) - { - yield = g; - yield->size = newsize; - yield->ptr = len; - yield->s = US value; /* known to be in new store i.e. a copy, so deconst safe */ - } - else - yield = string_catn(yield, value, len); + DEBUG(D_expand) debug_expansion_interim(US"value", value, len, flags); + if (!(flags & ESI_SKIPPING)) + if (!yield && newsize != 0) + { + yield = g; + yield->size = newsize; + yield->ptr = len; + yield->s = US value; /* known to be in new store i.e. a copy, so deconst safe */ + } + else + yield = string_catn(yield, value, len); continue; } @@ -4787,8 +4769,9 @@ while (*s) s = read_cnumber(&n, s); if (n >= 0 && n <= expand_nmax) { - DEBUG(D_expand) debug_expansion_interim(US"value", expand_nstring[n], expand_nlength[n], !!(flags & ESI_SKIPPING)); - yield = string_catn(yield, expand_nstring[n], expand_nlength[n]); + DEBUG(D_expand) debug_expansion_interim(US"value", expand_nstring[n], expand_nlength[n], flags); + if (!(flags & ESI_SKIPPING)) + yield = string_catn(yield, expand_nstring[n], expand_nlength[n]); } continue; } @@ -4815,8 +4798,9 @@ while (*s) } if (n >= 0 && n <= expand_nmax) { - DEBUG(D_expand) debug_expansion_interim(US"value", expand_nstring[n], expand_nlength[n], !!(flags & ESI_SKIPPING)); - yield = string_catn(yield, expand_nstring[n], expand_nlength[n]); + DEBUG(D_expand) debug_expansion_interim(US"value", expand_nstring[n], expand_nlength[n], flags); + if (!(flags & ESI_SKIPPING)) + yield = string_catn(yield, expand_nstring[n], expand_nlength[n]); } continue; } @@ -4941,9 +4925,9 @@ while (*s) DEBUG(D_expand) { - debug_expansion_interim(US"condition", s, (int)(next_s - s), !!(flags & ESI_SKIPPING)); + debug_expansion_interim(US"condition", s, (int)(next_s - s), flags); debug_expansion_interim(US"result", - cond ? US"true" : US"false", cond ? 4 : 5, !!(flags & ESI_SKIPPING)); + cond ? US"true" : US"false", cond ? 4 : 5, flags); } s = next_s; @@ -7162,7 +7146,7 @@ while (*s) if (yield && (expansion_start > 0 || *s)) debug_expansion_interim(US"item-res", yield->s + expansion_start, yield->ptr - expansion_start, - !!(flags & ESI_SKIPPING)); + flags); continue; NOT_ITEM: ; @@ -8347,27 +8331,13 @@ NOT_ITEM: ; int i = gstring_length(yield) - expansion_start; BOOL tainted = is_tainted(s); - DEBUG(D_noutf8) - { - debug_printf_indent("|-----op-res: %.*s\n", i, s); - if (tainted) - { - debug_printf_indent("%s \\__", flags & ESI_SKIPPING ? "| " : " "); - debug_print_taint(res); - } - } - else + debug_printf_indent("%Vop-res: %.*s\n", "K-----", i, s); + if (tainted) { - debug_printf_indent(UTF8_VERT_RIGHT - UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ - "op-res: %.*s\n", i, s); - if (tainted) - { - debug_printf_indent("%s", - flags & ESI_SKIPPING - ? UTF8_VERT " " : " " UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ); - debug_print_taint(res); - } + debug_printf_indent("%V %V", + flags & ESI_SKIPPING ? "|" : " ", + "\\__"); + debug_print_taint(res); } } continue; @@ -8459,39 +8429,25 @@ left != NULL, return a pointer to the terminator. */ DEBUG(D_expand) { BOOL tainted = is_tainted(res); - DEBUG(D_noutf8) - { - debug_printf_indent("|--expanding: %.*s\n", (int)(s - string), string); - debug_printf_indent("%sresult: %s\n", - flags & ESI_SKIPPING ? "|-----" : "\\_____", res); - if (tainted) - { - debug_printf_indent("%s \\__", flags & ESI_SKIPPING ? "| " : " "); - debug_print_taint(res); - } - if (flags & ESI_SKIPPING) - debug_printf_indent("\\___skipping: result is not used\n"); - } + debug_printf_indent("%Vexpanded: %.*W\n", + "K---", + (int)(s - string), string); + debug_printf_indent("%Vresult: ", + flags & ESI_SKIPPING ? "K-----" : "\\_____"); + if (*res || !(flags & ESI_SKIPPING)) + debug_printf("%W\n", res); else + debug_printf(" %Vskipped%V\n", "<", ">"); + if (tainted) { - debug_printf_indent(UTF8_VERT_RIGHT UTF8_HORIZ UTF8_HORIZ - "expanding: %.*s\n", - (int)(s - string), string); - debug_printf_indent("%s" UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ - "result: %s\n", - flags & ESI_SKIPPING ? UTF8_VERT_RIGHT : UTF8_UP_RIGHT, - res); - if (tainted) - { - debug_printf_indent("%s", - flags & ESI_SKIPPING - ? UTF8_VERT " " : " " UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ); - debug_print_taint(res); - } - if (flags & ESI_SKIPPING) - debug_printf_indent(UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ - "skipping: result is not used\n"); + debug_printf_indent("%V %V", + flags & ESI_SKIPPING ? "|" : " ", + "\\__" + ); + debug_print_taint(res); } + if (flags & ESI_SKIPPING) + debug_printf_indent("%Vskipping: result is not used\n", "\\___"); } if (textonly_p) *textonly_p = textonly; expand_level--; @@ -8517,25 +8473,11 @@ EXPAND_FAILED: if (left) *left = s; DEBUG(D_expand) { - DEBUG(D_noutf8) - { - debug_printf_indent("|failed to expand: %s\n", string); - debug_printf_indent("%serror message: %s\n", - f.expand_string_forcedfail ? "|---" : "\\___", expand_string_message); - if (f.expand_string_forcedfail) - debug_printf_indent("\\failure was forced\n"); - } - else - { - debug_printf_indent(UTF8_VERT_RIGHT "failed to expand: %s\n", - string); - debug_printf_indent("%s" UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ - "error message: %s\n", - f.expand_string_forcedfail ? UTF8_VERT_RIGHT : UTF8_UP_RIGHT, - expand_string_message); - if (f.expand_string_forcedfail) - debug_printf_indent(UTF8_UP_RIGHT "failure was forced\n"); - } + debug_printf_indent("%Vfailed to expand: %s\n", "K", string); + debug_printf_indent("%Verror message: %s\n", + f.expand_string_forcedfail ? "K---" : "\\___", expand_string_message); + if (f.expand_string_forcedfail) + debug_printf_indent("%Vfailure was forced\n", "\\"); } if (resetok_p && !resetok) *resetok_p = FALSE; expand_level--; diff --git a/src/src/macros.h b/src/src/macros.h index 902cdd165..3341f5f41 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -107,8 +107,9 @@ don't make the file descriptors two-way. */ /* Debugging control */ #define LOG_NAME_SIZE 256 -#define DEBUG(x) if (debug_selector & (x)) -#define HDEBUG(x) if (host_checking || debug_selector & (x)) +#define IS_DEBUG(x) (debug_selector & (x)) +#define DEBUG(x) if (IS_DEBUG(x)) +#define HDEBUG(x) if (host_checking || IS_DEBUG(x)) /* The default From: text for DSNs */ @@ -1057,6 +1058,10 @@ enum { FILTER_UNSET, FILTER_FORWARD, FILTER_EXIM, FILTER_SIEVE }; #define UTF8_VERT_RIGHT "\xE2\x94\x9C" #define UTF8_UP_RIGHT "\xE2\x95\xB0" #define UTF8_VERT_2DASH "\xE2\x95\x8E" +#define UTF8_LEFT_TRIANGLE "\xE2\x97\x80" +#define UTF8_RIGHT_TRIANGLE "\xE2\x96\xB6" +#define UTF8_LIGHT_SHADE "\xE2\x96\x91" +#define UTF8_L_ARROW_HOOK "\xE2\x86\xA9" /* Options on tls_close */ diff --git a/src/src/os.c b/src/src/os.c index c6d1e06c2..3b49e4b9e 100644 --- a/src/src/os.c +++ b/src/src/os.c @@ -12,7 +12,8 @@ # include # include #else -# define DEBUG(x) if (debug_selector & (x)) +# define IS_DEBUG(x) (debug_selector & (x)) +# define DEBUG(x) if (IS_DEBUG(x)) #endif #ifndef CS diff --git a/src/src/string.c b/src/src/string.c index 3bf2f1df7..af187c199 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -1348,7 +1348,7 @@ The return value can be NULL to signify overflow. Field width: decimal digits, or * Precision: dot, followed by decimal digits or * Length modifiers: h L l ll z -Conversion specifiers: n d o u x X p f e E g G % c s S T Y D M +Conversion specifiers: n d o u x X p f e E g G % c s S T W V Y D M Returns the possibly-new (if copy for growth or taint-handling was needed) string, not nul-terminated. @@ -1597,11 +1597,77 @@ while (*fp) case 'Y': /* gstring pointer */ { gstring * zg = va_arg(ap, gstring *); - if (zg) { s = CS zg->s; slen = zg->ptr; } + if (zg) { s = CS zg->s; slen = gstring_length(zg); } else { s = null; slen = Ustrlen(s); } goto INSERT_GSTRING; } +#ifndef COMPILE_UTILITY + case 'V': /* Maybe convert ascii-art to UTF-8 chars */ + { + gstring * zg = NULL; + s = va_arg(ap, char *); + if (IS_DEBUG(D_noutf8)) + for ( ; *s; s++) + zg = string_catn(zg, CUS (*s == 'K' ? "|" : s), 1); + else + for ( ; *s; s++) switch (*s) + { + case '\\': zg = string_catn(zg, US UTF8_UP_RIGHT, 3); break; + case '/': zg = string_catn(zg, US UTF8_DOWN_RIGHT, 3); break; + case '-': + case '_': zg = string_catn(zg, US UTF8_HORIZ, 3); break; + case '|': zg = string_catn(zg, US UTF8_VERT, 3); break; + case 'K': zg = string_catn(zg, US UTF8_VERT_RIGHT, 3); break; + case '<': zg = string_catn(zg, US UTF8_LEFT_TRIANGLE, 3); break; + case '>': zg = string_catn(zg, US UTF8_RIGHT_TRIANGLE, 3); break; + default: zg = string_catn(zg, CUS s, 1); break; + } + if (!zg) + break; + s = CS zg->s; + slen = gstring_length(zg); + goto INSERT_GSTRING; + } + + case 'W': /* Maybe mark up spaces & newlines */ + s = va_arg(ap, char *); + if (Ustrpbrk(s, " \n") && !IS_DEBUG(D_noutf8)) + { + gstring * zg = NULL; + int p = precision; + for ( ; *s; s++) + { + /* Take a given precision as applying to the input; expand + it for the transformed result */ + + if (p >= 0 && --p < 0) break; + switch (*s) + { + case ' ': + zg = string_catn(zg, CUS UTF8_LIGHT_SHADE, 3); + if (precision >= 0) precision += 2; + break; + case '\n': + zg = string_catn(zg, CUS UTF8_L_ARROW_HOOK "\n", 4); + if (precision >= 0) precision += 3; + break; + default: + zg = string_catn(zg, CUS s, 1); + break; + } + } + if (zg) { s = CS zg->s; slen = gstring_length(zg); } + else { s = null; slen = Ustrlen(s); } + } + else + { + if (!s) s = null; + slen = Ustrlen(s); + } + goto INSERT_GSTRING; + +#endif case 's': case 'S': /* Forces *lower* case */ case 'T': /* Forces *upper* case */ @@ -1610,7 +1676,7 @@ while (*fp) if (!s) s = null; slen = Ustrlen(s); - INSERT_GSTRING: /* Coome to from %Y above */ + INSERT_GSTRING: /* Come to from %Y above */ if (!(flags & SVFMT_TAINT_NOCHK) && is_incompatible(g->s, s)) if (flags & SVFMT_REBUFFER) @@ -1909,3 +1975,5 @@ return 0; #endif /* End of string.c */ +/* vi: aw ai sw=2 +*/ commit d4ff6d2a25234f7558baa99a54ca138c3f004b06 Author: Andrew Aitchison Date: Fri Mar 8 11:52:10 2024 +0000 Silence some compiler warnings. Bug 3076 diff --git a/src/src/routers/dnslookup.c b/src/src/routers/dnslookup.c index 33382671f..80b12cba2 100644 --- a/src/src/routers/dnslookup.c +++ b/src/src/routers/dnslookup.c @@ -472,7 +472,7 @@ return rf_queue_add(addr, addr_local, addr_remote, rblock, pw)? } #endif /*!MACRO_PREDEF*/ -#endif /*ROUTER_DNSLOOKUP +#endif /*ROUTER_DNSLOOKUP*/ /* End of routers/dnslookup.c */ /* vi: aw ai sw=2 */ diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 56ea93935..3e8ec6d84 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -1185,6 +1185,8 @@ tls_server_servercerts_cb(gnutls_session_t session, unsigned int htype, # ifdef notdef_crashes /*XXX crashes */ return gnutls_ext_raw_parse(NULL, tls_server_servercerts_ext, msg, 0); +# else +return GNUTLS_E_SUCCESS; # endif } #endif /*SUPPORT_GNUTLS_EXT_RAW_PARSE*/ @@ -1233,7 +1235,7 @@ switch (htype) return tls_server_ticket_cb(sess, htype, when, incoming, msg); # endif default: - return 0; + return GNUTLS_E_SUCCESS; } } #endif commit 35e7f5dc54c333d744a6b73d5668abddc085e0da Author: Peter Benie Date: Thu Mar 14 15:56:07 2024 +0000 Debug: fix transport-wait DB message. Bug 3082 diff --git a/src/src/transport.c b/src/src/transport.c index 5729f8e75..3609b78bb 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -1506,7 +1506,7 @@ open_db *dbm_file; if (!is_new_message_id(message_id)) { DEBUG(D_transport) debug_printf("message_id %s is not new format; " - "skipping wait-%s database update\n", tpname); + "skipping wait-%s database update\n", message_id, tpname); return; } commit c71f869820b45b911cc8edbd0de88bbc9621fd5f Author: Peter Benie Date: Thu Mar 14 16:11:14 2024 +0000 Rewrites: fix delivery crash from constant errors_to. Bug 3081 Broken-by: 753739fdef6d diff --git a/src/src/rewrite.c b/src/src/rewrite.c index 70bd79e26..9ee5a6794 100644 --- a/src/src/rewrite.c +++ b/src/src/rewrite.c @@ -104,7 +104,7 @@ rewrite_one(const uschar *s, int flag, BOOL *whole, BOOL add_header, uschar *nam { const uschar *yield = s; const uschar *subject = s; -uschar *domain = NULL; +const uschar *domain = NULL; BOOL done = FALSE; int rule_number = 1; int yield_start = 0, yield_end = 0; @@ -158,7 +158,7 @@ for (rewrite_rule * rule = rewrite_rules; else { - if (!domain) domain = Ustrrchr(subject, '@') + 1; + if (!domain) domain = CUstrrchr(subject, '@') + 1; /* Use the general function for matching an address against a list (here just one item, so use the "impossible value" separator UCHAR_MAX+1). */ @@ -182,16 +182,14 @@ for (rewrite_rule * rule = rewrite_rules; save_domain = deliver_domain; /* We have subject pointing to "localpart@domain" and domain pointing to - the domain. Temporarily terminate the local part so that it can be - set up as an expansion variable */ + the domain. Split into local part and domain so that it can be set up as + an expansion variable */ - domain[-1] = 0; - deliver_localpart = US subject; + deliver_localpart = US string_copyn(subject, domain-subject-1); deliver_domain = domain; new = expand_string(rule->replacement); - domain[-1] = '@'; deliver_localpart = save_localpart; deliver_domain = save_domain; } commit a7e6ad0ba38cf088e841c321042f81966d846b4b Author: Jeremy Harris Date: Sat Mar 16 13:50:45 2024 +0000 Lookups: fix dbmnz crash on zero-length datum. Bug 3079 Broken-by: 6d2c02560e5c diff --git a/src/src/dbfn.c b/src/src/dbfn.c index 3c51162a4..460fd8bb7 100644 --- a/src/src/dbfn.c +++ b/src/src/dbfn.c @@ -239,12 +239,13 @@ Returns: a pointer to the retrieved record, or */ void * -dbfn_read_with_length(open_db *dbblock, const uschar *key, int *length) +dbfn_read_with_length(open_db * dbblock, const uschar * key, int * length) { -void *yield; +void * yield; EXIM_DATUM key_datum, result_datum; int klen = Ustrlen(key) + 1; uschar * key_copy = store_get(klen, key); +unsigned dlen; memcpy(key_copy, key, klen); @@ -260,9 +261,10 @@ if (!exim_dbget(dbblock->dbptr, &key_datum, &result_datum)) return NULL; /* Assume the data store could have been tainted. Properly, we should store the taint status with the data. */ -yield = store_get(exim_datum_size_get(&result_datum), GET_TAINTED); -memcpy(yield, exim_datum_data_get(&result_datum), exim_datum_size_get(&result_datum)); -if (length) *length = exim_datum_size_get(&result_datum); +dlen = exim_datum_size_get(&result_datum); +yield = store_get(dlen, GET_TAINTED); +memcpy(yield, exim_datum_data_get(&result_datum), dlen); +if (length) *length = dlen; exim_datum_free(&result_datum); /* Some DBM libs require freeing */ return yield; diff --git a/src/src/exim_dbutil.c b/src/src/exim_dbutil.c index 3f70c2fd5..4d213773b 100644 --- a/src/src/exim_dbutil.c +++ b/src/src/exim_dbutil.c @@ -407,12 +407,13 @@ Returns: a pointer to the retrieved record, or */ void * -dbfn_read_with_length(open_db *dbblock, const uschar *key, int *length) +dbfn_read_with_length(open_db * dbblock, const uschar * key, int * length) { -void *yield; +void * yield; EXIM_DATUM key_datum, result_datum; int klen = Ustrlen(key) + 1; uschar * key_copy = store_get(klen, key); +unsigned dlen; memcpy(key_copy, key, klen); @@ -426,9 +427,10 @@ if (!exim_dbget(dbblock->dbptr, &key_datum, &result_datum)) return NULL; /* Assume for now that anything stored could have been tainted. Properly we should store the taint status along with the data. */ -yield = store_get(exim_datum_size_get(&result_datum), GET_TAINTED); -memcpy(yield, exim_datum_data_get(&result_datum), exim_datum_size_get(&result_datum)); -if (length) *length = exim_datum_size_get(&result_datum); +dlen = exim_datum_size_get(&result_datum); +yield = store_get(dlen, GET_TAINTED); +memcpy(yield, exim_datum_data_get(&result_datum), dlen); +if (length) *length = dlen; exim_datum_free(&result_datum); /* Some DBM libs require freeing */ return yield; diff --git a/src/src/lookups/dbmdb.c b/src/src/lookups/dbmdb.c index aa930e654..96665b6e4 100644 --- a/src/src/lookups/dbmdb.c +++ b/src/src/lookups/dbmdb.c @@ -102,7 +102,8 @@ exim_datum_size_set(&key, length); if (exim_dbget(d, &key, &data)) { - *result = string_copyn(exim_datum_data_get(&data), exim_datum_size_get(&data)); + unsigned len = exim_datum_size_get(&data); + *result = len > 0 ? string_copyn(exim_datum_data_get(&data), len) : US""; exim_datum_free(&data); /* Some DBM libraries need a free() call */ return OK; } @@ -283,3 +284,5 @@ static lookup_info *_lookup_list[] = { &dbm_lookup_info, &dbmz_lookup_info, &dbm lookup_module_info dbmdb_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 3 }; /* End of lookups/dbmdb.c */ +/* vi: aw ai sw=2 +*/ commit 481eeb93961ef9998901c522b498253d75124fb4 Author: Jeremy Harris Date: Sat Mar 16 13:54:42 2024 +0000 Autoreply: Remove Exim version 3 campatibility code diff --git a/src/src/transports/autoreply.c b/src/src/transports/autoreply.c index 2f2fe1e16..690ec4a92 100644 --- a/src/src/transports/autoreply.c +++ b/src/src/transports/autoreply.c @@ -496,19 +496,7 @@ if (oncelog && *oncelog && to) exim_datum_size_set(&key_datum, Ustrlen(to) + 1); if (exim_dbget(dbm_file, &key_datum, &result_datum)) - { - /* If the datum size is that of a binary time, we are in the new world - where messages are sent periodically. Otherwise the file is an old one, - where the datum was filled with a tod_log time, which is assumed to be - different in size. For that, only one message is ever sent. This change - introduced at Exim 3.00. In a couple of years' time the test on the size - can be abolished. */ - - if (exim_datum_size_get(&result_datum) == sizeof(time_t)) - memcpy(&then, exim_datum_data_get(&result_datum), sizeof(time_t)); - else - then = now; - } + memcpy(&then, exim_datum_data_get(&result_datum), sizeof(time_t)); } /* Either "then" is set zero, if no message has yet been sent, or it commit a1caa6da6f72e8c5b8b24678e32c4953a26a2b48 Author: Jeremy Harris Date: Thu Mar 21 23:48:32 2024 +0000 Option for taint when setting variable under -be diff --git a/src/src/acl.c b/src/src/acl.c index f4d65464e..29441dfc1 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -800,7 +800,7 @@ return TRUE; static BOOL acl_data_to_cond(const uschar * s, acl_condition_block * cond, - const uschar * name, uschar ** error) + const uschar * name, BOOL taint, uschar ** error) { if (*s++ != '=') { @@ -809,7 +809,7 @@ if (*s++ != '=') return FALSE;; } Uskip_whitespace(&s); -cond->arg = string_copy(s); +cond->arg = taint ? string_copy_taint(s, GET_TAINTED) : string_copy(s); return TRUE; } @@ -959,7 +959,7 @@ while ((s = (*func)())) "endpass" has no data */ if (c != ACLC_ENDPASS) - if (!acl_data_to_cond(s, cond, name, error)) return NULL; + if (!acl_data_to_cond(s, cond, name, FALSE, error)) return NULL; } return yield; @@ -4941,7 +4941,7 @@ fprintf(f, "acl%c %s %d\n%s\n", name[0], name+1, Ustrlen(value), value); uschar * -acl_standalone_setvar(const uschar * s) +acl_standalone_setvar(const uschar * s, BOOL taint) { acl_condition_block * cond = store_get(sizeof(acl_condition_block), GET_UNTAINTED); uschar * errstr = NULL, * log_msg = NULL; @@ -4951,7 +4951,7 @@ int e; cond->next = NULL; cond->type = ACLC_SET; if (!acl_varname_to_cond(&s, cond, &errstr)) return errstr; -if (!acl_data_to_cond(s, cond, US"'-be'", &errstr)) return errstr; +if (!acl_data_to_cond(s, cond, US"'-be'", taint, &errstr)) return errstr; if (acl_check_condition(ACL_WARN, cond, ACL_WHERE_UNKNOWN, NULL, 0, &endpass_seen, &errstr, &log_msg, &e) != OK) diff --git a/src/src/exim.c b/src/src/exim.c index c591ee60c..3170bef98 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -1704,8 +1704,10 @@ if (isupper(big_buffer[0])) if (macro_read_assignment(big_buffer)) printf("Defined macro '%s'\n", mlast->name); } +else if (Ustrncmp(big_buffer, "set,t ", 6) == 0) + printf("%s\n", acl_standalone_setvar(big_buffer+6, TRUE)); else if (Ustrncmp(big_buffer, "set ", 4) == 0) - printf("%s\n", acl_standalone_setvar(big_buffer+4)); + printf("%s\n", acl_standalone_setvar(big_buffer+4, FALSE)); else if ((s = expand_string(big_buffer))) printf("%s\n", CS s); else printf("Failed: %s\n", expand_string_message); diff --git a/src/src/functions.h b/src/src/functions.h index 77323a5c1..0e657d7c4 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -103,7 +103,7 @@ extern acl_block *acl_read(uschar *(*)(void), uschar **); extern int acl_check(int, const uschar *, uschar *, uschar **, uschar **); extern uschar *acl_current_verb(void); extern int acl_eval(int, uschar *, uschar **, uschar **); -extern uschar *acl_standalone_setvar(const uschar *); +extern uschar *acl_standalone_setvar(const uschar *, BOOL); extern tree_node *acl_var_create(uschar *); extern void acl_var_write(uschar *, uschar *, void *); commit cea451c53287e51175c9bbe1a4fd607d1db7a02a Author: Jeremy Harris Date: Fri Mar 22 21:11:25 2024 +0000 Debug: more clarity on a ret=key lookup diff --git a/src/src/search.c b/src/src/search.c index 62416da76..27c686a5d 100644 --- a/src/src/search.c +++ b/src/src/search.c @@ -980,9 +980,15 @@ than the result. Return a de-tainted version of the key on the grounds that it have been validated by the lookup. */ if (yield && ret_key) + { yield = string_copy_taint(keystring, GET_UNTAINTED); + DEBUG(D_lookup) + debug_printf_indent("lookup yield replace by key: %s\n", yield); + } return yield; } /* End of search.c */ +/* vi: aw ai sw=2 +*/ commit 64226a31616e631b9e10f7897d603cdf9847a458 Author: Jeremy Harris Date: Fri Mar 22 23:05:02 2024 +0000 Debug: mark up space/nl in lookup results diff --git a/src/src/search.c b/src/src/search.c index 27c686a5d..168c25e6a 100644 --- a/src/src/search.c +++ b/src/src/search.c @@ -686,7 +686,7 @@ out: DEBUG(D_lookup) { if (data) - debug_printf_indent("lookup yielded: %s\n", data); + debug_printf_indent("lookup yielded: %W\n", data); else if (f.search_find_defer) debug_printf_indent("lookup deferred: %s\n", search_error_message); else debug_printf_indent("lookup failed\n"); commit c90c2288e88f66ae1d8af198cfa56c401e573ce4 Author: Jeremy Harris Date: Mon Mar 25 12:13:09 2024 +0000 specified-initializers diff --git a/src/src/globals.c b/src/src/globals.c index 4af8668f0..e23188b34 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -467,6 +467,9 @@ uschar *acl_smtp_quit = NULL; uschar *acl_smtp_rcpt = NULL; uschar *acl_smtp_starttls = NULL; uschar *acl_smtp_vrfy = NULL; +#ifdef EXPERIMENTAL_WELLKNOWN +uschar *acl_smtp_wellknown = NULL; +#endif tree_node *acl_var_c = NULL; tree_node *acl_var_m = NULL; @@ -474,56 +477,49 @@ uschar *acl_verify_message = NULL; string_item *acl_warn_logged = NULL; /* Names of SMTP places for use in ACL error messages, and corresponding SMTP -error codes - keep in step with definitions of ACL_WHERE_xxxx in macros.h. */ - -uschar *acl_wherenames[] = { US"RCPT", - US"MAIL", - US"PREDATA", - US"MIME", - US"DKIM", - US"DATA", +error codes (only those used) */ + +uschar *acl_wherenames[] = { [ACL_WHERE_RCPT] = US"RCPT", + [ACL_WHERE_MAIL] = US"MAIL", + [ACL_WHERE_PREDATA] = US"PREDATA", + [ACL_WHERE_MIME] = US"MIME", + [ACL_WHERE_DKIM] = US"DKIM", + [ACL_WHERE_DATA] = US"DATA", #ifndef DISABLE_PRDR - US"PRDR", -#endif - US"non-SMTP", - US"AUTH", - US"connection", - US"ETRN", - US"EXPN", - US"EHLO or HELO", - US"MAILAUTH", - US"non-SMTP-start", - US"NOTQUIT", - US"QUIT", - US"STARTTLS", - US"VRFY", - US"delivery", - US"unknown" + [ACL_WHERE_PRDR] = US"PRDR", +#endif + [ACL_WHERE_NOTSMTP] = US"non-SMTP", + [ACL_WHERE_AUTH] = US"AUTH", + [ACL_WHERE_CONNECT] = US"connection", + [ACL_WHERE_ETRN] = US"ETRN", + [ACL_WHERE_EXPN] = US"EXPN", + [ACL_WHERE_HELO] = US"EHLO or HELO", + [ACL_WHERE_MAILAUTH] = US"MAILAUTH", + [ACL_WHERE_NOTSMTP_START] = US"non-SMTP-start", + [ACL_WHERE_NOTQUIT] = US"NOTQUIT", + [ACL_WHERE_QUIT] = US"QUIT", + [ACL_WHERE_STARTTLS] = US"STARTTLS", + [ACL_WHERE_VRFY] = US"VRFY", + [ACL_WHERE_DELIVERY] = US"delivery", + [ACL_WHERE_UNKNOWN] = US"unknown" }; -uschar *acl_wherecodes[] = { US"550", /* RCPT */ - US"550", /* MAIL */ - US"550", /* PREDATA */ - US"550", /* MIME */ - US"550", /* DKIM */ - US"550", /* DATA */ +uschar *acl_wherecodes[] = { [ACL_WHERE_RCPT] = US"550", + [ACL_WHERE_MAIL] = US"550", + [ACL_WHERE_PREDATA] = US"550", + [ACL_WHERE_MIME] = US"550", + [ACL_WHERE_DKIM] = US"550", + [ACL_WHERE_DATA] = US"550", #ifndef DISABLE_PRDR - US"550", /* RCPT PRDR */ -#endif - US"0", /* not SMTP; not relevant */ - US"503", /* AUTH */ - US"550", /* connect */ - US"458", /* ETRN */ - US"550", /* EXPN */ - US"550", /* HELO/EHLO */ - US"0", /* MAILAUTH; not relevant */ - US"0", /* not SMTP; not relevant */ - US"0", /* NOTQUIT; not relevant */ - US"0", /* QUIT; not relevant */ - US"550", /* STARTTLS */ - US"252", /* VRFY */ - US"0", /* delivery; not relevant */ - US"0" /* unknown; not relevant */ + [ACL_WHERE_PRDR] = US"550", +#endif + [ACL_WHERE_AUTH] = US"503", + [ACL_WHERE_CONNECT] = US"550", + [ACL_WHERE_ETRN] = US"458", + [ACL_WHERE_EXPN] = US"550", + [ACL_WHERE_HELO] = US"550", + [ACL_WHERE_STARTTLS] = US"550", + [ACL_WHERE_VRFY] = US"252", }; uschar *add_environment = NULL; @@ -1003,6 +999,9 @@ uschar *hosts_proxy = NULL; #endif uschar *hosts_treat_as_local = NULL; uschar *hosts_require_helo = US"*"; +#ifdef EXPERIMENTAL_WELLKNOWN +uschar *hosts_wellknown = NULL; +#endif #ifdef EXPERIMENTAL_XCLIENT uschar *hosts_xclient = NULL; #endif @@ -1671,3 +1670,5 @@ const uschar *warnmsg_delay = NULL; const uschar *warnmsg_recipients = NULL; /* End of globals.c */ +/* vi: aw ai sw=2 +*/ commit 4687a69c269ee3f2a7f0625e0147a503fd9d3d0b Author: Jeremy Harris Date: Tue Mar 26 17:36:59 2024 +0000 tidying diff --git a/src/src/acl.c b/src/src/acl.c index 29441dfc1..d719a937b 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -1366,7 +1366,7 @@ uschar * target = store_get(TARGET_SIZE, GET_TAINTED); client's HELO domain. If the client has not said HELO, use its IP address instead. If it's a local client (exim -bs), CSA isn't applicable. */ -while (isspace(*domain) && *domain != '\0') ++domain; +while (isspace(*domain) && *domain) ++domain; if (*domain == '\0') domain = sender_helo_name; if (!domain) domain = sender_host_address; if (!sender_host_address) return CSA_UNKNOWN; @@ -1867,9 +1867,10 @@ switch(vp->value) verify_sender_address = sender_address; else { - while (isspace(*s)) s++; - if (*s++ != '=') goto BAD_VERIFY; - while (isspace(*s)) s++; + if (Uskip_whitespace(&s) != '=') + goto BAD_VERIFY; + s++; + Uskip_whitespace(&s); verify_sender_address = string_copy(s); } } @@ -1911,13 +1912,13 @@ while ((ss = string_nextinlist(&list, &sep, NULL, 0))) callout = CALLOUT_TIMEOUT_DEFAULT; if (*(ss += 7)) { - while (isspace(*ss)) ss++; + Uskip_whitespace(&ss); if (*ss++ == '=') { const uschar * sublist = ss; int optsep = ','; - while (isspace(*sublist)) sublist++; + Uskip_whitespace(&sublist); for (uschar * opt; opt = string_nextinlist(&sublist, &optsep, NULL, 0); ) { callout_opt_t * op; @@ -1931,14 +1932,14 @@ while ((ss = string_nextinlist(&list, &sep, NULL, 0))) if (op->has_option) { opt += Ustrlen(op->name); - while (isspace(*opt)) opt++; + Uskip_whitespace(&opt); if (*opt++ != '=') { *log_msgptr = string_sprintf("'=' expected after " "\"%s\" in ACL verify condition \"%s\"", op->name, arg); return ERROR; } - while (isspace(*opt)) opt++; + Uskip_whitespace(&opt); } if (op->timeval && (period = v_period(opt, arg, log_msgptr)) < 0) return ERROR; @@ -1981,14 +1982,14 @@ while ((ss = string_nextinlist(&list, &sep, NULL, 0))) quota = TRUE; if (*(ss += 5)) { - while (isspace(*ss)) ss++; + Uskip_whitespace(&ss); if (*ss++ == '=') { const uschar * sublist = ss; int optsep = ','; int period; - while (isspace(*sublist)) sublist++; + Uskip_whitespace(&sublist); for (uschar * opt; opt = string_nextinlist(&sublist, &optsep, NULL, 0); ) if (Ustrncmp(opt, "cachepos=", 9) == 0) if ((period = v_period(opt += 9, arg, log_msgptr)) < 0) @@ -3892,7 +3893,7 @@ for (; cb; cb = cb->next) } s++; } - while (isspace(*s)) s++; + Uskip_whitespace(&s); if (logbits == 0) logbits = LOG_MAIN; log_write(0, logbits, "%s", string_printing(s)); @@ -4316,19 +4317,17 @@ if (!s) /* At top level, we expand the incoming string. At lower levels, it has already been expanded as part of condition processing. */ -if (acl_level == 0) +if (acl_level != 0) + ss = s; +else if (!(ss = expand_string(s))) { - if (!(ss = expand_string(s))) - { - if (f.expand_string_forcedfail) return OK; - *log_msgptr = string_sprintf("failed to expand ACL string \"%s\": %s", s, - expand_string_message); - return ERROR; - } + if (f.expand_string_forcedfail) return OK; + *log_msgptr = string_sprintf("failed to expand ACL string \"%s\": %s", s, + expand_string_message); + return ERROR; } -else ss = s; -while (isspace(*ss)) ss++; +Uskip_whitespace(&ss); /* If we can't find a named ACL, the default is to parse it as an inline one. (Unless it begins with a slash; non-existent files give rise to an error.) */ @@ -4617,8 +4616,8 @@ if (!(tmp = string_dequote(&s)) || !(name = expand_string(tmp))) for (i = 0; i < 9; i++) { - while (*s && isspace(*s)) s++; - if (!*s) break; + if (!Uskip_whitespace(&s)) + break; if (!(tmp = string_dequote(&s)) || !(tmp_arg[i] = expand_string(tmp))) { tmp = name; diff --git a/src/src/dbfn.c b/src/src/dbfn.c index 460fd8bb7..13a3c2d66 100644 --- a/src/src/dbfn.c +++ b/src/src/dbfn.c @@ -490,7 +490,7 @@ while (Ufgets(buffer, 256, stdin) != NULL) { count = Uatoi(cmd); while (isdigit((uschar)*cmd)) cmd++; - while (isspace((uschar)*cmd)) cmd++; + Uskip_whitespace(&cmd); } if (Ustrncmp(cmd, "open", 4) == 0) @@ -498,7 +498,7 @@ while (Ufgets(buffer, 256, stdin) != NULL) int i; open_db *odb; uschar *s = cmd + 4; - while (isspace((uschar)*s)) s++; + Uskip_whitespace(&s); for (i = 0; i < max_db; i++) if (dbblock[i].dbptr == NULL) break; @@ -534,8 +534,7 @@ while (Ufgets(buffer, 256, stdin) != NULL) else if (Ustrncmp(cmd, "write", 5) == 0) { int rc = 0; - uschar *key = cmd + 5; - uschar *data; + uschar * key = cmd + 5, * data; if (current < 0) { @@ -543,11 +542,11 @@ while (Ufgets(buffer, 256, stdin) != NULL) continue; } - while (isspace((uschar)*key)) key++; + Uskip_whitespace(&key); data = key; - while (*data != 0 && !isspace((uschar)*data)) data++; + while (*data && !isspace((uschar)*data)) data++; *data++ = 0; - while (isspace((uschar)*data)) data++; + Uskip_whitespace(&data); dbwait = (dbdata_wait *)(&structbuffer); Ustrcpy(dbwait->text, data); @@ -562,13 +561,13 @@ while (Ufgets(buffer, 256, stdin) != NULL) else if (Ustrncmp(cmd, "read", 4) == 0) { - uschar *key = cmd + 4; + uschar * key = cmd + 4; if (current < 0) { printf("No current database\n"); continue; } - while (isspace((uschar)*key)) key++; + Uskip_whitespace(&key); start = clock(); while (count-- > 0) dbwait = (dbdata_wait *)dbfn_read_with_length(dbblock+ current, key, NULL); @@ -578,13 +577,13 @@ while (Ufgets(buffer, 256, stdin) != NULL) else if (Ustrncmp(cmd, "delete", 6) == 0) { - uschar *key = cmd + 6; + uschar * key = cmd + 6; if (current < 0) { printf("No current database\n"); continue; } - while (isspace((uschar)*key)) key++; + Uskip_whitespace(&key); dbfn_delete(dbblock + current, key); } @@ -614,8 +613,8 @@ while (Ufgets(buffer, 256, stdin) != NULL) else if (Ustrncmp(cmd, "close", 5) == 0) { - uschar *s = cmd + 5; - while (isspace((uschar)*s)) s++; + uschar * s = cmd + 5; + Uskip_whitespace(&s); i = Uatoi(s); if (i >= max_db || dbblock[i].dbptr == NULL) printf("Not open\n"); else { @@ -629,8 +628,8 @@ while (Ufgets(buffer, 256, stdin) != NULL) else if (Ustrncmp(cmd, "file", 4) == 0) { - uschar *s = cmd + 4; - while (isspace((uschar)*s)) s++; + uschar * s = cmd + 4; + Uskip_whitespace(&s); i = Uatoi(s); if (i >= max_db || dbblock[i].dbptr == NULL) printf("Not open\n"); else current = i; @@ -682,3 +681,5 @@ return 0; #endif /* End of dbfn.c */ +/* vi: aw ai sw=2 +*/ diff --git a/src/src/exim.c b/src/src/exim.c index 3170bef98..5c9e64021 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -2632,14 +2632,11 @@ on the second character (the one after '-'), to save some effort. */ reset_point = store_mark(); while (Ufgets(big_buffer, big_buffer_size, trust_list)) { - uschar *start = big_buffer, *nl; - while (*start && isspace(*start)) - start++; - if (*start != '/') + uschar * start = big_buffer, * nl; + if (Uskip_whitespace(&start) != '/') continue; - nl = Ustrchr(start, '\n'); - if (nl) - *nl = 0; + if ((nl = Ustrchr(start, '\n'))) + *nl = '\0'; trusted_configs[nr_configs++] = string_copy(start); if (nr_configs == nelem(trusted_configs)) break; @@ -2698,7 +2695,7 @@ on the second character (the one after '-'), to save some effort. */ const uschar * s = argrest; opt_D_used = TRUE; - while (isspace(*s)) s++; + Uskip_whitespace(&s); if (*s < 'A' || *s > 'Z') exim_fail("exim: macro name set by -D must start with " @@ -2711,11 +2708,10 @@ on the second character (the one after '-'), to save some effort. */ } name[ptr] = 0; if (ptr == 0) { badarg = TRUE; break; } - while (isspace(*s)) s++; - if (*s != 0) + if (Uskip_whitespace(&s)) { if (*s++ != '=') { badarg = TRUE; break; } - while (isspace(*s)) s++; + Uskip_whitespace(&s); } for (m = macros_user; m; m = m->next) @@ -5918,7 +5914,7 @@ for (BOOL more = TRUE; more; ) receive_add_recipient(string_copy_taint(recipient, GET_TAINTED), -1); s = ss; if (!finished) - while (*(++s) != 0 && (*s == ',' || isspace(*s))); + while (*++s && (*s == ',' || isspace(*s))); } } diff --git a/src/src/expand.c b/src/src/expand.c index a916eee64..5ea4dc6d4 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -1262,7 +1262,8 @@ while (*s) while (*s && *s != '=' && !isspace(*s)) s++; dkeylength = s - dkey; - if (Uskip_whitespace(&s) == '=') while (isspace(*++s)); + if (Uskip_whitespace(&s) == '=') + while (isspace(*++s)) ; data = string_dequote(&s); if (length == dkeylength && strncmpic(key, dkey, length) == 0) @@ -4114,7 +4115,7 @@ if (!*error) if (*s != ')') *error = US"expecting closing parenthesis"; else - while (isspace(*++s)); + while (isspace(*++s)) ; else if (*s) *error = US"expecting operator"; *sptr = s; diff --git a/src/src/filter.c b/src/src/filter.c index b56b593b8..18567f17c 100644 --- a/src/src/filter.c +++ b/src/src/filter.c @@ -276,16 +276,11 @@ nextsigchar(const uschar *ptr, BOOL comment_allowed) for (;;) { while (isspace(*ptr)) - { - if (*ptr == '\n') line_number++; - ptr++; - } + if (*ptr++ == '\n') line_number++; if (comment_allowed && *ptr == '#') - { - while (*(++ptr) != '\n' && *ptr != 0); - continue; - } - else break; + while (*++ptr != '\n' && *ptr) ; + else + break; } return ptr; } @@ -311,18 +306,19 @@ Returns: pointer to the next significant character after the word static const uschar * nextword(const uschar *ptr, uschar *buffer, int size, BOOL bracket) { -uschar *bp = buffer; -while (*ptr != 0 && !isspace(*ptr) && +uschar * bp = buffer; +while (*ptr && !isspace(*ptr) && (!bracket || (*ptr != '(' && *ptr != ')'))) - { - if (bp - buffer < size - 1) *bp++ = *ptr++; else + if (bp - buffer < size - 1) + *bp++ = *ptr++; + else { *error_pointer = string_sprintf("word is too long in line %d of " "filter file (max = %d chars)", line_number, size); break; } - } -*bp = 0; + +*bp = '\0'; return nextsigchar(ptr, TRUE); } @@ -410,8 +406,8 @@ int value, count; if (sscanf(CS s, "%i%n", &value, &count) != 1) return 0; if (tolower(s[count]) == 'k') { value *= 1024; count++; } if (tolower(s[count]) == 'm') { value *= 1024*1024; count++; } -while (isspace((s[count]))) count++; -if (s[count] != 0) return 0; +while (isspace(s[count])) count++; +if (s[count]) return 0; *ok = TRUE; return value; } @@ -2006,8 +2002,7 @@ while (commands) if (subtype == TRUE) { - while (isspace(*s)) s++; - if (*s) + if (Uskip_whitespace(&s)) { header_add(htype_other, "%s%s", s, s[Ustrlen(s)-1] == '\n' ? "" : "\n"); @@ -2226,7 +2221,7 @@ while (commands) gstring * log_addr = NULL; if (!to) to = expand_string(US"$reply_address"); - while (isspace(*to)) to++; + Uskip_whitespace(&to); for (tt = to; *tt; tt++) /* Get rid of newlines */ if (*tt == '\n') @@ -2298,7 +2293,7 @@ while (commands) /* Move on past this address */ tt = ss + (*ss ? 1 : 0); - while (isspace(*tt)) tt++; + Uskip_whitespace(&tt); } if (log_addr) @@ -2612,3 +2607,5 @@ return yield; /* End of filter.c */ +/* vi: aw ai sw=2 +*/ diff --git a/src/src/lookups/dnsdb.c b/src/src/lookups/dnsdb.c index 050baa3b7..af1ad9dab 100644 --- a/src/src/lookups/dnsdb.c +++ b/src/src/lookups/dnsdb.c @@ -154,8 +154,7 @@ gstring * yield = string_get(256); /* If the string starts with '>' we change the output separator. If it's followed by ';' or ',' we set the TXT output separator. */ -while (isspace(*keystring)) keystring++; -if (*keystring == '>') +if (Uskip_whitespace(&keystring) == '>') { outsep = keystring + 1; keystring += 2; @@ -169,7 +168,7 @@ if (*keystring == '>') outsep2 = US""; keystring++; } - while (isspace(*keystring)) keystring++; + Uskip_whitespace(&keystring); } /* Check for a modifier keyword. */ @@ -234,14 +233,14 @@ for (;;) else break; - while (isspace(*keystring)) keystring++; + Uskip_whitespace(&keystring); if (*keystring++ != ',') { *errmsg = US"dnsdb modifier syntax error"; rc = DEFER; goto out; } - while (isspace(*keystring)) keystring++; + Uskip_whitespace(&keystring); } /* Figure out the "type" value if it is not T_TXT. @@ -272,7 +271,7 @@ if ((equals = Ustrchr(keystring, '=')) != NULL) } keystring = equals + 1; - while (isspace(*keystring)) keystring++; + Uskip_whitespace(&keystring); } /* Initialize the resolver in case this is the first time it has been used. */ diff --git a/src/src/lookups/ldap.c b/src/src/lookups/ldap.c index b93782cc3..90cde6c86 100644 --- a/src/src/lookups/ldap.c +++ b/src/src/lookups/ldap.c @@ -1083,20 +1083,15 @@ control_ldap_search(const uschar *ldap_url, int search_type, uschar **res, uschar **errmsg) { BOOL defer_break = FALSE; -int timelimit = LDAP_NO_LIMIT; -int sizelimit = LDAP_NO_LIMIT; +int timelimit = LDAP_NO_LIMIT, sizelimit = LDAP_NO_LIMIT; int tcplimit = 0; int sep = 0; int dereference = LDAP_DEREF_NEVER; -void* referrals = LDAP_OPT_ON; -const uschar *url = ldap_url; -const uschar *p; -uschar *user = NULL; -uschar *password = NULL; -uschar *local_servers = NULL; -const uschar *list; +void * referrals = LDAP_OPT_ON; +const uschar * url = ldap_url, * p, * list; +uschar * user = NULL, * password = NULL, * local_servers = NULL; -while (isspace(*url)) url++; +Uskip_whitespace(&url); /* Until the string begins "ldap", search for the other parameter settings that are recognized. They are of the form NAME=VALUE, with the value being @@ -1175,7 +1170,7 @@ while (strncmpic(url, US"ldap", 4) != 0) DEBUG(D_lookup) debug_printf_indent("LDAP query error: %s\n", *errmsg); return DEFER; } - while (isspace(*url)) url++; + Uskip_whitespace(&url); continue; } } diff --git a/src/src/lookups/lsearch.c b/src/src/lookups/lsearch.c index 33e8c5157..7b78de916 100644 --- a/src/src/lookups/lsearch.c +++ b/src/src/lookups/lsearch.c @@ -291,8 +291,8 @@ for (BOOL this_is_eol, last_was_eol = TRUE; this_is_comment = (this_is_comment || (buffer[0] == 0 || buffer[0] == '#')); if (this_is_comment) continue; if (!isspace((uschar)buffer[0])) break; - while (isspace((uschar)*s)) s++; - *(--s) = ' '; + Uskip_whitespace(&s); + *--s = ' '; } if (this_is_comment) continue; diff --git a/src/src/lookups/oracle.c b/src/src/lookups/oracle.c index d0604c245..c25b7c0ee 100644 --- a/src/src/lookups/oracle.c +++ b/src/src/lookups/oracle.c @@ -408,9 +408,9 @@ while (cda->rc != NO_DATA_FOUND) /* Loop for each row */ else for (int i = 0; i < num_fields; i++) { int slen; - uschar *s = US desc[i].buf; + uschar * s = US desc[i].buf; - while (*s != 0 && isspace(*s)) s++; + Uskip_whitespace(&s); slen = Ustrlen(s); while (slen > 0 && isspace(s[slen-1])) slen--; result = string_catn(result, s, slen); diff --git a/src/src/lookups/redis.c b/src/src/lookups/redis.c index 7b680f086..53b479142 100644 --- a/src/src/lookups/redis.c +++ b/src/src/lookups/redis.c @@ -212,7 +212,7 @@ if(sdata[1]) int siz, ptr, i; uschar c; - while (isspace(*s)) s++; + Uskip_whitespace(&s); for (i = 0; *s && i < nele(argv); i++) { @@ -224,7 +224,7 @@ if(sdata[1]) argv[i] = string_from_gstring(g); DEBUG(D_lookup) debug_printf_indent("REDIS: argv[%d] '%s'\n", i, argv[i]); - while (isspace(*s)) s++; + Uskip_whitespace(&s); } /* Run the command. We use the argv form rather than plain as that parses diff --git a/src/src/match.c b/src/src/match.c index 8b1a3bef6..2cb499359 100644 --- a/src/src/match.c +++ b/src/src/match.c @@ -599,7 +599,7 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) if (*ss == '!') { yield = FAIL; - while (isspace((*(++ss)))); + while (isspace(*++ss)) ; } else yield = OK; @@ -825,15 +825,14 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) while (Ufgets(filebuffer, sizeof(filebuffer), f) != NULL) { - uschar *error; - uschar *sss = filebuffer; + uschar * error, * sss = filebuffer; while ((ss = Ustrchr(sss, '#')) != NULL) { if ((type != MCL_ADDRESS && type != MCL_LOCALPART) || ss == filebuffer || isspace(ss[-1])) { - *ss = 0; + *ss = '\0'; break; } sss = ss + 1; @@ -841,20 +840,19 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) ss = filebuffer + Ustrlen(filebuffer); /* trailing space */ while (ss > filebuffer && isspace(ss[-1])) ss--; - *ss = 0; + *ss = '\0'; ss = filebuffer; - while (isspace(*ss)) ss++; /* leading space */ - - if (!*ss) continue; /* ignore empty */ + if (!Uskip_whitespace(&ss)) /* leading space */ + continue; /* ignore empty */ file_yield = yield; /* positive yield */ sss = ss; /* for debugging */ if (*ss == '!') /* negation */ { - file_yield = (file_yield == OK)? FAIL : OK; - while (isspace((*(++ss)))); + file_yield = file_yield == OK ? FAIL : OK; + while (isspace(*++ss)) ; } switch ((func)(arg, ss, valueptr, &error)) @@ -1157,7 +1155,7 @@ if (pattern[0] == '@' && pattern[1] == '@') if (*ss == '!') { local_yield = FAIL; - while (isspace((*(++ss)))); + while (isspace(*++ss)) ; } else local_yield = OK; diff --git a/src/src/moan.c b/src/src/moan.c index 6fe82b29c..641540839 100644 --- a/src/src/moan.c +++ b/src/src/moan.c @@ -762,13 +762,12 @@ llen = domain++ - recipient; while ((item = string_nextinlist(&listptr, &sep, NULL, 0))) { - const uschar *newaddress = item; - const uschar *pattern = string_dequote(&newaddress); + const uschar * newaddress = item; + const uschar * pattern = string_dequote(&newaddress); /* If no new address found, just skip this item. */ - while (isspace(*newaddress)) newaddress++; - if (*newaddress == 0) continue; + if (!Uskip_whitespace(&newaddress)) continue; /* We now have an item to match as an address in item, and the additional address in newaddress. If the pattern matches, expand the new address string diff --git a/src/src/parse.c b/src/src/parse.c index ecdbe9f8d..4a75d229e 100644 --- a/src/src/parse.c +++ b/src/src/parse.c @@ -1363,7 +1363,7 @@ for (;;) *error = string_sprintf("\"%.*s\" is not permitted", len, s); return FF_ERROR; } - while (*ss && isspace(*ss)) ss++; /* skip leading whitespace */ + Uskip_whitespace(&ss); /* skip leading whitespace */ if ((len = Ustrlen(ss)) > 0) /* ignore trailing newlines */ for (const uschar * t = ss + len - 1; t >= ss && *t == '\n'; t--) len--; *error = string_copyn(ss, len); /* becomes the error */ diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index 21e17c61e..565ee068e 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -679,8 +679,7 @@ for (const uschar * ele = raw_record, * tspec, * end, * val; *ele; ele = end) DEBUG(D_acl) debug_printf(" %.*s=%s\n", taglen, tspec, val); while (taglen > 1 && isspace(tspec[taglen-1])) taglen--; /* Ignore whitespace before = */ - while (isspace(*val)) - val++; /* Ignore whitespace after = */ + Uskip_whitespace(&val); /* Ignore whitespace after = */ if (isspace(val[ Ustrlen(val)-1 ])) { /* Ignore whitespace after value */ gstring * g = string_cat(NULL, val); diff --git a/src/src/proxy.c b/src/src/proxy.c index fbce11163..e2d3bace0 100644 --- a/src/src/proxy.c +++ b/src/src/proxy.c @@ -396,7 +396,7 @@ else if (ret >= 8 && memcmp(hdr.v1.line, "PROXY", 5) == 0) /* Step through the string looking for the required fields. Ensure strict adherence to required formatting, exit for any error. */ p += 5; - if (!isspace(*(p++))) + if (!isspace(*p++)) { DEBUG(D_receive) debug_printf("Missing space after PROXY command\n"); goto proxyfail; @@ -417,7 +417,7 @@ else if (ret >= 8 && memcmp(hdr.v1.line, "PROXY", 5) == 0) } p += Ustrlen(iptype); - if (!isspace(*(p++))) + if (!isspace(*p++)) { DEBUG(D_receive) debug_printf("Missing space after TCP4/6 command\n"); goto proxyfail; diff --git a/src/src/readconf.c b/src/src/readconf.c index 5b486d0b6..3db3bdd69 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -1050,7 +1050,7 @@ for (;;) (Ustrncmp(ss+8, "_if_exists", 10) == 0 && isspace(ss[18])))) { uschar *t; - int include_if_exists = isspace(ss[8])? 0 : 10; + int include_if_exists = isspace(ss[8]) ? 0 : 10; config_file_item *save; struct stat statbuf; @@ -1644,8 +1644,7 @@ uschar name2[EXIM_DRIVERNAME_MAX]; /* There may be leading spaces; thereafter, we expect an option name starting with a letter. */ -while (isspace(*s)) s++; -if (!isalpha(*s)) +if (!isalpha( Uskip_whitespace(&s) )) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "option setting expected: %s", s); /* Read the name of the option, and skip any subsequent white space. If @@ -1660,7 +1659,7 @@ for (int n = 0; n < 2; n++) s++; } name[ptr] = 0; - while (isspace(*s)) s++; + Uskip_whitespace(&s); if (Ustrcmp(name, "hide") != 0) break; issecure = opt_secure; ptr = 0; @@ -1720,7 +1719,7 @@ else if (*s && (offset != 0 || *s != '=')) /* Skip white space after = */ -if (*s == '=') while (isspace((*(++s)))); +if (*s == '=') while (isspace(*++s)); /* If there is a data block and the opt_public flag is not set, change the data block pointer to the private options block. */ @@ -2202,8 +2201,7 @@ switch (type) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "absolute value of integer \"%s\" is too large (overflow)", s); - while (isspace(*endptr)) endptr++; - if (*endptr) + if (Uskip_whitespace(&endptr)) extra_chars_error(endptr, inttype, US"integer value for ", name); value = (int)lvalue; @@ -2251,8 +2249,7 @@ switch (type) if (errno == ERANGE) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "absolute value of integer \"%s\" is too large (overflow)", s); - while (isspace(*endptr)) endptr++; - if (*endptr != 0) + if (Uskip_whitespace(&endptr)) extra_chars_error(endptr, inttype, US"integer value for ", name); if (data_block) @@ -2347,7 +2344,7 @@ switch (type) list[count+1] = value; if (snext == NULL) break; s = snext + 1; - while (isspace(*s)) s++; + Uskip_whitespace(&s); } if (count > list[0] - 2) @@ -3570,8 +3567,7 @@ if (host_number_string) "failed to expand localhost_number \"%s\": %s", host_number_string, expand_string_message); n = Ustrtol(s, &end, 0); - while (isspace(*end)) end++; - if (*end) + if (Uskip_whitespace(&end)) log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "localhost_number value is not a number: %s", s); if (n > LOCALHOST_MAX) @@ -4019,10 +4015,9 @@ Returns: time in seconds or fixed point number * 1000 */ static int -retry_arg(const uschar **paddr, int type) +retry_arg(const uschar ** paddr, int type) { -const uschar *p = *paddr; -const uschar *pp; +const uschar * p = *paddr, * pp; if (*p++ != ',') log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "comma expected"); @@ -4030,7 +4025,7 @@ Uskip_whitespace(&p); pp = p; while (isalnum(*p) || (type == 1 && *p == '.')) p++; -if (*p != 0 && !isspace(*p) && *p != ',' && *p != ';') +if (*p && !isspace(*p) && *p != ',' && *p != ';') log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "comma or semicolon expected"); *paddr = p; @@ -4433,10 +4428,8 @@ for (const config_line_item * i = config_lines; i; i = i->next) r = store_mark(); /* skip over to the first non-space */ - for (current = string_copy(i->line); *current && isspace(*current); ++current) - ; - - if (!*current) + current = string_copy(i->line); + if (!Uskip_whitespace(¤t)) continue; /* Collapse runs of spaces. We stop this if we encounter one of the @@ -4444,11 +4437,10 @@ for (const config_line_item * i = config_lines; i; i = i->next) for (p = current; *p; p++) if (isspace(*p)) { - uschar *next; + uschar * next = p; if (*p != ' ') *p = ' '; - for (next = p; isspace(*next); ++next) - ; + Uskip_whitespace(&p); if (next - p > 1) memmove(p+1, next, Ustrlen(next)+1); diff --git a/src/src/receive.c b/src/src/receive.c index a56ff473e..9dbf45df9 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -2251,8 +2251,7 @@ OVERSIZE: if (isspace(*p)) break; while (mac_isgraph(*p) && *p != ':') p++; - while (isspace(*p)) p++; - if (*p != ':') + if (Uskip_whitespace(&p) != ':') { body_zerocount = had_zero; break; @@ -2453,13 +2452,14 @@ for (header_line * h = header_list->next; h; h = h->next) if (!smtp_input) { int len; - uschar *s = Ustrchr(h->text, ':') + 1; - while (isspace(*s)) s++; + uschar * s = Ustrchr(h->text, ':') + 1; + + Uskip_whitespace(&s); len = h->slen - (s - h->text) - 1; if (Ustrlen(originator_login) == len && strncmpic(s, originator_login, len) == 0) { - uschar *name = is_resent? US"Resent-From" : US"From"; + uschar * name = is_resent ? US"Resent-From" : US"From"; header_add(htype_from, "%s: %s <%s@%s>\n", name, originator_name, originator_login, qualify_domain_sender); from_header = header_last; @@ -2514,15 +2514,13 @@ for (header_line * h = header_list->next; h; h = h->next) if (filter_test != FTEST_NONE) { - uschar *start = h->text + 12; - uschar *end = start + Ustrlen(start); - while (isspace(*start)) start++; + uschar * start = h->text + 12; + uschar * end = start + Ustrlen(start); + + Uskip_whitespace(&start); while (end > start && isspace(end[-1])) end--; if (*start == '<' && end[-1] == '>') - { - start++; - end--; - } + { start++; end--; } return_path = string_copyn(start, end - start); printf("Return-path taken from \"Return-path:\" header line\n"); } @@ -2626,7 +2624,7 @@ if (extract_recip) (!contains_resent_headers || strncmpic(h->text, US"resent-", 7) == 0)) { uschar * s = Ustrchr(h->text, ':') + 1; - while (isspace(*s)) s++; + Uskip_whitespace(&s); f.parse_allow_group = TRUE; /* Allow address group syntax */ @@ -2706,7 +2704,7 @@ if (extract_recip) /* Move on past this address */ s = ss + (*ss ? 1 : 0); - while (isspace(*s)) s++; + Uskip_whitespace(&s); } /* Next address */ f.parse_allow_group = FALSE; /* Reset group syntax flags */ diff --git a/src/src/rewrite.c b/src/src/rewrite.c index 9ee5a6794..ce35c4365 100644 --- a/src/src/rewrite.c +++ b/src/src/rewrite.c @@ -452,7 +452,7 @@ header_line * newh = NULL; rmark function_reset_point = store_mark(); uschar * s = Ustrchr(h->text, ':') + 1; -while (isspace(*s)) s++; +Uskip_whitespace(&s); DEBUG(D_rewrite) /* The header text includes the trailing newline */ debug_printf_indent("rewrite_one_header: type=%c:\n %s", h->type, h->text); @@ -493,7 +493,7 @@ while (*s) recipient = parse_extract_address(s, &errmess, &start, &end, &domain, FALSE); *ss1 = terminator; s = ss + (*ss ? 1 : 0); - while (isspace(*s)) s++; + Uskip_whitespace(&s); /* There isn't much we can do for syntactic disasters at this stage. Pro tem (possibly for ever) ignore them. diff --git a/src/src/rfc2047.c b/src/src/rfc2047.c index 18263f9ab..1ab25150a 100644 --- a/src/src/rfc2047.c +++ b/src/src/rfc2047.c @@ -313,8 +313,8 @@ while (mimeword) mimeword = decode_mimeword(string, lencheck, &q1, &q2, &endword, &dlen, &dptr); if (mimeword) { - uschar *s = string; - while (isspace(*s)) s++; + uschar * s = string; + Uskip_whitespace(&s); if (s == mimeword) string = s; } } diff --git a/src/src/route.c b/src/src/route.c index 0fdcdf2ed..afd43d866 100644 --- a/src/src/route.c +++ b/src/src/route.c @@ -266,12 +266,12 @@ for (router_instance * r = routers; r; r = r->next) else if (Ustrncmp(s, "reroute:", 8) == 0) { s += 8; - while (isspace(*s)) s++; + Uskip_whitespace(&s); if (Ustrncmp(s, "rewrite:", 8) == 0) { r->self_rewrite = TRUE; s += 8; - while (isspace(*s)) s++; + Uskip_whitespace(&s); } r->self = s; r->self_code = self_reroute; @@ -693,13 +693,13 @@ while ((check = string_nextinlist(&listptr, &sep, NULL, 0))) if (*ss == '+') { eacces_code = 1; - while (isspace((*(++ss)))); + while (isspace(*++ss)); } if (*ss == '!') { invert = TRUE; - while (isspace((*(++ss)))); + while (isspace(*++ss)); } if (*ss != '/') @@ -1463,7 +1463,7 @@ for (uschar * ele; (ele = string_nextinlist(&varlist, &sep, NULL, 0)); ) } name += 2; - while (isspace(*assignment)) assignment++; + Uskip_whitespace(&assignment); if (!(val = expand_string(US assignment))) if (f.expand_string_forcedfail) diff --git a/src/src/routers/manualroute.c b/src/src/routers/manualroute.c index 45378ce3d..ed497336b 100644 --- a/src/src/routers/manualroute.c +++ b/src/src/routers/manualroute.c @@ -156,17 +156,17 @@ static BOOL parse_route_item(const uschar *s, const uschar **domain, const uschar **hostlist, const uschar **options) { -while (*s != 0 && isspace(*s)) s++; +Uskip_whitespace(&s); if (domain) { if (!*s) return FALSE; /* missing data */ *domain = string_dequote(&s); - while (*s && isspace(*s)) s++; + Uskip_whitespace(&s); } *hostlist = string_dequote(&s); -while (*s && isspace(*s)) s++; +Uskip_whitespace(&s); *options = s; return TRUE; } @@ -333,8 +333,9 @@ lookup_type = LK_DEFAULT; while (*options) { unsigned n; - const uschar *s = options; - while (*options != 0 && !isspace(*options)) options++; + const uschar * s = options; + + while (*options && !isspace(*options)) options++; n = options-s; if (Ustrncmp(s, "randomize", n) == 0) randomize = TRUE; @@ -368,7 +369,7 @@ while (*options) if (*options) { options++; - while (*options != 0 && isspace(*options)) options++; + while (*options && isspace(*options)) options++; } } diff --git a/src/src/routers/queryprogram.c b/src/src/routers/queryprogram.c index b1d736f94..5f0f50cd9 100644 --- a/src/src/routers/queryprogram.c +++ b/src/src/routers/queryprogram.c @@ -363,10 +363,10 @@ buffer[len] = 0; DEBUG(D_route) debug_printf("command wrote: %s\n", buffer); rword = buffer; -while (isspace(*rword)) rword++; +Uskip_whitespace(&rword); rdata = rword; while (*rdata && !isspace(*rdata)) rdata++; -if (*rdata) *rdata++ = 0; +if (*rdata) *rdata++ = '\0'; /* The word must be a known yield name. If it is "REDIRECT", the rest of the line is redirection data, as for a .forward file. It may not contain filter diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index f62d6fbf2..79176687d 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -1227,7 +1227,7 @@ for (smtp_cmd_list * p = cmd_list; p < cmd_list + nelem(cmd_list); p++) follow the sender address. */ smtp_cmd_argument = smtp_cmd_buffer + p->len; - while (isspace(*smtp_cmd_argument)) smtp_cmd_argument++; + Uskip_whitespace(&smtp_cmd_argument); Ustrcpy(smtp_data_buffer, smtp_cmd_argument); smtp_cmd_data = smtp_data_buffer; @@ -3807,8 +3807,8 @@ while (done <= 0) if (*smtp_cmd_data) { - *smtp_cmd_data++ = 0; - while (isspace(*smtp_cmd_data)) smtp_cmd_data++; + *smtp_cmd_data++ = '\0'; + Uskip_whitespace(&smtp_cmd_data); } /* Search for an authentication mechanism which is configured for use @@ -3919,10 +3919,10 @@ while (done <= 0) if (!f.sender_host_unknown) { BOOL old_helo_verified = f.helo_verified; - uschar *p = smtp_cmd_data; + uschar * p = smtp_cmd_data; - while (*p != 0 && !isspace(*p)) { *p = tolower(*p); p++; } - *p = 0; + while (*p && !isspace(*p)) { *p = tolower(*p); p++; } + *p = '\0'; /* Force a reverse lookup if HELO quoted something in helo_lookup_domains because otherwise the log can be confusing. */ diff --git a/src/src/smtp_out.c b/src/src/smtp_out.c index 895b594fc..888a0006e 100644 --- a/src/src/smtp_out.c +++ b/src/src/smtp_out.c @@ -704,13 +704,13 @@ if (format) if (outblock->authenticating) { - uschar *p = big_buffer; + uschar * p = big_buffer; if (Ustrncmp(big_buffer, "AUTH ", 5) == 0) { p += 5; - while (isspace(*p)) p++; + Uskip_whitespace(&p); while (!isspace(*p)) p++; - while (isspace(*p)) p++; + Uskip_whitespace(&p); } while (*p) *p++ = '*'; } diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index e6a1c6fa4..64e8c711a 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -5170,8 +5170,7 @@ if (!expand_check(option_spec, US"openssl_options", &exp, &end)) for (uschar * s = exp; *s; /**/) { - while (isspace(*s)) ++s; - if (*s == '\0') + if (!Uskip_whitespace(&s)) break; if (*s != '+' && *s != '-') { diff --git a/src/src/transports/autoreply.c b/src/src/transports/autoreply.c index 690ec4a92..67d48a1cb 100644 --- a/src/src/transports/autoreply.c +++ b/src/src/transports/autoreply.c @@ -235,13 +235,12 @@ that needs to be removed */ s = newlist + Ustrlen(newlist); while (s > newlist && (isspace(s[-1]) || s[-1] == ',')) s--; -*s = 0; +*s = '\0'; /* Check to see if there any addresses left; if not, return NULL */ s = newlist; -while (s && isspace(*s)) s++; -if (*s) +if (Uskip_whitespace(&s)) return newlist; store_reset(reset_point); @@ -592,7 +591,7 @@ for (h = header_list; h; h = h->next) if (h) { message_id = Ustrchr(h->text, ':') + 1; - while (isspace(*message_id)) message_id++; + Uskip_whitespace(&message_id); fprintf(fp, "In-Reply-To: %s", message_id); } diff --git a/src/src/transports/pipe.c b/src/src/transports/pipe.c index 60a20c8fe..f8a1427b9 100644 --- a/src/src/transports/pipe.c +++ b/src/src/transports/pipe.c @@ -562,7 +562,7 @@ if (testflag(addr, af_pfr) && addr->local_part[0] == '|') else { cmd = addr->local_part + 1; - while (isspace(*cmd)) cmd++; + Uskip_whitespace(&cmd); expand_arguments = testflag(addr, af_expand_pipe); expand_fail = FAIL; } diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 736592352..ed5994241 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -533,7 +533,7 @@ switch(*errno_value) case ERRNO_SMTPFORMAT: /* Handle malformed SMTP response */ s = string_printing(buffer); - while (isspace(*s)) s++; + Uskip_whitespace(&s); *message = *s == 0 ? string_sprintf("Malformed SMTP reply (an empty line) " "in response to %s%s", pl, smtp_command) @@ -815,8 +815,7 @@ uschar * match; if (regex_match(regex_LIMITS, sx->buffer, -1, &match)) for (const uschar * s = sx->buffer + Ustrlen(match); *s; ) { - while (isspace(*s)) s++; - if (*s == '\n') break; + if (Uskip_whitespace(&s) == '\n') break; if (strncmpic(s, US"MAILMAX=", 8) == 0) { @@ -1694,8 +1693,8 @@ if ( sx->esmtp { DEBUG(D_transport) debug_printf("skipping %s authenticator: %s\n", au->name, - (au->client)? "client_condition is false" : - "not configured as a client"); + au->client ? "client_condition is false" + : "not configured as a client"); continue; } @@ -1703,15 +1702,14 @@ if ( sx->esmtp while (*p) { - int len = Ustrlen(au->public_name); - int rc; + int len = Ustrlen(au->public_name), rc; - while (isspace(*p)) p++; + Uskip_whitespace(&p); if (strncmpic(au->public_name, p, len) != 0 || - (p[len] != 0 && !isspace(p[len]))) + (p[len] && !isspace(p[len]))) { - while (*p != 0 && !isspace(*p)) p++; + while (*p && !isspace(*p)) p++; continue; } diff --git a/src/src/verify.c b/src/src/verify.c index 78923651c..4f3bafb04 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -2612,12 +2612,12 @@ for (int i = 0; i < 3 && !done; i++) f.parse_allow_group = TRUE; - while (*s != 0) + while (*s) { - address_item *vaddr; + address_item * vaddr; while (isspace(*s) || *s == ',') s++; - if (*s == 0) break; /* End of header */ + if (!*s) break; /* End of header */ ss = parse_find_address_end(s, FALSE); @@ -2628,7 +2628,7 @@ for (int i = 0; i < 3 && !done; i++) while (isspace(ss[-1])) ss--; terminator = *ss; - *ss = 0; + *ss = '\0'; HDEBUG(D_verify) debug_printf("verifying %.*s header address %s\n", (int)(endname - h->text), h->text, s); @@ -2873,17 +2873,17 @@ if (sscanf(CS buffer + qlen, "%d , %d%n", &received_sender_port, goto END_OFF; p = buffer + qlen + n; -while(isspace(*p)) p++; +Uskip_whitespace(&p); if (*p++ != ':') goto END_OFF; -while(isspace(*p)) p++; +Uskip_whitespace(&p); if (Ustrncmp(p, "USERID", 6) != 0) goto END_OFF; p += 6; -while(isspace(*p)) p++; +Uskip_whitespace(&p); if (*p++ != ':') goto END_OFF; -while (*p != 0 && *p != ':') p++; -if (*p++ == 0) goto END_OFF; -while(isspace(*p)) p++; -if (*p == 0) goto END_OFF; +while (*p && *p != ':') p++; +if (!*p++) goto END_OFF; +Uskip_whitespace(&p); +if (!*p) goto END_OFF; /* The rest of the line is the data we want. We turn it into printing characters when we save it, so that it cannot mess up the format of any logging @@ -3083,7 +3083,7 @@ if (iplookup) key = filename; while (*key != 0 && !isspace(*key)) key++; filename = string_copyn(filename, key - filename); - while (isspace(*key)) key++; + Uskip_whitespace(&key); } else if (mac_islookup(search_type, lookup_querystyle)) { diff --git a/src/src/xclient.c b/src/src/xclient.c index 2a8be9b0e..2219cb027 100644 --- a/src/src/xclient.c +++ b/src/src/xclient.c @@ -248,7 +248,7 @@ for (state = XCLIENT_SKIP_SPACES; *s; ) } case XCLIENT_SKIP_SPACES: - while (*s && isspace (*s)) s++; + Uskip_whitespace(&s); state = XCLIENT_READ_COMMAND; break; commit 3f77bd134b64c532748b83c3931df07058268b5e Author: Jeremy Harris Date: Tue Mar 26 19:52:49 2024 +0000 tidying diff --git a/src/exim_monitor/em_log.c b/src/exim_monitor/em_log.c index fbcb6b1c0..49f649d7e 100644 --- a/src/exim_monitor/em_log.c +++ b/src/exim_monitor/em_log.c @@ -294,7 +294,7 @@ if (LOG != NULL) if ((p = Ustrstr(buffer, "==")) != NULL) { - queue_item *qq = find_queue(id, queue_noop, 0); + queue_item * qq = find_queue(id, queue_noop, 0); if (qq) { dest_item *d; @@ -302,14 +302,12 @@ if (LOG != NULL) p += 2; while (isspace(*p)) p++; q = p; - while (*p != 0 && !isspace(*p)) + while (*p && !isspace(*p)) { if (*p++ != '\"') continue; - while (*p != 0) - { + while (*p) if (*p == '\\') p += 2; - else if (*p++ == '\"') break; - } + else if (*p++ == '\"') break; } *p++ = 0; if ((r = strstric(q, qualify_domain, FALSE)) != NULL && diff --git a/src/src/auths/cram_md5.c b/src/src/auths/cram_md5.c index 8f00ee498..5817be7c9 100644 --- a/src/src/auths/cram_md5.c +++ b/src/src/auths/cram_md5.c @@ -198,7 +198,7 @@ The former is now the preferred variable; the latter is the original one. Then check that the remaining length is 32. */ auth_vars[0] = expand_nstring[1] = clear; -while (*clear && !isspace(*clear)) clear++; +Uskip_nonwhite(&clear); if (!isspace(*clear)) return FAIL; *clear++ = 0; diff --git a/src/src/dbfn.c b/src/src/dbfn.c index 13a3c2d66..389d1518e 100644 --- a/src/src/dbfn.c +++ b/src/src/dbfn.c @@ -544,8 +544,8 @@ while (Ufgets(buffer, 256, stdin) != NULL) Uskip_whitespace(&key); data = key; - while (*data && !isspace((uschar)*data)) data++; - *data++ = 0; + Uskip_nonwhite(&data); + *data++ = '\0'; Uskip_whitespace(&data); dbwait = (dbdata_wait *)(&structbuffer); diff --git a/src/src/exim_dbmbuild.c b/src/src/exim_dbmbuild.c index 9f5f2f042..bd24faa5b 100644 --- a/src/src/exim_dbmbuild.c +++ b/src/src/exim_dbmbuild.c @@ -379,7 +379,7 @@ while (Ufgets(line, max_insize, f) != NULL) else { keystart = s; - while (*s != 0 && *s != ':' && !isspace(*s)) s++; + while (*s && *s != ':' && !isspace(*s)) s++; exim_datum_size_set(&key, s - keystart + add_zero); } @@ -401,11 +401,11 @@ while (Ufgets(line, max_insize, f) != NULL) keybuffer[i] = 0; started = 1; - while (isspace(*s))s++; + while (isspace(*s)) s++; if (*s == ':') { s++; - while (isspace(*s))s++; + while (isspace(*s)) s++; } if (*s != 0) { diff --git a/src/src/functions.h b/src/src/functions.h index 0e657d7c4..d811c62a3 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -767,6 +767,12 @@ Return the next char as there's enough places using it to be useful. */ static inline uschar skip_whitespace(const uschar ** sp) { while (isspace(**sp)) (*sp)++; return **sp; } +/* Ditto, non-whitespace */ + +#define Uskip_nonwhite(sp) skip_nonwhite(CUSS sp) +static inline uschar skip_nonwhite(const uschar ** sp) +{ while (**sp && !isspace(**sp)) (*sp)++; return **sp; } + /******************************************************************************/ diff --git a/src/src/routers/manualroute.c b/src/src/routers/manualroute.c index ed497336b..ffd6eeaca 100644 --- a/src/src/routers/manualroute.c +++ b/src/src/routers/manualroute.c @@ -335,7 +335,7 @@ while (*options) unsigned n; const uschar * s = options; - while (*options && !isspace(*options)) options++; + Uskip_nonwhite(&options); n = options-s; if (Ustrncmp(s, "randomize", n) == 0) randomize = TRUE; @@ -369,7 +369,7 @@ while (*options) if (*options) { options++; - while (*options && isspace(*options)) options++; + Uskip_whitespace(&options); } } diff --git a/src/src/routers/queryprogram.c b/src/src/routers/queryprogram.c index 5f0f50cd9..39645cd23 100644 --- a/src/src/routers/queryprogram.c +++ b/src/src/routers/queryprogram.c @@ -365,7 +365,7 @@ DEBUG(D_route) debug_printf("command wrote: %s\n", buffer); rword = buffer; Uskip_whitespace(&rword); rdata = rword; -while (*rdata && !isspace(*rdata)) rdata++; +Uskip_nonwhite(&rdata); if (*rdata) *rdata++ = '\0'; /* The word must be a known yield name. If it is "REDIRECT", the rest of the diff --git a/src/src/search.c b/src/src/search.c index 168c25e6a..d0eb84b3b 100644 --- a/src/src/search.c +++ b/src/src/search.c @@ -247,7 +247,7 @@ if (mac_islookup(search_type, lookup_absfilequery)) if (*query == '/') { uschar * s = query; - while (*query && !isspace(*query)) query++; + Uskip_nonwhite(&query); *fnamep = string_copyn(s, query - s); Uskip_whitespace(&query); } diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 79176687d..70e4b95df 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -1946,6 +1946,9 @@ while (done <= 0) case HELP_CMD: case NOOP_CMD: case ETRN_CMD: +#ifdef EXPERIMENTAL_WELLKNOWN + case WELLKNOWN_CMD: +#endif bsmtp_transaction_linecount = receive_linecount; break; @@ -2991,8 +2994,8 @@ switch (where) if (where == ACL_WHERE_AUTH) /* avoid logging auth creds */ { - uschar * s; - for (s = smtp_cmd_data; *s && !isspace(*s); ) s++; + uschar * s = smtp_cmd_data; + Uskip_nonwhite(&s); lim = s - smtp_cmd_data; /* atop after method */ } what = string_sprintf("%s %.*s", acl_wherenames[where], lim, place); @@ -5694,7 +5697,7 @@ while (done <= 0) case TOO_MANY_NONMAIL_CMD: s = smtp_cmd_buffer; - while (*s && !isspace(*s)) s++; + Uskip_nonwhite(&s); incomplete_transaction_log(US"too many non-mail commands"); log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many " "nonmail commands (last was \"%.*s\")", host_and_ident(FALSE), diff --git a/src/src/smtp_out.c b/src/src/smtp_out.c index 888a0006e..a78b15fbd 100644 --- a/src/src/smtp_out.c +++ b/src/src/smtp_out.c @@ -709,7 +709,7 @@ if (format) { p += 5; Uskip_whitespace(&p); - while (!isspace(*p)) p++; + Uskip_nonwhite(&p); Uskip_whitespace(&p); } while (*p) *p++ = '*'; diff --git a/src/src/string.c b/src/src/string.c index af187c199..379be2035 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -649,7 +649,7 @@ uschar * t, * yield; /* First find the end of the string */ if (*s != '\"') - while (*s && !isspace(*s)) s++; + Uskip_nonwhite(&s); else { s++; diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 64e8c711a..34612adea 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -5179,7 +5179,8 @@ for (uschar * s = exp; *s; /**/) return FALSE; } adding = *s++ == '+'; - for (end = s; *end && !isspace(*end); ) end++; + end = s; + Uskip_nonwhite(&end); item_parsed = tls_openssl_one_option_parse(string_copyn(s, end-s), &item); if (!item_parsed) { diff --git a/src/src/verify.c b/src/src/verify.c index 4f3bafb04..065deec41 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -3081,7 +3081,7 @@ if (iplookup) { filename = semicolon + 1; key = filename; - while (*key != 0 && !isspace(*key)) key++; + Uskip_nonwhite(&key); filename = string_copyn(filename, key - filename); Uskip_whitespace(&key); } diff --git a/src/src/xclient.c b/src/src/xclient.c index 2219cb027..fb9d95c5e 100644 --- a/src/src/xclient.c +++ b/src/src/xclient.c @@ -172,7 +172,7 @@ for (state = XCLIENT_SKIP_SPACES; *s; ) uschar * val; word = ++s; /* skip the = */ - while (*s && !isspace(*s)) s++; + Uskip_nonwhite(&s); len = s - word; DEBUG(D_transport) debug_printf(" XCLIENT: \tvalue %.*s\n", len, word); commit f168d6a2c93ee92d87744ce09c45a0ec666f889c Author: Jeremy Harris Date: Wed Mar 27 15:40:03 2024 +0000 typoes diff --git a/src/src/globals.c b/src/src/globals.c index e23188b34..2885caa23 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -387,7 +387,7 @@ BOOL mua_wrapper = FALSE; BOOL preserve_message_logs = FALSE; BOOL print_topbitchars = FALSE; BOOL prod_requires_admin = TRUE; -#if defined(SUPPORT_PROXY) || defined(SUPPORT_SOCKS) || defined(EXPERIMETAL_XCLIENT) +#if defined(SUPPORT_PROXY) || defined(SUPPORT_SOCKS) || defined(EXPERIMENTAL_XCLIENT) BOOL proxy_session = FALSE; #endif diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 70e4b95df..f65ab6603 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -2554,7 +2554,7 @@ if (!f.sender_host_unknown) if (smtp_batched_input) return TRUE; -#if defined(SUPPORT_PROXY) || defined(SUPPORT_SOCKS) || defined(EXPERIMETAL_XCLIENT) +#if defined(SUPPORT_PROXY) || defined(SUPPORT_SOCKS) || defined(EXPERIMENTAL_XCLIENT) proxy_session = FALSE; #endif commit 4e19eed51a7f777e1e18b60c636e0c9bfb82c23b Author: Jeremy Harris Date: Wed Mar 27 16:04:50 2024 +0000 Fix encoding for AUTH= on MAIL FROM diff --git a/src/src/auths/xtextencode.c b/src/src/auths/xtextencode.c index c08288831..75be18161 100644 --- a/src/src/auths/xtextencode.c +++ b/src/src/auths/xtextencode.c @@ -29,31 +29,14 @@ Returns: a pointer to the zero-terminated xtext string, which uschar * auth_xtextencode(uschar *clear, int len) { -uschar *code; -uschar *p = US clear; -uschar *pp; -int c = len; -int count = 1; -register int x; - -/* We have to do a prepass to find out how many specials there are, -in order to get the right amount of store. */ - -while (c -- > 0) - count += ((x = *p++) < 33 || x > 127 || x == '+' || x == '=')? 3 : 1; - -pp = code = store_get(count, clear); - -p = US clear; -c = len; -while (c-- > 0) - if ((x = *p++) < 33 || x > 127 || x == '+' || x == '=') - pp += sprintf(CS pp, "+%.02x", x); /* There's always room */ - else - *pp++ = x; - -*pp = 0; -return code; +gstring * g = NULL; +for(uschar ch; len > 0; len--, clear++) + g = (ch = *clear) < 33 || ch > 126 || ch == '+' || ch == '=' + ? string_fmt_append(g, "+%.02X", ch) + : string_catn(g, clear, 1); +gstring_release_unused(g); +return string_from_gstring(g); } + /* End of xtextencode.c */ commit a600ccb4a236549ed1a950d3fc783a1e2e9de2cb Author: Jeremy Harris Date: Fri Mar 29 17:37:05 2024 +0000 Logging: fix receive-time crash with recipients-logging. Broken-by: d12746bc15d8 diff --git a/src/src/log.c b/src/src/log.c index f85823520..1a77f2cdb 100644 --- a/src/src/log.c +++ b/src/src/log.c @@ -601,6 +601,22 @@ if (type == lt_debug) unlink(CS debuglog_name); +/* Append to a gstring, in no-extend (or rebuffer) mode +and without taint-checking. Thanks to the no-extend, the +gstring struct never needs reallocation; we ignore the +infore that the initial space allocation for the string +was exceeded, and just leave it potentially truncated. */ + +static void +string_fmt_append_noextend(gstring * g, char * fmt, ...) +{ +va_list ap; +va_start(ap, fmt); +/* can return NULL if it truncates; just ignore */ +(void) string_vformat(g, SVFMT_TAINT_NOCHK, fmt, ap); +va_end(ap); +} + /************************************************* * Add configuration file info to log line * *************************************************/ @@ -609,24 +625,28 @@ if (type == lt_debug) unlink(CS debuglog_name); once for real). Arguments: - ptr pointer to the end of the line we are building - flags log flags + g extensible-string referring to static buffer, + usable after return + flags log flags -Returns: updated pointer +Returns: nothing */ -static gstring * +static void log_config_info(gstring * g, int flags) { -g = string_cat(g, US"Exim configuration error"); +string_fmt_append_noextend(g, "Exim configuration error"); if (flags & (LOG_CONFIG_FOR & ~LOG_CONFIG)) - return string_cat(g, US" for "); - -if (flags & (LOG_CONFIG_IN & ~LOG_CONFIG)) - g = string_fmt_append(g, " in line %d of %s", config_lineno, config_filename); + string_fmt_append_noextend(g, " for "); +else + { + if (flags & (LOG_CONFIG_IN & ~LOG_CONFIG)) + string_fmt_append_noextend(g, " in line %d of %s", + config_lineno, config_filename); -return string_catn(g, US":\n ", 4); + string_fmt_append_noextend(g, ":\n "); + } } @@ -813,7 +833,7 @@ log_write(unsigned int selector, int flags, const char *format, ...) int paniclogfd; ssize_t written_len; gstring gs = { .size = LOG_BUFFER_SIZE-2, .ptr = 0, .s = log_buffer }; -gstring * g; +gstring * g = &gs; va_list ap; /* If panic_recurseflag is set, we have failed to open the panic log. This is @@ -823,7 +843,7 @@ original log line that caused the problem. Afterwards, expire. */ if (panic_recurseflag) { - uschar *extra = panic_save_buffer ? panic_save_buffer : US""; + uschar * extra = panic_save_buffer ? panic_save_buffer : US""; if (debug_file) debug_printf("%s%s", extra, log_buffer); if (log_stderr && log_stderr != debug_file) fprintf(log_stderr, "%s%s", extra, log_buffer); @@ -924,45 +944,43 @@ in one go so that it doesn't get split when multi-processing. */ DEBUG(D_any|D_v) { - int i; - - g = string_catn(&gs, US"LOG:", 4); + string_fmt_append_noextend(g, "LOG:"); /* Show the selector that was passed into the call. */ - for (i = 0; i < log_options_count; i++) + for (int i = 0; i < log_options_count; i++) { unsigned int bitnum = log_options[i].bit; if (bitnum < BITWORDSIZE && selector == BIT(bitnum)) - g = string_fmt_append(g, " %s", log_options[i].name); + string_fmt_append_noextend(g, " %s", log_options[i].name); } - g = string_fmt_append(g, "%s%s%s%s\n ", + string_fmt_append_noextend(g, "%s%s%s%s\n ", flags & LOG_MAIN ? " MAIN" : "", flags & LOG_PANIC ? " PANIC" : "", (flags & LOG_PANIC_DIE) == LOG_PANIC_DIE ? " DIE" : "", flags & LOG_REJECT ? " REJECT" : ""); - if (flags & LOG_CONFIG) g = log_config_info(g, flags); + if (flags & LOG_CONFIG) log_config_info(g, flags); /* We want to be able to log tainted info, but log_buffer is directly malloc'd. So use deliberately taint-nonchecking routines to build into it, trusting that we will never expand the results. */ va_start(ap, format); - i = g->ptr; if (!string_vformat(g, SVFMT_TAINT_NOCHK, format, ap)) { - g->ptr = i; - g = string_cat(g, US"**** log string overflowed log buffer ****"); + uschar * s = US"**** log string overflowed log buffer ****"; + gstring_trim(g, Ustrlen(s)); + string_fmt_append_noextend(g, "%s", s); } va_end(ap); debug_printf("%Y\n", g); - gs.size = LOG_BUFFER_SIZE-2; /* Having used the buffer for debug output, */ - gs.ptr = 0; /* reset it for the real use. */ - gs.s = log_buffer; + /* Having used the buffer for debug output, reset it for the real use. */ + + gstring_reset(g); } /* If no log file is specified, we are in a mess. */ @@ -986,33 +1004,32 @@ if (!write_rejectlog) flags &= ~LOG_REJECT; /* Create the main message in the log buffer. Do not include the message id when called by a utility. */ -g = string_fmt_append(&gs, "%s ", tod_stamp(tod_log)); +string_fmt_append_noextend(&gs, "%s ", tod_stamp(tod_log)); if (LOGGING(pid)) { if (!syslog_pid) pid_position[0] = g->ptr; /* remember begin … */ - g = string_fmt_append(g, "[%d] ", (int)getpid()); + string_fmt_append_noextend(g, "[%ld] ", (long)getpid()); if (!syslog_pid) pid_position[1] = g->ptr; /* … and end+1 of the PID */ } if (f.really_exim && message_id[0]) - g = string_fmt_append(g, "%s ", message_id); + string_fmt_append_noextend(g, "%s ", message_id); if (flags & LOG_CONFIG) - g = log_config_info(g, flags); + log_config_info(g, flags); va_start(ap, format); { - int i = g->ptr; - /* We want to be able to log tainted info, but log_buffer is directly malloc'd. So use deliberately taint-nonchecking routines to build into it, trusting that we will never expand the results. */ if (!string_vformat(g, SVFMT_TAINT_NOCHK, format, ap)) { - g->ptr = i; - g = string_cat(g, US"**** log string overflowed log buffer ****\n"); + uschar * s = US"**** log string overflowed log buffer ****\n"; + gstring_trim(g, Ustrlen(s)); + string_fmt_append_noextend(g, "%s", s); } } va_end(ap); @@ -1020,30 +1037,29 @@ va_end(ap); /* Add the raw, unrewritten, sender to the message if required. This is done this way because it kind of fits with LOG_RECIPIENTS. */ -if ( flags & LOG_SENDER - && g->ptr < LOG_BUFFER_SIZE - 10 - Ustrlen(raw_sender)) - g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " from <%s>", raw_sender); +if (flags & LOG_SENDER) + string_fmt_append_noextend(g, " from <%s>", raw_sender); /* Add list of recipients to the message if required; the raw list, before rewriting, was saved in raw_recipients. There may be none, if an ACL discarded them all. */ -if ( flags & LOG_RECIPIENTS - && g->ptr < LOG_BUFFER_SIZE - 6 - && raw_recipients_count > 0) +if (flags & LOG_RECIPIENTS && raw_recipients_count > 0) { - g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " for", NULL); + string_fmt_append_noextend(g, " for"); for (int i = 0; i < raw_recipients_count; i++) - { - const uschar * s = raw_recipients[i]; - if (LOG_BUFFER_SIZE - g->ptr < Ustrlen(s) + 3) break; - g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " %s", s); + if (!string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " %s", raw_recipients[i])) + { + uschar * s = US""; + gstring_trim(g, Ustrlen(s)); + string_fmt_append_noextend(g, "%s", s); + break; } } /* actual size, now we are placing the newline (and space for NUL) */ gs.size = LOG_BUFFER_SIZE; -g = string_catn(g, US"\n", 1); +string_fmt_append_noextend(g, "\n"); string_from_gstring(g); /* Handle loggable errors when running a utility, or when address testing. @@ -1117,8 +1133,7 @@ if ( flags & LOG_MAIN /* Failing to write to the log is disastrous */ - written_len = write_gstring_to_fd_buf(mainlogfd, g); - if (written_len != g->ptr) + if ((written_len = write_gstring_to_fd_buf(mainlogfd, g)) != g->ptr) { log_write_failed(US"main log", g->ptr, written_len); /* That function does not return */ @@ -1135,53 +1150,39 @@ if (flags & LOG_REJECT) { if (header_list && LOGGING(rejected_header)) { - gstring * g2; int i; if (recipients_count > 0) { /* List the sender */ - g2 = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, + string_fmt_append_noextend(g, "Envelope-from: <%s>\n", sender_address); - if (g2) g = g2; /* List up to 5 recipients */ - g2 = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, + string_fmt_append_noextend(g, "Envelope-to: <%s>\n", recipients_list[0].address); - if (g2) g = g2; for (i = 1; i < recipients_count && i < 5; i++) - { - g2 = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, + string_fmt_append_noextend(g, " <%s>\n", recipients_list[i].address); - if (g2) g = g2; - } if (i < recipients_count) - { - g2 = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " ...\n", NULL); - if (g2) g = g2; - } + string_fmt_append_noextend(g, " ...\n", NULL); } - /* A header with a NULL text is an unfilled in Received: header */ + /* A header with a NULL text is an unfilled-in Received: header */ for (header_line * h = header_list; h; h = h->next) if (h->text) - { - g2 = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, - "%c %s", h->type, h->text); - if (g2) - g = g2; - else /* Buffer is full; truncate */ - { - gstring_trim(g, 100); /* For message and separator */ + if (!string_fmt_append_f(g, SVFMT_TAINT_NOCHK, + "%c %s", h->type, h->text)) + { /* Buffer is full; truncate */ + gstring_trim(g, 100); /* space for message and separator */ gstring_trim_trailing(g, '\n'); - g = string_cat(g, US"\n*** truncated ***\n"); + string_fmt_append_noextend(g, "\n*** truncated ***\n"); break; } - } } /* Write to syslog or to a log file */ @@ -1232,8 +1233,7 @@ if (flags & LOG_REJECT) if (fstat(rejectlogfd, &statbuf) >= 0) rejectlog_inode = statbuf.st_ino; } - written_len = write_gstring_to_fd_buf(rejectlogfd, g); - if (written_len != g->ptr) + if ((written_len = write_gstring_to_fd_buf(rejectlogfd, g)) != g->ptr) { log_write_failed(US"reject log", g->ptr, written_len); /* That function does not return */ @@ -1267,8 +1267,7 @@ if (flags & LOG_PANIC) if (panic_save_buffer) (void) write(paniclogfd, panic_save_buffer, Ustrlen(panic_save_buffer)); - written_len = write_gstring_to_fd_buf(paniclogfd, g); - if (written_len != g->ptr) + if ((written_len = write_gstring_to_fd_buf(paniclogfd, g)) != g->ptr) { int save_errno = errno; write_syslog(LOG_CRIT, log_buffer); @@ -1562,3 +1561,5 @@ if (kill) unlink_log(lt_debug); /* End of log.c */ +/* vi: sw ai sw=2 +*/ diff --git a/src/src/string.c b/src/src/string.c index 379be2035..baa19f426 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -1333,7 +1333,7 @@ Arguments: ap variable-args pointer Flags: - SVFMT_EXTEND buffer can be created or exteded as needed + SVFMT_EXTEND buffer can be created or extended as needed SVFMT_REBUFFER buffer can be recopied to tainted mem as needed SVFMT_TAINT_NOCHK do not check inputs for taint commit ad64ad24d66f32af7af302b625f7a7be0ac6b988 Author: Jeremy Harris Date: Fri Mar 29 20:42:47 2024 +0000 inlining diff --git a/src/src/functions.h b/src/src/functions.h index d811c62a3..b2f948650 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -567,7 +567,6 @@ extern gstring *string_append_listele_n(gstring *, uschar, const uschar *, unsig extern gstring *string_append2_listele_n(gstring *, const uschar *, const uschar *, unsigned) WARN_UNUSED_RESULT; extern uschar *string_base62_32(unsigned long int); extern uschar *string_base62_64(unsigned long int); -extern gstring *string_cat (gstring *, const uschar * ) WARN_UNUSED_RESULT; extern gstring *string_catn(gstring *, const uschar *, int) WARN_UNUSED_RESULT; extern int string_compare_by_pointer(const void *, const void *); extern uschar *string_copy_dnsdomain(uschar *); @@ -1050,6 +1049,18 @@ if (g) store_release_above_3(g->s + (g->size = g->ptr + 1), file, line); } +/* plain string append to a growable-string */ + +static inline gstring * string_cat(gstring * g, const uschar * s) + WARN_UNUSED_RESULT; + +static inline gstring * +string_cat(gstring * g, const uschar * s) +{ +return string_catn(g, s, Ustrlen(s)); +} + + /* sprintf-append to a growable-string */ #define string_fmt_append(g, fmt, ...) \ @@ -1085,6 +1096,7 @@ g->s = s; } /* Append one gstring to another */ + static inline gstring * gstring_append(gstring * dest, gstring * item) { diff --git a/src/src/string.c b/src/src/string.c index baa19f426..cd76e1f1c 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -1234,13 +1234,6 @@ return g; } -gstring * -string_cat(gstring * g, const uschar * s) -{ -return string_catn(g, s, Ustrlen(s)); -} - - /************************************************* * Append strings to another string * commit 5e0ccec4397bd2d21cc1473681a62e7c2fb6afbe Author: Jeremy Harris Date: Thu Apr 4 15:53:53 2024 +0100 Retries: rework DB keys, and fix exinext IPv6. Bug 3086 diff --git a/src/src/deliver.c b/src/src/deliver.c index 70f44535f..0157743a1 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -2039,7 +2039,7 @@ static BOOL previously_transported(address_item *addr, BOOL testing) { uschar * s = string_sprintf("%s/%s", - addr->unique + (testflag(addr, af_homonym)? 3:0), addr->transport->name); + addr->unique + (testflag(addr, af_homonym) ? 3:0), addr->transport->name); if (tree_search(tree_nonrecipients, s) != 0) { @@ -3142,7 +3142,7 @@ while (addr_local) if (result == DEFER || testflag(addr2, af_lt_retry_exists)) { int flags = result == DEFER ? 0 : rf_delete; - uschar *retry_key = string_copy(tp->retry_use_local_part + uschar * retry_key = string_copy(tp->retry_use_local_part ? addr2->address_retry_key : addr2->domain_retry_key); *retry_key = 'T'; retry_add_item(addr2, retry_key, flags); @@ -7292,7 +7292,7 @@ while (addr_new) /* Loop until all addresses dealt with */ addr->unique = string_sprintf("%s:%s", addr->address, addr->parent->unique + - (testflag(addr->parent, af_homonym)? 3:0)); + (testflag(addr->parent, af_homonym) ? 3:0)); addr->address_retry_key = addr->domain_retry_key = string_sprintf("T:%s", addr->unique); @@ -7729,7 +7729,7 @@ while (addr_new) /* Loop until all addresses dealt with */ else if (testflag(addr, af_dr_retry_exists)) { - uschar *altkey = string_sprintf("%s:<%s>", addr->address_retry_key, + uschar * altkey = string_sprintf("%s:<%s>", addr->address_retry_key, sender_address); retry_add_item(addr, altkey, rf_delete); retry_add_item(addr, addr->address_retry_key, rf_delete); diff --git a/src/src/exim_dbutil.c b/src/src/exim_dbutil.c index 4d213773b..c868832b1 100644 --- a/src/src/exim_dbutil.c +++ b/src/src/exim_dbutil.c @@ -628,7 +628,7 @@ for (uschar * key = dbfn_scan(dbm, TRUE, &cursor); print_time(retry->first_failed)); printf("%s ", print_time(retry->last_try)); printf("%s %s\n", print_time(retry->next_try), - (retry->expired)? "*" : ""); + retry->expired ? "*" : ""); break; case type_wait: diff --git a/src/src/exinext.src b/src/src/exinext.src index 882ad5f60..5724812c4 100644 --- a/src/src/exinext.src +++ b/src/src/exinext.src @@ -195,45 +195,18 @@ perl - $exim_path "$eximmacdef" $argone $spool_directory $qualify_domain $config $printed = 1; if (/^\s*T:[^:\s]*:/) { + # We rely on non-space-containing strings, for parsing + ($key,$error,$error2,$text) = /^\s*T:(\S+)\s+(\S+)\s+(\S+)\s*(.*)$/; - # Parsing the keys is a nightmare because of IPv6. The design of the - # format for the keys is a complete shambles. All my fault (PH). But - # I don't want to change it just for this purpose. If they key - # contains more than 3 colons, we have an IPv6 address, because - # an IPv6 address must contain at least two colons. - - # Deal with IPv4 addresses (3 colons or fewer) - - if ($key !~ /:([^:]*?:){3}/) - { - ($host,$ip,$port,$msgid) = $key =~ - /^([^:]*):([^:]*)(?::([^:]*)(?::(\S*)|)|)/; - } - - # Deal with IPv6 addresses; sorting out the colons is a complete - # mess. We should be able to find the host name and IP address from - # further in the message. That seems the easiest escape plan here. We - # can use those to match the rest of the key. - - else - { - ($host,$ip) = $text =~ /host\s(\S+)\s\[([^]]+)\]/; - if (defined $host) - { - ($port,$msgid) = $key =~ - /^$host:$ip(?::([^:]*)(?::(\S*)|)|)/; - } - - # This will probably be wrong... - - else - { - ($host,$ip) = $key =~ /([^:]*):(.*)/; - } - } - - printf("Transport: %s [%s]", $host, $ip); + ($host,$ip,$port,$msgid) = $key =~ + /^([^:[]*|\[[^]]*\]) # host (could be an ip) + :([^:[]*|\[[^]]*\]) # ip + (?::(\d{1,5}))? # maybe port + (?::(\S{23}))? # maybe msgid + $/x; + + printf("Transport: %s %s", $host, $ip); print ":$port" if defined $port; print " $msgid" if defined $msgid; print " error $error: $text\n"; diff --git a/src/src/functions.h b/src/src/functions.h index b2f948650..9f6396a17 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -454,11 +454,14 @@ extern BOOL regex_match_and_setup(const pcre2_code *, const uschar *, int, in extern const pcre2_code *regex_compile(const uschar *, mcs_flags, uschar **, pcre2_compile_context *); extern const pcre2_code *regex_must_compile(const uschar *, mcs_flags, BOOL); -extern void retry_add_item(address_item *, uschar *, int); + +extern void retry_add_item(address_item *, const uschar *, int); extern BOOL retry_check_address(const uschar *, host_item *, uschar *, BOOL, - uschar **, uschar **); + const uschar **, const uschar **); extern retry_config *retry_find_config(const uschar *, const uschar *, int, int); -extern BOOL retry_ultimate_address_timeout(uschar *, const uschar *, +extern const uschar *retry_host_key_build(const host_item *, BOOL, + const uschar *); +extern BOOL retry_ultimate_address_timeout(const uschar *, const uschar *, dbdata_retry *, time_t); extern void retry_update(address_item **, address_item **, address_item **); extern const uschar *rewrite_address(const uschar *, BOOL, BOOL, rewrite_rule *, int); diff --git a/src/src/retry.c b/src/src/retry.c index 42393d424..6b977dd58 100644 --- a/src/src/retry.c +++ b/src/src/retry.c @@ -31,7 +31,7 @@ Returns: TRUE if the ultimate timeout has been reached */ BOOL -retry_ultimate_address_timeout(uschar *retry_key, const uschar *domain, +retry_ultimate_address_timeout(const uschar * retry_key, const uschar *domain, dbdata_retry *retry_record, time_t now) { BOOL address_timeout; @@ -75,6 +75,29 @@ return address_timeout; +const uschar * +retry_host_key_build(const host_item * host, BOOL incl_ip, + const uschar * portstring) +{ +const uschar * s = host->name; +gstring * g = string_is_ip_address(s, NULL) + ? string_fmt_append(NULL, "T:[%s]", s) /* wrap a name which is a bare ip */ + : string_fmt_append(NULL, "T:%s", s); + +s = host->address; +if (incl_ip) + g = Ustrchr(s, ':') + ? string_fmt_append(g, ":[%s]", s) /* wrap an ipv6 */ + : string_fmt_append(g, ":%s", s); + +if (portstring) + g = string_cat(g, portstring); + +gstring_release_unused(g); +return string_from_gstring(g); +} + + /************************************************* * Set status of a host+address item * *************************************************/ @@ -124,11 +147,12 @@ Returns: TRUE if the host has expired but is usable because BOOL retry_check_address(const uschar *domain, host_item *host, uschar *portstring, - BOOL include_ip_address, uschar **retry_host_key, uschar **retry_message_key) + BOOL include_ip_address, + const uschar **retry_host_key, const uschar **retry_message_key) { BOOL yield = FALSE; time_t now = time(NULL); -uschar * host_key, * message_key; +const uschar * host_key, * message_key; open_db dbblock, * dbm_file; tree_node * node; dbdata_retry * host_retry_record, * message_retry_record; @@ -143,14 +167,11 @@ if (host->status != hstatus_unknown) return FALSE; host->status = hstatus_usable; /* Generate the host key for the unusable tree and the retry database. Ensure -host names are lower cased (that's what %S does). */ - -host_key = include_ip_address - ? string_sprintf("T:%S:%s%s", host->name, host->address, portstring) - : string_sprintf("T:%S%s", host->name, portstring); - -/* Generate the message-specific key */ +host names are lower cased (that's what %S does). +Generate the message-specific key too. +Be sure to maintain lack-of-spaces in retry keys; exinext depends on it. */ +host_key = retry_host_key_build(host, include_ip_address, portstring); message_key = string_sprintf("%s:%s", host_key, message_id); /* Search the tree of unusable IP addresses. This is filled in when deliveries @@ -290,7 +311,7 @@ Returns: nothing */ void -retry_add_item(address_item *addr, uschar *key, int flags) +retry_add_item(address_item * addr, const uschar * key, int flags) { retry_item * rti = store_get(sizeof(retry_item), GET_UNTAINTED); host_item * host = addr->host_used; @@ -343,11 +364,11 @@ Returns: pointer to retry rule, or NULL */ retry_config * -retry_find_config(const uschar *key, const uschar *alternate, int basic_errno, +retry_find_config(const uschar * key, const uschar * alternate, int basic_errno, int more_errno) { -const uschar *colon = Ustrchr(key, ':'); -retry_config *yield; +const uschar * colon = Ustrchr(key, ':'); +retry_config * yield; /* If there's a colon in the key, there are two possibilities: @@ -355,7 +376,8 @@ retry_config *yield; hostname:ip+port - In this case, we copy the host name. + In this case, we copy the host name (which could be an [ip], including + being an [ipv6], and we drop the []). (2) This is a key for a pipe, file, or autoreply delivery, in the format @@ -369,6 +391,8 @@ retry_config *yield; if (colon) key = isalnum(*key) ? string_copyn(key, colon-key) /* the hostname */ + : *key == '[' + ? string_copyn(key+1, Ustrchr(key, ']')-1-key) /* the ip */ : Ustrrchr(key, ':') + 1; /* Take from the last colon */ /* Sort out the keys */ @@ -932,3 +956,5 @@ DEBUG(D_retry) debug_printf("end of retry processing\n"); } /* End of retry.c */ +/* vi: aw ai sw=2 +*/ diff --git a/src/src/string.c b/src/src/string.c index cd76e1f1c..a627611af 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -1698,7 +1698,7 @@ while (*fp) } /* If a width is not specified and the precision is specified, set - the width to the precision, or the string length if shorted. */ + the width to the precision, or the string length if shorter. */ else if (precision >= 0) width = precision < slen ? precision : slen; diff --git a/src/src/structs.h b/src/src/structs.h index b16a8a3f2..8aab54055 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -480,7 +480,7 @@ can be tried. */ typedef struct retry_item { struct retry_item *next; /* for chaining */ - uschar *key; /* string identifying host/address/message */ + const uschar *key; /* string identifying host/address/message */ int basic_errno; /* error code for this destination */ int more_errno; /* additional error information */ uschar *message; /* local error message */ diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index ed5994241..a5f99edaa 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -1306,7 +1306,7 @@ while (count-- > 0) if (testflag(addr, af_dr_retry_exists)) { - uschar *altkey = string_sprintf("%s:<%s>", addr->address_retry_key, + uschar * altkey = string_sprintf("%s:<%s>", addr->address_retry_key, sender_address); retry_add_item(addr, altkey, rf_delete); retry_add_item(addr, addr->address_retry_key, rf_delete); @@ -1322,7 +1322,7 @@ while (count-- > 0) else if (errno == ETIMEDOUT) { - uschar *message = string_sprintf("SMTP timeout after RCPT TO:<%s>", + uschar * message = string_sprintf("SMTP timeout after RCPT TO:<%s>", transport_rcpt_address(addr, sx->conn_args.tblock->rcpt_include_affixes)); set_errno_nohost(sx->first_addr, ETIMEDOUT, message, DEFER, FALSE, &sx->delivery_start); retry_add_item(addr, addr->address_retry_key, 0); @@ -5376,7 +5376,7 @@ retry_non_continued: BOOL host_is_expired = FALSE, message_defer = FALSE, some_deferred = FALSE; address_item * first_addr = NULL; uschar * interface = NULL; - uschar * retry_host_key = NULL, * retry_message_key = NULL; + const uschar * retry_host_key = NULL, * retry_message_key = NULL; uschar * serialize_key = NULL; /* Deal slightly better with a possible Linux kernel bug that results @@ -5873,9 +5873,7 @@ retry_non_continued: ob->expand_retry_include_ip_address, &incl_ip) != OK) incl_ip = TRUE; /* error; use most-specific retry record */ - retry_host_key = incl_ip - ? string_sprintf("T:%S:%s%s", host->name, host->address, pistring) - : string_sprintf("T:%S%s", host->name, pistring); + retry_host_key = retry_host_key_build(host, incl_ip, pistring); } /* If a delivery of another message over an existing SMTP connection @@ -5921,10 +5919,8 @@ retry_non_continued: ob->expand_retry_include_ip_address, &incl_ip) != OK) incl_ip = TRUE; /* error; use most-specific retry record */ - retry_message_key = incl_ip - ? string_sprintf("T:%S:%s%s:%s", host->name, host->address, pistring, - message_id) - : string_sprintf("T:%S%s:%s", host->name, pistring, message_id); + retry_message_key = string_sprintf("%s:%s", + retry_host_key_build(host, incl_ip, pistring), message_id); } retry_add_item(addrlist, retry_message_key, rf_message | rf_host | delete_flag); diff --git a/src/src/tree.c b/src/src/tree.c index eb54829db..2c703583e 100644 --- a/src/src/tree.c +++ b/src/src/tree.c @@ -65,6 +65,7 @@ if (!tree_insertnode(&tree_duplicates, node)) store_reset(rpoint); +#ifndef COMPILE_UTILITY /************************************************* * Add entry to unusable addresses tree * *************************************************/ @@ -76,12 +77,11 @@ Returns: nothing */ void -tree_add_unusable(const host_item *h) +tree_add_unusable(const host_item * h) { rmark rpoint = store_mark(); -tree_node *node; -uschar s[256]; -sprintf(CS s, "T:%.200s:%s", h->name, h->address); +tree_node * node; +const uschar * s = retry_host_key_build(h, TRUE, NULL); node = store_get(sizeof(tree_node) + Ustrlen(s), is_tainted(h->name) || is_tainted(h->address) ? GET_TAINTED : GET_UNTAINTED); Ustrcpy(node->name, s); @@ -89,7 +89,7 @@ node->data.val = h->why; if (h->status == hstatus_unusable_expired) node->data.val += 256; if (!tree_insertnode(&tree_unusable, node)) store_reset(rpoint); } - +#endif /************************************************* commit 9ae8613607b12257e1fe59c603119bc5d4cd3474 Author: Daniel Golle Date: Fri Apr 5 00:39:56 2024 +0100 Debug: use difftime() diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index a5f99edaa..ddd6ad7e1 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -945,7 +945,8 @@ if ( sx->early_pipe_active if (!(er = dbfn_read_enforce_length(dbm_file, ehlo_resp_key, sizeof(dbdata_ehlo_resp)))) debug_printf("no ehlo-resp record!\n"); else - debug_printf("ehlo-resp record is %d seconds old\n", time(NULL) - er->time_stamp); + debug_printf("ehlo-resp record is %.0f seconds old\n", + difftime(time(NULL), er->time_stamp)); } dbfn_delete(dbm_file, ehlo_resp_key); commit 86e5b23559e50f107620aefeb52f4a856f988421 Author: Jeremy Harris Date: Fri Apr 5 11:27:10 2024 +0100 SRS: fix encode for local-part with zero-length quoted element. Bug 3087 diff --git a/src/src/expand.c b/src/src/expand.c index 5ea4dc6d4..1d121756d 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -7117,7 +7117,7 @@ while (*s) gstring_release_unused(h); s = string_from_gstring(h); } - g = string_cat(g, s); + if (s) g = string_cat(g, s); } /* Assume that if the original local_part had quotes commit 8a5d7421f117c332c90b94bfd6f50ca71494d705 Author: Jeremy Harris Date: Wed Apr 10 17:10:05 2024 +0100 DMARC: Avoid using Resent-From: header. Bug 3029 diff --git a/src/src/receive.c b/src/src/receive.c index 9dbf45df9..9fae6ad60 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -1748,7 +1748,7 @@ uschar *user_msg, *log_msg; /* Working header pointers */ rmark reset_point; -header_line *next; +header_line * next; /* Flags for noting the existence of certain headers (only one left) */ @@ -1756,15 +1756,16 @@ BOOL date_header_exists = FALSE; /* Pointers to receive the addresses of headers whose contents we need. */ -header_line *from_header = NULL; -header_line *subject_header = NULL; -header_line *msgid_header = NULL; -header_line *received_header; +header_line * from_header = NULL; +#ifdef SUPPORT_DMARC +header_line * dmarc_from_header = NULL; +#endif +header_line * subject_header = NULL, * msgid_header = NULL, * received_header; BOOL msgid_header_newly_created = FALSE; /* Variables for use when building the Received: header. */ -uschar *timestamp; +uschar * timestamp; int tslen; /* Time of creation of message_id */ @@ -2446,6 +2447,9 @@ for (header_line * h = header_list->next; h; h = h->next) case htype_from: h->type = htype_from; +#ifdef SUPPORT_DMARC + if (!is_resent) dmarc_from_header = h; +#endif if (!resents_exist || is_resent) { from_header = h; @@ -3619,7 +3623,7 @@ else #endif /* WITH_CONTENT_SCAN */ #ifdef SUPPORT_DMARC - dmarc_store_data(from_header); + dmarc_store_data(dmarc_from_header); #endif #ifndef DISABLE_PRDR @@ -4622,3 +4626,5 @@ return yield; /* TRUE if more messages (SMTP only) */ } /* End of receive.c */ +/* vi: se aw ai sw=2 +*/ commit e4cdc6558fcf05e3923442ec7831adbe58c3e7a7 Author: Jeremy Harris Date: Thu Apr 11 11:53:24 2024 +0100 Use compressed form of ipv6 in $sender_host_address under -bh. Bug 3027 diff --git a/src/src/exim.c b/src/src/exim.c index 5c9e64021..040df2cd0 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -2339,9 +2339,9 @@ on the second character (the one after '-'), to save some effort. */ /* -bh: Host checking - an IP address must follow. */ case 'h': - if (!*argrest || Ustrcmp(argrest, "c") == 0) + if ( (!*argrest || Ustrcmp(argrest, "c") == 0) + && ++i < argc) { - if (++i >= argc) { badarg = TRUE; break; } sender_host_address = string_copy_taint( exim_str_fail_toolong(argv[i], EXIM_IPADDR_MAX, "-bh"), GET_TAINTED); @@ -2349,7 +2349,8 @@ on the second character (the one after '-'), to save some effort. */ f.host_checking_callout = *argrest == 'c'; message_logs = FALSE; } - else badarg = TRUE; + else + badarg = TRUE; break; /* -bi: This option is used by sendmail to initialize *the* alias file, @@ -5443,11 +5444,14 @@ if (host_checking) } /* In case the given address is a non-canonical IPv6 address, canonicalize - it. The code works for both IPv4 and IPv6, as it happens. */ + it. Use the compressed form for IPv6. */ size = host_aton(sender_host_address, x); sender_host_address = store_get(48, GET_UNTAINTED); /* large enough for full IPv6 */ - (void)host_nmtoa(size, x, -1, sender_host_address, ':'); + if (size == 1) + (void) host_nmtoa(size, x, -1, sender_host_address, ':'); + else + (void) ipv6_nmtoa(x, sender_host_address); /* Now set up for testing */ @@ -6189,3 +6193,5 @@ return 0; /* To stop compiler warning */ /* End of exim.c */ +/* vi: aw ai sw=2 +*/ diff --git a/src/src/functions.h b/src/src/functions.h index 9f6396a17..f0b600ccc 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -311,7 +311,7 @@ extern BOOL host_is_tls_on_connect_port(int); extern int host_item_get_port(host_item *); extern void host_mask(int, int *, int); extern int host_name_lookup(void); -extern int host_nmtoa(int, int *, int, uschar *, int); +extern int host_nmtoa(int, const int *, int, uschar *, int); extern uschar *host_ntoa(int, const void *, uschar *, int *); extern int host_scan_for_local_hosts(host_item *, host_item **, BOOL *); diff --git a/src/src/host.c b/src/src/host.c index 08e946548..381f2a5fc 100644 --- a/src/src/host.c +++ b/src/src/host.c @@ -1061,19 +1061,15 @@ Returns: the number of characters placed in buffer, not counting */ int -host_nmtoa(int count, int *binary, int mask, uschar *buffer, int sep) +host_nmtoa(int count, const int * binary, int mask, uschar * buffer, int sep) { -int j; -uschar *tt = buffer; +uschar * tt = buffer; if (count == 1) - { - j = binary[0]; - for (int i = 24; i >= 0; i -= 8) + for (int j = binary[0], i = 24; i >= 0; i -= 8) tt += sprintf(CS tt, "%d.", (j >> i) & 255); - } else - for (int i = 0; i < 4; i++) + for (int j, i = 0; i < 4; i++) { j = binary[i]; tt += sprintf(CS tt, "%04x%c%04x%c", (j >> 16) & 0xffff, sep, j & 0xffff, sep); commit cfc6689a7a29220a44457961283773fdbfdce3ef Author: Jeremy Harris Date: Sun Apr 14 10:04:37 2024 +0100 tidying diff --git a/src/src/filter.c b/src/src/filter.c index 18567f17c..8f29eda3e 100644 --- a/src/src/filter.c +++ b/src/src/filter.c @@ -71,35 +71,6 @@ enum { had_neither, had_else, had_elif, had_endif }; static BOOL read_command_list(const uschar **, filter_cmd ***, BOOL); -/* The string arguments for the mail command. The header line ones (that are -permitted to include \n followed by white space) first, and then the body text -one (it can have \n anywhere). Then the file names and once_repeat, which may -not contain \n. */ - -static const char *mailargs[] = { /* "to" must be first, and */ - "to", /* "cc" and "bcc" must follow */ - "cc", - "bcc", - "from", - "reply_to", - "subject", - "extra_headers", /* miscellaneous added header lines */ - "text", - "file", - "log", - "once", - "once_repeat" -}; - -/* The count of string arguments */ - -#define MAILARGS_STRING_COUNT (nelem(mailargs)) - -/* The count of string arguments that are actually passed over as strings -(once_repeat is converted to an int). */ - -#define mailargs_string_passed (MAILARGS_STRING_COUNT - 1) - /* This defines the offsets for the arguments; first the string ones, and then the non-string ones. The order must be as above. */ @@ -120,21 +91,50 @@ enum { mailarg_index_to, mailargs_total /* total number of arguments */ }; +/* The string arguments for the mail command. The header line ones (that are +permitted to include \n followed by white space) first, and then the body text +one (it can have \n anywhere). Then the file names and once_repeat, which may +not contain \n. */ + +static const char *mailargs[] = { /* "to" must be first, and */ + [mailarg_index_to] = "to", /* "cc" and "bcc" must follow */ + [mailarg_index_cc] = "cc", + [mailarg_index_bcc] = "bcc", + [mailarg_index_from] = "from", + [mailarg_index_reply_to] = "reply_to", + [mailarg_index_subject] = "subject", + [mailarg_index_headers] = "extra_headers", /* misc added header lines */ + [mailarg_index_text] = "text", + [mailarg_index_file] = "file", + [mailarg_index_log] = "log", + [mailarg_index_once] = "once", + [mailarg_index_once_repeat] = "once_repeat" +}; + +/* The count of string arguments */ + +#define MAILARGS_STRING_COUNT (nelem(mailargs)) + +/* The count of string arguments that are actually passed over as strings +(once_repeat is converted to an int). */ + +#define mailargs_string_passed (MAILARGS_STRING_COUNT - 1) + /* Offsets in the data structure for the string arguments (note that once_repeat isn't a string argument at this point.) */ -static int reply_offsets[] = { /* must be in same order as above */ - offsetof(reply_item, to), - offsetof(reply_item, cc), - offsetof(reply_item, bcc), - offsetof(reply_item, from), - offsetof(reply_item, reply_to), - offsetof(reply_item, subject), - offsetof(reply_item, headers), - offsetof(reply_item, text), - offsetof(reply_item, file), - offsetof(reply_item, logfile), - offsetof(reply_item, oncelog), +static int reply_offsets[] = { + [mailarg_index_to] = offsetof(reply_item, to), + [mailarg_index_cc] = offsetof(reply_item, cc), + [mailarg_index_bcc] = offsetof(reply_item, bcc), + [mailarg_index_from] = offsetof(reply_item, from), + [mailarg_index_reply_to] = offsetof(reply_item, reply_to), + [mailarg_index_subject] = offsetof(reply_item, subject), + [mailarg_index_headers] = offsetof(reply_item, headers), + [mailarg_index_text] = offsetof(reply_item, text), + [mailarg_index_file] = offsetof(reply_item, file), + [mailarg_index_log] = offsetof(reply_item, logfile), + [mailarg_index_once] = offsetof(reply_item, oncelog), }; /* Condition identities and names, with negated versions for some @@ -147,20 +147,48 @@ enum { cond_and, cond_or, cond_personal, cond_begins, cond_BEGINS, cond_manualthaw, cond_foranyaddress }; static const char *cond_names[] = { - "and", "or", "personal", - "begins", "BEGINS", "ends", "ENDS", - "is", "IS", "matches", "MATCHES", "contains", - "CONTAINS", "delivered", "above", "below", "error_message", - "first_delivery", "manually_thawed", "foranyaddress" }; + [cond_and] = "and", + [cond_or] = "or", + [cond_personal] = "personal", + [cond_begins] = "begins", + [cond_BEGINS] = "BEGINS", + [cond_ends] = "ends", + [cond_ENDS] = "ENDS", + [cond_is] = "is", + [cond_IS] = "IS", + [cond_matches] = "matches", + [cond_MATCHES] = "MATCHES", + [cond_contains] = "contains", + [cond_CONTAINS] = "CONTAINS", + [cond_delivered] = "delivered", + [cond_above] = "above", + [cond_below] = "below", + [cond_errormsg] = "error_message", + [cond_firsttime] = "first_delivery", + [cond_manualthaw] = "manually_thawed", + [cond_foranyaddress] = "foranyaddress" }; static const char *cond_not_names[] = { - "", "", "not personal", - "does not begin", "does not BEGIN", - "does not end", "does not END", - "is not", "IS not", "does not match", - "does not MATCH", "does not contain", "does not CONTAIN", - "not delivered", "not above", "not below", "not error_message", - "not first_delivery", "not manually_thawed", "not foranyaddress" }; + [cond_and] = "", + [cond_or] = "", + [cond_personal] = "not personal", + [cond_begins] = "does not begin", + [cond_BEGINS] = "does not BEGIN", + [cond_ends] = "does not end", + [cond_ENDS] = "does not END", + [cond_is] = "is not", + [cond_IS] = "IS not", + [cond_matches] = "does not match", + [cond_MATCHES] = "does not MATCH", + [cond_contains] = "does not contain", + [cond_CONTAINS] = "does not CONTAIN", + [cond_delivered] = "not delivered", + [cond_above] = "not above", + [cond_below] = "not below", + [cond_errormsg] = "not error_message", + [cond_firsttime] = "not first_delivery", + [cond_manualthaw] = "not manually_thawed", + [cond_foranyaddress] = "not foranyaddress" }; /* Tables of binary condition words and their corresponding types. Not easy to amalgamate with the above because of the different variants. */ @@ -196,34 +224,34 @@ static int cond_types[] = { cond_BEGINS, cond_BEGINS, cond_CONTAINS, /* Command identities */ -enum { add_command, defer_command, deliver_command, elif_command, else_command, - endif_command, finish_command, fail_command, freeze_command, - headers_command, if_command, logfile_command, logwrite_command, - mail_command, noerror_command, pipe_command, save_command, seen_command, - testprint_command, unseen_command, vacation_command }; +enum { ADD_COMMAND, DEFER_COMMAND, DELIVER_COMMAND, ELIF_COMMAND, ELSE_COMMAND, + ENDIF_COMMAND, FINISH_COMMAND, FAIL_COMMAND, FREEZE_COMMAND, + HEADERS_COMMAND, IF_COMMAND, LOGFILE_COMMAND, LOGWRITE_COMMAND, + MAIL_COMMAND, NOERROR_COMMAND, PIPE_COMMAND, SAVE_COMMAND, SEEN_COMMAND, + TESTPRINT_COMMAND, UNSEEN_COMMAND, VACATION_COMMAND }; static const char * command_list[] = { - [add_command] = "add", - [defer_command] = "defer", - [deliver_command] = "deliver", - [elif_command] = "elif", - [else_command] = "else", - [endif_command] = "endif", - [finish_command] = "finish", - [fail_command] = "fail", - [freeze_command] = "freeze", - [headers_command] = "headers", - [if_command] = "if", - [logfile_command] = "logfile", - [logwrite_command] = "logwrite", - [mail_command] = "mail", - [noerror_command] = "noerror", - [pipe_command] = "pipe", - [save_command] = "save", - [seen_command] = "seen", - [testprint_command] = "testprint", - [unseen_command] = "unseen", - [vacation_command] = "vacation" + [ADD_COMMAND] = "add", + [DEFER_COMMAND] = "defer", + [DELIVER_COMMAND] = "deliver", + [ELIF_COMMAND] = "elif", + [ELSE_COMMAND] = "else", + [ENDIF_COMMAND] = "endif", + [FINISH_COMMAND] = "finish", + [FAIL_COMMAND] = "fail", + [FREEZE_COMMAND] = "freeze", + [HEADERS_COMMAND] = "headers", + [IF_COMMAND] = "if", + [LOGFILE_COMMAND] = "logfile", + [LOGWRITE_COMMAND] = "logwrite", + [MAIL_COMMAND] = "mail", + [NOERROR_COMMAND] = "noerror", + [PIPE_COMMAND] = "pipe", + [SAVE_COMMAND] = "save", + [SEEN_COMMAND] = "seen", + [TESTPRINT_COMMAND] = "testprint", + [UNSEEN_COMMAND] = "unseen", + [VACATION_COMMAND] = "vacation" }; static int command_list_count = nelem(command_list); @@ -232,27 +260,27 @@ static int command_list_count = nelem(command_list); If the top bit is set, it means that the default for the command is "seen". */ static uschar command_exparg_count[] = { - [add_command] = 2, - [defer_command] = 1, - [deliver_command] = 128+2, - [elif_command] = 0, - [else_command] = 0, - [endif_command] = 0, - [finish_command] = 0, - [fail_command] = 1, - [freeze_command] = 1, - [headers_command] = 1, - [if_command] = 0, - [logfile_command] = 1, - [logwrite_command] = 1, - [mail_command] = MAILARGS_STRING_COUNT, - [noerror_command] = 0, - [pipe_command] = 128+0, - [save_command] = 128+1, - [seen_command] = 0, - [testprint_command] = 1, - [unseen_command] = 0, - [vacation_command] = MAILARGS_STRING_COUNT + [ADD_COMMAND] = 2, + [DEFER_COMMAND] = 1, + [DELIVER_COMMAND] = 128+2, + [ELIF_COMMAND] = 0, + [ELSE_COMMAND] = 0, + [ENDIF_COMMAND] = 0, + [FINISH_COMMAND] = 0, + [FAIL_COMMAND] = 1, + [FREEZE_COMMAND] = 1, + [HEADERS_COMMAND] = 1, + [IF_COMMAND] = 0, + [LOGFILE_COMMAND] = 1, + [LOGWRITE_COMMAND] = 1, + [MAIL_COMMAND] = MAILARGS_STRING_COUNT, + [NOERROR_COMMAND] = 0, + [PIPE_COMMAND] = 128+0, + [SAVE_COMMAND] = 128+1, + [SEEN_COMMAND] = 0, + [TESTPRINT_COMMAND] = 1, + [UNSEEN_COMMAND] = 0, + [VACATION_COMMAND] = MAILARGS_STRING_COUNT }; @@ -795,8 +823,8 @@ switch(c->type) case cond_errormsg: case cond_firsttime: case cond_manualthaw: - debug_printf("%s", name); - break; + debug_printf("%s", name); + break; case cond_is: case cond_IS: @@ -810,31 +838,31 @@ switch(c->type) case cond_ENDS: case cond_above: case cond_below: - debug_printf("%s %s %s", c->left.u, name, c->right.u); - break; + debug_printf("%s %s %s", c->left.u, name, c->right.u); + break; case cond_and: - if (!c->testfor) debug_printf("not ("); - print_condition(c->left.c, FALSE); - debug_printf(" %s ", cond_names[c->type]); - print_condition(c->right.c, FALSE); - if (!c->testfor) debug_printf(")"); - break; + if (!c->testfor) debug_printf("not ("); + print_condition(c->left.c, FALSE); + debug_printf(" %s ", cond_names[c->type]); + print_condition(c->right.c, FALSE); + if (!c->testfor) debug_printf(")"); + break; case cond_or: - if (!c->testfor) debug_printf("not ("); - else if (!toplevel) debug_printf("("); - print_condition(c->left.c, FALSE); - debug_printf(" %s ", cond_names[c->type]); - print_condition(c->right.c, FALSE); - if (!toplevel || !c->testfor) debug_printf(")"); - break; + if (!c->testfor) debug_printf("not ("); + else if (!toplevel) debug_printf("("); + print_condition(c->left.c, FALSE); + debug_printf(" %s ", cond_names[c->type]); + print_condition(c->right.c, FALSE); + if (!toplevel || !c->testfor) debug_printf(")"); + break; case cond_foranyaddress: - debug_printf("%s %s (", name, c->left.u); - print_condition(c->right.c, FALSE); - debug_printf(")"); - break; + debug_printf("%s %s (", name, c->left.u); + print_condition(c->right.c, FALSE); + debug_printf(")"); + break; } } @@ -904,15 +932,15 @@ switch (command) stored in the second argument slot. Neither may be preceded by seen, unseen or noerror. */ - case add_command: - case headers_command: - if (seen_force || noerror_force) - { - *error_pointer = string_sprintf("\"seen\", \"unseen\", or \"noerror\" " - "found before an \"%s\" command near line %d", - command_list[command], line_number); - yield = FALSE; - } + case ADD_COMMAND: + case HEADERS_COMMAND: + if (seen_force || noerror_force) + { + *error_pointer = string_sprintf("\"seen\", \"unseen\", or \"noerror\" " + "found before an \"%s\" command near line %d", + command_list[command], line_number); + yield = FALSE; + } /* Fall through */ /* Logwrite, logfile, pipe, and testprint all take a single argument, save @@ -920,303 +948,303 @@ switch (command) have "errors_to
" in a system filter, or in a user filter if the address is the current one. */ - case deliver_command: - case logfile_command: - case logwrite_command: - case pipe_command: - case save_command: - case testprint_command: + case DELIVER_COMMAND: + case LOGFILE_COMMAND: + case LOGWRITE_COMMAND: + case PIPE_COMMAND: + case SAVE_COMMAND: + case TESTPRINT_COMMAND: - ptr = nextitem(ptr, buffer, sizeof(buffer), FALSE); - if (!*buffer) - *error_pointer = string_sprintf("\"%s\" requires an argument " - "near line %d of filter file", command_list[command], line_number); + ptr = nextitem(ptr, buffer, sizeof(buffer), FALSE); + if (!*buffer) + *error_pointer = string_sprintf("\"%s\" requires an argument " + "near line %d of filter file", command_list[command], line_number); - if (*error_pointer) yield = FALSE; else - { - union argtypes argument, second_argument; + if (*error_pointer) yield = FALSE; else + { + union argtypes argument, second_argument; - argument.u = second_argument.u = NULL; + argument.u = second_argument.u = NULL; - if (command == add_command) - { - argument.u = string_copy(buffer); - ptr = nextitem(ptr, buffer, sizeof(buffer), FALSE); - if (!*buffer || Ustrcmp(buffer, "to") != 0) - *error_pointer = string_sprintf("\"to\" expected in \"add\" command " - "near line %d of filter file", line_number); - else - { - ptr = nextitem(ptr, buffer, sizeof(buffer), FALSE); - if (!*buffer) - *error_pointer = string_sprintf("value missing after \"to\" " - "near line %d of filter file", line_number); - else second_argument.u = string_copy(buffer); - } - } + if (command == ADD_COMMAND) + { + argument.u = string_copy(buffer); + ptr = nextitem(ptr, buffer, sizeof(buffer), FALSE); + if (!*buffer || Ustrcmp(buffer, "to") != 0) + *error_pointer = string_sprintf("\"to\" expected in \"add\" command " + "near line %d of filter file", line_number); + else + { + ptr = nextitem(ptr, buffer, sizeof(buffer), FALSE); + if (!*buffer) + *error_pointer = string_sprintf("value missing after \"to\" " + "near line %d of filter file", line_number); + else second_argument.u = string_copy(buffer); + } + } - else if (command == headers_command) - { - if (Ustrcmp(buffer, "add") == 0) - second_argument.b = TRUE; - else - if (Ustrcmp(buffer, "remove") == 0) second_argument.b = FALSE; - else - if (Ustrcmp(buffer, "charset") == 0) - second_argument.b = TRUE_UNSET; - else - { - *error_pointer = string_sprintf("\"add\", \"remove\", or \"charset\" " - "expected after \"headers\" near line %d of filter file", - line_number); - yield = FALSE; - } + else if (command == HEADERS_COMMAND) + { + if (Ustrcmp(buffer, "add") == 0) + second_argument.b = TRUE; + else + if (Ustrcmp(buffer, "remove") == 0) second_argument.b = FALSE; + else + if (Ustrcmp(buffer, "charset") == 0) + second_argument.b = TRUE_UNSET; + else + { + *error_pointer = string_sprintf("\"add\", \"remove\", or \"charset\" " + "expected after \"headers\" near line %d of filter file", + line_number); + yield = FALSE; + } - if (!f.system_filtering && second_argument.b != TRUE_UNSET) - { - *error_pointer = string_sprintf("header addition and removal is " - "available only in system filters: near line %d of filter file", - line_number); - yield = FALSE; - break; - } + if (!f.system_filtering && second_argument.b != TRUE_UNSET) + { + *error_pointer = string_sprintf("header addition and removal is " + "available only in system filters: near line %d of filter file", + line_number); + yield = FALSE; + break; + } - if (yield) - { - ptr = nextitem(ptr, buffer, sizeof(buffer), FALSE); - if (!*buffer) - *error_pointer = string_sprintf("value missing after \"add\", " - "\"remove\", or \"charset\" near line %d of filter file", - line_number); - else argument.u = string_copy(buffer); - } - } + if (yield) + { + ptr = nextitem(ptr, buffer, sizeof(buffer), FALSE); + if (!*buffer) + *error_pointer = string_sprintf("value missing after \"add\", " + "\"remove\", or \"charset\" near line %d of filter file", + line_number); + else argument.u = string_copy(buffer); + } + } - /* The argument for the logwrite command must end in a newline, and the save - and logfile commands can have an optional mode argument. The deliver - command can have an optional "errors_to
" for a system filter, - or for a user filter if the address is the user's address. Accept the - syntax here - the check is later. */ + /* The argument for the logwrite command must end in a newline, and the save + and logfile commands can have an optional mode argument. The deliver + command can have an optional "errors_to
" for a system filter, + or for a user filter if the address is the user's address. Accept the + syntax here - the check is later. */ - else - { - if (command == logwrite_command) - { - int len = Ustrlen(buffer); - if (len == 0 || buffer[len-1] != '\n') Ustrcat(buffer, US"\n"); - } + else + { + if (command == LOGWRITE_COMMAND) + { + int len = Ustrlen(buffer); + if (len == 0 || buffer[len-1] != '\n') Ustrcat(buffer, US"\n"); + } - argument.u = string_copy(buffer); + argument.u = string_copy(buffer); - if (command == save_command || command == logfile_command) - { - if (isdigit(*ptr)) - { - ptr = nextword(ptr, buffer, sizeof(buffer), FALSE); - second_argument.i = (int)Ustrtol(buffer, NULL, 8); - } - else second_argument.i = -1; - } + if (command == SAVE_COMMAND || command == LOGFILE_COMMAND) + { + if (isdigit(*ptr)) + { + ptr = nextword(ptr, buffer, sizeof(buffer), FALSE); + second_argument.i = (int)Ustrtol(buffer, NULL, 8); + } + else second_argument.i = -1; + } - else if (command == deliver_command) - { - const uschar *save_ptr = ptr; - ptr = nextword(ptr, buffer, sizeof(buffer), FALSE); - if (Ustrcmp(buffer, "errors_to") == 0) - { - ptr = nextword(ptr, buffer, sizeof(buffer), FALSE); - second_argument.u = string_copy(buffer); - } - else ptr = save_ptr; - } - } + else if (command == DELIVER_COMMAND) + { + const uschar *save_ptr = ptr; + ptr = nextword(ptr, buffer, sizeof(buffer), FALSE); + if (Ustrcmp(buffer, "errors_to") == 0) + { + ptr = nextword(ptr, buffer, sizeof(buffer), FALSE); + second_argument.u = string_copy(buffer); + } + else ptr = save_ptr; + } + } - /* Set up the command block. Seen defaults TRUE for delivery commands, - FALSE for logging commands, and it doesn't matter for testprint, as - that doesn't change the "delivered" status. */ + /* Set up the command block. Seen defaults TRUE for delivery commands, + FALSE for logging commands, and it doesn't matter for testprint, as + that doesn't change the "delivered" status. */ - if (*error_pointer) yield = FALSE; - else - { - new = store_get(sizeof(filter_cmd) + sizeof(union argtypes), GET_UNTAINTED); - new->next = NULL; - **lastcmdptr = new; - *lastcmdptr = &(new->next); - new->command = command; - new->seen = seen_force? seen_value : command_exparg_count[command] >= 128; - new->noerror = noerror_force; - new->args[0] = argument; - new->args[1] = second_argument; + if (*error_pointer) yield = FALSE; + else + { + new = store_get(sizeof(filter_cmd) + sizeof(union argtypes), GET_UNTAINTED); + new->next = NULL; + **lastcmdptr = new; + *lastcmdptr = &(new->next); + new->command = command; + new->seen = seen_force? seen_value : command_exparg_count[command] >= 128; + new->noerror = noerror_force; + new->args[0] = argument; + new->args[1] = second_argument; + } } - } - break; + break; /* Elif, else and endif just set a flag if expected. */ - case elif_command: - case else_command: - case endif_command: - if (seen_force || noerror_force) - { - *error_pointer = string_sprintf("\"seen\", \"unseen\", or \"noerror\" " - "near line %d is not followed by a command", line_number); - yield = FALSE; - } + case ELIF_COMMAND: + case ELSE_COMMAND: + case ENDIF_COMMAND: + if (seen_force || noerror_force) + { + *error_pointer = string_sprintf("\"seen\", \"unseen\", or \"noerror\" " + "near line %d is not followed by a command", line_number); + yield = FALSE; + } - if (expect_endif > 0) - had_else_endif = (command == elif_command)? had_elif : - (command == else_command)? had_else : had_endif; - else - { - *error_pointer = string_sprintf("unexpected \"%s\" command near " - "line %d of filter file", buffer, line_number); - yield = FALSE; - } - break; + if (expect_endif > 0) + had_else_endif = (command == ELIF_COMMAND)? had_elif : + (command == ELSE_COMMAND)? had_else : had_endif; + else + { + *error_pointer = string_sprintf("unexpected \"%s\" command near " + "line %d of filter file", buffer, line_number); + yield = FALSE; + } + break; /* Defer, freeze, and fail are available only if permitted. */ - case defer_command: - cmd_bit = RDO_DEFER; - goto DEFER_FREEZE_FAIL; + case DEFER_COMMAND: + cmd_bit = RDO_DEFER; + goto DEFER_FREEZE_FAIL; - case fail_command: - cmd_bit = RDO_FAIL; - goto DEFER_FREEZE_FAIL; + case FAIL_COMMAND: + cmd_bit = RDO_FAIL; + goto DEFER_FREEZE_FAIL; - case freeze_command: - cmd_bit = RDO_FREEZE; + case FREEZE_COMMAND: + cmd_bit = RDO_FREEZE; DEFER_FREEZE_FAIL: - if ((filter_options & cmd_bit) == 0) - { - *error_pointer = string_sprintf("filtering command \"%s\" is disabled: " - "near line %d of filter file", buffer, line_number); - yield = FALSE; - break; - } + if ((filter_options & cmd_bit) == 0) + { + *error_pointer = string_sprintf("filtering command \"%s\" is disabled: " + "near line %d of filter file", buffer, line_number); + yield = FALSE; + break; + } - /* A text message can be provided after the "text" keyword, or - as a string in quotes. */ + /* A text message can be provided after the "text" keyword, or + as a string in quotes. */ - saveptr = ptr; - ptr = nextitem(ptr, buffer, sizeof(buffer), FALSE); - if (*saveptr != '\"' && (!*buffer || Ustrcmp(buffer, "text") != 0)) - { - ptr = saveptr; - fmsg = US""; - } - else - { - if (*saveptr != '\"') - ptr = nextitem(ptr, buffer, sizeof(buffer), FALSE); - fmsg = string_copy(buffer); - } + saveptr = ptr; + ptr = nextitem(ptr, buffer, sizeof(buffer), FALSE); + if (*saveptr != '\"' && (!*buffer || Ustrcmp(buffer, "text") != 0)) + { + ptr = saveptr; + fmsg = US""; + } + else + { + if (*saveptr != '\"') + ptr = nextitem(ptr, buffer, sizeof(buffer), FALSE); + fmsg = string_copy(buffer); + } - /* Drop through and treat as "finish", but never set "seen". */ + /* Drop through and treat as "finish", but never set "seen". */ - seen_value = FALSE; + seen_value = FALSE; - /* Finish has no arguments; fmsg defaults to NULL */ + /* Finish has no arguments; fmsg defaults to NULL */ - case finish_command: - new = store_get(sizeof(filter_cmd), GET_UNTAINTED); - new->next = NULL; - **lastcmdptr = new; - *lastcmdptr = &(new->next); - new->command = command; - new->seen = seen_force ? seen_value : FALSE; - new->args[0].u = fmsg; - break; + case FINISH_COMMAND: + new = store_get(sizeof(filter_cmd), GET_UNTAINTED); + new->next = NULL; + **lastcmdptr = new; + *lastcmdptr = &(new->next); + new->command = command; + new->seen = seen_force ? seen_value : FALSE; + new->args[0].u = fmsg; + break; /* Seen, unseen, and noerror are not allowed before if, which takes a condition argument and then and else sub-commands. */ - case if_command: - if (seen_force || noerror_force) - { - *error_pointer = string_sprintf("\"seen\", \"unseen\", or \"noerror\" " - "found before an \"if\" command near line %d", - line_number); - yield = FALSE; - } + case IF_COMMAND: + if (seen_force || noerror_force) + { + *error_pointer = string_sprintf("\"seen\", \"unseen\", or \"noerror\" " + "found before an \"if\" command near line %d", + line_number); + yield = FALSE; + } - /* Set up the command block for if */ + /* Set up the command block for if */ - new = store_get(sizeof(filter_cmd) + 4 * sizeof(union argtypes), GET_UNTAINTED); - new->next = NULL; - **lastcmdptr = new; - *lastcmdptr = &new->next; - new->command = command; - new->seen = FALSE; - new->args[0].u = NULL; - new->args[1].u = new->args[2].u = NULL; - new->args[3].u = ptr; + new = store_get(sizeof(filter_cmd) + 4 * sizeof(union argtypes), GET_UNTAINTED); + new->next = NULL; + **lastcmdptr = new; + *lastcmdptr = &new->next; + new->command = command; + new->seen = FALSE; + new->args[0].u = NULL; + new->args[1].u = new->args[2].u = NULL; + new->args[3].u = ptr; - /* Read the condition */ + /* Read the condition */ - ptr = read_condition(ptr, &new->args[0].c, TRUE); - if (*error_pointer) { yield = FALSE; break; } + ptr = read_condition(ptr, &new->args[0].c, TRUE); + if (*error_pointer) { yield = FALSE; break; } - /* Read the commands to be obeyed if the condition is true */ + /* Read the commands to be obeyed if the condition is true */ - newlastcmdptr = &(new->args[1].f); - if (!read_command_list(&ptr, &newlastcmdptr, TRUE)) yield = FALSE; + newlastcmdptr = &(new->args[1].f); + if (!read_command_list(&ptr, &newlastcmdptr, TRUE)) yield = FALSE; - /* If commands were successfully read, handle the various possible - terminators. There may be a number of successive "elif" sections. */ + /* If commands were successfully read, handle the various possible + terminators. There may be a number of successive "elif" sections. */ - else - { - while (had_else_endif == had_elif) + else { - filter_cmd *newnew = - store_get(sizeof(filter_cmd) + 4 * sizeof(union argtypes), GET_UNTAINTED); - new->args[2].f = newnew; - new = newnew; - new->next = NULL; - new->command = command; - new->seen = FALSE; - new->args[0].u = NULL; - new->args[1].u = new->args[2].u = NULL; - new->args[3].u = ptr; - - ptr = read_condition(ptr, &new->args[0].c, TRUE); - if (*error_pointer) { yield = FALSE; break; } - newlastcmdptr = &(new->args[1].f); - if (!read_command_list(&ptr, &newlastcmdptr, TRUE)) - yield = FALSE; - } + while (had_else_endif == had_elif) + { + filter_cmd *newnew = + store_get(sizeof(filter_cmd) + 4 * sizeof(union argtypes), GET_UNTAINTED); + new->args[2].f = newnew; + new = newnew; + new->next = NULL; + new->command = command; + new->seen = FALSE; + new->args[0].u = NULL; + new->args[1].u = new->args[2].u = NULL; + new->args[3].u = ptr; + + ptr = read_condition(ptr, &new->args[0].c, TRUE); + if (*error_pointer) { yield = FALSE; break; } + newlastcmdptr = &(new->args[1].f); + if (!read_command_list(&ptr, &newlastcmdptr, TRUE)) + yield = FALSE; + } - if (yield == FALSE) break; + if (yield == FALSE) break; - /* Handle termination by "else", possibly following one or more - "elsif" sections. */ + /* Handle termination by "else", possibly following one or more + "elsif" sections. */ - if (had_else_endif == had_else) - { - newlastcmdptr = &(new->args[2].f); - if (!read_command_list(&ptr, &newlastcmdptr, TRUE)) - yield = FALSE; - else if (had_else_endif != had_endif) - { - *error_pointer = string_sprintf("\"endif\" missing near line %d of " - "filter file", line_number); - yield = FALSE; - } - } + if (had_else_endif == had_else) + { + newlastcmdptr = &(new->args[2].f); + if (!read_command_list(&ptr, &newlastcmdptr, TRUE)) + yield = FALSE; + else if (had_else_endif != had_endif) + { + *error_pointer = string_sprintf("\"endif\" missing near line %d of " + "filter file", line_number); + yield = FALSE; + } + } - /* Otherwise the terminator was "endif" - this is checked by - read_command_list(). The pointer is already set to NULL. */ - } + /* Otherwise the terminator was "endif" - this is checked by + read_command_list(). The pointer is already set to NULL. */ + } - /* Reset the terminator flag. */ + /* Reset the terminator flag. */ - had_else_endif = had_neither; - break; + had_else_endif = had_neither; + break; /* The mail & vacation commands have a whole slew of keyworded arguments. @@ -1225,150 +1253,150 @@ switch (command) are logically booleans, because they are stored in a uschar * value, we use NULL and not FALSE, to keep 64-bit compilers happy. */ - case mail_command: - case vacation_command: - new = store_get(sizeof(filter_cmd) + mailargs_total * sizeof(union argtypes), GET_UNTAINTED); - new->next = NULL; - new->command = command; - new->seen = seen_force ? seen_value : FALSE; - new->noerror = noerror_force; - for (i = 0; i < mailargs_total; i++) new->args[i].u = NULL; - - /* Read keyword/value pairs until we hit one that isn't. The data - must contain only printing chars plus tab, though the "text" value - can also contain newlines. The "file" keyword can be preceded by the - word "expand", and "return message" has no data. */ - - for (;;) - { - const uschar *saveptr = ptr; - ptr = nextword(ptr, buffer, sizeof(buffer), FALSE); - if (*error_pointer) - { yield = FALSE; break; } - - /* Ensure "return" is followed by "message"; that's a complete option */ - - if (Ustrcmp(buffer, "return") == 0) + case MAIL_COMMAND: + case VACATION_COMMAND: + new = store_get(sizeof(filter_cmd) + mailargs_total * sizeof(union argtypes), GET_UNTAINTED); + new->next = NULL; + new->command = command; + new->seen = seen_force ? seen_value : FALSE; + new->noerror = noerror_force; + for (i = 0; i < mailargs_total; i++) new->args[i].u = NULL; + + /* Read keyword/value pairs until we hit one that isn't. The data + must contain only printing chars plus tab, though the "text" value + can also contain newlines. The "file" keyword can be preceded by the + word "expand", and "return message" has no data. */ + + for (;;) { - new->args[mailarg_index_return].u = US""; /* not NULL => TRUE */ + const uschar *saveptr = ptr; ptr = nextword(ptr, buffer, sizeof(buffer), FALSE); - if (Ustrcmp(buffer, "message") != 0) - { - *error_pointer = string_sprintf("\"return\" not followed by \"message\" " - " near line %d of filter file", line_number); - yield = FALSE; - break; - } - continue; - } + if (*error_pointer) + { yield = FALSE; break; } - /* Ensure "expand" is followed by "file", then fall through to process the - file keyword. */ + /* Ensure "return" is followed by "message"; that's a complete option */ - if (Ustrcmp(buffer, "expand") == 0) - { - new->args[mailarg_index_expand].u = US""; /* not NULL => TRUE */ - ptr = nextword(ptr, buffer, sizeof(buffer), FALSE); - if (Ustrcmp(buffer, "file") != 0) - { - *error_pointer = string_sprintf("\"expand\" not followed by \"file\" " - " near line %d of filter file", line_number); - yield = FALSE; - break; - } - } + if (Ustrcmp(buffer, "return") == 0) + { + new->args[mailarg_index_return].u = US""; /* not NULL => TRUE */ + ptr = nextword(ptr, buffer, sizeof(buffer), FALSE); + if (Ustrcmp(buffer, "message") != 0) + { + *error_pointer = string_sprintf("\"return\" not followed by \"message\" " + " near line %d of filter file", line_number); + yield = FALSE; + break; + } + continue; + } - /* Scan for the keyword */ + /* Ensure "expand" is followed by "file", then fall through to process the + file keyword. */ - for (i = 0; i < MAILARGS_STRING_COUNT; i++) - if (Ustrcmp(buffer, mailargs[i]) == 0) break; + if (Ustrcmp(buffer, "expand") == 0) + { + new->args[mailarg_index_expand].u = US""; /* not NULL => TRUE */ + ptr = nextword(ptr, buffer, sizeof(buffer), FALSE); + if (Ustrcmp(buffer, "file") != 0) + { + *error_pointer = string_sprintf("\"expand\" not followed by \"file\" " + " near line %d of filter file", line_number); + yield = FALSE; + break; + } + } - /* Not found keyword; assume end of this command */ + /* Scan for the keyword */ - if (i >= MAILARGS_STRING_COUNT) - { - ptr = saveptr; - break; - } + for (i = 0; i < MAILARGS_STRING_COUNT; i++) + if (Ustrcmp(buffer, mailargs[i]) == 0) break; - /* Found keyword, read the data item */ + /* Not found keyword; assume end of this command */ - ptr = nextitem(ptr, buffer, sizeof(buffer), FALSE); - if (*error_pointer) - { yield = FALSE; break; } - else new->args[i].u = string_copy(buffer); - } + if (i >= MAILARGS_STRING_COUNT) + { + ptr = saveptr; + break; + } - /* If this is the vacation command, apply some default settings to - some of the arguments. */ + /* Found keyword, read the data item */ - if (command == vacation_command) - { - if (!new->args[mailarg_index_file].u) + ptr = nextitem(ptr, buffer, sizeof(buffer), FALSE); + if (*error_pointer) + { yield = FALSE; break; } + else new->args[i].u = string_copy(buffer); + } + + /* If this is the vacation command, apply some default settings to + some of the arguments. */ + + if (command == VACATION_COMMAND) { - new->args[mailarg_index_file].u = string_copy(US".vacation.msg"); - new->args[mailarg_index_expand].u = US""; /* not NULL => TRUE */ + if (!new->args[mailarg_index_file].u) + { + new->args[mailarg_index_file].u = string_copy(US".vacation.msg"); + new->args[mailarg_index_expand].u = US""; /* not NULL => TRUE */ + } + if (!new->args[mailarg_index_log].u) + new->args[mailarg_index_log].u = string_copy(US".vacation.log"); + if (!new->args[mailarg_index_once].u) + new->args[mailarg_index_once].u = string_copy(US".vacation"); + if (!new->args[mailarg_index_once_repeat].u) + new->args[mailarg_index_once_repeat].u = string_copy(US"7d"); + if (!new->args[mailarg_index_subject].u) + new->args[mailarg_index_subject].u = string_copy(US"On vacation"); } - if (!new->args[mailarg_index_log].u) - new->args[mailarg_index_log].u = string_copy(US".vacation.log"); - if (!new->args[mailarg_index_once].u) - new->args[mailarg_index_once].u = string_copy(US".vacation"); - if (!new->args[mailarg_index_once_repeat].u) - new->args[mailarg_index_once_repeat].u = string_copy(US"7d"); - if (!new->args[mailarg_index_subject].u) - new->args[mailarg_index_subject].u = string_copy(US"On vacation"); - } - /* Join the address on to the chain of generated addresses */ + /* Join the address on to the chain of generated addresses */ - **lastcmdptr = new; - *lastcmdptr = &(new->next); - break; + **lastcmdptr = new; + *lastcmdptr = &(new->next); + break; /* Seen and unseen just set flags */ - case seen_command: - case unseen_command: - if (!*ptr) - { - *error_pointer = string_sprintf("\"seen\" or \"unseen\" " - "near line %d is not followed by a command", line_number); - yield = FALSE; - } - if (seen_force) - { - *error_pointer = string_sprintf("\"seen\" or \"unseen\" repeated " - "near line %d", line_number); - yield = FALSE; - } - seen_value = (command == seen_command); - seen_force = TRUE; - was_seen_or_unseen = TRUE; - break; + case SEEN_COMMAND: + case UNSEEN_COMMAND: + if (!*ptr) + { + *error_pointer = string_sprintf("\"seen\" or \"unseen\" " + "near line %d is not followed by a command", line_number); + yield = FALSE; + } + if (seen_force) + { + *error_pointer = string_sprintf("\"seen\" or \"unseen\" repeated " + "near line %d", line_number); + yield = FALSE; + } + seen_value = (command == SEEN_COMMAND); + seen_force = TRUE; + was_seen_or_unseen = TRUE; + break; /* So does noerror */ - case noerror_command: - if (!*ptr) - { - *error_pointer = string_sprintf("\"noerror\" " - "near line %d is not followed by a command", line_number); - yield = FALSE; - } - noerror_force = TRUE; - was_noerror = TRUE; - break; + case NOERROR_COMMAND: + if (!*ptr) + { + *error_pointer = string_sprintf("\"noerror\" " + "near line %d is not followed by a command", line_number); + yield = FALSE; + } + noerror_force = TRUE; + was_noerror = TRUE; + break; /* Oops */ default: - *error_pointer = string_sprintf("unknown filtering command \"%s\" " - "near line %d of filter file", buffer, line_number); - yield = FALSE; - break; + *error_pointer = string_sprintf("unknown filtering command \"%s\" " + "near line %d of filter file", buffer, line_number); + yield = FALSE; + break; } if (!was_seen_or_unseen && !was_noerror) @@ -1712,7 +1740,7 @@ while (commands) switch(commands->command) { - case add_command: + case ADD_COMMAND: for (i = 0; i < 2; i++) { const uschar *ss = expargs[i]; @@ -1744,7 +1772,7 @@ while (commands) /* A deliver command's argument must be a valid address. Its optional second argument (system filter only) must also be a valid address. */ - case deliver_command: + case DELIVER_COMMAND: for (i = 0; i < 2; i++) { s = expargs[i]; @@ -1820,7 +1848,7 @@ while (commands) } break; - case save_command: + case SAVE_COMMAND: s = expargs[0]; mode = commands->args[1].i; @@ -1863,7 +1891,7 @@ while (commands) } break; - case pipe_command: + case PIPE_COMMAND: s = string_copy(commands->args[0].u); if (filter_test != FTEST_NONE) { @@ -1913,7 +1941,7 @@ while (commands) /* Set up the file name and mode, and close any previously open file. */ - case logfile_command: + case LOGFILE_COMMAND: log_mode = commands->args[1].i; if (log_mode == -1) log_mode = 0600; if (log_fd >= 0) @@ -1929,7 +1957,7 @@ while (commands) } break; - case logwrite_command: + case LOGWRITE_COMMAND: s = expargs[0]; if (filter_test != FTEST_NONE) @@ -1988,7 +2016,7 @@ while (commands) command is rejected at parse time otherwise. However "headers charset" is always permitted. */ - case headers_command: + case HEADERS_COMMAND: { int subtype = commands->args[1].i; s = expargs[0]; @@ -2032,17 +2060,17 @@ while (commands) very long by the inclusion of message headers; truncate if it is, and also ensure printing characters so as not to mess up log files. */ - case defer_command: + case DEFER_COMMAND: ff_name = US"defer"; ff_ret = FF_DEFER; goto DEFERFREEZEFAIL; - case fail_command: + case FAIL_COMMAND: ff_name = US"fail"; ff_ret = FF_FAIL; goto DEFERFREEZEFAIL; - case freeze_command: + case FREEZE_COMMAND: ff_name = US"freeze"; ff_ret = FF_FREEZE; @@ -2062,7 +2090,7 @@ while (commands) DEBUG(D_filter) debug_printf_indent("Filter: %s \"%s\"\n", ff_name, fmsg); return ff_ret; - case finish_command: + case FINISH_COMMAND: if (filter_test != FTEST_NONE) { indent(); @@ -2074,7 +2102,7 @@ while (commands) finish_obeyed = TRUE; return filter_delivered ? FF_DELIVERED : FF_NOTDELIVERED; - case if_command: + case IF_COMMAND: { uschar *save_address = filter_thisaddress; int ok = FF_DELIVERED; @@ -2099,8 +2127,8 @@ while (commands) return path is unset or if a non-trusted user supplied -f <> as the return path. */ - case mail_command: - case vacation_command: + case MAIL_COMMAND: + case VACATION_COMMAND: if (!return_path || !*return_path) { if (filter_test != FTEST_NONE) @@ -2196,7 +2224,7 @@ while (commands) indent(); printf("%sail to: %s%s%s\n", (commands->seen)? "Seen m" : "M", to ? to : US"", - commands->command == vacation_command ? " (vacation)" : "", + commands->command == VACATION_COMMAND ? " (vacation)" : "", commands->noerror ? " (noerror)" : ""); for (i = 1; i < MAILARGS_STRING_COUNT; i++) { @@ -2238,7 +2266,7 @@ while (commands) debug_printf_indent("Filter: %smail to: %s%s%s\n", commands->seen ? "seen " : "", to, - commands->command == vacation_command ? " (vacation)" : "", + commands->command == VACATION_COMMAND ? " (vacation)" : "", commands->noerror ? " (noerror)" : ""); for (i = 1; i < MAILARGS_STRING_COUNT; i++) { @@ -2344,7 +2372,7 @@ while (commands) } break; - case testprint_command: + case TESTPRINT_COMMAND: if (filter_test != FTEST_NONE || (debug_selector & D_filter) != 0) { const uschar *s = string_printing(expargs[0]); diff --git a/src/src/routers/redirect.c b/src/src/routers/redirect.c index 37b5f5e24..799bee062 100644 --- a/src/src/routers/redirect.c +++ b/src/src/routers/redirect.c @@ -731,7 +731,7 @@ if (eblock != NULL) ob->syntax_errors_text)) /* Custom message */ return DEFER; - if (filtertype != FILTER_FORWARD || generated == NULL) + if (filtertype != FILTER_FORWARD || !generated) { addr->message = US"syntax error in redirection data"; return DECLINE; @@ -744,7 +744,7 @@ calling sort_errors_and_headers() in case this router declines - that function may modify the errors_address field in the current address, and we don't want to do that for a decline. */ -if (generated != NULL) +if (generated) { if ((xrc = sort_errors_and_headers(rblock, addr, verify, &addr_prop)) != OK) return xrc; diff --git a/src/src/transports/autoreply.c b/src/src/transports/autoreply.c index 67d48a1cb..803202b52 100644 --- a/src/src/transports/autoreply.c +++ b/src/src/transports/autoreply.c @@ -475,10 +475,9 @@ if (oncelog && *oncelog && to) else { EXIM_DATUM key_datum, result_datum; - uschar * dirname, * s; + uschar * s = Ustrrchr(oncelog, '/'); + uschar * dirname = s ? string_copyn(oncelog, s - oncelog) : NULL; - dirname = (s = Ustrrchr(oncelog, '/')) - ? string_copyn(oncelog, s - oncelog) : NULL; if (!(dbm_file = exim_dbopen(oncelog, dirname, O_RDWR|O_CREAT, ob->mode))) { addr->transport_return = DEFER; @@ -811,3 +810,5 @@ return FALSE; #endif /*!MACRO_PREDEF*/ #endif /*TRANSPORT_AUTOREPOL*/ /* End of transport/autoreply.c */ +/* vi: aw ai sw=2 +*/ commit b84bf19797130ef766e668ea3f26f664c9afb0e8 Author: Jeremy Harris Date: Fri May 3 18:18:00 2024 +0100 constify diff --git a/src/src/deliver.c b/src/src/deliver.c index 0157743a1..43ac7fd41 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -859,9 +859,10 @@ Return: string expansion from listener, or NULL */ uschar * -event_raise(uschar * action, const uschar * event, uschar * ev_data, int * errnop) +event_raise(const uschar * action, const uschar * event, const uschar * ev_data, + int * errnop) { -uschar * s; +const uschar * s; if (action) { DEBUG(D_deliver) @@ -872,7 +873,7 @@ if (action) event_name = event; event_data = ev_data; - if (!(s = expand_string(action)) && *expand_string_message) + if (!(s = expand_cstring(action)) && *expand_string_message) log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand event_action %s in %s: %s\n", event, transport_name ? transport_name : US"main", expand_string_message); @@ -880,7 +881,8 @@ if (action) event_name = event_data = NULL; /* If the expansion returns anything but an empty string, flag for - the caller to modify his normal processing + the caller to modify his normal processing. Copy the string to + de-const it. */ if (s && *s) { @@ -888,7 +890,7 @@ if (action) debug_printf("Event(%s): event_action returned \"%s\"\n", event, s); if (errnop) *errnop = ERRNO_EVENT; - return s; + return string_copy(s); } } return NULL; diff --git a/src/src/functions.h b/src/src/functions.h index f0b600ccc..9407b44df 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -253,7 +253,7 @@ extern BOOL dscp_lookup(const uschar *, int, int *, int *, int *); extern void enq_end(uschar *); extern BOOL enq_start(uschar *, unsigned); #ifndef DISABLE_EVENT -extern uschar *event_raise(uschar *, const uschar *, uschar *, int *); +extern uschar *event_raise(const uschar *, const uschar *, const uschar *, int *); extern void msg_event_raise(const uschar *, const address_item *); #endif diff --git a/src/src/globals.c b/src/src/globals.c index 2885caa23..4e5fd2991 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -909,10 +909,10 @@ int error_handling = ERRORS_SENDER; uschar *errors_reply_to = NULL; int errors_sender_rc = EXIT_FAILURE; #ifndef DISABLE_EVENT -uschar *event_action = NULL; /* expansion for delivery events */ -uschar *event_data = NULL; /* auxiliary data variable for event */ -int event_defer_errno = 0; -const uschar *event_name = NULL; /* event name variable */ +uschar *event_action = NULL; /* expansion for delivery events */ +const uschar *event_data = NULL; /* auxiliary data variable for event */ +int event_defer_errno = 0; +const uschar *event_name = NULL; /* event name variable */ #endif diff --git a/src/src/globals.h b/src/src/globals.h index 427590050..30c8bbad4 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -602,7 +602,7 @@ extern int errors_sender_rc; /* Return after message to sender*/ #ifndef DISABLE_EVENT extern uschar *event_action; /* expansion for delivery events */ -extern uschar *event_data; /* event data */ +extern const uschar *event_data; /* event data */ extern int event_defer_errno; /* error number set when a remote delivery is deferred with a host error */ extern const uschar *event_name; /* event classification */ #endif commit e8a0da772adc2892e661dec2bad4f36196c9128f Author: Jeremy Harris Date: Sat May 4 15:43:36 2024 +0100 Debug: markup ASCII control-chars diff --git a/src/src/string.c b/src/src/string.c index a627611af..170b47b23 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -1623,7 +1623,7 @@ while (*fp) goto INSERT_GSTRING; } - case 'W': /* Maybe mark up spaces & newlines */ + case 'W': /* Maybe mark up ctrls, spaces & newlines */ s = va_arg(ap, char *); if (Ustrpbrk(s, " \n") && !IS_DEBUG(D_noutf8)) { @@ -1646,7 +1646,15 @@ while (*fp) if (precision >= 0) precision += 3; break; default: - zg = string_catn(zg, CUS s, 1); + if (*s <= ' ') + { /* base of UTF8 symbols for ASCII control chars */ + uschar ctrl_symbol[3] = {[0]=0xe2, [1]=0x90, [2]=0x80}; + ctrl_symbol[2] |= *s; + zg = string_catn(zg, ctrl_symbol, 3); + if (precision >= 0) precision += 2; + } + else + zg = string_catn(zg, CUS s, 1); break; } } commit 4fd2b1700eb79bbeeea4887b93f1235171976e24 Author: Jeremy Harris Date: Sun May 5 18:11:45 2024 +0100 Events: dns:fail Bug 3011 diff --git a/src/src/acl.c b/src/src/acl.c index d719a937b..4e88fc1ac 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -4450,7 +4450,7 @@ while ((acl_current = acl)) verbs[acl->verb], acl_name); if (basic_errno != ERRNO_CALLOUTDEFER) { - if (search_error_message != NULL && *search_error_message != 0) + if (search_error_message && *search_error_message) *log_msgptr = search_error_message; if (smtp_return_error_details) f.acl_temp_details = TRUE; } diff --git a/src/src/dns.c b/src/src/dns.c index 74c5a58c5..b22f4d327 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -1010,7 +1010,7 @@ won't return any. If fully_qualified_name is not NULL, set it to point to the full name returned by the resolver, if this is different to what it is given, unless the returned name starts with "*" as some nameservers seem to be returning -wildcards in this form. In international mode "different" means "alabel +wildcards in this form. In international mode "different" means "a-label forms are different". Arguments: @@ -1028,11 +1028,13 @@ Returns: DNS_SUCCEED successful lookup */ int -dns_lookup(dns_answer *dnsa, const uschar *name, int type, - const uschar **fully_qualified_name) +dns_lookup(dns_answer * dnsa, const uschar * name, int type, + const uschar ** fully_qualified_name) { -const uschar *orig_name = name; +const uschar * orig_name = name; BOOL secure_so_far = TRUE; +int rc = DNS_FAIL; +const uschar * errstr = NULL; /* By default, assume the resolver follows CNAME chains (and returns NODATA for an unterminated one). If it also does that for a CNAME loop, fine; if it returns @@ -1046,12 +1048,11 @@ for (int i = 0; i <= dns_cname_loops; i++) uschar * data; dns_record cname_rr, type_rr; dns_scan dnss; - int rc; /* DNS lookup failures get passed straight back. */ if ((rc = dns_basic_lookup(dnsa, name, type)) != DNS_SUCCEED) - return rc; + goto not_good; /* We should have either records of the required type, or a CNAME record, or both. We need to know whether both exist for getting the fully qualified @@ -1105,13 +1106,19 @@ for (int i = 0; i <= dns_cname_loops; i++) its not existing. */ if (!cname_rr.data) - return DNS_FAIL; + { + errstr = US"no_hit_yet_no_cname"; + goto not_good; + } /* DNS data comes from the outside, hence tainted */ data = store_get(256, GET_TAINTED); if (dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, 256) < 0) - return DNS_FAIL; + { + errstr = US"bad_expand"; + goto not_good; + } name = data; if (!dns_is_secure(dnsa)) @@ -1120,11 +1127,48 @@ for (int i = 0; i <= dns_cname_loops; i++) DEBUG(D_dns) debug_printf("CNAME found: change to %s\n", name); } /* Loop back to do another lookup */ -/*Control reaches here after 10 times round the CNAME loop. Something isn't +/* Control reaches here after 10 times round the CNAME loop. Something isn't right... */ log_write(0, LOG_MAIN, "CNAME loop for %s encountered", orig_name); -return DNS_FAIL; +errstr = US"cname_loop"; + +not_good: + { + const uschar * s = NULL; + BOOL save_flag = f.search_find_defer; + uschar * save_serr = search_error_message; + + if (!transport_name) + s = event_action; + else + for(transport_instance * tp = transports; tp; tp = tp->next) + if (Ustrcmp(tp->name, transport_name) == 0) + { s = tp->event_action; break; } + + if (s) + { + if (Ustrchr(name, ':')) /* unlikely, but may as well bugproof */ + { + gstring * g = NULL; + while (*name) + { + if (*name == ':') g = string_catn(g, name, 1); + g = string_catn(g, name++, 1); + } + name = string_from_gstring(g); + } + event_raise(s, US"dns:fail", + string_sprintf("%s:%s:%s", + errstr ? errstr : dns_rc_names[rc], name, dns_text_type(type)), + NULL); + } + + /*XXX what other state could an expansion in the eventhandler mess up? */ + search_error_message = save_serr; + f.search_find_defer = save_flag; + return rc; + } } commit cf9f324212bddcc09f405b94b9a01a5c99ffe19b Author: Jeremy Harris Date: Mon May 6 17:57:21 2024 +0100 Testsuite: munge retry DB dumps for long pathnames diff --git a/src/src/EDITME b/src/src/EDITME index 85d24f73f..4a33677d5 100644 --- a/src/src/EDITME +++ b/src/src/EDITME @@ -60,8 +60,8 @@ # for you by the OS-specific configuration. If Exim compiles without any # problems, you probably do not have to worry about the DBM library. If you # do want or need to change it, you should first read the discussion in the -# file doc/dbm.discuss.txt, which also contains instructions for testing Exim's -# interface to the DBM library. +# file doc/doc-txt/dbm.discuss.txt, which also contains instructions for testing +# Exim's interface to the DBM library. # In Local/Makefiles blank lines and lines starting with # are ignored. It is # also permitted to use the # character to add a comment to a setting, for diff --git a/src/src/hintsdb.h b/src/src/hintsdb.h index 2d7199eda..ed1b5566d 100644 --- a/src/src/hintsdb.h +++ b/src/src/hintsdb.h @@ -24,6 +24,10 @@ utilities as well as the main Exim binary. */ #if defined(USE_TDB) +# if defined(USE_DB) || defined(USE_GDBM) +# error USE_TDB conflict with alternate definition +# endif + /* ************************* tdb interface ************************ */ /*XXX https://manpages.org/tdb/3 mentions concurrent writes. Could we lose the file lock? */ @@ -157,6 +161,10 @@ d->dptr = NULL; #elif defined USE_DB +# if defined(USE_TDB) || defined(USE_GDBM) +# error USE_DB conflict with alternate definition +# endif + # include /* 1.x did no locking @@ -482,6 +490,10 @@ exim_datum_free(EXIM_DATUM * d) #elif defined USE_GDBM /*XXX TODO: exim's locfile not needed */ +# if defined(USE_TDB) || defined(USE_DB) +# error USE_GDBM conflict with alternate definition +# endif + # include /* Basic DB type */ @@ -609,7 +621,7 @@ static inline void exim_datum_free(EXIM_DATUM * d) { free(d->dptr); } -/* size limit */ +/* size limit. GDBM is int-max limited, but we want to be less silly */ # define EXIM_DB_RLIMIT 150 @@ -621,7 +633,7 @@ exim_datum_free(EXIM_DATUM * d) /* If none of USE_DB, USG_GDBM, or USE_TDB are set, the default is the NDBM -interface */ +interface (which seems to be a wrapper for GDBM) */ /********************* ndbm interface definitions **********************/ @@ -745,7 +757,7 @@ exim_datum_free(EXIM_DATUM * d) # define EXIM_DB_RLIMIT 150 -#endif /* USE_GDBM */ +#endif /* !USE_GDBM */ diff --git a/src/src/retry.c b/src/src/retry.c index 6b977dd58..6ccef4f98 100644 --- a/src/src/retry.c +++ b/src/src/retry.c @@ -678,7 +678,13 @@ for (int i = 0; i < 3; i++) ? US string_printing(rti->message) : US"unknown error"; message_length = Ustrlen(message); - if (message_length > EXIM_DB_RLIMIT) message_length = EXIM_DB_RLIMIT; + if (message_length > EXIM_DB_RLIMIT) + { + DEBUG(D_retry) + debug_printf_indent("truncating message from %u to %u bytes\n", + message_length, EXIM_DB_RLIMIT); + message_length = EXIM_DB_RLIMIT; + } /* 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. */ diff --git a/src/src/transports/appendfile.c b/src/src/transports/appendfile.c index 0279659f2..f495f9571 100644 --- a/src/src/transports/appendfile.c +++ b/src/src/transports/appendfile.c @@ -1368,7 +1368,7 @@ if (!isdirectory) && ob->create_file == create_belowhome) if (is_tainted(path)) { - DEBUG(D_transport) debug_printf("de-tainting path '%s'\n", path); + DEBUG(D_transport) debug_printf("below-home: de-tainting path '%s'\n", path); path = string_copy_taint(path, GET_UNTAINTED); } @@ -2207,7 +2207,7 @@ else if (is_tainted(path)) if (ob->create_file == create_belowhome) { - DEBUG(D_transport) debug_printf("de-tainting path '%s'\n", path); + DEBUG(D_transport) debug_printf("below-home: de-tainting path '%s'\n", path); path = string_copy_taint(path, GET_UNTAINTED); } else commit 1d2868b2d90c230dd4ae42e6f3c7e4958ed75de5 Author: Jeremy Harris Date: Wed May 8 11:31:52 2024 +0100 Debug: fix showing option name for list (vs. list content) It's done in a hacky way (always was) and is fragile. Turns out, too fragile to be good. - some lists, we cannot match up to an option name eg. stderr/0002 - some, we're falling back to comparing the list content; this gets duplicates and thus gives incorrect info, which is worse than no info (we could scan for dups, I guess?) Why do we get dups when we're comparing the addrs of the string? - mmm, do we Really get dups? - Yes: same option across two routers. Must be a static init for a default. In which case, does it being a dup matter? - OK, we can check router_name / transport_name Resulting testsuite sterr changes only partial, here. There's a double check on local_parts in routers (always was). Why? One buildfarm animal (groundhog) is missing a "list element" line possibly associated with dnssec_request_domains. Why? diff --git a/src/src/deliver.c b/src/src/deliver.c index 43ac7fd41..f3c9203e4 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -903,6 +903,7 @@ const uschar * save_domain = deliver_domain; const uschar * save_local = deliver_localpart; const uschar * save_host = deliver_host; const uschar * save_address = deliver_host_address; +uschar * save_rn = router_name, * save_tn = transport_name; const int save_port = deliver_host_port; router_name = addr->router ? addr->router->name : NULL; @@ -939,7 +940,8 @@ deliver_host_address = save_address; deliver_host = save_host; deliver_localpart = save_local; deliver_domain = save_domain; -router_name = transport_name = NULL; +router_name = save_rn; +router_name = save_tn; } #endif /*DISABLE_EVENT*/ diff --git a/src/src/functions.h b/src/src/functions.h index 9407b44df..08fa24f3d 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -427,7 +427,7 @@ extern int rda_is_filter(const uschar *); extern BOOL readconf_depends(driver_instance *, uschar *); extern void readconf_driver_init(uschar *, driver_instance **, driver_info *, int, void *, int, optionlist *, int); -extern uschar *readconf_find_option(void *); +extern const uschar *readconf_find_option(void *); extern void readconf_main(BOOL); extern void readconf_options_from_list(optionlist *, unsigned, const uschar *, uschar *); extern BOOL readconf_print(const uschar *, const uschar *, BOOL); diff --git a/src/src/macros.h b/src/src/macros.h index 3341f5f41..2938b2523 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -1195,7 +1195,8 @@ When doing en extended loop of matching, release store periodically. */ #define REGEX_LOOPCOUNT_STORE_RESET 1000 -/* Debug an option access. Use for non-list ones about to be expanded. */ +/* Debug an option access. Use for non-list ones about to be expanded +(lists have their own debugging, under D_list). */ #define GET_OPTION(name) \ DEBUG(D_expand) debug_printf("try option " name "\n"); diff --git a/src/src/match.c b/src/src/match.c index 2cb499359..4b128a609 100644 --- a/src/src/match.c +++ b/src/src/match.c @@ -437,18 +437,19 @@ BOOL textonly_re; /* Save time by not scanning for the option name when we don't need it. */ -HDEBUG(D_any) +HDEBUG(D_any) /* always give the query. Give results only for D_lists */ { - uschar * listname = readconf_find_option(listptr); + const uschar * listname = readconf_find_option(listptr); if (*listname) ot = string_sprintf("%s in %s?", name, listname); } -/* If the list is empty, the answer is no. Skip the debugging output for -an unnamed list. */ +/* If the list is empty, the answer is no. */ if (!*listptr) { - HDEBUG(D_lists) if (ot) debug_printf_indent("%s no (option unset)\n", ot); + HDEBUG(D_lists) + if (ot) debug_printf_indent("%s no (option unset)\n", ot); + else debug_printf_indent("%s not in empty list (option unset? cannot trace name)\n", name); return FAIL; } @@ -505,7 +506,7 @@ if (textonly_re) switch (type) #define LIST_LIMIT_PR 2048 HDEBUG(D_any) if (!ot) - { + { /* We failed to identify an option name, so give the list text */ int n, m; gstring * g = string_fmt_append(NULL, "%s in \"%n%.*s%n\"", name, &n, LIST_LIMIT_PR, list, &m); @@ -527,7 +528,7 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) { uschar * ss = sss; - HDEBUG(D_lists) debug_printf_indent("list element: %s\n", ss); + HDEBUG(D_lists) debug_printf_indent("list element: %W\n", ss); /* Address lists may contain +caseful, to restore caseful matching of the local part. We have to know the layout of the control block, unfortunately. @@ -618,7 +619,7 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) namedlist_block * nb; tree_node * t; - DEBUG(D_lists) + HDEBUG(D_lists) { debug_printf_indent(" start sublist %s\n", ss+1); expand_level += 2; } if (!(t = tree_search(*anchorptr, ss+1))) @@ -655,7 +656,7 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) { int res = match_check_list(&(nb->string), 0, anchorptr, &use_cache_bits, func, arg, type, name, valueptr); - DEBUG(D_lists) + HDEBUG(D_lists) { expand_level -= 2; debug_printf_indent(" end sublist %s\n", ss+1); } switch (res) @@ -695,7 +696,7 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) p->next = nb->cache_data; nb->cache_data = p; if (*valueptr) - DEBUG(D_lists) debug_printf_indent("data from lookup saved for " + HDEBUG(D_lists) debug_printf_indent("data from lookup saved for " "cache for %s: key '%s' value '%s'\n", ss, p->key, *valueptr); } } @@ -707,7 +708,7 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) else { - DEBUG(D_lists) + HDEBUG(D_lists) { expand_level -= 2; debug_printf_indent("cached %s match for %s\n", @@ -725,7 +726,7 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) *valueptr = p->data; break; } - DEBUG(D_lists) debug_printf_indent("cached lookup data = %s\n", *valueptr); + HDEBUG(D_lists) debug_printf_indent("cached lookup data = %s\n", *valueptr); } } @@ -749,7 +750,7 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) { case OK: HDEBUG(D_lists) debug_printf_indent("%s %s (matched \"%s\")\n", ot, - (yield == OK)? "yes" : "no", sss); + yield == OK ? "yes" : "no", sss); goto YIELD_RETURN; case DEFER: @@ -757,8 +758,8 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) error = string_sprintf("DNS lookup of \"%s\" deferred", ss); if (ignore_defer) { - HDEBUG(D_lists) debug_printf_indent("%s: item ignored by +ignore_defer\n", - error); + HDEBUG(D_lists) + debug_printf_indent("%s: item ignored by +ignore_defer\n", error); break; } if (include_defer) @@ -777,8 +778,8 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) case ERROR: if (ignore_unknown) { - HDEBUG(D_lists) debug_printf_indent("%s: item ignored by +ignore_unknown\n", - error); + HDEBUG(D_lists) debug_printf_indent( + "%s: item ignored by +ignore_unknown\n", error); } else { @@ -812,8 +813,8 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) if (!f) { - uschar * listname = readconf_find_option(listptr); - if (listname[0] == 0) + const uschar * listname = readconf_find_option(listptr); + if (!*listname) listname = string_sprintf("\"%s\"", *listptr); log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", string_open_failed("%s when checking %s", sss, listname)); @@ -859,8 +860,8 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) { case OK: (void)fclose(f); - HDEBUG(D_lists) debug_printf_indent("%s %s (matched \"%s\" in %s)\n", ot, - yield == OK ? "yes" : "no", sss, filename); + HDEBUG(D_lists) debug_printf_indent("%s %s (matched \"%s\" in %s)\n", + ot, yield == OK ? "yes" : "no", sss, filename); /* The "pattern" being matched came from the file; we use a stack-local. Copy it to allocated memory now we know it matched. */ @@ -874,8 +875,8 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) error = string_sprintf("DNS lookup of %s deferred", ss); if (ignore_defer) { - HDEBUG(D_lists) debug_printf_indent("%s: item ignored by +ignore_defer\n", - error); + HDEBUG(D_lists) + debug_printf_indent("%s: item ignored by +ignore_defer\n", error); break; } (void)fclose(f); @@ -892,13 +893,13 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) case ERROR: if (ignore_unknown) { - HDEBUG(D_lists) debug_printf_indent("%s: item ignored by +ignore_unknown\n", - error); + HDEBUG(D_lists) debug_printf_indent( + "%s: item ignored by +ignore_unknown\n", error); } else { HDEBUG(D_lists) debug_printf_indent("%s %s (%s)\n", ot, - include_unknown? "yes":"no", error); + include_unknown ? "yes":"no", error); (void)fclose(f); if (!include_unknown) { @@ -922,20 +923,19 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) /* End of list reached: if the last item was negated yield OK, else FAIL. */ -HDEBUG(D_lists) - HDEBUG(D_lists) - { - expand_level--; - debug_printf_indent("%s %s (end of list)\n", ot, yield == OK ? "no":"yes"); - } - return yield == OK ? FAIL : OK; - +HDEBUG(D_any) + { + HDEBUG(D_lists) expand_level--; + debug_printf_indent("%s %s (end of list)\n", ot, yield == OK ? "no":"yes"); + } +return yield == OK ? FAIL : OK; + /* Something deferred */ DEFER_RETURN: - HDEBUG(D_lists) + HDEBUG(D_any) { - expand_level--; + HDEBUG(D_lists) expand_level--; debug_printf_indent("%s list match deferred for %s\n", ot, sss); } return DEFER; @@ -1390,3 +1390,5 @@ return match_address_list(address, TRUE, TRUE, listptr, NULL, -1, sep, NULL); } /* End of match.c */ +/* vi: aw ai sw=2 +*/ diff --git a/src/src/readconf.c b/src/src/readconf.c index 3db3bdd69..d87a56f3d 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -592,46 +592,104 @@ static int syslog_list_size = sizeof(syslog_list)/sizeof(syslog_fac_item); pointer variables in the options table or in option tables for various drivers. For debugging output, it is useful to be able to find the name of the option which is currently being processed. This function finds it, if it exists, by -searching the table(s). +searching the table(s) for a value with the given content. Arguments: a value that is presumed to be in the table above Returns: the option name, or an empty string */ -uschar * -readconf_find_option(void *p) +const uschar * +readconf_find_option(void * listptr) { -for (int i = 0; i < nelem(optionlist_config); i++) - if (p == optionlist_config[i].v.value) return US optionlist_config[i].name; +uschar * list = * USS listptr; +const uschar * name = NULL, * drname = NULL; + +for (optionlist * o = optionlist_config; /* main-config options */ + o < optionlist_config + optionlist_config_size; o++) + if (listptr == o->v.value) + return US o->name; for (router_instance * r = routers; r; r = r->next) + if (router_name && Ustrcmp(r->name, router_name) == 0) { - router_info *ri = r->info; - for (int i = 0; i < *ri->options_count; i++) - { - if ((ri->options[i].type & opt_mask) != opt_stringptr) continue; - if (p == CS (r->options_block) + ri->options[i].v.offset) - return US ri->options[i].name; - } + const router_info * ri = r->info; + + /* Check for a listptr match first */ + + for (optionlist * o = optionlist_routers; /* generic options */ + o < optionlist_routers + optionlist_routers_size; o++) + if ( (o->type & opt_mask) == opt_stringptr + && listptr == CS r + o->v.offset) + return US o->name; + + for (optionlist * o = ri->options; /* private options */ + o < ri->options + *ri->options_count; o++) + if ( (o->type & opt_mask) == opt_stringptr + && listptr == CS (r->options_block) + o->v.offset) + return US o->name; + + /* Check for a list addr match, unless null */ + + if (!list) continue; + + for (optionlist * o = optionlist_routers; /* generic options */ + o < optionlist_routers + optionlist_routers_size; o++) + if ( (o->type & opt_mask) == opt_stringptr + && list == * USS(CS r + o->v.offset)) + if (name) return string_sprintf("DUP: %s %s vs. %s %s", + drname, name, r->name, o->name); + else { name = US o->name; drname = r->name; } + + for (optionlist * o = ri->options; /* private options */ + o < ri->options + *ri->options_count; o++) + if ( (o->type & opt_mask) == opt_stringptr + && list == * USS(CS (r->options_block) + o->v.offset)) + if (name) return string_sprintf("DUP: %s %s vs. %s %s", + drname, name, r->name, o->name); + else { name = US o->name; drname = r->name; } } for (transport_instance * t = transports; t; t = t->next) + if (transport_name && Ustrcmp(t->name, transport_name) == 0) { - transport_info *ti = t->info; - for (int i = 0; i < *ti->options_count; i++) - { - optionlist * op = &ti->options[i]; - if ((op->type & opt_mask) != opt_stringptr) continue; - if (p == ( op->type & opt_public - ? CS t - : CS t->options_block - ) - + op->v.offset) - return US op->name; - } + const transport_info * ti = t->info; + + /* Check for a listptr match first */ + + for (optionlist * o = optionlist_transports; /* generic options */ + o < optionlist_transports + optionlist_transports_size; o++) + if ( (o->type & opt_mask) == opt_stringptr + && listptr == CS t + o->v.offset) + return US o->name; + + for (optionlist * o = ti->options; /* private options */ + o < ti->options + *ti->options_count; o++) + if ( (o->type & opt_mask) == opt_stringptr + && listptr == CS t->options_block + o->v.offset) + return US o->name; + + /* Check for a list addr match, unless null */ + + if (!list) continue; + + for (optionlist * o = optionlist_transports; /* generic options */ + o < optionlist_transports + optionlist_transports_size; o++) + if ( (o->type & opt_mask) == opt_stringptr + && list == * USS(CS t + o->v.offset)) + if (name) return string_sprintf("DUP: %s %s vs. %s %s", + drname, name, t->name, o->name); + else { name = US o->name; drname = t->name; } + + for (optionlist * o = ti->options; /* private options */ + o < ti->options + *ti->options_count; o++) + if ( (o->type & opt_mask) == opt_stringptr + && list == * USS(CS t->options_block + o->v.offset)) + if (name) return string_sprintf("DUP: %s %s vs. %s %s", + drname, name, t->name, o->name); + else { name = US o->name; drname = t->name; } } -return US""; +return name ? name : US""; } diff --git a/src/src/smtp_out.c b/src/src/smtp_out.c index a78b15fbd..81042003f 100644 --- a/src/src/smtp_out.c +++ b/src/src/smtp_out.c @@ -365,6 +365,7 @@ if (!save_errno) { #ifdef TCP_FASTOPEN /* See if TCP Fast Open usable. Default is a traditional 3WHS connect */ + expand_level++; if (verify_check_given_host(CUSS &ob->hosts_try_fastopen, sc->host) == OK) { if (!early_data) @@ -380,6 +381,7 @@ if (!save_errno) } # endif } + expand_level--; #endif if (ip_connect(sock, sc->host_af, sc->host->address, sc->host->port, timeout, fastopen_blob) < 0) @@ -412,7 +414,7 @@ if (!save_errno) /* Both bind() and connect() succeeded, and any early-data */ - HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" connected\n"); + HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("connected\n"); if (getsockname(sock, (struct sockaddr *)(&interface_sock), &size) == 0) sending_ip_address = host_ntoa(-1, &interface_sock, NULL, &sending_port); else @@ -496,7 +498,7 @@ HDEBUG(D_transport|D_acl|D_v) #ifdef SUPPORT_SOCKS if (ob->socks_proxy) s = string_sprintf("%svia proxy ", s); #endif - debug_printf_indent("Connecting to %s %s%s... ", sc->host->name, callout_address, s); + debug_printf_indent("Connecting to %s %s%s...\n", sc->host->name, callout_address, s); } /* Create and connect the socket */ diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index ddd6ad7e1..0338d6301 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -668,12 +668,15 @@ deferred_event_raise(address_item * addr, host_item * host, uschar * evstr) { uschar * action = addr->transport->event_action; const uschar * save_domain, * save_local; +uschar * save_rn, * save_tn; if (!action) return; save_domain = deliver_domain; save_local = deliver_localpart; +save_rn = router_name; +save_tn = transport_name; /*XXX would ip & port already be set up? */ deliver_host_address = string_copy(host->address); @@ -697,7 +700,8 @@ deliver_localpart = addr->local_part; deliver_localpart = save_local; deliver_domain = save_domain; -router_name = transport_name = NULL; +router_name = save_rn; +router_name = save_tn; } #endif commit 9d7795dba14939e2b743d8c3c4df81a5582b3092 Author: Jeremy Harris Date: Thu May 9 15:37:49 2024 +0100 Debug: indent DNS ops diff --git a/src/src/dns.c b/src/src/dns.c index b22f4d327..5a8c23d62 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -62,7 +62,7 @@ if (stat(CS utilname, &statbuf) >= 0) int infd, outfd, rc; uschar *argv[5]; - DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) using fakens\n", + DEBUG(D_dns) debug_printf_indent("DNS lookup of %s (%s) using fakens\n", name, dns_text_type(type)); argv[0] = utilname; @@ -105,17 +105,17 @@ if (stat(CS utilname, &statbuf) >= 0) case 3: h_errno = NO_RECOVERY; return -1; case 4: h_errno = NO_DATA; return -1; case 5: /* Pass on to res_search() */ - DEBUG(D_dns) debug_printf("fakens returned PASS_ON\n"); + DEBUG(D_dns) debug_printf_indent("fakens returned PASS_ON\n"); } } else { - DEBUG(D_dns) debug_printf("fakens (%s) not found\n", utilname); + DEBUG(D_dns) debug_printf_indent("fakens (%s) not found\n", utilname); } /* fakens utility not found, or it returned "pass on" */ -DEBUG(D_dns) debug_printf("passing %s on to res_search()\n", domain); +DEBUG(D_dns) debug_printf_indent("passing %s on to res_search()\n", domain); return res_search(CS domain, C_IN, type, answerptr, size); } @@ -165,13 +165,13 @@ if (dns_use_edns0 >= 0) else resp->options &= ~RES_USE_EDNS0; DEBUG(D_resolver) - debug_printf("Coerced resolver EDNS0 support %s.\n", + debug_printf_indent("Coerced resolver EDNS0 support %s.\n", dns_use_edns0 ? "on" : "off"); } #else if (dns_use_edns0 >= 0) DEBUG(D_resolver) - debug_printf("Unable to %sset EDNS0 without resolver support.\n", + debug_printf_indent("Unable to %sset EDNS0 without resolver support.\n", dns_use_edns0 ? "" : "un"); #endif @@ -187,7 +187,7 @@ if (dns_dnssec_ok >= 0) if (dns_use_edns0 == 0 && dns_dnssec_ok != 0) { DEBUG(D_resolver) - debug_printf("CONFLICT: dns_use_edns0 forced false, dns_dnssec_ok forced true, ignoring latter!\n"); + debug_printf_indent("CONFLICT: dns_use_edns0 forced false, dns_dnssec_ok forced true, ignoring latter!\n"); } else { @@ -195,18 +195,18 @@ if (dns_dnssec_ok >= 0) resp->options |= RES_USE_DNSSEC; else resp->options &= ~RES_USE_DNSSEC; - DEBUG(D_resolver) debug_printf("Coerced resolver DNSSEC support %s.\n", + DEBUG(D_resolver) debug_printf_indent("Coerced resolver DNSSEC support %s.\n", dns_dnssec_ok ? "on" : "off"); } } # else if (dns_dnssec_ok >= 0) DEBUG(D_resolver) - debug_printf("Unable to %sset DNSSEC without resolver support.\n", + debug_printf_indent("Unable to %sset DNSSEC without resolver support.\n", dns_dnssec_ok ? "" : "un"); if (use_dnssec) DEBUG(D_resolver) - debug_printf("Unable to set DNSSEC without resolver support.\n"); + debug_printf_indent("Unable to set DNSSEC without resolver support.\n"); # endif #endif /* DISABLE_DNSSEC */ @@ -353,7 +353,7 @@ char * trace = NULL; if (reset != RESET_NEXT) { dnss->rrcount = ntohs(h->qdcount); - TRACE debug_printf("%s: reset (Q rrcount %d)\n", __FUNCTION__, dnss->rrcount); + TRACE debug_printf_indent("%s: reset (Q rrcount %d)\n", __FUNCTION__, dnss->rrcount); dnss->aptr = dnsa->answer + sizeof(HEADER); /* Skip over questions; failure to expand the name just gives up */ @@ -372,7 +372,7 @@ if (reset != RESET_NEXT) /* Get the number of answer records. */ dnss->rrcount = ntohs(h->ancount); - TRACE debug_printf("%s: reset (A rrcount %d)\n", __FUNCTION__, dnss->rrcount); + TRACE debug_printf_indent("%s: reset (A rrcount %d)\n", __FUNCTION__, dnss->rrcount); /* Skip over answers if we want to look at the authority section. Also skip the NS records (i.e. authority section) if wanting to look at the additional @@ -380,15 +380,15 @@ if (reset != RESET_NEXT) if (reset == RESET_ADDITIONAL) { - TRACE debug_printf("%s: additional\n", __FUNCTION__); + TRACE debug_printf_indent("%s: additional\n", __FUNCTION__); dnss->rrcount += ntohs(h->nscount); - TRACE debug_printf("%s: reset (NS rrcount %d)\n", __FUNCTION__, dnss->rrcount); + TRACE debug_printf_indent("%s: reset (NS rrcount %d)\n", __FUNCTION__, dnss->rrcount); } if (reset == RESET_AUTHORITY || reset == RESET_ADDITIONAL) { TRACE if (reset == RESET_AUTHORITY) - debug_printf("%s: authority\n", __FUNCTION__); + debug_printf_indent("%s: authority\n", __FUNCTION__); while (dnss->rrcount-- > 0) { TRACE trace = "A-namelen"; @@ -409,13 +409,13 @@ if (reset != RESET_NEXT) } dnss->rrcount = reset == RESET_AUTHORITY ? ntohs(h->nscount) : ntohs(h->arcount); - TRACE debug_printf("%s: reset (%s rrcount %d)\n", __FUNCTION__, + TRACE debug_printf_indent("%s: reset (%s rrcount %d)\n", __FUNCTION__, reset == RESET_AUTHORITY ? "NS" : "AR", dnss->rrcount); } - TRACE debug_printf("%s: %d RRs to read\n", __FUNCTION__, dnss->rrcount); + TRACE debug_printf_indent("%s: %d RRs to read\n", __FUNCTION__, dnss->rrcount); } else - TRACE debug_printf("%s: next (%d left)\n", __FUNCTION__, dnss->rrcount); + TRACE debug_printf_indent("%s: next (%d left)\n", __FUNCTION__, dnss->rrcount); /* The variable dnss->aptr is now pointing at the next RR, and dnss->rrcount contains the number of RR records left. */ @@ -458,11 +458,11 @@ if (dnss_inc_aptr(dnsa, dnss, dnss->srr.size)) /* Return a pointer to the dns_record structure within the dns_answer. This is for convenience so that the scans can use nice-looking for loops. */ -TRACE debug_printf("%s: return %s\n", __FUNCTION__, dns_text_type(dnss->srr.type)); +TRACE debug_printf_indent("%s: return %s\n", __FUNCTION__, dns_text_type(dnss->srr.type)); return &dnss->srr; null_return: - TRACE debug_printf("%s: terminate (%d RRs left). Last op: %s; errno %d %s\n", + TRACE debug_printf_indent("%s: terminate (%d RRs left). Last op: %s; errno %d %s\n", __FUNCTION__, dnss->rrcount, trace, errno, strerror(errno)); dnss->rrcount = 0; return NULL; @@ -515,7 +515,7 @@ dns_is_secure(const dns_answer * dnsa) { #ifdef DISABLE_DNSSEC DEBUG(D_dns) - debug_printf("DNSSEC support disabled at build-time; dns_is_secure() false\n"); + debug_printf_indent("DNSSEC support disabled at build-time; dns_is_secure() false\n"); return FALSE; #else const HEADER * h = (const HEADER *) dnsa->answer; @@ -542,7 +542,7 @@ if ( !h->aa ) return FALSE; -DEBUG(D_dns) debug_printf("DNS faked the AD bit " +DEBUG(D_dns) debug_printf_indent("DNS faked the AD bit " "(got AA and matched with dns_trust_aa (%s in %s))\n", auth_name, dns_trust_aa); @@ -666,7 +666,7 @@ else (void)tree_insertnode(&tree_dns_fails, new); } -DEBUG(D_dns) debug_printf(" %s neg-cache entry for %s, ttl %d\n", +DEBUG(D_dns) debug_printf_indent(" %s neg-cache entry for %s, ttl %d\n", previous ? "update" : "writing", node_name, expiry ? (int)(expiry - time(NULL)) : -1); e->expiry = expiry; @@ -693,7 +693,7 @@ e = previous->data.ptr; val = e->data.val; rc = e->expiry && e->expiry <= time(NULL) ? -1 : val; -DEBUG(D_dns) debug_printf("DNS lookup of %.255s (%s): %scached value %s%s\n", +DEBUG(D_dns) debug_printf_indent("DNS lookup of %.255s (%s): %scached value %s%s\n", name, dns_text_type(type), rc == -1 ? "" : "using ", dns_rc_names[val], @@ -731,12 +731,12 @@ if ( h->qr == 1 /* a response */ && ntohs(h->ancount) == 0 /* no answer records */ && ntohs(h->nscount) >= 1) /* authority records */ { - DEBUG(D_dns) debug_printf("faking res_search(%s) response length as %d\n", + DEBUG(D_dns) debug_printf_indent("faking res_search(%s) response length as %d\n", dns_text_type(type), (int)sizeof(dnsa->answer)); dnsa->answerlen = sizeof(dnsa->answer); return TRUE; } -DEBUG(D_dns) debug_printf("DNS: couldn't fake dnsa len\n"); +DEBUG(D_dns) debug_printf_indent("DNS: couldn't fake dnsa len\n"); /* Maybe we should just do a second lookup for an SOA? */ return FALSE; } @@ -783,7 +783,7 @@ if (fake_dnsa_len_for_fail(dnsa, type)) return time(NULL) + ttl; } -DEBUG(D_dns) debug_printf("DNS: no SOA record found for neg-TTL\n"); +DEBUG(D_dns) debug_printf_indent("DNS: no SOA record found for neg-TTL\n"); return 0; } @@ -844,11 +844,11 @@ if ((rc = dns_fail_cache_hit(name, type)) > 0) uschar * alabel; uschar * errstr = NULL; DEBUG(D_dns) if (string_is_utf8(name)) - debug_printf("convert utf8 '%s' to alabel for for lookup\n", name); + debug_printf_indent("convert utf8 '%s' to alabel for for lookup\n", name); if ((alabel = string_domain_utf8_to_alabel(name, &errstr)), errstr) { DEBUG(D_dns) - debug_printf("DNS name '%s' utf8 conversion to alabel failed: %s\n", name, + debug_printf_indent("DNS name '%s' utf8 conversion to alabel failed: %s\n", name, errstr); f.host_find_failed_syntax = TRUE; return DNS_NOMATCH; @@ -877,7 +877,7 @@ if (check_dns_names_pattern[0] != 0 && type != T_PTR && type != T_TXT) if (!regex_match(regex_check_dns_names, name, -1, NULL)) { DEBUG(D_dns) - debug_printf("DNS name syntax check failed: %s (%s)\n", name, + debug_printf_indent("DNS name syntax check failed: %s (%s)\n", name, dns_text_type(type)); f.host_find_failed_syntax = TRUE; return DNS_NOMATCH; @@ -909,7 +909,7 @@ dnsa->answerlen = f.running_in_test_harness if (dnsa->answerlen > (int) sizeof(dnsa->answer)) { - DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) resulted in overlong packet" + DEBUG(D_dns) debug_printf_indent("DNS lookup of %s (%s) resulted in overlong packet" " (size %d), truncating to %u.\n", name, dns_text_type(type), dnsa->answerlen, (unsigned int) sizeof(dnsa->answer)); dnsa->answerlen = sizeof(dnsa->answer); @@ -918,12 +918,12 @@ if (dnsa->answerlen > (int) sizeof(dnsa->answer)) if (dnsa->answerlen < 0) switch (h_errno) { case HOST_NOT_FOUND: - DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave HOST_NOT_FOUND\n" + DEBUG(D_dns) debug_printf_indent("DNS lookup of %s (%s) gave HOST_NOT_FOUND\n" "returning DNS_NOMATCH\n", name, dns_text_type(type)); return dns_fail_return(name, type, dns_expire_from_soa(dnsa, type), DNS_NOMATCH); case TRY_AGAIN: - DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave TRY_AGAIN\n", + DEBUG(D_dns) debug_printf_indent("DNS lookup of %s (%s) gave TRY_AGAIN\n", name, dns_text_type(type)); /* Cut this out for various test programs */ @@ -955,10 +955,10 @@ if (dnsa->answerlen < 0) switch (h_errno) if (rc != OK) { - DEBUG(D_dns) debug_printf("returning DNS_AGAIN\n"); + DEBUG(D_dns) debug_printf_indent("returning DNS_AGAIN\n"); return dns_fail_return(name, type, 0, DNS_AGAIN); } - DEBUG(D_dns) debug_printf("%s is in dns_again_means_nonexist: returning " + DEBUG(D_dns) debug_printf_indent("%s is in dns_again_means_nonexist: returning " "DNS_NOMATCH\n", name); return dns_fail_return(name, type, dns_expire_from_soa(dnsa, type), DNS_NOMATCH); @@ -967,22 +967,22 @@ if (dnsa->answerlen < 0) switch (h_errno) #endif case NO_RECOVERY: - DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave NO_RECOVERY\n" + DEBUG(D_dns) debug_printf_indent("DNS lookup of %s (%s) gave NO_RECOVERY\n" "returning DNS_FAIL\n", name, dns_text_type(type)); return dns_fail_return(name, type, 0, DNS_FAIL); case NO_DATA: - DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave NO_DATA\n" + DEBUG(D_dns) debug_printf_indent("DNS lookup of %s (%s) gave NO_DATA\n" "returning DNS_NODATA\n", name, dns_text_type(type)); return dns_fail_return(name, type, dns_expire_from_soa(dnsa, type), DNS_NODATA); default: - DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave unknown DNS error %d\n" + DEBUG(D_dns) debug_printf_indent("DNS lookup of %s (%s) gave unknown DNS error %d\n" "returning DNS_FAIL\n", name, dns_text_type(type), h_errno); return dns_fail_return(name, type, 0, DNS_FAIL); } -DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) succeeded\n", +DEBUG(D_dns) debug_printf_indent("DNS lookup of %s (%s) succeeded\n", name, dns_text_type(type)); return DNS_SUCCEED; @@ -1124,7 +1124,7 @@ for (int i = 0; i <= dns_cname_loops; i++) if (!dns_is_secure(dnsa)) secure_so_far = FALSE; - DEBUG(D_dns) debug_printf("CNAME found: change to %s\n", name); + DEBUG(D_dns) debug_printf_indent("CNAME found: change to %s\n", name); } /* Loop back to do another lookup */ /* Control reaches here after 10 times round the CNAME loop. Something isn't @@ -1216,12 +1216,12 @@ switch (type) case T_SOA: { const uschar *d = name; - while (d != 0) + while (d) { int rc = dns_lookup(dnsa, d, type, fully_qualified_name); if (rc != DNS_NOMATCH && rc != DNS_NODATA) return rc; - while (*d != 0 && *d != '.') d++; - if (*d++ == 0) break; + while (*d && *d != '.') d++; + if (!*d++) break; } return DNS_NOMATCH; } @@ -1241,7 +1241,7 @@ switch (type) dns_record *rr; dns_scan dnss; - DEBUG(D_dns) debug_printf("CSA lookup of %s\n", name); + DEBUG(D_dns) debug_printf_indent("CSA lookup of %s\n", name); srvname = string_sprintf("_client._smtp.%s", name); rc = dns_lookup(dnsa, srvname, T_SRV, NULL); @@ -1279,7 +1279,7 @@ switch (type) limit = 3; } - DEBUG(D_dns) debug_printf("CSA TLD %s\n", tld); + DEBUG(D_dns) debug_printf_indent("CSA TLD %s\n", tld); /* Do not perform the search if the top level or 2nd level domains do not exist. This is quite common, and when it occurs all the search queries would @@ -1306,7 +1306,7 @@ switch (type) if (--namesuff <= name) return DNS_NOMATCH; while (*namesuff != '.'); - DEBUG(D_dns) debug_printf("CSA parent search at %s\n", namesuff + 1); + DEBUG(D_dns) debug_printf_indent("CSA parent search at %s\n", namesuff + 1); srvname = string_sprintf("_client._smtp.%s", namesuff + 1); rc = dns_lookup(dnsa, srvname, T_SRV, NULL); diff --git a/src/src/host.c b/src/src/host.c index 381f2a5fc..26b3c2c68 100644 --- a/src/src/host.c +++ b/src/src/host.c @@ -1298,30 +1298,30 @@ host_item *last = *lastptr; host_item *prev = NULL; host_item *h; -if (removed != NULL) *removed = FALSE; +if (removed) *removed = FALSE; -if (local_interface_data == NULL) local_interface_data = host_find_interfaces(); +if (!local_interface_data) local_interface_data = host_find_interfaces(); for (h = host; h != last->next; h = h->next) { - #ifndef STAND_ALONE - if (hosts_treat_as_local != NULL) +#ifndef STAND_ALONE + if (hosts_treat_as_local) { int rc; - const uschar *save = deliver_domain; + const uschar * save = deliver_domain; deliver_domain = h->name; /* set $domain */ rc = match_isinlist(string_copylc(h->name), CUSS &hosts_treat_as_local, 0, &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL); deliver_domain = save; if (rc == OK) goto FOUND_LOCAL; } - #endif +#endif /* It seems that on many operating systems, 0.0.0.0 is treated as a synonym for 127.0.0.1 and refers to the local host. We therefore force it always to be treated as local. */ - if (h->address != NULL) + if (h->address) { if (Ustrcmp(h->address, "0.0.0.0") == 0) goto FOUND_LOCAL; for (ip_address_item * ip = local_interface_data; ip; ip = ip->next) @@ -1332,7 +1332,8 @@ for (h = host; h != last->next; h = h->next) /* Update prev to point to the last host item before any that have the same MX value as the one we have just considered. */ - if (h->next == NULL || h->next->mx != h->mx) prev = h; + if (!h->next || h->next->mx != h->mx) + prev = h; } return yield; /* No local hosts found: return HOST_FOUND or HOST_FIND_FAILED */ @@ -1342,7 +1343,7 @@ something in hosts_treat_as_local has been found. */ FOUND_LOCAL: -if (prev == NULL) +if (!prev) { HDEBUG(D_host_lookup) debug_printf((h->mx >= 0)? "local host has lowest MX\n" : @@ -1357,7 +1358,7 @@ HDEBUG(D_host_lookup) debug_printf(" %s %s %d\n", h->name, h->address, h->mx); } -if (removed != NULL) *removed = TRUE; +if (removed) *removed = TRUE; prev->next = last->next; *lastptr = prev; return yield; @@ -1568,8 +1569,7 @@ connection. */ int host_name_lookup(void) { -int old_pool, rc; -int sep = 0; +int sep = 0, old_pool, rc, yield; uschar *save_hostname; uschar **aliases; uschar *ordername; @@ -1581,6 +1581,7 @@ sender_host_dnssec = host_lookup_deferred = host_lookup_failed = FALSE; HDEBUG(D_host_lookup) debug_printf("looking up host name for %s\n", sender_host_address); +expand_level++; /* For testing the case when a lookup does not complete, we have a special reserved IP address. */ @@ -1591,7 +1592,8 @@ if (f.running_in_test_harness && HDEBUG(D_host_lookup) debug_printf("Test harness: host name lookup returns DEFER\n"); host_lookup_deferred = TRUE; - return DEFER; + yield = DEFER; + goto out; } /* Do lookups directly in the DNS or via gethostbyaddr() (or equivalent), in @@ -1649,7 +1651,7 @@ while ((ordername = string_nextinlist(&list, &sep, NULL, 0))) truncated and dn_expand may fail. */ if (dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, - US (rr->data), (DN_EXPAND_ARG4_TYPE)(s), ssize) < 0) + US rr->data, (DN_EXPAND_ARG4_TYPE)(s), ssize) < 0) { log_write(0, LOG_MAIN, "host name alias list truncated for %s", sender_host_address); @@ -1689,7 +1691,8 @@ while ((ordername = string_nextinlist(&list, &sep, NULL, 0))) HDEBUG(D_host_lookup) debug_printf("IP address PTR lookup gave temporary error\n"); host_lookup_deferred = TRUE; - return DEFER; + yield = DEFER; + goto out; } } @@ -1703,7 +1706,8 @@ while ((ordername = string_nextinlist(&list, &sep, NULL, 0))) if (rc == DEFER) { host_lookup_deferred = TRUE; - return rc; /* Can't carry on */ + yield = rc; /* Can't carry on */ + goto out; } if (rc == OK) break; /* Found a name */ } @@ -1719,7 +1723,8 @@ if (!sender_host_name) "address %s", sender_host_address); host_lookup_msg = US" (failed to find host name from IP address)"; host_lookup_failed = TRUE; - return FAIL; + yield = FAIL; + goto out; } HDEBUG(D_host_lookup) @@ -1784,7 +1789,8 @@ for (uschar * hname = sender_host_name; hname; hname = *aliases++) HDEBUG(D_host_lookup) debug_printf("temporary error for host name lookup\n"); host_lookup_deferred = TRUE; sender_host_name = NULL; - return DEFER; + yield = DEFER; + goto out; } else HDEBUG(D_host_lookup) debug_printf("no IP addresses found for %s\n", hname); @@ -1806,12 +1812,12 @@ for (uschar * hname = sender_host_name; hname; hname = *aliases++) /* If sender_host_name == NULL, it means we didn't like the name. Replace it with the first alias, if there is one. */ -if (sender_host_name == NULL && *sender_host_aliases != NULL) +if (!sender_host_name && *sender_host_aliases) sender_host_name = *sender_host_aliases++; /* If we now have a main name, all is well. */ -if (sender_host_name != NULL) return OK; +if (sender_host_name) { yield = OK; goto out; } /* We have failed to find an address that matches. */ @@ -1827,7 +1833,11 @@ host_lookup_msg = string_sprintf(" (%s does not match any IP address for %s)", sender_host_address, save_hostname); store_pool = old_pool; host_lookup_failed = TRUE; -return FAIL; +yield = FAIL; + +out: + expand_level--; + return yield; } @@ -2489,31 +2499,46 @@ Returns: HOST_FIND_FAILED Failed to find the host or domain; */ int -host_find_bydns(host_item *host, const uschar *ignore_target_hosts, int whichrrs, - uschar *srv_service, uschar *srv_fail_domains, uschar *mx_fail_domains, - const dnssec_domains *dnssec_d, - const uschar **fully_qualified_name, BOOL *removed) +host_find_bydns(host_item * host, const uschar * ignore_target_hosts, + int whichrrs, + uschar * srv_service, uschar * srv_fail_domains, uschar * mx_fail_domains, + const dnssec_domains * dnssec_d, + const uschar ** fully_qualified_name, BOOL * removed) { -host_item *h, *last; -int rc = DNS_FAIL; -int ind_type = 0; -int yield; +host_item * h, * last; +int rc = DNS_FAIL, ind_type = 0, yield; dns_answer * dnsa = store_get_dns_answer(); dns_scan dnss; -BOOL dnssec_require = dnssec_d +BOOL dnssec_require, dnssec_request; +dnssec_status_t dnssec; + +HDEBUG(D_host_lookup) + { + debug_printf_indent("check dnssec require list\n"); + expand_level++; + } +dnssec_require = dnssec_d && match_isinlist(host->name, CUSS &dnssec_d->require, 0, &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) == OK; -BOOL dnssec_request = dnssec_require + +HDEBUG(D_host_lookup) + { + expand_level--; + debug_printf_indent("check dnssec request list\n"); + expand_level++; + } +dnssec_request = dnssec_require || ( dnssec_d && match_isinlist(host->name, CUSS &dnssec_d->request, 0, &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) == OK); -dnssec_status_t dnssec; +HDEBUG(D_host_lookup) + expand_level--; /* Set the default fully qualified name to the incoming name, initialize the resolver if necessary, set up the relevant options, and initialize the flag that gets set for DNS syntax check errors. */ -if (fully_qualified_name != NULL) *fully_qualified_name = host->name; +if (fully_qualified_name) *fully_qualified_name = host->name; dns_init((whichrrs & HOST_FIND_QUALIFY_SINGLE) != 0, (whichrrs & HOST_FIND_SEARCH_PARENTS) != 0, dnssec_request); @@ -2525,13 +2550,12 @@ characters, so the code below should be safe. */ if (whichrrs & HOST_FIND_BY_SRV) { - gstring * g; - uschar * temp_fully_qualified_name; + uschar * s, * temp_fully_qualified_name; int prefix_length; - g = string_fmt_append(NULL, "_%s._tcp.%n%.256s", + s = string_sprintf("_%s._tcp.%n%.256s", srv_service, &prefix_length, host->name); - temp_fully_qualified_name = string_from_gstring(g); + temp_fully_qualified_name = s; ind_type = T_SRV; /* Search for SRV records. If the fully qualified name is different to @@ -2547,7 +2571,7 @@ if (whichrrs & HOST_FIND_BY_SRV) if ((dnssec_request || dnssec_require) && !dns_is_secure(dnsa) && dns_is_aa(dnsa)) - debug_printf("DNS lookup of %.256s (SRV) requested AD, but got AA\n", host->name); + debug_printf_indent("DNS lookup of %.256s (SRV) requested AD, but got AA\n", host->name); if (dnssec_request) { @@ -2557,7 +2581,7 @@ if (whichrrs & HOST_FIND_BY_SRV) { dnssec = DS_NO; lookup_dnssec_authenticated = US"no"; } } - if (temp_fully_qualified_name != g->s && fully_qualified_name != NULL) + if (temp_fully_qualified_name != s && fully_qualified_name) *fully_qualified_name = temp_fully_qualified_name + prefix_length; /* On DNS failures, we give the "try again" error unless the domain is @@ -2576,7 +2600,7 @@ if (whichrrs & HOST_FIND_BY_SRV) &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) != OK) #endif { yield = HOST_FIND_AGAIN; goto out; } - DEBUG(D_host_lookup) debug_printf("DNS_%s treated as DNS_NODATA " + DEBUG(D_host_lookup) debug_printf_indent("DNS_%s treated as DNS_NODATA " "(domain in srv_fail_domains)\n", rc == DNS_FAIL ? "FAIL":"AGAIN"); } } @@ -2599,12 +2623,12 @@ if (rc != DNS_SUCCEED && whichrrs & HOST_FIND_BY_MX) if ( (dnssec_request || dnssec_require) && !dns_is_secure(dnsa) && dns_is_aa(dnsa)) - debug_printf("DNS lookup of %.256s (MX) requested AD, but got AA\n", host->name); + debug_printf_indent("DNS lookup of %.256s (MX) requested AD, but got AA\n", host->name); if (dnssec_request) if (dns_is_secure(dnsa)) { - DEBUG(D_host_lookup) debug_printf("%s (MX resp) DNSSEC\n", host->name); + DEBUG(D_host_lookup) debug_printf_indent("%s (MX resp) DNSSEC\n", host->name); dnssec = DS_YES; lookup_dnssec_authenticated = US"yes"; } else @@ -2615,13 +2639,14 @@ if (rc != DNS_SUCCEED && whichrrs & HOST_FIND_BY_MX) switch (rc) { case DNS_NOMATCH: - yield = HOST_FIND_FAILED; goto out; + yield = HOST_FIND_FAILED; + goto out; case DNS_SUCCEED: if (!dnssec_require || dns_is_secure(dnsa)) break; DEBUG(D_host_lookup) - debug_printf("dnssec fail on MX for %.256s", host->name); + debug_printf_indent("dnssec fail on MX for %.256s\n", host->name); #ifndef STAND_ALONE if (match_isinlist(host->name, CUSS &mx_fail_domains, 0, &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) != OK) @@ -2637,7 +2662,7 @@ if (rc != DNS_SUCCEED && whichrrs & HOST_FIND_BY_MX) &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) != OK) #endif { yield = HOST_FIND_AGAIN; goto out; } - DEBUG(D_host_lookup) debug_printf("DNS_%s treated as DNS_NODATA " + DEBUG(D_host_lookup) debug_printf_indent("DNS_%s treated as DNS_NODATA " "(domain in mx_fail_domains)\n", (rc == DNS_FAIL)? "FAIL":"AGAIN"); break; } @@ -2651,7 +2676,7 @@ if (rc != DNS_SUCCEED) { if (!(whichrrs & (HOST_FIND_BY_A | HOST_FIND_BY_AAAA))) { - DEBUG(D_host_lookup) debug_printf("Address records are not being sought\n"); + DEBUG(D_host_lookup) debug_printf_indent("Address records are not being sought\n"); yield = HOST_FIND_FAILED; goto out; } @@ -2672,16 +2697,16 @@ if (rc != DNS_SUCCEED) if (rc == HOST_FOUND) rc = host_scan_for_local_hosts(host, &last, removed); - else - if (rc == HOST_IGNORED) rc = HOST_FIND_FAILED; /* No special action */ + else if (rc == HOST_IGNORED) + rc = HOST_FIND_FAILED; /* No special action */ DEBUG(D_host_lookup) if (host->address) { if (fully_qualified_name) - debug_printf("fully qualified name = %s\n", *fully_qualified_name); + debug_printf_indent("fully qualified name = %s\n", *fully_qualified_name); for (host_item * h = host; h != last->next; h = h->next) - debug_printf("%s %s mx=%d sort=%d %s\n", h->name, + debug_printf_indent("%s %s mx=%d sort=%d %s\n", h->name, h->address ? h->address : US"", h->mx, h->sort_key, h->status >= hstatus_unusable ? US"*" : US""); } @@ -2758,7 +2783,7 @@ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); if (strcmpic(h->name, data) == 0) { DEBUG(D_host_lookup) - debug_printf("discarded duplicate host %s (MX=%d)\n", data, + debug_printf_indent("discarded duplicate host %s (MX=%d)\n", data, precedence > h->mx ? precedence : h->mx); if (precedence >= h->mx) goto NEXT_MX_RR; /* Skip greater precedence */ if (h == host) /* Override first item */ @@ -2868,16 +2893,16 @@ if (ind_type == T_SRV) if (host == last && host->name[0] == 0) { - DEBUG(D_host_lookup) debug_printf("the single SRV record is \".\"\n"); + DEBUG(D_host_lookup) debug_printf_indent("the single SRV record is \".\"\n"); yield = HOST_FIND_FAILED; goto out; } DEBUG(D_host_lookup) { - debug_printf("original ordering of hosts from SRV records:\n"); + debug_printf_indent("original ordering of hosts from SRV records:\n"); for (h = host; h != last->next; h = h->next) - debug_printf(" %s P=%d W=%d\n", h->name, h->mx, h->sort_key % 1000); + debug_printf_indent(" %s P=%d W=%d\n", h->name, h->mx, h->sort_key % 1000); } for (pptr = &host, h = host; h != last; pptr = &h->next, h = h->next) @@ -3070,7 +3095,7 @@ if (h != last && !disable_ipv6) for (h = host; h != last; h = h->next) h->next = next; *next = temp; } -#endif +#endif /*HAVE_IPV6*/ /* Remove any duplicate IP addresses and then scan the list of hosts for any whose IP addresses are on the local host. If any are found, all hosts with the @@ -3088,8 +3113,8 @@ if (rc != HOST_FIND_FAILED) yield = rc; DEBUG(D_host_lookup) { if (fully_qualified_name) - debug_printf("fully qualified name = %s\n", *fully_qualified_name); - debug_printf("host_find_bydns yield = %s (%d); returned hosts:\n", + debug_printf_indent("fully qualified name = %s\n", *fully_qualified_name); + debug_printf_indent("host_find_bydns yield = %s (%d); returned hosts:\n", yield == HOST_FOUND ? "HOST_FOUND" : yield == HOST_FOUND_LOCAL ? "HOST_FOUND_LOCAL" : yield == HOST_FIND_SECURITY ? "HOST_FIND_SECURITY" : @@ -3098,7 +3123,7 @@ DEBUG(D_host_lookup) yield); for (h = host; h != last->next; h = h->next) { - debug_printf(" %s %s MX=%d %s", h->name, + debug_printf_indent(" %s %s MX=%d %s", h->name, !h->address ? US"" : h->address, h->mx, h->dnssec == DS_YES ? US"DNSSEC " : US""); if (h->port != PORT_NONE) debug_printf("port=%d ", h->port); diff --git a/src/src/match.c b/src/src/match.c index 4b128a609..6acbd9f61 100644 --- a/src/src/match.c +++ b/src/src/match.c @@ -194,27 +194,23 @@ if (cb->flags & MCS_AT_SPECIAL && pattern[0] == '@') { int rc; host_item h; - BOOL prim = FALSE; - BOOL secy = FALSE; - BOOL removed = FALSE; + BOOL prim = FALSE, secy = FALSE, removed = FALSE; const uschar *ss = pattern + 4; const uschar *ignore_target_hosts = NULL; - if (strncmpic(ss, US"any", 3) == 0) ss += 3; + if (strncmpic(ss, US"any", 3) == 0) + ss += 3; else if (strncmpic(ss, US"primary", 7) == 0) - { - ss += 7; - prim = TRUE; - } + { ss += 7; prim = TRUE; } else if (strncmpic(ss, US"secondary", 9) == 0) - { - ss += 9; - secy = TRUE; - } - else goto NOT_AT_SPECIAL; + { ss += 9; secy = TRUE; } + else + goto NOT_AT_SPECIAL; - if (strncmpic(ss, US"/ignore=", 8) == 0) ignore_target_hosts = ss + 8; - else if (*ss) goto NOT_AT_SPECIAL; + if (strncmpic(ss, US"/ignore=", 8) == 0) + ignore_target_hosts = ss + 8; + else if (*ss) + goto NOT_AT_SPECIAL; h.next = NULL; h.name = s; commit 1137efd94771e13988fd0ca219c575d27d31ad89 Author: Jeremy Harris Date: Fri May 10 14:51:06 2024 +0100 Testsuite: fix munging for ipv6 dns diff --git a/src/src/host.c b/src/src/host.c index 26b3c2c68..1b416997c 100644 --- a/src/src/host.c +++ b/src/src/host.c @@ -2204,7 +2204,7 @@ set_address_from_dns(host_item *host, host_item **lastptr, const uschar **fully_qualified_name, BOOL dnssec_request, BOOL dnssec_require, int whichrrs) { -host_item *thishostlast = NULL; /* Indicates not yet filled in anything */ +host_item * thishostlast = NULL; /* Indicates not yet filled in anything */ BOOL v6_find_again = FALSE; BOOL dnssec_fail = FALSE; int i; @@ -2223,12 +2223,12 @@ those sites that feel they have to flaunt the RFC rules. */ if (allow_ip && string_is_ip_address(host->name, NULL) != 0) { - #ifndef STAND_ALONE +#ifndef STAND_ALONE if ( ignore_target_hosts && verify_check_this_host(&ignore_target_hosts, NULL, host->name, host->name, NULL) == OK) return HOST_IGNORED; - #endif +#endif host->address = host->name; return HOST_FOUND; @@ -2242,7 +2242,7 @@ testing, we force an IPv4 lookup if the domain matches dns_ipv4_lookup global. On an IPv4 system, go round the loop once only, looking only for A records. */ #if HAVE_IPV6 - #ifndef STAND_ALONE +# ifndef STAND_ALONE if ( disable_ipv6 || !(whichrrs & HOST_FIND_BY_AAAA) || dns_ipv4_lookup @@ -2251,7 +2251,7 @@ On an IPv4 system, go round the loop once only, looking only for A records. */ ) i = 0; /* look up A records only */ else - #endif /* STAND_ALONE */ +# endif /* STAND_ALONE */ i = 1; /* look up AAAA and A records */ @@ -2628,7 +2628,8 @@ if (rc != DNS_SUCCEED && whichrrs & HOST_FIND_BY_MX) if (dnssec_request) if (dns_is_secure(dnsa)) { - DEBUG(D_host_lookup) debug_printf_indent("%s (MX resp) DNSSEC\n", host->name); + DEBUG(D_host_lookup) + debug_printf_indent("%s (MX resp) DNSSEC\n", host->name); dnssec = DS_YES; lookup_dnssec_authenticated = US"yes"; } else diff --git a/src/src/match.c b/src/src/match.c index 6acbd9f61..956ce60ad 100644 --- a/src/src/match.c +++ b/src/src/match.c @@ -427,13 +427,12 @@ unsigned int * original_cache_bits = *cache_ptr; BOOL include_unknown = FALSE, ignore_unknown = FALSE, include_defer = FALSE, ignore_defer = FALSE; const uschar * list; -uschar * sss; -uschar * ot = NULL; +uschar * ot = NULL, * sss; BOOL textonly_re; /* Save time by not scanning for the option name when we don't need it. */ -HDEBUG(D_any) /* always give the query. Give results only for D_lists */ +HDEBUG(D_any) { const uschar * listname = readconf_find_option(listptr); if (*listname) ot = string_sprintf("%s in %s?", name, listname); diff --git a/src/src/routers/dnslookup.c b/src/src/routers/dnslookup.c index 80b12cba2..d3c4349b6 100644 --- a/src/src/routers/dnslookup.c +++ b/src/src/routers/dnslookup.c @@ -286,10 +286,15 @@ for (;;) if (ob->search_parents) flags |= HOST_FIND_SEARCH_PARENTS; } - rc = host_find_bydns(&h, CUS rblock->ignore_target_hosts, flags, - srv_service, ob->srv_fail_domains, ob->mx_fail_domains, - &rblock->dnssec, - &fully_qualified_name, &removed); + DEBUG(D_route) debug_printf_indent("main lookup for domain\n"); + { + expand_level++; + rc = host_find_bydns(&h, CUS rblock->ignore_target_hosts, flags, + srv_service, ob->srv_fail_domains, ob->mx_fail_domains, + &rblock->dnssec, + &fully_qualified_name, &removed); + expand_level--; + } if (removed) setflag(addr, af_local_host_removed); @@ -362,7 +367,7 @@ for (;;) As a common cause of this problem is MX records with IP addresses on the RHS, give a special message in this case. */ - if (h.mx >= 0 && h.address == NULL) + if (h.mx >= 0 && !h.address) { setflag(addr, af_pass_message); /* This is not a security risk */ if (h.name[0] == 0) commit 79c7bae7b372b89a8051ebc7c850ae9339597949 Author: Jeremy Harris Date: Sun May 12 11:49:04 2024 +0100 Fix GNU/Hurd build. Bug 3044 Contributions from Samuel Thibault and Andreas Metzler diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base index 6dcf18b58..b3bb87c22 100644 --- a/src/OS/Makefile-Base +++ b/src/OS/Makefile-Base @@ -108,7 +108,7 @@ config.h: Makefile buildconfig ../src/config.h.defaults $(EDITME) # Build the builtin-macros data struct -MACRO_HSRC = macro_predef.h os.h globals.h config.h macros.h \ +MACRO_HSRC = macro_predef.h os.h globals.h config.h macros.h path_max.h \ routers/accept.h routers/dnslookup.h routers/ipliteral.h \ routers/iplookup.h routers/manualroute.h routers/queryprogram.h \ routers/redirect.h @@ -664,7 +664,7 @@ OBJ_MONBIN = util-host_address.o \ $(MONBIN) eximon.bin: $(EXIMON_EDITME) eximon $(OBJ_MONBIN) ../exim_monitor/em_version.c \ - mytypes.h store.h macros.h + mytypes.h store.h path_max.h macros.h @echo "$(CC) exim_monitor/em_version.c" $(FE)$(CC) -o em_version.o -c \ $(CFLAGS) $(XINCLUDE) -I. ../exim_monitor/em_version.c @@ -698,6 +698,7 @@ HDRS = blob.h \ local_scan.h \ macros.h \ mytypes.h \ + path_max.h \ sha_ver.h \ structs.h \ os.h @@ -711,6 +712,7 @@ PHDRS = ../config.h \ ../local_scan.h \ ../macros.h \ ../mytypes.h \ + ../path_max.h \ ../structs.h \ ../os.h diff --git a/src/exim_monitor/em_hdr.h b/src/exim_monitor/em_hdr.h index f79686a80..074fef1d3 100644 --- a/src/exim_monitor/em_hdr.h +++ b/src/exim_monitor/em_hdr.h @@ -99,6 +99,7 @@ this interface so that this kind of kludge isn't needed. */ typedef void * hctx; #include "local_scan.h" +#include "path_max.h" #include "macros.h" #include "structs.h" #include "blob.h" diff --git a/src/exim_monitor/em_version.c b/src/exim_monitor/em_version.c index 4c562925c..432796bff 100644 --- a/src/exim_monitor/em_version.c +++ b/src/exim_monitor/em_version.c @@ -9,19 +9,9 @@ #define EM_VERSION_C -/* Needed by macros.h */ -/* Some systems have PATH_MAX and some have MAX_PATH_LEN. */ - -#ifndef PATH_MAX -# ifdef MAX_PATH_LEN -# define PATH_MAX MAX_PATH_LEN -# else -# define PATH_MAX 1024 -# endif -#endif - #include "mytypes.h" #include "store.h" +#include "path_max.h" #include "macros.h" #include #include diff --git a/src/scripts/MakeLinks b/src/scripts/MakeLinks index 352176fa4..03e0b4124 100755 --- a/src/scripts/MakeLinks +++ b/src/scripts/MakeLinks @@ -98,7 +98,8 @@ cd .. for f in blob.h dbfunctions.h exim.h functions.h globals.h \ hash.h hintsdb.h hintsdb_structs.h local_scan.h \ - macros.h mytypes.h osfunctions.h store.h structs.h lookupapi.h sha_ver.h \ + macros.h mytypes.h osfunctions.h path_max.h store.h \ + structs.h lookupapi.h sha_ver.h \ \ acl.c buildconfig.c base64.c child.c crypt16.c daemon.c dbfn.c debug.c \ deliver.c directory.c dns.c dnsbl.c drtables.c dummies.c enq.c exim.c \ diff --git a/src/src/exim.h b/src/src/exim.h index 699b39165..6baf1fbb4 100644 --- a/src/src/exim.h +++ b/src/src/exim.h @@ -127,16 +127,6 @@ making unique names. */ # define EXIM_ARITH_MIN (-EXIM_ARITH_MAX - 1) #endif -/* Some systems have PATH_MAX and some have MAX_PATH_LEN. */ - -#ifndef PATH_MAX -# ifdef MAX_PATH_LEN -# define PATH_MAX MAX_PATH_LEN -# else -# define PATH_MAX 1024 -# endif -#endif - /* RFC 5321 specifies that the maximum length of a local-part is 64 octets and the maximum length of a domain is 255 octets, but then also defines the maximum length of a forward/reverse path as 256 not 64+1+255. @@ -532,6 +522,7 @@ config.h, mytypes.h, and store.h, so we don't need to mention them explicitly. */ #include "local_scan.h" +#include "path_max.h" #include "macros.h" #include "hintsdb.h" #include "hintsdb_structs.h" diff --git a/src/src/os.c b/src/src/os.c index 3b49e4b9e..c4c86df95 100644 --- a/src/src/os.c +++ b/src/src/os.c @@ -878,9 +878,7 @@ os_getcwd(unsigned char * buffer, size_t size) return US getcwd(CS buffer, size); } #else -#ifndef PATH_MAX -# define PATH_MAX 4096 -#endif +# include "path_max.h" unsigned char * os_getcwd(unsigned char * buffer, size_t size) { diff --git a/src/src/path_max.h b/src/src/path_max.h new file mode 100644 index 000000000..d8df2c7bc --- /dev/null +++ b/src/src/path_max.h @@ -0,0 +1,19 @@ +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) The Exim Maintainers 2024 */ +/* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* This was in exim.h - but not all files needing it can include all of that. */ +/* Needed by macros.h */ +/* Some systems have PATH_MAX and some have MAX_PATH_LEN. */ +#ifndef PATH_MAX +# ifdef MAX_PATH_LEN +# define PATH_MAX MAX_PATH_LEN +# else +# define PATH_MAX 4096 +# endif +#endif + diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 34612adea..ca8e9b0de 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -1999,16 +1999,6 @@ SSL_CTX_free(ob->tls_preload.lib_ctx); ob->tls_preload = null_tls_preload; } -#else - -static void -tls_server_creds_invalidate(void) -{ return; } - -static void -tls_client_creds_invalidate(transport_instance * t) -{ return; } - #endif /*EXIM_HAVE_INOTIFY*/ diff --git a/src/src/tls.c b/src/src/tls.c index e6203b768..0e9a7c534 100644 --- a/src/src/tls.c +++ b/src/src/tls.c @@ -40,13 +40,16 @@ functions from the OpenSSL or GNU TLS libraries. */ static void tls_per_lib_daemon_init(void); static void tls_per_lib_daemon_tick(void); static unsigned tls_server_creds_init(void); -static void tls_server_creds_invalidate(void); static void tls_client_creds_init(transport_instance *, BOOL); -static void tls_client_creds_invalidate(transport_instance *); static void tls_daemon_creds_reload(void); static BOOL opt_set_and_noexpand(const uschar *); static BOOL opt_unset_or_noexpand(const uschar *); +#if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT) +static void tls_server_creds_invalidate(void); +static void tls_client_creds_invalidate(transport_instance *); +#endif + /* This module is compiled only when it is specifically requested in the @@ -324,7 +327,9 @@ tls_client_creds_reload(BOOL watch) for(transport_instance * t = transports; t; t = t->next) if (Ustrcmp(t->driver_name, "smtp") == 0) { +#if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT) tls_client_creds_invalidate(t); +#endif tls_client_creds_init(t, watch); } } @@ -360,7 +365,9 @@ unsigned lifetime; tls_watch_invalidate(); #endif +#if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT) tls_server_creds_invalidate(); +#endif /* _expire is for a time-limited selfsign server cert */ tls_creds_expire = (lifetime = tls_server_creds_init()) commit a2d8f8e2e39b0a24613c03d61355d1f23a481664 Author: Jeremy Harris Date: Tue May 14 12:28:36 2024 +0100 Debug: typo Broken-by: 1d2868b2d90c diff --git a/src/src/deliver.c b/src/src/deliver.c index f3c9203e4..a8376e58c 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -941,7 +941,7 @@ deliver_host = save_host; deliver_localpart = save_local; deliver_domain = save_domain; router_name = save_rn; -router_name = save_tn; +transport_name = save_tn; } #endif /*DISABLE_EVENT*/ commit b97c55112656f7d9cc5629b0cdb2e14a1b2ac649 Author: Jeremy Harris Date: Mon May 20 14:47:04 2024 +0100 Handle MAIL-before-EHLO under smtp_max_synprot_errors. Bug 3096 diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index f65ab6603..ff50c80f9 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -4352,9 +4352,10 @@ while (done <= 0) if ( fl.helo_verify_required || verify_check_host(&hosts_require_helo) == OK) { - smtp_printf("503 HELO or EHLO required\r\n", SP_NO_MORE); log_write(0, LOG_MAIN|LOG_REJECT, "rejected MAIL from %s: no " "HELO/EHLO given", host_and_ident(FALSE)); + done = synprot_error(L_smtp_protocol_error, 503, NULL, + US"HELO or EHLO required"); break; } else if (smtp_mailcmd_max < 0) commit 732f9b98e5b47b1ddf312f41c03060f1279be5fb Author: Jeremy Harris Date: Tue May 14 17:04:58 2024 +0100 tidying diff --git a/src/src/daemon.c b/src/src/daemon.c index 88114d648..9ed8a1c45 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -311,7 +311,7 @@ if (smtp_accept_max_per_host) /* For speed, interpret a decimal number inline here */ else { - uschar *s = expanded; + uschar * s = expanded; while (isdigit(*s)) max_for_this_host = max_for_this_host * 10 + *s++ - '0'; if (*s) @@ -320,9 +320,9 @@ if (smtp_accept_max_per_host) } } -/* If we have fewer connections than max_for_this_host, we can skip the tedious -per host_address checks. Note that at this stage smtp_accept_count contains the -count of *other* connections, not including this one. */ +/* If we have fewer total connections than max_for_this_host, we can skip the +tedious per host_address checks. Note that at this stage smtp_accept_count +contains the count of *other* connections, not including this one. */ if (max_for_this_host > 0 && smtp_accept_count >= max_for_this_host) { @@ -920,7 +920,7 @@ while ((pid = waitpid(-1, &status, WNOHANG)) > 0) smtp_slots[i] = empty_smtp_slot; if (--smtp_accept_count < 0) smtp_accept_count = 0; DEBUG(D_any) debug_printf("%d SMTP accept process%s now running\n", - smtp_accept_count, (smtp_accept_count == 1)? "" : "es"); + smtp_accept_count, smtp_accept_count == 1 ? "" : "es"); break; } if (i < smtp_accept_max) continue; /* Found an accepting process */ commit 48d6f3b6c68a33a4ff11650f9f0de858cb5fc256 Author: Jeremy Harris Date: Sun May 26 17:17:50 2024 +0100 Lookups: sub-path for dsearch diff --git a/src/src/lookups/dsearch.c b/src/src/lookups/dsearch.c index 74439bfc8..22003f4e5 100644 --- a/src/src/lookups/dsearch.c +++ b/src/src/lookups/dsearch.c @@ -70,6 +70,7 @@ return FALSE; #define FILTER_FILE BIT(2) #define FILTER_DIR BIT(3) #define FILTER_SUBDIR BIT(4) +#define ALLOW_PATH BIT(5) /* See local README for interface description. We use lstat() instead of scanning the directory, as it is hopefully faster to let the OS do the scanning @@ -85,13 +86,6 @@ int save_errno; uschar * filename; unsigned flags = 0; -if (Ustrchr(keystring, '/') != 0) - { - *errmsg = string_sprintf("key for dsearch lookup contains a slash: %s", - keystring); - return DEFER; - } - if (opts) { int sep = ','; @@ -110,6 +104,24 @@ if (opts) else if (Ustrcmp(ele, "subdir") == 0) flags |= FILTER_TYPE | FILTER_SUBDIR; /* like dir but not "." or ".." */ } + else if (Ustrcmp(ele, "key=path") == 0) + flags |= ALLOW_PATH; + } + +if (flags & ALLOW_PATH) + { + if (Ustrstr(keystring, "/../") != NULL || Ustrstr(keystring, "/./")) + { + *errmsg = string_sprintf( + "key for dsearch lookup contains bad component: %s", keystring); + return DEFER; + } + } +else if (Ustrchr(keystring, '/') != NULL) + { + *errmsg = string_sprintf("key for dsearch lookup contains a slash: %s", + keystring); + return DEFER; } filename = string_sprintf("%s/%s", dirname, keystring); @@ -189,3 +201,5 @@ static lookup_info *_lookup_list[] = { &_lookup_info }; lookup_module_info dsearch_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 1 }; /* End of lookups/dsearch.c */ +/* vi: aw ai sw=2 +*/ commit e0b3815dc398d7abaa1f81c9f26b1c9b050e94c0 Author: Jeremy Harris Date: Thu May 30 16:20:52 2024 +0100 SMTP WELLKNOWN extension diff --git a/src/src/EDITME b/src/src/EDITME index 4a33677d5..1440b4b44 100644 --- a/src/src/EDITME +++ b/src/src/EDITME @@ -591,6 +591,9 @@ DISABLE_MAL_MKS=yes # using only native facilities. # SUPPORT_SRS=yes +# Uncomment the following to remove support for the ESMTP extension "WELLKNOWN" +# DISABLE_WELLKNOWN=yes + #------------------------------------------------------------------------------ # Compiling Exim with experimental features. These are documented in diff --git a/src/src/acl.c b/src/src/acl.c index 4e88fc1ac..bfb32df6f 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -57,9 +57,7 @@ static int msgcond[] = { #endif -/* ACL condition and modifier codes - keep in step with the table that -follows. -down. */ +/* ACL condition and modifier codes */ enum { ACLC_ACL, ACLC_ADD_HEADER, @@ -119,7 +117,8 @@ enum { ACLC_ACL, ACLC_SPF_GUESS, #endif ACLC_UDPSEND, - ACLC_VERIFY }; + ACLC_VERIFY, +}; /* ACL conditions/modifiers: "delay", "control", "continue", "endpass", "message", "log_message", "log_reject_target", "logwrite", "queue" and "set" are @@ -149,7 +148,7 @@ static condition_def conditions[] = { [ACLC_ACL] = { US"acl", FALSE, FALSE, 0 }, [ACLC_ADD_HEADER] = { US"add_header", TRUE, TRUE, - (unsigned int) + (unsigned) ~(ACL_BIT_MAIL | ACL_BIT_RCPT | ACL_BIT_PREDATA | ACL_BIT_DATA | #ifndef DISABLE_PRDR @@ -188,7 +187,7 @@ static condition_def conditions[] = { #ifdef EXPERIMENTAL_DCC [ACLC_DCC] = { US"dcc", TRUE, FALSE, - (unsigned int) + (unsigned) ~(ACL_BIT_DATA | # ifndef DISABLE_PRDR ACL_BIT_PRDR | @@ -204,7 +203,7 @@ static condition_def conditions[] = { #ifndef DISABLE_DKIM [ACLC_DKIM_SIGNER] = { US"dkim_signers", TRUE, FALSE, (unsigned int) ~ACL_BIT_DKIM }, [ACLC_DKIM_STATUS] = { US"dkim_status", TRUE, FALSE, - (unsigned int) + (unsigned) ~(ACL_BIT_DKIM | ACL_BIT_DATA | ACL_BIT_MIME # ifndef DISABLE_PRDR | ACL_BIT_PRDR @@ -221,7 +220,7 @@ static condition_def conditions[] = { [ACLC_DNSLISTS] = { US"dnslists", TRUE, FALSE, 0 }, [ACLC_DOMAINS] = { US"domains", FALSE, FALSE, - (unsigned int) + (unsigned) ~(ACL_BIT_RCPT | ACL_BIT_VRFY #ifndef DISABLE_PRDR |ACL_BIT_PRDR @@ -239,7 +238,7 @@ static condition_def conditions[] = { ACL_BIT_NOTSMTP | ACL_BIT_NOTSMTP_START, }, [ACLC_LOCAL_PARTS] = { US"local_parts", FALSE, FALSE, - (unsigned int) + (unsigned) ~(ACL_BIT_RCPT | ACL_BIT_VRFY #ifndef DISABLE_PRDR | ACL_BIT_PRDR @@ -253,7 +252,7 @@ static condition_def conditions[] = { #ifdef WITH_CONTENT_SCAN [ACLC_MALWARE] = { US"malware", TRUE, FALSE, - (unsigned int) + (unsigned) ~(ACL_BIT_DATA | # ifndef DISABLE_PRDR ACL_BIT_PRDR | @@ -280,7 +279,7 @@ static condition_def conditions[] = { #ifdef WITH_CONTENT_SCAN [ACLC_REGEX] = { US"regex", TRUE, FALSE, - (unsigned int) + (unsigned) ~(ACL_BIT_DATA | # ifndef DISABLE_PRDR ACL_BIT_PRDR | @@ -291,7 +290,7 @@ static condition_def conditions[] = { #endif [ACLC_REMOVE_HEADER] = { US"remove_header", TRUE, TRUE, - (unsigned int) + (unsigned) ~(ACL_BIT_MAIL|ACL_BIT_RCPT | ACL_BIT_PREDATA | ACL_BIT_DATA | #ifndef DISABLE_PRDR @@ -320,7 +319,7 @@ static condition_def conditions[] = { #ifdef WITH_CONTENT_SCAN [ACLC_SPAM] = { US"spam", TRUE, FALSE, - (unsigned int) ~(ACL_BIT_DATA | + (unsigned) ~(ACL_BIT_DATA | # ifndef DISABLE_PRDR ACL_BIT_PRDR | # endif @@ -370,8 +369,7 @@ for (condition_def * c = conditions; c < conditions + nelem(conditions); c++) #ifndef MACRO_PREDEF -/* Return values from decode_control(); used as index so keep in step -with the controls_list table that follows! */ +/* Return values from decode_control() */ enum { CONTROL_AUTH_UNADVERTISED, @@ -411,6 +409,9 @@ enum { #ifdef SUPPORT_I18N CONTROL_UTF8_DOWNCONVERT, #endif +#ifndef DISABLE_WELLKNOWN + CONTROL_WELLKNOWN, +#endif }; @@ -564,7 +565,12 @@ static control_def controls_list[] = { #ifdef SUPPORT_I18N [CONTROL_UTF8_DOWNCONVERT] = { US"utf8_downconvert", TRUE, (unsigned) ~(ACL_BIT_RCPT | ACL_BIT_VRFY) - } + }, +#endif +#ifndef DISABLE_WELLKNOWN +[CONTROL_WELLKNOWN] = + { US"wellknown", TRUE, (unsigned) ~ACL_BIT_WELLKNOWN + }, #endif }; @@ -806,7 +812,7 @@ if (*s++ != '=') { *error = string_sprintf("\"=\" missing after ACL \"%s\" %s", name, conditions[cond->type].is_modifier ? US"modifier" : US"condition"); - return FALSE;; + return FALSE; } Uskip_whitespace(&s); cond->arg = taint ? string_copy_taint(s, GET_TAINTED) : string_copy(s); @@ -3122,6 +3128,80 @@ return DEFER; +#ifndef DISABLE_WELLKNOWN +/************************************************* +* The "wellknown" ACL modifier * +*************************************************/ + +/* Called by acl_check_condition() below. + +Retrieve the given file and encode content as xtext. +Prefix with a summary line giving the length of plaintext. +Leave a global pointer to the whole, for output by +the smtp verb handler code (smtp_in.c). + +Arguments: + arg the option string for wellknown= + log_msgptr for error messages + +Returns: OK/FAIL +*/ + +static int +wellknown_process(const uschar * arg, uschar ** log_msgptr) +{ +struct stat statbuf; +FILE * rf; +gstring * g; + +wellknown_response = NULL; +if (f.no_multiline_responses) return FAIL; + +/* Check for file existence */ + +if (!*arg) return FAIL; +if (Ustat(arg, &statbuf) != 0) + { *log_msgptr = US"stat"; goto fail; } + +/*XXX perhaps refuse to serve a group- or world-writeable file? */ + +if (!(rf = Ufopen(arg, "r"))) + { *log_msgptr = US"open"; goto fail; } + +/* Set up summary line for output */ + +g = string_fmt_append(NULL, "SIZE=%lu\n", (long) statbuf.st_size); + +#define LINE_LIM 75 +for (int n = 0, ch; (ch = fgetc(rf)) != EOF; ) + { + /* Xtext-encode, adding output linebreaks for input linebreaks + or when the line gets long enough */ + + if (ch == '\n') + { g = string_fmt_append(g, "+%02X", ch); n = LINE_LIM; } + else if (ch < 33 || ch > 126 || ch == '+' || ch == '=') + { g = string_fmt_append(g, "+%02X", ch); n += 3; } + else + { g = string_fmt_append(g, "%c", ch); n++; } + + if (n >= LINE_LIM) + { g = string_catn(g, US"\n", 1); n = 0; } + } +#undef LINE_LIM + +gstring_release_unused(g); +wellknown_response = string_from_gstring(g); +return OK; + +fail: + *log_msgptr = string_sprintf("wellknown: failed to %s file \"%s\": %s", + *log_msgptr, arg, strerror(errno)); + return FAIL; +} +#endif + + /************************************************* * Handle conditions/modifiers on an ACL item * *************************************************/ @@ -3311,7 +3391,7 @@ for (; cb; cb = cb->next) case ACLC_CONTROL: { - const uschar *p = NULL; + const uschar * p = NULL; control_type = decode_control(arg, &p, where, log_msgptr); /* Check if this control makes sense at this time */ @@ -3323,6 +3403,7 @@ for (; cb; cb = cb->next) return ERROR; } + /*XXX ought to sort these, just for sanity */ switch(control_type) { case CONTROL_AUTH_UNADVERTISED: @@ -3668,8 +3749,13 @@ for (; cb; cb = cb->next) break; } return ERROR; -#endif +#endif /*I18N*/ +#ifndef DISABLE_WELLKNOWN + case CONTROL_WELLKNOWN: + rc = *p == '/' ? wellknown_process(p+1, log_msgptr) : FAIL; + break; +#endif } break; } diff --git a/src/src/auths/xtextencode.c b/src/src/auths/xtextencode.c index 75be18161..00f36e544 100644 --- a/src/src/auths/xtextencode.c +++ b/src/src/auths/xtextencode.c @@ -40,3 +40,5 @@ return string_from_gstring(g); /* End of xtextencode.c */ +/* vi: aw ai sw=2 +*/ diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults index c08f1874d..22749b174 100644 --- a/src/src/config.h.defaults +++ b/src/src/config.h.defaults @@ -59,6 +59,7 @@ Do not put spaces between # and the 'define'. #define DISABLE_QUEUE_RAMP #define DISABLE_TLS #define DISABLE_TLS_RESUME +#define DISABLE_WELLKNOWN #define ENABLE_DISABLE_FSYNC diff --git a/src/src/exim.c b/src/src/exim.c index 040df2cd0..b78839b57 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -1108,6 +1108,9 @@ g = string_cat(g, US"Support for:"); #ifndef DISABLE_ESMTP_LIMITS g = string_cat(g, US" ESMTP_Limits"); #endif +#ifndef DISABLE_WELLKNOWN + g = string_cat(g, US" ESMTP_Wellknown"); +#endif #ifndef DISABLE_EVENT g = string_cat(g, US" Event"); #endif diff --git a/src/src/expand.c b/src/src/expand.c index 1d121756d..4bc680544 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -259,7 +259,9 @@ static uschar *op_table_main[] = { US"strlen", US"substr", US"uc", - US"utf8clean" }; + US"utf8clean", + US"xtextd", + }; enum { EOP_ADDRESS = nelem(op_table_underscore), @@ -307,7 +309,9 @@ enum { EOP_STRLEN, EOP_SUBSTR, EOP_UC, - EOP_UTF8CLEAN }; + EOP_UTF8CLEAN, + EOP_XTEXTD, + }; /* Table of condition names, and corresponding switch numbers. The names must @@ -7326,19 +7330,20 @@ NOT_ITEM: ; case EOP_LC: { - int count = 0; - uschar *t = sub - 1; - while (*(++t) != 0) { *t = tolower(*t); count++; } - yield = string_catn(yield, sub, count); + uschar * t = sub - 1; + while (*++t) *t = tolower(*t); + yield = string_catn(yield, sub, t-sub); break; } + { + uschar * s = sub; + } case EOP_UC: { - int count = 0; - uschar *t = sub - 1; - while (*(++t) != 0) { *t = toupper(*t); count++; } - yield = string_catn(yield, sub, count); + uschar * t = sub - 1; + while (*++t) *t = toupper(*t); + yield = string_catn(yield, sub, t-sub); break; } @@ -7774,7 +7779,6 @@ NOT_ITEM: ; } else yield = string_cat(yield, sub); - break; } /* quote_lookuptype does lookup-specific quoting */ @@ -7806,526 +7810,533 @@ NOT_ITEM: ; } yield = string_cat(yield, sub); - break; } + break; - /* rx quote sticks in \ before any non-alphameric character so that - the insertion works in a regular expression. */ + /* rx quote sticks in \ before any non-alphameric character so that + the insertion works in a regular expression. */ - case EOP_RXQUOTE: + case EOP_RXQUOTE: + { + uschar *t = sub - 1; + while (*(++t) != 0) { - uschar *t = sub - 1; - while (*(++t) != 0) - { - if (!isalnum(*t)) - yield = string_catn(yield, US"\\", 1); - yield = string_catn(yield, t, 1); - } - break; + if (!isalnum(*t)) + yield = string_catn(yield, US"\\", 1); + yield = string_catn(yield, t, 1); } + break; + } - /* RFC 2047 encodes, assuming headers_charset (default ISO 8859-1) as - prescribed by the RFC, if there are characters that need to be encoded */ + /* RFC 2047 encodes, assuming headers_charset (default ISO 8859-1) as + prescribed by the RFC, if there are characters that need to be encoded */ - case EOP_RFC2047: - yield = string_cat(yield, - parse_quote_2047(sub, Ustrlen(sub), headers_charset, - FALSE)); - break; + case EOP_RFC2047: + yield = string_cat(yield, + parse_quote_2047(sub, Ustrlen(sub), headers_charset, + FALSE)); + break; - /* RFC 2047 decode */ + /* RFC 2047 decode */ - case EOP_RFC2047D: + case EOP_RFC2047D: + { + int len; + uschar *error; + uschar *decoded = rfc2047_decode(sub, check_rfc2047_length, + headers_charset, '?', &len, &error); + if (error) { - int len; - uschar *error; - uschar *decoded = rfc2047_decode(sub, check_rfc2047_length, - headers_charset, '?', &len, &error); - if (error) - { - expand_string_message = error; - goto EXPAND_FAILED; - } - yield = string_catn(yield, decoded, len); - break; + expand_string_message = error; + goto EXPAND_FAILED; } + yield = string_catn(yield, decoded, len); + break; + } - /* from_utf8 converts UTF-8 to 8859-1, turning non-existent chars into - underscores */ + /* from_utf8 converts UTF-8 to 8859-1, turning non-existent chars into + underscores */ - case EOP_FROM_UTF8: + case EOP_FROM_UTF8: + { + uschar * buff = store_get(4, sub); + while (*sub) { - uschar * buff = store_get(4, sub); - while (*sub) - { - int c; - GETUTF8INC(c, sub); - if (c > 255) c = '_'; - buff[0] = c; - yield = string_catn(yield, buff, 1); - } - break; + int c; + GETUTF8INC(c, sub); + if (c > 255) c = '_'; + buff[0] = c; + yield = string_catn(yield, buff, 1); } + break; + } - /* replace illegal UTF-8 sequences by replacement character */ + /* replace illegal UTF-8 sequences by replacement character */ - #define UTF8_REPLACEMENT_CHAR US"?" + #define UTF8_REPLACEMENT_CHAR US"?" + + case EOP_UTF8CLEAN: + { + int seq_len = 0, index = 0, bytes_left = 0, complete; + u_long codepoint = (u_long)-1; + uschar seq_buff[4]; /* accumulate utf-8 here */ - case EOP_UTF8CLEAN: + /* Manually track tainting, as we deal in individual chars below */ + + if (!yield) + yield = string_get_tainted(Ustrlen(sub), sub); + else if (!yield->s || !yield->ptr) { - int seq_len = 0, index = 0, bytes_left = 0, complete; - u_long codepoint = (u_long)-1; - uschar seq_buff[4]; /* accumulate utf-8 here */ + yield->s = store_get(yield->size = Ustrlen(sub), sub); + gstring_reset(yield); + } + else if (is_incompatible(yield->s, sub)) + gstring_rebuffer(yield, sub); - /* Manually track tainting, as we deal in individual chars below */ + /* Check the UTF-8, byte-by-byte */ + + while (*sub) + { + complete = 0; + uschar c = *sub++; - if (!yield) - yield = string_get_tainted(Ustrlen(sub), sub); - else if (!yield->s || !yield->ptr) + if (bytes_left) { - yield->s = store_get(yield->size = Ustrlen(sub), sub); - gstring_reset(yield); + if ((c & 0xc0) != 0x80) + /* wrong continuation byte; invalidate all bytes */ + complete = 1; /* error */ + else + { + codepoint = (codepoint << 6) | (c & 0x3f); + seq_buff[index++] = c; + if (--bytes_left == 0) /* codepoint complete */ + if(codepoint > 0x10FFFF) /* is it too large? */ + complete = -1; /* error (RFC3629 limit) */ + else if ( (codepoint & 0x1FF800 ) == 0xD800 ) /* surrogate */ + /* A UTF-16 surrogate (which should be one of a pair that + encode a Unicode codepoint that is outside the Basic + Multilingual Plane). Error, not UTF8. + RFC2279.2 is slightly unclear on this, but + https://unicodebook.readthedocs.io/issues.html#strict-utf8-decoder + says "Surrogates characters are also invalid in UTF-8: + characters in U+D800—U+DFFF have to be rejected." */ + complete = -1; + else + { /* finished; output utf-8 sequence */ + yield = string_catn(yield, seq_buff, seq_len); + index = 0; + } + } } - else if (is_incompatible(yield->s, sub)) - gstring_rebuffer(yield, sub); - - /* Check the UTF-8, byte-by-byte */ - - while (*sub) + else /* no bytes left: new sequence */ { - complete = 0; - uschar c = *sub++; - - if (bytes_left) + if (!(c & 0x80)) /* 1-byte sequence, US-ASCII, keep it */ { - if ((c & 0xc0) != 0x80) - /* wrong continuation byte; invalidate all bytes */ - complete = 1; /* error */ + yield = string_catn(yield, &c, 1); + continue; + } + if ((c & 0xe0) == 0xc0) /* 2-byte sequence */ + if (c == 0xc0 || c == 0xc1) /* 0xc0 and 0xc1 are illegal */ + complete = -1; else { - codepoint = (codepoint << 6) | (c & 0x3f); - seq_buff[index++] = c; - if (--bytes_left == 0) /* codepoint complete */ - if(codepoint > 0x10FFFF) /* is it too large? */ - complete = -1; /* error (RFC3629 limit) */ - else if ( (codepoint & 0x1FF800 ) == 0xD800 ) /* surrogate */ - /* A UTF-16 surrogate (which should be one of a pair that - encode a Unicode codepoint that is outside the Basic - Multilingual Plane). Error, not UTF8. - RFC2279.2 is slightly unclear on this, but - https://unicodebook.readthedocs.io/issues.html#strict-utf8-decoder - says "Surrogates characters are also invalid in UTF-8: - characters in U+D800—U+DFFF have to be rejected." */ - complete = -1; - else - { /* finished; output utf-8 sequence */ - yield = string_catn(yield, seq_buff, seq_len); - index = 0; - } + bytes_left = 1; + codepoint = c & 0x1f; } + else if ((c & 0xf0) == 0xe0) /* 3-byte sequence */ + { + bytes_left = 2; + codepoint = c & 0x0f; } - else /* no bytes left: new sequence */ + else if ((c & 0xf8) == 0xf0) /* 4-byte sequence */ { - if (!(c & 0x80)) /* 1-byte sequence, US-ASCII, keep it */ - { - yield = string_catn(yield, &c, 1); - continue; - } - if ((c & 0xe0) == 0xc0) /* 2-byte sequence */ - if (c == 0xc0 || c == 0xc1) /* 0xc0 and 0xc1 are illegal */ - complete = -1; - else - { - bytes_left = 1; - codepoint = c & 0x1f; - } - else if ((c & 0xf0) == 0xe0) /* 3-byte sequence */ - { - bytes_left = 2; - codepoint = c & 0x0f; - } - else if ((c & 0xf8) == 0xf0) /* 4-byte sequence */ - { - bytes_left = 3; - codepoint = c & 0x07; - } - else /* invalid or too long (RFC3629 allows only 4 bytes) */ - complete = -1; + bytes_left = 3; + codepoint = c & 0x07; + } + else /* invalid or too long (RFC3629 allows only 4 bytes) */ + complete = -1; - seq_buff[index++] = c; - seq_len = bytes_left + 1; - } /* if(bytes_left) */ + seq_buff[index++] = c; + seq_len = bytes_left + 1; + } /* if(bytes_left) */ - if (complete != 0) - { - bytes_left = index = 0; - yield = string_catn(yield, UTF8_REPLACEMENT_CHAR, 1); - } - if ((complete == 1) && ((c & 0x80) == 0)) - /* ASCII character follows incomplete sequence */ - yield = string_catn(yield, &c, 1); + if (complete != 0) + { + bytes_left = index = 0; + yield = string_catn(yield, UTF8_REPLACEMENT_CHAR, 1); } - /* If given a sequence truncated mid-character, we also want to report ? - Eg, ${length_1:フィル} is one byte, not one character, so we expect - ${utf8clean:${length_1:フィル}} to yield '?' */ + if ((complete == 1) && ((c & 0x80) == 0)) + /* ASCII character follows incomplete sequence */ + yield = string_catn(yield, &c, 1); + } + /* If given a sequence truncated mid-character, we also want to report ? + Eg, ${length_1:フィル} is one byte, not one character, so we expect + ${utf8clean:${length_1:フィル}} to yield '?' */ - if (bytes_left != 0) - yield = string_catn(yield, UTF8_REPLACEMENT_CHAR, 1); + if (bytes_left != 0) + yield = string_catn(yield, UTF8_REPLACEMENT_CHAR, 1); - break; - } + break; + } #ifdef SUPPORT_I18N - case EOP_UTF8_DOMAIN_TO_ALABEL: + case EOP_UTF8_DOMAIN_TO_ALABEL: + { + uschar * error = NULL; + uschar * s = string_domain_utf8_to_alabel(sub, &error); + if (error) { - uschar * error = NULL; - uschar * s = string_domain_utf8_to_alabel(sub, &error); - if (error) - { - expand_string_message = string_sprintf( - "error converting utf8 (%s) to alabel: %s", - string_printing(sub), error); - goto EXPAND_FAILED; - } - yield = string_cat(yield, s); - break; + expand_string_message = string_sprintf( + "error converting utf8 (%s) to alabel: %s", + string_printing(sub), error); + goto EXPAND_FAILED; } + yield = string_cat(yield, s); + break; + } - case EOP_UTF8_DOMAIN_FROM_ALABEL: + case EOP_UTF8_DOMAIN_FROM_ALABEL: + { + uschar * error = NULL; + uschar * s = string_domain_alabel_to_utf8(sub, &error); + if (error) { - uschar * error = NULL; - uschar * s = string_domain_alabel_to_utf8(sub, &error); - if (error) - { - expand_string_message = string_sprintf( - "error converting alabel (%s) to utf8: %s", - string_printing(sub), error); - goto EXPAND_FAILED; - } - yield = string_cat(yield, s); - break; + expand_string_message = string_sprintf( + "error converting alabel (%s) to utf8: %s", + string_printing(sub), error); + goto EXPAND_FAILED; } + yield = string_cat(yield, s); + break; + } - case EOP_UTF8_LOCALPART_TO_ALABEL: + case EOP_UTF8_LOCALPART_TO_ALABEL: + { + uschar * error = NULL; + uschar * s = string_localpart_utf8_to_alabel(sub, &error); + if (error) { - uschar * error = NULL; - uschar * s = string_localpart_utf8_to_alabel(sub, &error); - if (error) - { - expand_string_message = string_sprintf( - "error converting utf8 (%s) to alabel: %s", - string_printing(sub), error); - goto EXPAND_FAILED; - } - yield = string_cat(yield, s); - DEBUG(D_expand) debug_printf_indent("yield: '%Y'\n", yield); - break; + expand_string_message = string_sprintf( + "error converting utf8 (%s) to alabel: %s", + string_printing(sub), error); + goto EXPAND_FAILED; } + yield = string_cat(yield, s); + DEBUG(D_expand) debug_printf_indent("yield: '%Y'\n", yield); + break; + } - case EOP_UTF8_LOCALPART_FROM_ALABEL: + case EOP_UTF8_LOCALPART_FROM_ALABEL: + { + uschar * error = NULL; + uschar * s = string_localpart_alabel_to_utf8(sub, &error); + if (error) { - uschar * error = NULL; - uschar * s = string_localpart_alabel_to_utf8(sub, &error); - if (error) - { - expand_string_message = string_sprintf( - "error converting alabel (%s) to utf8: %s", - string_printing(sub), error); - goto EXPAND_FAILED; - } - yield = string_cat(yield, s); - break; + expand_string_message = string_sprintf( + "error converting alabel (%s) to utf8: %s", + string_printing(sub), error); + goto EXPAND_FAILED; } + yield = string_cat(yield, s); + break; + } #endif /* EXPERIMENTAL_INTERNATIONAL */ - /* escape turns all non-printing characters into escape sequences. */ + /* escape turns all non-printing characters into escape sequences. */ - case EOP_ESCAPE: - { - const uschar * t = string_printing(sub); - yield = string_cat(yield, t); - break; - } + case EOP_ESCAPE: + { + const uschar * t = string_printing(sub); + yield = string_cat(yield, t); + break; + } - case EOP_ESCAPE8BIT: - { - uschar c; + case EOP_ESCAPE8BIT: + { + uschar c; - for (const uschar * s = sub; (c = *s); s++) - yield = c < 127 && c != '\\' - ? string_catn(yield, s, 1) - : string_fmt_append(yield, "\\%03o", c); - break; - } + for (const uschar * s = sub; (c = *s); s++) + yield = c < 127 && c != '\\' + ? string_catn(yield, s, 1) + : string_fmt_append(yield, "\\%03o", c); + break; + } - /* Handle numeric expression evaluation */ + /* Handle numeric expression evaluation */ - case EOP_EVAL: - case EOP_EVAL10: + case EOP_EVAL: + case EOP_EVAL10: + { + uschar *save_sub = sub; + uschar *error = NULL; + int_eximarith_t n = eval_expr(&sub, (c == EOP_EVAL10), &error, FALSE); + if (error) { - uschar *save_sub = sub; - uschar *error = NULL; - int_eximarith_t n = eval_expr(&sub, (c == EOP_EVAL10), &error, FALSE); - if (error) - { - expand_string_message = string_sprintf("error in expression " - "evaluation: %s (after processing \"%.*s\")", error, - (int)(sub-save_sub), save_sub); - goto EXPAND_FAILED; - } - yield = string_fmt_append(yield, PR_EXIM_ARITH, n); - break; + expand_string_message = string_sprintf("error in expression " + "evaluation: %s (after processing \"%.*s\")", error, + (int)(sub-save_sub), save_sub); + goto EXPAND_FAILED; } + yield = string_fmt_append(yield, PR_EXIM_ARITH, n); + break; + } - /* Handle time period formatting */ + /* Handle time period formatting */ - case EOP_TIME_EVAL: + case EOP_TIME_EVAL: + { + int n = readconf_readtime(sub, 0, FALSE); + if (n < 0) { - int n = readconf_readtime(sub, 0, FALSE); - if (n < 0) - { - expand_string_message = string_sprintf("string \"%s\" is not an " - "Exim time interval in \"%s\" operator", sub, name); - goto EXPAND_FAILED; - } - yield = string_fmt_append(yield, "%d", n); - break; + expand_string_message = string_sprintf("string \"%s\" is not an " + "Exim time interval in \"%s\" operator", sub, name); + goto EXPAND_FAILED; } + yield = string_fmt_append(yield, "%d", n); + break; + } - case EOP_TIME_INTERVAL: + case EOP_TIME_INTERVAL: + { + int n; + uschar *t = read_number(&n, sub); + if (*t != 0) /* Not A Number*/ { - int n; - uschar *t = read_number(&n, sub); - if (*t != 0) /* Not A Number*/ - { - expand_string_message = string_sprintf("string \"%s\" is not a " - "positive number in \"%s\" operator", sub, name); - goto EXPAND_FAILED; - } - t = readconf_printtime(n); - yield = string_cat(yield, t); - break; + expand_string_message = string_sprintf("string \"%s\" is not a " + "positive number in \"%s\" operator", sub, name); + goto EXPAND_FAILED; } + t = readconf_printtime(n); + yield = string_cat(yield, t); + break; + } - /* Convert string to base64 encoding */ + /* Convert string to base64 encoding */ - case EOP_STR2B64: - case EOP_BASE64: - { + case EOP_STR2B64: + case EOP_BASE64: + { #ifndef DISABLE_TLS - uschar * s = vp && *(void **)vp->value - ? tls_cert_der_b64(*(void **)vp->value) - : b64encode(CUS sub, Ustrlen(sub)); + uschar * s = vp && *(void **)vp->value + ? tls_cert_der_b64(*(void **)vp->value) + : b64encode(CUS sub, Ustrlen(sub)); #else - uschar * s = b64encode(CUS sub, Ustrlen(sub)); + uschar * s = b64encode(CUS sub, Ustrlen(sub)); #endif - yield = string_cat(yield, s); - break; - } + yield = string_cat(yield, s); + break; + } - case EOP_BASE64D: + case EOP_BASE64D: + { + uschar * s; + int len = b64decode(sub, &s, sub); + if (len < 0) { - uschar * s; - int len = b64decode(sub, &s, sub); - if (len < 0) - { - expand_string_message = string_sprintf("string \"%s\" is not " - "well-formed for \"%s\" operator", sub, name); - goto EXPAND_FAILED; - } - yield = string_cat(yield, s); - break; + expand_string_message = string_sprintf("string \"%s\" is not " + "well-formed for \"%s\" operator", sub, name); + goto EXPAND_FAILED; } + yield = string_cat(yield, s); + break; + } - /* strlen returns the length of the string */ + /* strlen returns the length of the string */ - case EOP_STRLEN: - yield = string_fmt_append(yield, "%d", Ustrlen(sub)); - break; + case EOP_STRLEN: + yield = string_fmt_append(yield, "%d", Ustrlen(sub)); + break; + + /* length_n or l_n takes just the first n characters or the whole string, + whichever is the shorter; + + substr_m_n, and s_m_n take n characters from offset m; negative m take + from the end; l_n is synonymous with s_0_n. If n is omitted in substr it + takes the rest, either to the right or to the left. + + hash_n or h_n makes a hash of length n from the string, yielding n + characters from the set a-z; hash_n_m makes a hash of length n, but + uses m characters from the set a-zA-Z0-9. + + nhash_n returns a single number between 0 and n-1 (in text form), while + nhash_n_m returns a div/mod hash as two numbers "a/b". The first lies + between 0 and n-1 and the second between 0 and m-1. */ + + case EOP_LENGTH: + case EOP_L: + case EOP_SUBSTR: + case EOP_S: + case EOP_HASH: + case EOP_H: + case EOP_NHASH: + case EOP_NH: + { + int sign = 1; + int value1 = 0; + int value2 = -1; + int *pn; + int len; + uschar *ret; - /* length_n or l_n takes just the first n characters or the whole string, - whichever is the shorter; - - substr_m_n, and s_m_n take n characters from offset m; negative m take - from the end; l_n is synonymous with s_0_n. If n is omitted in substr it - takes the rest, either to the right or to the left. - - hash_n or h_n makes a hash of length n from the string, yielding n - characters from the set a-z; hash_n_m makes a hash of length n, but - uses m characters from the set a-zA-Z0-9. - - nhash_n returns a single number between 0 and n-1 (in text form), while - nhash_n_m returns a div/mod hash as two numbers "a/b". The first lies - between 0 and n-1 and the second between 0 and m-1. */ - - case EOP_LENGTH: - case EOP_L: - case EOP_SUBSTR: - case EOP_S: - case EOP_HASH: - case EOP_H: - case EOP_NHASH: - case EOP_NH: + if (!arg) { - int sign = 1; - int value1 = 0; - int value2 = -1; - int *pn; - int len; - uschar *ret; + expand_string_message = string_sprintf("missing values after %s", + name); + goto EXPAND_FAILED; + } - if (!arg) - { - expand_string_message = string_sprintf("missing values after %s", - name); - goto EXPAND_FAILED; - } + /* "length" has only one argument, effectively being synonymous with + substr_0_n. */ - /* "length" has only one argument, effectively being synonymous with - substr_0_n. */ + if (c == EOP_LENGTH || c == EOP_L) + { + pn = &value2; + value2 = 0; + } - if (c == EOP_LENGTH || c == EOP_L) + /* The others have one or two arguments; for "substr" the first may be + negative. The second being negative means "not supplied". */ + + else + { + pn = &value1; + if (name[0] == 's' && *arg == '-') { sign = -1; arg++; } + } + + /* Read up to two numbers, separated by underscores */ + + ret = arg; + while (*arg != 0) + { + if (arg != ret && *arg == '_' && pn == &value1) { pn = &value2; value2 = 0; + if (arg[1] != 0) arg++; } - - /* The others have one or two arguments; for "substr" the first may be - negative. The second being negative means "not supplied". */ - - else + else if (!isdigit(*arg)) { - pn = &value1; - if (name[0] == 's' && *arg == '-') { sign = -1; arg++; } + expand_string_message = + string_sprintf("non-digit after underscore in \"%s\"", name); + goto EXPAND_FAILED; } + else *pn = (*pn)*10 + *arg++ - '0'; + } + value1 *= sign; - /* Read up to two numbers, separated by underscores */ - - ret = arg; - while (*arg != 0) - { - if (arg != ret && *arg == '_' && pn == &value1) - { - pn = &value2; - value2 = 0; - if (arg[1] != 0) arg++; - } - else if (!isdigit(*arg)) - { - expand_string_message = - string_sprintf("non-digit after underscore in \"%s\"", name); - goto EXPAND_FAILED; - } - else *pn = (*pn)*10 + *arg++ - '0'; - } - value1 *= sign; + /* Perform the required operation */ - /* Perform the required operation */ + ret = c == EOP_HASH || c == EOP_H + ? compute_hash(sub, value1, value2, &len) + : c == EOP_NHASH || c == EOP_NH + ? compute_nhash(sub, value1, value2, &len) + : extract_substr(sub, value1, value2, &len); + if (!ret) goto EXPAND_FAILED; - ret = c == EOP_HASH || c == EOP_H - ? compute_hash(sub, value1, value2, &len) - : c == EOP_NHASH || c == EOP_NH - ? compute_nhash(sub, value1, value2, &len) - : extract_substr(sub, value1, value2, &len); - if (!ret) goto EXPAND_FAILED; + yield = string_catn(yield, ret, len); + break; + } - yield = string_catn(yield, ret, len); - break; - } + /* Stat a path */ - /* Stat a path */ + case EOP_STAT: + { + uschar smode[12]; + uschar **modetable[3]; + mode_t mode; + struct stat st; - case EOP_STAT: + if (expand_forbid & RDO_EXISTS) { - uschar smode[12]; - uschar **modetable[3]; - mode_t mode; - struct stat st; + expand_string_message = US"Use of the stat() expansion is not permitted"; + goto EXPAND_FAILED; + } - if (expand_forbid & RDO_EXISTS) - { - expand_string_message = US"Use of the stat() expansion is not permitted"; - goto EXPAND_FAILED; - } + if (stat(CS sub, &st) < 0) + { + expand_string_message = string_sprintf("stat(%s) failed: %s", + sub, strerror(errno)); + goto EXPAND_FAILED; + } + mode = st.st_mode; + switch (mode & S_IFMT) + { + case S_IFIFO: smode[0] = 'p'; break; + case S_IFCHR: smode[0] = 'c'; break; + case S_IFDIR: smode[0] = 'd'; break; + case S_IFBLK: smode[0] = 'b'; break; + case S_IFREG: smode[0] = '-'; break; + default: smode[0] = '?'; break; + } - if (stat(CS sub, &st) < 0) - { - expand_string_message = string_sprintf("stat(%s) failed: %s", - sub, strerror(errno)); - goto EXPAND_FAILED; - } - mode = st.st_mode; - switch (mode & S_IFMT) - { - case S_IFIFO: smode[0] = 'p'; break; - case S_IFCHR: smode[0] = 'c'; break; - case S_IFDIR: smode[0] = 'd'; break; - case S_IFBLK: smode[0] = 'b'; break; - case S_IFREG: smode[0] = '-'; break; - default: smode[0] = '?'; break; - } + modetable[0] = ((mode & 01000) == 0)? mtable_normal : mtable_sticky; + modetable[1] = ((mode & 02000) == 0)? mtable_normal : mtable_setid; + modetable[2] = ((mode & 04000) == 0)? mtable_normal : mtable_setid; - modetable[0] = ((mode & 01000) == 0)? mtable_normal : mtable_sticky; - modetable[1] = ((mode & 02000) == 0)? mtable_normal : mtable_setid; - modetable[2] = ((mode & 04000) == 0)? mtable_normal : mtable_setid; + for (int i = 0; i < 3; i++) + { + memcpy(CS(smode + 7 - i*3), CS(modetable[i][mode & 7]), 3); + mode >>= 3; + } - for (int i = 0; i < 3; i++) - { - memcpy(CS(smode + 7 - i*3), CS(modetable[i][mode & 7]), 3); - mode >>= 3; - } + smode[10] = 0; + yield = string_fmt_append(yield, + "mode=%04lo smode=%s inode=%ld device=%ld links=%ld " + "uid=%ld gid=%ld size=" OFF_T_FMT " atime=%ld mtime=%ld ctime=%ld", + (long)(st.st_mode & 077777), smode, (long)st.st_ino, + (long)st.st_dev, (long)st.st_nlink, (long)st.st_uid, + (long)st.st_gid, st.st_size, (long)st.st_atime, + (long)st.st_mtime, (long)st.st_ctime); + break; + } - smode[10] = 0; - yield = string_fmt_append(yield, - "mode=%04lo smode=%s inode=%ld device=%ld links=%ld " - "uid=%ld gid=%ld size=" OFF_T_FMT " atime=%ld mtime=%ld ctime=%ld", - (long)(st.st_mode & 077777), smode, (long)st.st_ino, - (long)st.st_dev, (long)st.st_nlink, (long)st.st_uid, - (long)st.st_gid, st.st_size, (long)st.st_atime, - (long)st.st_mtime, (long)st.st_ctime); - break; - } + /* vaguely random number less than N */ - /* vaguely random number less than N */ + case EOP_RANDINT: + { + int_eximarith_t max = expanded_string_integer(sub, TRUE); - case EOP_RANDINT: - { - int_eximarith_t max = expanded_string_integer(sub, TRUE); + if (expand_string_message) + goto EXPAND_FAILED; + yield = string_fmt_append(yield, "%d", vaguely_random_number((int)max)); + break; + } - if (expand_string_message) - goto EXPAND_FAILED; - yield = string_fmt_append(yield, "%d", vaguely_random_number((int)max)); - break; - } + /* Reverse IP, including IPv6 to dotted-nibble */ - /* Reverse IP, including IPv6 to dotted-nibble */ + case EOP_REVERSE_IP: + { + int family, maskptr; + uschar reversed[128]; - case EOP_REVERSE_IP: + family = string_is_ip_address(sub, &maskptr); + if (family == 0) { - int family, maskptr; - uschar reversed[128]; - - family = string_is_ip_address(sub, &maskptr); - if (family == 0) - { - expand_string_message = string_sprintf( - "reverse_ip() not given an IP address [%s]", sub); - goto EXPAND_FAILED; - } - invert_address(reversed, sub); - yield = string_cat(yield, reversed); - break; + expand_string_message = string_sprintf( + "reverse_ip() not given an IP address [%s]", sub); + goto EXPAND_FAILED; } + invert_address(reversed, sub); + yield = string_cat(yield, reversed); + break; + } - /* Unknown operator */ + case EOP_XTEXTD: + { + uschar * s; + int len = auth_xtextdecode(sub, &s); + yield = string_catn(yield, s, len); + break; + } - default: - expand_string_message = - string_sprintf("unknown expansion operator \"%s\"", name); - goto EXPAND_FAILED; - } /* EOP_* switch */ + /* Unknown operator */ + default: + expand_string_message = + string_sprintf("unknown expansion operator \"%s\"", name); + goto EXPAND_FAILED; + } /* EOP_* switch */ - DEBUG(D_expand) + DEBUG(D_expand) { const uschar * res = string_from_gstring(yield); const uschar * s = res + expansion_start; diff --git a/src/src/globals.c b/src/src/globals.c index 4e5fd2991..8230fe9cb 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -467,7 +467,7 @@ uschar *acl_smtp_quit = NULL; uschar *acl_smtp_rcpt = NULL; uschar *acl_smtp_starttls = NULL; uschar *acl_smtp_vrfy = NULL; -#ifdef EXPERIMENTAL_WELLKNOWN +#ifndef DISABLE_WELLKNOWN uschar *acl_smtp_wellknown = NULL; #endif @@ -500,6 +500,9 @@ uschar *acl_wherenames[] = { [ACL_WHERE_RCPT] = US"RCPT", [ACL_WHERE_QUIT] = US"QUIT", [ACL_WHERE_STARTTLS] = US"STARTTLS", [ACL_WHERE_VRFY] = US"VRFY", +#ifndef DISABLE_WELLKNOWN + [ACL_WHERE_WELLKNOWN] = US"WELLKNOWN", +#endif [ACL_WHERE_DELIVERY] = US"delivery", [ACL_WHERE_UNKNOWN] = US"unknown" }; @@ -519,6 +522,9 @@ uschar *acl_wherecodes[] = { [ACL_WHERE_RCPT] = US"550", [ACL_WHERE_EXPN] = US"550", [ACL_WHERE_HELO] = US"550", [ACL_WHERE_STARTTLS] = US"550", +#ifndef DISABLE_WELLKNOWN + [ACL_WHERE_WELLKNOWN] =US"550", +#endif [ACL_WHERE_VRFY] = US"252", }; @@ -999,9 +1005,6 @@ uschar *hosts_proxy = NULL; #endif uschar *hosts_treat_as_local = NULL; uschar *hosts_require_helo = US"*"; -#ifdef EXPERIMENTAL_WELLKNOWN -uschar *hosts_wellknown = NULL; -#endif #ifdef EXPERIMENTAL_XCLIENT uschar *hosts_xclient = NULL; #endif @@ -1669,6 +1672,11 @@ int warning_count = 0; const uschar *warnmsg_delay = NULL; const uschar *warnmsg_recipients = NULL; +#ifndef DISABLE_WELLKNOWN +uschar *wellknown_advertise_hosts = NULL; +uschar *wellknown_response = NULL; +#endif + /* End of globals.c */ /* vi: aw ai sw=2 */ diff --git a/src/src/globals.h b/src/src/globals.h index 30c8bbad4..febc30c84 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -342,6 +342,9 @@ extern uschar *acl_smtp_quit; /* ACL run for QUIT */ extern uschar *acl_smtp_rcpt; /* ACL run for RCPT */ extern uschar *acl_smtp_starttls; /* ACL run for STARTTLS */ extern uschar *acl_smtp_vrfy; /* ACL run for VRFY */ +#ifndef DISABLE_WELLKNOWN +extern uschar *acl_smtp_wellknown; /* ACL run for WELLKNOWN */ +#endif extern tree_node *acl_var_c; /* ACL connection variables */ extern tree_node *acl_var_m; /* ACL message variables */ extern uschar *acl_verify_message; /* User message for verify failure */ @@ -1134,4 +1137,9 @@ extern uschar *version_string; /* Version string */ extern int warning_count; /* Delay warnings sent for this msg */ +#ifndef DISABLE_WELLKNOWN +extern uschar *wellknown_advertise_hosts;/* Allow WELLKNOWN command for specified hosts */ +extern uschar *wellknown_response; /* SMTP response for WELLKNOWN verb */ +#endif + /* End of globals.h */ diff --git a/src/src/macro_predef.c b/src/src/macro_predef.c index 9b354d345..55401e316 100644 --- a/src/src/macro_predef.c +++ b/src/src/macro_predef.c @@ -214,6 +214,9 @@ due to conflicts with other common macros. */ #ifndef DISABLE_TLS_RESUME builtin_macro_create(US"_HAVE_TLS_RESUME"); #endif +#ifndef DISABLE_WELLKNOWN + builtin_macro_create(US"_HAVE_WELLKNOWN"); +#endif #ifdef EXPERIMENTAL_XCLIENT builtin_macro_create(US"_HAVE_XCLIENT"); #endif diff --git a/src/src/macros.h b/src/src/macros.h index 2938b2523..16d1503f2 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -828,6 +828,9 @@ enum { SCH_NONE, SCH_AUTH, SCH_DATA, SCH_BDAT, SCH_EHLO, SCH_ETRN, SCH_EXPN, SCH_HELO, SCH_HELP, SCH_MAIL, SCH_NOOP, SCH_QUIT, SCH_RCPT, SCH_RSET, SCH_STARTTLS, SCH_VRFY, +#ifndef DISABLE_WELLKNOWN + SCH_WELLKNOWN, +#endif #ifdef EXPERIMENTAL_XCLIENT SCH_XCLIENT, #endif @@ -972,6 +975,9 @@ enum { ACL_WHERE_RCPT, /* Some controls are for RCPT only */ ACL_WHERE_NOTQUIT, ACL_WHERE_QUIT, ACL_WHERE_STARTTLS, +#ifndef DISABLE_WELLKNOWN + ACL_WHERE_WELLKNOWN, +#endif ACL_WHERE_VRFY, ACL_WHERE_DELIVERY, @@ -1001,6 +1007,9 @@ enum { ACL_WHERE_RCPT, /* Some controls are for RCPT only */ #define ACL_BIT_QUIT BIT(ACL_WHERE_QUIT) #define ACL_BIT_STARTTLS BIT(ACL_WHERE_STARTTLS) #define ACL_BIT_VRFY BIT(ACL_WHERE_VRFY) +#ifndef DISABLE_WELLKNOWN +# define ACL_BIT_WELLKNOWN BIT(ACL_WHERE_WELLKNOWN) +#endif #define ACL_BIT_DELIVERY BIT(ACL_WHERE_DELIVERY) #define ACL_BIT_UNKNOWN BIT(ACL_WHERE_UNKNOWN) @@ -1201,3 +1210,5 @@ When doing en extended loop of matching, release store periodically. */ DEBUG(D_expand) debug_printf("try option " name "\n"); /* End of macros.h */ +/* vi: aw ai sw=2 +*/ diff --git a/src/src/readconf.c b/src/src/readconf.c index d87a56f3d..aef07184a 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -66,6 +66,9 @@ static optionlist optionlist_config[] = { { "acl_smtp_starttls", opt_stringptr, {&acl_smtp_starttls} }, #endif { "acl_smtp_vrfy", opt_stringptr, {&acl_smtp_vrfy} }, +#ifndef DISABLE_WELLKNOWN + { "acl_smtp_wellknown", opt_stringptr, {&acl_smtp_wellknown} }, +#endif { "add_environment", opt_stringptr, {&add_environment} }, { "admin_groups", opt_gidlist, {&admin_groups} }, { "allow_domain_literals", opt_bool, {&allow_domain_literals} }, @@ -402,6 +405,9 @@ static optionlist optionlist_config[] = { { "uucp_from_pattern", opt_stringptr, {&uucp_from_pattern} }, { "uucp_from_sender", opt_stringptr, {&uucp_from_sender} }, { "warn_message_file", opt_stringptr, {&warn_message_file} }, +#ifndef DISABLE_WELLKNOWN + { "wellknown_advertise_hosts",opt_stringptr, {&wellknown_advertise_hosts} }, +#endif { "write_rejectlog", opt_bool, {&write_rejectlog} }, }; diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index ff50c80f9..c941d115c 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -86,6 +86,9 @@ enum { /* These commands need not be synchronized when pipelining */ MAIL_CMD, RCPT_CMD, RSET_CMD, +#ifndef DISABLE_WELLKNOWN + WELLKNOWN_CMD, +#endif /* This is a dummy to identify the non-sync commands when not pipelining */ @@ -121,7 +124,8 @@ enum { /* These are specials that don't correspond to actual commands */ EOF_CMD, OTHER_CMD, BADARG_CMD, BADCHAR_CMD, BADSYN_CMD, - TOO_MANY_NONMAIL_CMD }; + TOO_MANY_NONMAIL_CMD +}; /* This is a convenience macro for adding the identity of an SMTP command @@ -230,7 +234,10 @@ static smtp_cmd_list cmd_list[] = { { "etrn", sizeof("etrn")-1, ETRN_CMD, TRUE, FALSE }, { "vrfy", sizeof("vrfy")-1, VRFY_CMD, TRUE, FALSE }, { "expn", sizeof("expn")-1, EXPN_CMD, TRUE, FALSE }, - { "help", sizeof("help")-1, HELP_CMD, TRUE, FALSE } + { "help", sizeof("help")-1, HELP_CMD, TRUE, FALSE }, +#ifndef DISABLE_WELLKNOWN + { "wellknown", sizeof("wellknown")-1, WELLKNOWN_CMD, TRUE, FALSE }, +#endif }; /* This list of names is used for performing the smtp_no_mail logging action. */ @@ -253,6 +260,9 @@ uschar * smtp_names[] = [SCH_RSET] = US"RSET", [SCH_STARTTLS] = US"STARTTLS", [SCH_VRFY] = US"VRFY", +#ifndef DISABLE_WELLKNOWN + [SCH_WELLKNOWN] = US"WELLKNOWN", +#endif #ifdef EXPERIMENTAL_XCLIENT [SCH_XCLIENT] = US"XCLIENT", #endif @@ -1946,7 +1956,7 @@ while (done <= 0) case HELP_CMD: case NOOP_CMD: case ETRN_CMD: -#ifdef EXPERIMENTAL_WELLKNOWN +#ifndef DISABLE_WELLKNOWN case WELLKNOWN_CMD: #endif bsmtp_transaction_linecount = receive_linecount; @@ -2832,10 +2842,8 @@ if (fl.rcpt_in_progress) We only handle pipelining these responses as far as nonfinal/final groups, not the whole MAIL/RCPT/DATA response set. */ -for (;;) - { - uschar *nl = Ustrchr(msg, '\n'); - if (!nl) +for (uschar * nl;;) + if (!(nl = Ustrchr(msg, '\n'))) { smtp_printf("%.3s%c%.*s%s\r\n", !final, code, final ? ' ':'-', esclen, esc, msg); return; @@ -2852,7 +2860,6 @@ for (;;) msg = nl + 1; Uskip_whitespace(&msg); } - } } @@ -2971,7 +2978,8 @@ smtp_code = rc == FAIL ? acl_wherecodes[where] : US"451"; smtp_message_code(&smtp_code, &codelen, &user_msg, &log_msg, where != ACL_WHERE_VRFY); -/* We used to have sender_address here; however, there was a bug that was not +/* Get info for logging. +We used to have sender_address here; however, there was a bug that was not updating sender_address after a rewrite during a verify. When this bug was fixed, sender_address at this point became the rewritten address. I'm not sure this is what should be logged, so I've changed to logging the unrewritten @@ -2996,7 +3004,7 @@ switch (where) { uschar * s = smtp_cmd_data; Uskip_nonwhite(&s); - lim = s - smtp_cmd_data; /* atop after method */ + lim = s - smtp_cmd_data; /* stop after method */ } what = string_sprintf("%s %.*s", acl_wherenames[where], lim, place); } @@ -3375,7 +3383,7 @@ Returns: nothing */ static void -smtp_user_msg(uschar *code, uschar *user_msg) +smtp_user_msg(uschar * code, uschar * user_msg) { int len = 3; smtp_message_code(&code, &len, &user_msg, NULL, TRUE); @@ -3581,6 +3589,36 @@ if (chunking_state > CHUNKING_OFFERED) } +#ifndef DISABLE_WELLKNOWN +static int +smtp_wellknown_handler(void) +{ +if (verify_check_host(&wellknown_advertise_hosts) != FAIL) + { + GET_OPTION("acl_smtp_wellknown"); + if (acl_smtp_wellknown) + { + uschar * user_msg = NULL, * log_msg; + int rc; + + if ((rc = acl_check(ACL_WHERE_WELLKNOWN, NULL, acl_smtp_wellknown, + &user_msg, &log_msg)) != OK) + return smtp_handle_acl_fail(ACL_WHERE_WELLKNOWN, rc, user_msg, log_msg); + else if (!wellknown_response) + return smtp_handle_acl_fail(ACL_WHERE_WELLKNOWN, ERROR, user_msg, log_msg); + smtp_user_msg(US"250", wellknown_response); + return 0; + } + } + +smtp_printf("554 not permitted\r\n", SP_NO_MORE); +log_write(0, LOG_MAIN|LOG_REJECT, "rejected \"%s\" from %s", + smtp_cmd_buffer, sender_helo_name, host_and_ident(FALSE)); +return 0; +} +#endif + + static int expand_mailmax(const uschar * s) { @@ -3686,9 +3724,8 @@ while (done <= 0) void (*oldsignal)(int); pid_t pid; int start, end, sender_domain, recipient_domain; - int rc; - int c; - uschar *orcpt = NULL; + int rc, c; + uschar * orcpt = NULL; int dsn_flags; gstring * g; @@ -4213,12 +4250,12 @@ while (done <= 0) chunking_state = CHUNKING_OFFERED; } +#ifndef DISABLE_TLS /* Advertise TLS (Transport Level Security) aka SSL (Secure Socket Layer) if it has been included in the binary, and the host matches tls_advertise_hosts. We must *not* advertise if we are already in a secure connection. */ -#ifndef DISABLE_TLS if (tls_in.active.sock < 0 && verify_check_host(&tls_advertise_hosts) != FAIL) { @@ -4252,6 +4289,13 @@ while (done <= 0) fl.smtputf8_advertised = TRUE; } #endif +#ifndef DISABLE_WELLKNOWN + if (verify_check_host(&wellknown_advertise_hosts) != FAIL) + { + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US"-WELLKNOWN\r\n", 12); + } +#endif /* Finish off the multiline reply with one that is always available. */ @@ -4299,6 +4343,14 @@ while (done <= 0) toomany = FALSE; break; /* HELO/EHLO */ +#ifndef DISABLE_WELLKNOWN + case WELLKNOWN_CMD: + HAD(SCH_WELLKNOWN); + smtp_mailcmd_count++; + smtp_wellknown_handler(); + break; +#endif + #ifdef EXPERIMENTAL_XCLIENT case XCLIENT_CMD: { @@ -5455,6 +5507,10 @@ while (done <= 0) if (acl_smtp_etrn) smtp_printf(" ETRN", SP_MORE); if (acl_smtp_expn) smtp_printf(" EXPN", SP_MORE); if (acl_smtp_vrfy) smtp_printf(" VRFY", SP_MORE); +#ifndef DISABLE_WELLKNOWN + if (verify_check_host(&wellknown_advertise_hosts) != FAIL) + smtp_printf(" WELLKNOWN", SP_MORE); +#endif #ifdef EXPERIMENTAL_XCLIENT if (proxy_session || verify_check_host(&hosts_xclient) != FAIL) smtp_printf(" XCLIENT", SP_MORE); diff --git a/src/util/mailtest b/src/util/mailtest new file mode 100755 index 000000000..0c50d93f5 --- /dev/null +++ b/src/util/mailtest @@ -0,0 +1,486 @@ +#!/usr/bin/perl +# +############################################################### +############################################################### + +use strict; + +use Net::SMTP; +#use IO::Socket::SSL qw( SSL_ERROR ); +use Net::Domain qw(hostfqdn); +use Getopt::Long qw(:config default bundling no_ignore_case auto_version); +use Pod::Usage; +use Net::Cmd; +use Data::Dumper; + +our @ISA = qw(Net::Cmd); + +############################################################### +############################################################### + +my ($smtp,$optsin,$opt,$mess,$rcpt,@headers,$finished_header,$ofh); +$main::VERSION = '1.2.2'; + +$optsin = { + 'body|b' => \&optset, + 'debug|d' => \&optset, + 'ehlo|helo|m=s' => \&optset, + 'rcptto|recipient|r=s' => \&optset, + 'host|h=s' => \&optset, + 'from822|u=s' => \&optset, + 'vrfy|v' => \&optset, + 'expn|e' => \&optset, + 'mailfrom|from821|from|f=s' => \&optset, + 'port|p=i' => \&optset, + 'wellknown|w=s' => \&optset, + 'output|W=s' => \&optset, + 'include_options|O' => \&optset, + 'include_headers|H' => \&optset, + 'bounce|B' => \&optset, + 'tls|S' => \&optset, + 'nostarttls|s' => \&optset, + 'stricttls|strict_tls' => \&optset, + 'sslargs|tlsargs=s' => \&optset, + 'verbose' => \&optset, + 'help' => \&optset, + 'man' => \&optset, +}; +map { my $t = $_; $t =~ s/\|.*//; $opt->{$t} = undef; } keys %$optsin; +GetOptions( %$optsin ) or pod2usage(2); +pod2usage(1) if $opt->{'help'}; +pod2usage(-exitval => 0, -verbose => 2) if $opt->{'man'}; + +print _Dumper($opt, 'Options') + if $opt->{'debug'}; + +############################################################### +############################################################### +## +## parameter checking +## +############################################################### +############################################################### + +bail( 1, "Host(--host) must be provided" ) + if !$opt->{'host'}; + +$opt->{'port'} = $opt->{'tls'} ? 465 : 25 + if ! $opt->{'port'}; + +if (!$opt->{'ehlo'}) +{ + $opt->{'ehlo'} = hostfqdn(); + fret( "Machine set to $opt->{'ehlo'}" ) if $opt->{'debug'}; +} +if (!$opt->{'mailfrom'} && !$opt->{'bounce'}) +{ + $opt->{'mailfrom'} = $ENV{USER}. "@". $opt->{'ehlo'}; + fret( "MAIL FROM set to $opt->{'mailfrom'}" ) if $opt->{'debug'}; +} +if (!$opt->{'from822'}) +{ + $opt->{'from822'} = $opt->{'mailfrom'}; + fret( "From: set to $opt->{'from822'}" ) if $opt->{'debug'}; +} +if ($opt->{'bounce'}) +{ + $opt->{'mailfrom'} = ""; + $opt->{'from822'} = 'mailer-daemon@'. hostfqdn(); + fret( "MAIL FROM set to $opt->{'mailfrom'}", "From: set to $opt->{'from822'}" ) if $opt->{'debug'}; +} + +bail( 1, "EXPN or VRFY cannot be used without a recipient" ) + if ($opt->{'expn'} || $opt->{'vrfy'}) && ! $opt->{'rcptto'}; + +bail( 1, "Either a recipient or well-known resource must be specified" ) + if ! $opt->{'wellknown'} && ! $opt->{'rcptto'}; + +bail( 1, "Only one of recipient or well-known resource can be specified" ) + if $opt->{'wellknown'} && $opt->{'rcptto'}; + +if ( $opt->{'sslargs'} ) +{ + my @p = split /[=,]/, $opt->{'sslargs'}; + $opt->{'sslparams'} = \@p; +} +else +{ + $opt->{'sslparams'} = [ 'SSL_verify_mode', $opt->{'stricttls'} ? 1 : 0 ]; +} +fret( _Dumper( $opt->{'sslparams'}, 'sslparams' ) ) + if $opt->{'debug'} && ( $opt->{'tls'} || ! $opt->{'nostarttls'} ); + +############################################################### +############################################################### +## +## parameter checking complete. now onto operations +## +## +############################################################### +############################################################### + + + +$smtp= Net::SMTP->new( $opt->{'host'}, + Hello => $opt->{'ehlo'}, + Debug => $opt->{'debug'}, + ( $opt->{'tls'} ? ( 'SSL' => $opt->{'sslargs'} || 1 ) : () ), + Port => $opt->{'port'}, + ); +bail( 1, "Connection Failed: $@" ) + if !$smtp; + +if (!$opt->{'nostarttls'}) +{ + bail( $smtp, 1, "Failed to STARTTLS - $@" ) + if ! $smtp->starttls( @{$opt->{'sslparams'}} ); + + fret( $smtp->message() ) + if $opt->{'verbose'}; +} + +if ($opt->{'wellknown'}) +{ + bail( $smtp, 1, "Server does not support WELLKNOWN" ) + if ! $smtp->supports('WELLKNOWN'); + + my $e_wk = encode_xtext( $opt->{'wellknown'} ); + + bail( $smtp, 1, "Failed to WELLKNOWN - $e_wk", $smtp->message() ) + if ! ( $smtp->command( 'WELLKNOWN', $e_wk )->response() == CMD_OK ); + + fret( "Protocol violation. Code was OK, but not 250", $smtp->code. " - ". $smtp->message ) + if $smtp->code ne '250'; + + $mess = $smtp->message; + my ($info,$size); + ($info,$mess) = split( /\n/, $mess, 2 ); + $info =~ /size=(\d+)/i; + $size = $1 + 0; + $mess = decode_xtext( $mess ); + fret( "Size mismatch on wellknown fetch", "Expected: ". $size, "Received: ". length($mess) ) + if length($mess) != $size; + + if ( $opt->{'output'} ) + { + # Output to named file + # + bail( $smtp, 1, "Unable to open file $opt->{'output'} for WELLKNOWN output - $!" ) + if ! ( $ofh = IO::File->new("> $opt->{'output'}") ); + + print $ofh $mess; + $ofh->close; + } + else + { + # might be hazardous, output via pager + print STDERR "$mess\n"; + } +} + +if ($opt->{'vrfy'}) +{ + $smtp->verify($opt->{'vrfy'}); + fret( $smtp->message() ); +} + +if ($opt->{'expn'}) +{ + $smtp->expand($opt->{'expn'}); + fret( $smtp->message() ); +} + +if ($opt->{'rcptto'}) +{ + bail( $smtp, 1, "MAIL FROM $opt->{'mailfrom'} failed", $@ ) + if ! $smtp->mail($opt->{'mailfrom'}); + + bail( $smtp, 1, "RCPT TO $opt->{'rcptto'} failed", $@ ) + if ! $smtp->to($opt->{'rcptto'}); + + # handle any recipients on command line + while( $rcpt = shift @ARGV ) + { + last if $rcpt eq '--'; + fret( "RCPT TO $rcpt failed", $@ ) + if ! $smtp->to($rcpt); + } + + bail( $smtp, 1, "Unable to set data mode", @_ ) + if ! $smtp->data(); + + if ($opt->{'body'}) + { + push @headers, "Subject: Test Message\n"; + $smtp->datasend("From: $opt->{'from822'}\n"); + $smtp->datasend("To: $opt->{'rcptto'}\n"); + $smtp->datasend("Subject: Test Message\n"); + $smtp->datasend("\n"); + $smtp->datasend("This is a test message\n"); + $smtp->datasend("generated with mailtest\n"); + }else + { + while(<>) + { + if($finished_header==0) + { + if (length($_)<=1) + { + $finished_header = 1; + }else + { + push @headers," ".$_; + } + } + $smtp->datasend("$_"); + } + } + if($opt->{'include_headers'} && @headers) + { + $smtp->datasend("\n Copy of headers follow....\n"); + foreach(@headers) + { + $smtp->datasend("$_"); + } + $smtp->datasend("\n"); + } + if($opt->{'include_options'}) + { + $smtp->datasend("\n Copy of options follow....\n"); + $smtp->datasend(" SMTP HOST $opt->{'host'}\n"); + $smtp->datasend(" HELO $opt->{'ehlo'}\n"); + $smtp->datasend(" MAIL FROM: $opt->{'mailfrom'}\n"); + $smtp->datasend(" RCPT TO: $opt->{'rcptto'}\n\n"); + } + fret( "dataend failed", $@ ) + if ! $smtp->dataend(); +} +$smtp->quit(); + +exit; + +############################################################## +############################################################## + +sub +optset +{ + my $n = shift; + my $v = shift; + #print STDERR "Setting $n to $v\n"; + $opt->{$n->{'name'}} = $v; +} + +sub +decode_xtext +{ + my $mess = shift; + $mess =~ s/[\n\r]//g; + $mess =~ s/\+([0-9a-fA-F]{2})/chr(hex($1))/ge; + return $mess; +} + +sub +encode_xtext +{ + my $mess = shift; + $mess =~ s/([^!-*,-<>-~])/'+'.uc(unpack('H*', $1))/eg; + return $mess; +} + +sub +_Dumper +{ + return Data::Dumper->Dump( [$_[0]], [$_[1] || 'VAR1'] ); +} + +sub +fret +{ + map { print STDERR $_,"\n"; } @_; +} + +sub +bail +{ + shift->quit + if ref($_[0]); + my $rc = shift; + fret( @_ ); + exit $rc; +} + +############################################################## +############################################################## + +__END__ + +=head1 NAME + +mailtest - Simple SMTP sending for diagnostics + +=head1 SYNOPSIS + +mailtest --host host.example.com --rcptto recipient@example.com [ send_options ... ] [ additional recipients ...] + + +Options: + --help + brief help message + --debug + enable debugging + + --host host + host to connect to + --rcptto recipient + recipient for message + + --helo machine + machine name for EHLO + + --vrfy request VRFY of recipient + --expn request EXPN of recipient + + --mailfrom from + use as MAIL FROM value + --from822 from + content From: + + --port port + port to connect to + + --body generate body + --include_options + include Options in body + --include_headers + include generated headers in body + + --tls perform TLS on connect + --nostarttls do no attempt STARTTLS + --stricttls Enable strict verification on TLS connection + + --tlsargs arg=value[,arg=value] + Explicitly define TLS options. + + --bounce sending as bounce (<>) + + --wellknown path + well-known path + --output file + Output file to receive well-known data + +=head1 OPTIONS + +=over 8 + + +=item B<--help> + +Print a brief help message and exits. + +=item B<-d, --debug> + +Enables debugging, outpus additional information whilst processing requests. + +=item B<-h, --host>=I + +Specifies the host to connect to. Must be specified and must be IP-resolvable. + +=item B<-m, --ehlo>=I + +Specified the machine name to use as the B value. Defaults to the fully-qualified name of the host running the command. + +=item B<-r, --rcptto>=I + +Specifies the recipient of message. This is used as the B value. + +=item B<-v, --vrfy> + +Uses the I parameter for the value in a B request. This disables the sending of an email. + +=item B<-e, --expn> + +Uses the I parameter for the value in an B request. This disables the sending of an email. + +=item B<-f, --mailfrom>=I + +Specified the value to use in the B command. Defaults to the current username at the FQDN of the machine B<-m> unless the B<-B> option is used. + +=item B<-u, --from822>=I + +Specified the value to use in the message headers. Defaults to the B<-f> I value unless the B<-B> option is used. + +=item B<-B, --bounce> + +Replace the B<--mailfrom> I with B<\<\>> and the B<--from833> I with B where the host is the value of B<--ehlo> I + +=item B<-p, --port>=I + +Specified the port to connect to on the specified host. Defaults to port 25 unless B<-S> is given in which case it defaults to 465. + +=item B<-S, --tls> + +Specifies that TLS be used directly on the connection prior to any SMTP commands. Changes the connection port to 465 unless it has been explicitly provided. Disables any attempts at B. + +=item B<-s, --nostarttls> + +Disables attempting STARTTLS if offered. Disabled by use of B<-S>. + +=item B<--stricttls> + +Enables strict verification of the TLS connection. Sets the underlying SSL option B to 1/SSL_VERIFY_PEER rather than 0/SSL_VERIFY_NONE. Since the aim of this tool is to test the SMTP protocol behaviour and not the TLS behaviour the decision was made to default the B to 0/SSL_VERIFY_NONE. + +=item B<--sslargs>=argname=argvalue[,argname=argvalue...] + +Allow full control over underlying SSL options. Overrides B<--stricttls>. See the documentation for B for further details. + + --sslargs SSL_verifycn_name=certname.example.com + +=item B<-b, --body> + +Generate a body for the message being sent. + +=item B<-O, --include-options> + +Include details of options used in the message body. + +=item B<-H, --include-headers> + +Include a copy of the generated headers in the message body. + +=item B<-w, --wellknown>=I + +Provides the path value for a B command. + +=item B<-W, --output>=I + +Provides the output file where the B data should be stored. + +=back + +=head1 DESCRIPTION + +B is a simple utility for testing SMTP connections. +It is designed to debug endpoints and not for full email generation. + +It support a number of basic operations, SEND, VRFY, EXPN, WELLKNOWN. + +=head1 COMPATIBILITY + +C only requires modules that should be in all normal distributions. + +=head1 AUTHOR + +Bernard Quatermass + +=head1 COPYRIGHT AND LICENSE + +This software is Copyright (c) 2008,2020,2024 by Bernard Quatermass. + + +=cut + +############################################################### +# vi: sw=4 et +# End of File +############################################################### commit c1d124daf6ccb5b2ec59cf7fa6c1d76002ab0a42 Author: Jeremy Harris Date: Sat Jun 1 16:32:22 2024 +0100 Lookups: dsearch subdir with leading "..". Bug 2994 diff --git a/src/src/lookups/dsearch.c b/src/src/lookups/dsearch.c index 22003f4e5..8258a121e 100644 --- a/src/src/lookups/dsearch.c +++ b/src/src/lookups/dsearch.c @@ -132,7 +132,7 @@ if ( Ulstat(filename, &statbuf) >= 0 && S_ISDIR(statbuf.st_mode) && ( flags & FILTER_DIR || keystring[0] != '.' - || keystring[1] && keystring[1] != '.' + || keystring[1] && (keystring[1] != '.' || keystring[2]) ) ) ) ) { /* Since the filename exists in the filesystem, we can return a commit 6fa5cd352fc9c1e86ec56ac4f2b96804b6472a5f Author: Jeremy Harris Date: Mon Jun 3 16:20:20 2024 +0100 Unbreak no-TLS build Broken-by: 4243a209fd94 diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index c941d115c..5110ed3d6 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -2067,9 +2067,12 @@ log_connect_tls_drop(const uschar * what, const uschar * log_msg) { if (log_reject_target) { +#ifdef DISABLE_TLS + uschar * tls = NULL; +#else gstring * g = s_tlslog(NULL); uschar * tls = string_from_gstring(g); - +#endif log_write(L_connection_reject, log_reject_target, "%s%s%s dropped by %s%s%s", LOGGING(dnssec) && sender_host_dnssec ? US" DS" : US"", diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 0338d6301..8645c150d 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -1091,7 +1091,7 @@ if (pending_BANNER) } /*XXX EXPERIMENTAL_ESMTP_LIMITS ? */ -# ifndef DISABLE_TLS_RESUME +# if !defined(DISABLE_TLS) && !defined(DISABLE_TLS_RESUME) GET_OPTION("host_name_extract"); s = ((smtp_transport_options_block *)sx->conn_args.ob)->host_name_extract; if (!s) s = HNE_DEFAULT; @@ -2635,7 +2635,7 @@ goto SEND_QUIT; } } #endif -#ifndef DISABLE_TLS_RESUME +#if !defined(DISABLE_TLS) && !defined(DISABLE_TLS_RESUME) GET_OPTION("host_name_extract"); if (!(s = ob->host_name_extract)) s = HNE_DEFAULT; ehlo_response_lbserver(sx, s); commit b371572e89e8ca593b8d6bb5c919fedbb67b2eeb Author: Jeremy Harris Date: Thu Jun 6 14:45:14 2024 +0100 Docs: hintsdb internal APIs diff --git a/src/src/dbfn.c b/src/src/dbfn.c index 389d1518e..37ff7f504 100644 --- a/src/src/dbfn.c +++ b/src/src/dbfn.c @@ -20,7 +20,7 @@ with database files like $spooldirectory/db/ */ different DBM files. This module does not contain code for reading DBM files for (e.g.) alias expansion. That is all contained within the general search functions. As Exim now has support for several DBM interfaces, all the relevant -functions are called as macros. +functions are called as inlinable functions from an included file. All the data in Exim's database is in the nature of *hints*. Therefore it doesn't matter if it gets destroyed by accident. These functions are not @@ -35,7 +35,25 @@ means of locking on independent lock files. (Earlier attempts to lock on the DBM files themselves were never completely successful.) Since callers may in general want to do more than one read or write while holding the lock, there are separate open and close functions. However, the calling modules should -arrange to hold the locks for the bare minimum of time. */ +arrange to hold the locks for the bare minimum of time. + +API: + dbfn_open + dbfn_close + dbfn_read_with_length + dbfn_read_enforce_length + dbfn_write + dbfn_delete + dbfn_scan unused; ifdeffout out + +Users: + ACL ratelimit & seen conditions + delivery retry handling + delivery serialization + TLS session resumption + peer capability cache + callout & quota cache +*/ diff --git a/src/src/hintsdb.h b/src/src/hintsdb.h index ed1b5566d..3332f748c 100644 --- a/src/src/hintsdb.h +++ b/src/src/hintsdb.h @@ -16,7 +16,40 @@ from Pierre A. Humblet, so Exim could be made to work with Cygwin. For convenience, the definitions of the structures used in the various hints databases are also kept in this file, which is used by the maintenance -utilities as well as the main Exim binary. */ +utilities as well as the main Exim binary. + +A key/value store is supported (only). Keys are strings; values arbitrary +binary blobs. + +The API is: + Functions: + exim_dbopen + exim_dbclose + exim_dbget + exim_dbput + exim_dbputb (non-overwriting put) + exim_dbdel + exim_dbcreate_cursor + exim_dbscan (get, and bump cursor) + exim_dbdelete_cursor + exim_datum_init + exim_datum_size_get + exim_datum_data_get + exim_datum_free + Defines: + EXIM_DB access handle + EXIM_CURSOR datatype for cursor + EXIM_DATUM datatype for "value" + EXIM_DBTYPE text for logging & debuug + +Selection of the shim layer implementation, and backend, is by #defines. + +The users of this API are: + hintsdb interface dbfn.c + hintsdb utilities exim_dbutil.c and exim_dbmvuild.c + dbmdb lookup lookups/dbmdb,c + autoreply transport transports/autoreply.c +*/ #ifndef HINTSDB_H #define HINTSDB_H commit 799253d6a75db70c631716a190736c285a97155c Author: Jeremy Harris Date: Thu Jun 6 14:47:28 2024 +0100 Hintsdb: USE_SQLITE build option diff --git a/src/src/EDITME b/src/src/EDITME index 1440b4b44..426c1da3c 100644 --- a/src/src/EDITME +++ b/src/src/EDITME @@ -47,7 +47,8 @@ # compile the Exim monitor utility. Exim itself does not use X11. # Another area of variability between systems is the type and location of the -# DBM library package. Exim has support for ndbm, gdbm, tdb, and Berkeley DB. +# DBM library package. Exim has support for ndbm, gdbm, tdb, Berkeley DB and +# sqlite3. # By default the code assumes ndbm; this often works with gdbm or DB, provided # they are correctly installed, via their compatibility interfaces. However, # Exim can also be configured to use the native calls for Berkeley DB (obsolete @@ -676,6 +677,10 @@ DISABLE_MAL_MKS=yes # USE_DB = yes # DBMLIB = -ldb +# sqlite +# USE_SQLITE = yes +# DBMLIB = -lsqlite3 + #------------------------------------------------------------------------------ # Although Exim is normally a setuid program, owned by root, it refuses to run diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults index 22749b174..0425279db 100644 --- a/src/src/config.h.defaults +++ b/src/src/config.h.defaults @@ -188,6 +188,7 @@ Do not put spaces between # and the 'define'. #define USE_OPENSSL #define USE_READLINE #define USE_TCP_WRAPPERS +#define USE_SQLITE #define USE_TDB #define WHITELIST_D_MACROS diff --git a/src/src/dbfn.c b/src/src/dbfn.c index 37ff7f504..069c885f0 100644 --- a/src/src/dbfn.c +++ b/src/src/dbfn.c @@ -274,7 +274,11 @@ exim_datum_init(&result_datum); /* to be cleared before use. */ exim_datum_data_set(&key_datum, key_copy); exim_datum_size_set(&key_datum, klen); -if (!exim_dbget(dbblock->dbptr, &key_datum, &result_datum)) return NULL; +if (!exim_dbget(dbblock->dbptr, &key_datum, &result_datum)) + { + DEBUG(D_hints_lookup) debug_printf_indent("dbfn_read: null return\n"); + return NULL; + } /* Assume the data store could have been tainted. Properly, we should store the taint status with the data. */ @@ -282,6 +286,7 @@ store the taint status with the data. */ dlen = exim_datum_size_get(&result_datum); yield = store_get(dlen, GET_TAINTED); memcpy(yield, exim_datum_data_get(&result_datum), dlen); +DEBUG(D_hints_lookup) debug_printf_indent("dbfn_read: size %u return\n", dlen); if (length) *length = dlen; exim_datum_free(&result_datum); /* Some DBM libs require freeing */ @@ -344,7 +349,8 @@ uschar * key_copy = store_get(klen, key); memcpy(key_copy, key, klen); gptr->time_stamp = time(NULL); -DEBUG(D_hints_lookup) debug_printf_indent("dbfn_write: key=%s\n", key); +DEBUG(D_hints_lookup) + debug_printf_indent("dbfn_write: key=%s datalen %d\n", key, length); exim_datum_init(&key_datum); /* Some DBM libraries require the datum */ exim_datum_init(&value_datum); /* to be cleared before use. */ @@ -387,6 +393,11 @@ return exim_dbdel(dbblock->dbptr, &key_datum); +#ifdef notdef +/* XXX This appears to be unused. There's a separate implementation +in dbutils.c for dumpdb and fixdb, using the same underlying support. +*/ + /************************************************* * Scan the keys of a database file * *************************************************/ @@ -426,6 +437,7 @@ yield = exim_dbscan(dbblock->dbptr, &key_datum, &value_datum, start, *cursor) if (!yield) exim_dbdelete_cursor(*cursor); return yield; } +#endif diff --git a/src/src/exim.c b/src/src/exim.c index b78839b57..1e5643713 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -997,32 +997,35 @@ if (s) static gstring * show_db_version(gstring * g) { +g = string_cat(g, US"Hints DB:\n"); #ifdef DB_VERSION_STRING DEBUG(D_any) { - g = string_fmt_append(g, "Library version: BDB: Compile: %s\n", DB_VERSION_STRING); - g = string_fmt_append(g, " Runtime: %s\n", + g = string_fmt_append(g, " Library version: BDB: Compile: %s\n", DB_VERSION_STRING); + g = string_fmt_append(g, " Runtime: %s\n", db_version(NULL, NULL, NULL)); } else - g = string_fmt_append(g, "Berkeley DB: %s\n", DB_VERSION_STRING); + g = string_fmt_append(g, " Berkeley DB: %s\n", DB_VERSION_STRING); #elif defined(BTREEVERSION) && defined(HASHVERSION) # ifdef USE_DB - g = string_cat(g, US"Probably Berkeley DB version 1.8x (native mode)\n"); + g = string_cat(g, US" Probably Berkeley DB version 1.8x (native mode)\n"); # else - g = string_cat(g, US"Probably Berkeley DB version 1.8x (compatibility mode)\n"); + g = string_cat(g, US" Probably Berkeley DB version 1.8x (compatibility mode)\n"); # endif #elif defined(_DBM_RDONLY) || defined(dbm_dirfno) -g = string_cat(g, US"Probably ndbm\n"); +g = string_cat(g, US" Probably ndbm\n"); +#elif defined(USE_SQLITE) +g = string_cat(g, US" Using sqlite3\n"); #elif defined(USE_TDB) -g = string_cat(g, US"Using tdb\n"); +g = string_cat(g, US" Using tdb\n"); #else # ifdef USE_GDBM - g = string_cat(g, US"Probably GDBM (native mode)\n"); +g = string_cat(g, US" Probably GDBM (native mode)\n"); # else - g = string_cat(g, US"Probably GDBM (compatibility mode)\n"); +g = string_cat(g, US" Probably GDBM (compatibility mode)\n"); # endif #endif return g; diff --git a/src/src/exim.h b/src/src/exim.h index 6baf1fbb4..585416592 100644 --- a/src/src/exim.h +++ b/src/src/exim.h @@ -524,6 +524,7 @@ config.h, mytypes.h, and store.h, so we don't need to mention them explicitly. #include "local_scan.h" #include "path_max.h" #include "macros.h" +#include "blob.h" #include "hintsdb.h" #include "hintsdb_structs.h" #include "structs.h" diff --git a/src/src/exim_dbutil.c b/src/src/exim_dbutil.c index c868832b1..5d8846d93 100644 --- a/src/src/exim_dbutil.c +++ b/src/src/exim_dbutil.c @@ -520,7 +520,7 @@ Arguments: cursor a pointer to a pointer to a cursor anchor, for those dbm libraries that use the notion of a cursor -Returns: the next record from the file, or +Returns: the next *key* (nul-terminated) from the file, or NULL if there are no more */ diff --git a/src/src/hintsdb.h b/src/src/hintsdb.h index 3332f748c..545c83dbd 100644 --- a/src/src/hintsdb.h +++ b/src/src/hintsdb.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ @@ -55,7 +55,272 @@ The users of this API are: #define HINTSDB_H -#if defined(USE_TDB) +#ifdef USE_SQLITE + +/* ********************* sqlite3 interface ************************ */ + +# include + +/* Basic DB type */ +# define EXIM_DB sqlite3 + +# define EXIM_CURSOR int + +# /* The datum type used for queries */ +# define EXIM_DATUM blob + +/* Some text for messages */ +# define EXIM_DBTYPE "sqlite3" + +# /* Access functions */ + +/* EXIM_DBOPEN - return pointer to an EXIM_DB, NULL if failed */ +static inline EXIM_DB * +exim_dbopen__(const uschar * name, const uschar * dirname, int flags, + unsigned mode) +{ +EXIM_DB * dbp; +int ret, sflags = flags & O_RDWR ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY; +if (flags & O_CREAT) sflags |= SQLITE_OPEN_CREATE; +if ((ret = sqlite3_open_v2(CCS name, &dbp, sflags, NULL)) == SQLITE_OK) + { + sqlite3_busy_timeout(dbp, 5000); + if (flags & O_CREAT) + ret == sqlite3_exec(dbp, + "CREATE TABLE tbl (ky TEXT PRIMARY KEY, dat BLOB);", NULL, NULL, NULL); + } +//else +// fprintf(stderr, "sqlite3_open_v2: %s\n", sqlite3_errmsg(dbp)); +return ret == SQLITE_OK ? dbp : NULL; +} + +/* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */ +/* note we alloc'n'copy - the caller need not do so */ +/* result has a NUL appended, but the length is as per the DB */ + +static inline BOOL +exim_dbget__(EXIM_DB * dbp, const uschar * s, EXIM_DATUM * res) +{ +sqlite3_stmt * statement; +int ret; + +res->len = (size_t) -1; +/* fprintf(stderr, "exim_dbget__(%s)\n", s); */ +if ((ret = sqlite3_prepare_v2(dbp, CCS s, -1, &statement, NULL)) != SQLITE_OK) + { +/* fprintf(stderr, "prepare fail: %s\n", sqlite3_errmsg(dbp)); */ + return FALSE; + } +if (sqlite3_step(statement) != SQLITE_ROW) + { +/* fprintf(stderr, "step fail: %s\n", sqlite3_errmsg(dbp)); */ + sqlite3_finalize(statement); + return FALSE; + } + +res->len = sqlite3_column_bytes(statement, 0); +res->data = store_get(res->len + 1, GET_TAINTED); +memcpy(res->data, sqlite3_column_blob(statement, 0), res->len); +res->data[res->len] = '\0'; +/* fprintf(stderr, "res %d bytes: '%.*s'\n", (int)res->len, (int)res->len, res->data); */ +sqlite3_finalize(statement); +return TRUE; +} + +static inline BOOL +exim_dbget(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res) +{ +# define FMT "SELECT dat FROM tbl WHERE ky = '%.*s';" +uschar * qry; +int i; +BOOL ret; + +# ifdef COMPILE_UTILITY +/* fprintf(stderr, "exim_dbget(k len %d '%.*s')\n", (int)key->len, (int)key->len, key->data); */ +qry = malloc(i = snprintf(NULL, 0, FMT, (int) key->len, key->data)); +snprintf(CS qry, i, FMT, (int) key->len, key->data); +ret = exim_dbget__(dbp, qry, res); +free(qry); +# else +/* fprintf(stderr, "exim_dbget(k len %d '%.*s')\n", (int)key->len, (int)key->len, key->data); */ +qry = string_sprintf(FMT, (int) key->len, key->data); +ret = exim_dbget__(dbp, qry, res); +# endif + +return ret; +# undef FMT +} + +/**/ +# define EXIM_DBPUTB_OK 0 +# define EXIM_DBPUTB_DUP (-1) + +static inline int +exim_s_dbp(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data, const uschar * alt) +{ +# define FMT "INSERT OR %s INTO tbl (ky,dat) VALUES ('%.*s', X'%.*s');" +uschar * hex = store_get(data->len * 2, data->data), * qry; +int res; + +for (const uschar * s = data->data, * t = s + data->len; s < t; s++) + sprintf(CS hex + 2 * (s - data->data), "%02X", *s); + +# ifdef COMPILE_UTILITY +res = snprintf(NULL, 0, FMT, + alt, (int) key->len, key->data, (int)data->len * 2, hex); +qry = malloc(res); +snprintf(CS qry, res, FMT, alt, (int) key->len, key->data, (int)data->len * 2, hex); +/* fprintf(stderr, "exim_s_dbp(%s)\n", qry); */ +res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL); +free(qry); +# else +qry = string_sprintf(FMT, alt, (int) key->len, key->data, (int)data->len * 2, hex); +/* fprintf(stderr, "exim_s_dbp(%s)\n", qry); */ +res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL); +/* fprintf(stderr, "exim_s_dbp res %d\n", res); */ +# endif + +if (res != SQLITE_OK) + fprintf(stderr, "sqlite3_exec: %s\n", sqlite3_errmsg(dbp)); + +return res == SQLITE_OK ? EXIM_DBPUTB_OK : EXIM_DBPUTB_DUP; +# undef FMT +} + +/* EXIM_DBPUT - returns nothing useful, assumes replace mode */ + +static inline int +exim_dbput(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data) +{ +/* fprintf(stderr, "exim_dbput()\n"); */ +(void) exim_s_dbp(dbp, key, data, US"REPLACE"); +return 0; +} + +/* EXIM_DBPUTB - non-overwriting for use by dbmbuild */ + +/* Returns from EXIM_DBPUTB */ + +static inline int +exim_dbputb(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data) +{ +return exim_s_dbp(dbp, key, data, US"ABORT"); +} + +/* EXIM_DBDEL */ +static inline int +exim_dbdel(EXIM_DB * dbp, EXIM_DATUM * key) +{ +# define FMT "DELETE FROM tbl WHERE ky = '%.*s';" +uschar * qry; +int res; + +# ifdef COMPILE_UTILITY +res = snprintf(NULL, 0, FMT, (int) key->len, key->data); +qry = malloc(res); +snprintf(CS qry, res, FMT, (int) key->len, key->data); +res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL); +free(qry); +# else +qry = string_sprintf(FMT, (int) key->len, key->data); +res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL); +# endif + +return res; +# undef FMT +} + + +/* EXIM_DBCREATE_CURSOR - initialize for scanning operation */ +/* Cursors are inefficiently emulated by repeating searches */ + +static inline EXIM_CURSOR * +exim_dbcreate_cursor(EXIM_DB * dbp) +{ +EXIM_CURSOR * c = store_malloc(sizeof(int)); +*c = 0; +return c; +} + +/* EXIM_DBSCAN */ +/* Note that we return the (next) key, not the record value */ +static inline BOOL +exim_dbscan(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res, BOOL first, + EXIM_CURSOR * cursor) +{ +# define FMT "SELECT ky FROM tbl ORDER BY ky LIMIT 1 OFFSET %d;" +uschar * qry; +int i; +BOOL ret; + +# ifdef COMPILE_UTILITY +qry = malloc(i = snprintf(NULL, 0, FMT, *cursor)); +snprintf(CS qry, i, FMT, *cursor); +/* fprintf(stderr, "exim_dbscan(%s)\n", qry); */ +ret = exim_dbget__(dbp, qry, key); +free(qry); +/* fprintf(stderr, "exim_dbscan ret %c\n", ret ? 'T':'F'); */ +# else +qry = string_sprintf(FMT, *cursor); +/* fprintf(stderr, "exim_dbscan(%s)\n", qry); */ +ret = exim_dbget__(dbp, qry, key); +/* fprintf(stderr, "exim_dbscan ret %c\n", ret ? 'T':'F'); */ +# endif +if (ret) *cursor = *cursor + 1; +return ret; +# undef FMT +} + +/* EXIM_DBDELETE_CURSOR - terminate scanning operation. */ +static inline void +exim_dbdelete_cursor(EXIM_CURSOR * cursor) +{ store_free(cursor); } + + +/* EXIM_DBCLOSE */ +static void +exim_dbclose__(EXIM_DB * db) +{ sqlite3_close(db); } + + +/* Datum access */ + +static uschar * +exim_datum_data_get(EXIM_DATUM * dp) +{ return US dp->data; } +static void +exim_datum_data_set(EXIM_DATUM * dp, void * s) +{ dp->data = s; } + +static unsigned +exim_datum_size_get(EXIM_DATUM * dp) +{ return dp->len; } +static void +exim_datum_size_set(EXIM_DATUM * dp, unsigned n) +{ dp->len = n; } + + + +static inline void +exim_datum_init(EXIM_DATUM * dp) +{ dp->data = NULL; } /* compiler quietening */ + +/* No free needed for a datum */ + +static inline void +exim_datum_free(EXIM_DATUM * dp) +{ } + +/* size limit */ + +# define EXIM_DB_RLIMIT 150 + + + + + + +#elif defined(USE_TDB) # if defined(USE_DB) || defined(USE_GDBM) # error USE_TDB conflict with alternate definition @@ -161,7 +426,7 @@ exim_datum_data_set(EXIM_DATUM * dp, void * s) static inline unsigned exim_datum_size_get(EXIM_DATUM * dp) -{ return dp->dsize; } +{ return dp->len; } static inline void exim_datum_size_set(EXIM_DATUM * dp, unsigned n) { dp->dsize = n; } @@ -854,3 +1119,5 @@ exim_dbclose__(dbp); #endif /* whole file */ /* End of hintsdb.h */ +/* vi: aw ai sw=2 +*/ diff --git a/src/src/lookups/sqlite.c b/src/src/lookups/sqlite.c index 6c7af4225..fd7b3e033 100644 --- a/src/src/lookups/sqlite.c +++ b/src/src/lookups/sqlite.c @@ -34,7 +34,7 @@ if (!filename || *filename != '/') *errmsg = US"absolute file name expected for \"sqlite\" lookup"; else if ((ret = sqlite3_open(CCS filename, &db)) != 0) { - *errmsg = (void *)sqlite3_errmsg(db); + *errmsg = string_copy(US sqlite3_errmsg(db)); sqlite3_close(db); db = NULL; DEBUG(D_lookup) debug_printf_indent("Error opening database: %s\n", *errmsg); commit 1145b1aea70658de9f7f85b03772bc06408c027a Author: Jeremy Harris Date: Fri Jun 7 14:33:11 2024 +0100 Update copyright dates diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base index b3bb87c22..afa2a7a23 100644 --- a/src/OS/Makefile-Base +++ b/src/OS/Makefile-Base @@ -5,7 +5,7 @@ # optional, Local/* files at the front of this file, to create Makefile in the # build directory. # -# Copyright (c) The Exim Maintainers 1995 - 2023 +# Copyright (c) The Exim Maintainers 1995 - 2024 # SPDX-License-Identifier: GPL-2.0-or-later SHELL = $(MAKE_SHELL) diff --git a/src/OS/os.h-FreeBSD b/src/OS/os.h-FreeBSD index 11f8b1ae7..57ea82265 100644 --- a/src/OS/os.h-FreeBSD +++ b/src/OS/os.h-FreeBSD @@ -1,6 +1,6 @@ /* Exim: OS-specific C header file for FreeBSD */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ -/* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* See the file NOTICE for conditions of use and distribution. */ diff --git a/src/OS/os.h-Linux b/src/OS/os.h-Linux index cebda3367..dcd9ec02d 100644 --- a/src/OS/os.h-Linux +++ b/src/OS/os.h-Linux @@ -1,6 +1,6 @@ /* Exim: OS-specific C header file for Linux */ +/* Copyright (c) The Exim Maintainers 2021 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2020 */ -/* Copyright (c) The Exim Maintainers 2021 */ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* See the file NOTICE for conditions of use and distribution. */ diff --git a/src/exim_monitor/em_globals.c b/src/exim_monitor/em_globals.c index 3822746ba..b23accc65 100644 --- a/src/exim_monitor/em_globals.c +++ b/src/exim_monitor/em_globals.c @@ -2,8 +2,8 @@ * Exim Monitor * *************************************************/ +/* Copyright (c) The Exim Maintainers 2021 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ -/* Copyright (c) The Exim Maintainers 2021 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/exim_monitor/em_hdr.h b/src/exim_monitor/em_hdr.h index 074fef1d3..315c1d250 100644 --- a/src/exim_monitor/em_hdr.h +++ b/src/exim_monitor/em_hdr.h @@ -2,8 +2,8 @@ * Exim Monitor * *************************************************/ +/* Copyright (c) The Exim Maintainers 2021 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2009 */ -/* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/exim_monitor/em_log.c b/src/exim_monitor/em_log.c index 49f649d7e..55dad0bae 100644 --- a/src/exim_monitor/em_log.c +++ b/src/exim_monitor/em_log.c @@ -2,8 +2,8 @@ * Exim Monitor * *************************************************/ +/* Copyright (c) The Exim Maintainers 2021 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ -/* Copyright (c) The Exim Maintainters 2021 - 2022 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/exim_monitor/em_queue.c b/src/exim_monitor/em_queue.c index c7c61a652..892b4f856 100644 --- a/src/exim_monitor/em_queue.c +++ b/src/exim_monitor/em_queue.c @@ -2,8 +2,8 @@ * Exim Monitor * *************************************************/ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/exim_monitor/em_version.c b/src/exim_monitor/em_version.c index 432796bff..066ccab56 100644 --- a/src/exim_monitor/em_version.c +++ b/src/exim_monitor/em_version.c @@ -2,8 +2,8 @@ * Exim Monitor * *************************************************/ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ -/* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/scripts/MakeLinks b/src/scripts/MakeLinks index 03e0b4124..ddb237980 100755 --- a/src/scripts/MakeLinks +++ b/src/scripts/MakeLinks @@ -3,7 +3,7 @@ # Script to build links for all the exim source files from the system- # specific build directory. It should be run from within that directory. # -# Copyright (c) The Exim Maintainers 1995 - 2023 +# Copyright (c) The Exim Maintainers 1995 - 2024 # SPDX-License-Identifier: GPL-2.0-or-later test ! -d ../src && \ diff --git a/src/src/EDITME b/src/src/EDITME index 426c1da3c..ebfaf640a 100644 --- a/src/src/EDITME +++ b/src/src/EDITME @@ -1,7 +1,7 @@ ################################################## # The Exim mail transport agent # ################################################## -# Copyright (c) The Exim Maintainers 2022 - 2023 +# Copyright (c) The Exim Maintainers 2022 - 2024 # SPDX-License-Identifier: GPL-2.0-or-later # This is the template for Exim's main build-time configuration file. It diff --git a/src/src/acl.c b/src/src/acl.c index bfb32df6f..0aa789dbf 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/arc.c b/src/src/arc.c index bd2fa3bae..48f69a8cf 100644 --- a/src/src/arc.c +++ b/src/src/arc.c @@ -2,8 +2,8 @@ * Exim - an Internet mail transport agent * *************************************************/ /* Experimental ARC support for Exim + Copyright (c) The Exim Maintainers 2021 - 2024 Copyright (c) Jeremy Harris 2018 - 2020 - Copyright (c) The Exim Maintainers 2021 - 2023 License: GPL SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/auths/cram_md5.c b/src/src/auths/cram_md5.c index 5817be7c9..6d31d4b1d 100644 --- a/src/src/auths/cram_md5.c +++ b/src/src/auths/cram_md5.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/auths/dovecot.c b/src/src/auths/dovecot.c index 7f437fa0d..8b80f2c3f 100644 --- a/src/src/auths/dovecot.c +++ b/src/src/auths/dovecot.c @@ -1,5 +1,5 @@ /* - * Copyright (c) The Exim Maintainers 2006 - 2023 + * Copyright (c) The Exim Maintainers 2006 - 2024 * Copyright (c) 2004 Andrey Panin * SPDX-License-Identifier: GPL-2.0-or-later * diff --git a/src/src/auths/external.c b/src/src/auths/external.c index 6ae5833af..a8e04310f 100644 --- a/src/src/auths/external.c +++ b/src/src/auths/external.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2023 */ +/* Copyright (c) The Exim Maintainers 2023 - 2024 */ /* Copyright (c) Jeremy Harris 2019-2020 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/auths/get_data.c b/src/src/auths/get_data.c index 0c9fb37fe..4b79cbfa4 100644 --- a/src/src/auths/get_data.c +++ b/src/src/auths/get_data.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/auths/get_no64_data.c b/src/src/auths/get_no64_data.c index ae11ae5dc..3f9ac2f3c 100644 --- a/src/src/auths/get_no64_data.c +++ b/src/src/auths/get_no64_data.c @@ -2,6 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ +/* Copyright (c) The Exim Maintainers 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/auths/plaintext.c b/src/src/auths/plaintext.c index 814896047..7f59e4c7d 100644 --- a/src/src/auths/plaintext.c +++ b/src/src/auths/plaintext.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/auths/spa.c b/src/src/auths/spa.c index 51418c4ed..7ec3974e1 100644 --- a/src/src/auths/spa.c +++ b/src/src/auths/spa.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/auths/tls.c b/src/src/auths/tls.c index 005032f57..85b9a6722 100644 --- a/src/src/auths/tls.c +++ b/src/src/auths/tls.c @@ -2,6 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ +/* Copyright (c) The Exim Maintainers 2024 */ /* Copyright (c) Jeremy Harris 1995 - 2020 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/auths/xtextencode.c b/src/src/auths/xtextencode.c index 00f36e544..04cd302de 100644 --- a/src/src/auths/xtextencode.c +++ b/src/src/auths/xtextencode.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2022 */ +/* Copyright (c) The Exim Maintainers 2022 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults index 0425279db..13b203e80 100644 --- a/src/src/config.h.defaults +++ b/src/src/config.h.defaults @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2018 - 2023 */ +/* Copyright (c) The Exim Maintainers 2018 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ diff --git a/src/src/daemon.c b/src/src/daemon.c index 9ed8a1c45..2ef632065 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/dbfn.c b/src/src/dbfn.c index 069c885f0..6b2027aa9 100644 --- a/src/src/dbfn.c +++ b/src/src/dbfn.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/deliver.c b/src/src/deliver.c index a8376e58c..767a75fdc 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/dkim.c b/src/src/dkim.c index 9e76c51a0..e0b76c3b1 100644 --- a/src/src/dkim.c +++ b/src/src/dkim.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge, 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/dkim_transport.c b/src/src/dkim_transport.c index 8c8d9b41a..63870c57f 100644 --- a/src/src/dkim_transport.c +++ b/src/src/dkim_transport.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2022 - 2023 */ +/* Copyright (c) The Exim Maintainers 2022 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/dmarc.c b/src/src/dmarc.c index f684e08ea..28fce0624 100644 --- a/src/src/dmarc.c +++ b/src/src/dmarc.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ /* DMARC support. - Copyright (c) The Exim Maintainers 2019 - 2023 + Copyright (c) The Exim Maintainers 2019 - 2024 Copyright (c) Todd Lyons 2012 - 2014 License: GPL */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/dns.c b/src/src/dns.c index 5a8c23d62..510011d51 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/exim.c b/src/src/exim.c index 1e5643713..d9039ad0c 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/exim.h b/src/src/exim.h index 585416592..9bf5dcdfc 100644 --- a/src/src/exim.h +++ b/src/src/exim.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2021 - 2022 */ +/* Copyright (c) The Exim Maintainers 2021 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/exim_dbmbuild.c b/src/src/exim_dbmbuild.c index bd24faa5b..d68333079 100644 --- a/src/src/exim_dbmbuild.c +++ b/src/src/exim_dbmbuild.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/exim_dbutil.c b/src/src/exim_dbutil.c index 5d8846d93..4e8b291c6 100644 --- a/src/src/exim_dbutil.c +++ b/src/src/exim_dbutil.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/exinext.src b/src/src/exinext.src index 5724812c4..882db6873 100644 --- a/src/src/exinext.src +++ b/src/src/exinext.src @@ -1,6 +1,6 @@ #! /bin/sh -# Copyright (c) The Exim Maintainers 2023 +# Copyright (c) The Exim Maintainers 2023 - 2024 # Copyright (c) University of Cambridge, 1995 - 2007 # See the file NOTICE for conditions of use and distribution. diff --git a/src/src/expand.c b/src/src/expand.c index 4bc680544..e7d089909 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/filter.c b/src/src/filter.c index 8f29eda3e..813ffdd7c 100644 --- a/src/src/filter.c +++ b/src/src/filter.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/filtertest.c b/src/src/filtertest.c index 2af5f87c2..60a853dcf 100644 --- a/src/src/filtertest.c +++ b/src/src/filtertest.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2021 - 2022 */ +/* Copyright (c) The Exim Maintainers 2021 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/functions.h b/src/src/functions.h index 08fa24f3d..cdf97f8bd 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/globals.c b/src/src/globals.c index 8230fe9cb..d51644e05 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/globals.h b/src/src/globals.h index febc30c84..dc9d384db 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/hintsdb_structs.h b/src/src/hintsdb_structs.h index 5da311c2b..5f50dfd5f 100644 --- a/src/src/hintsdb_structs.h +++ b/src/src/hintsdb_structs.h @@ -2,8 +2,8 @@ * Exim - an Internet mail transport agent * *************************************************/ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ -/* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/host.c b/src/src/host.c index 1b416997c..597971814 100644 --- a/src/src/host.c +++ b/src/src/host.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/local_scan.h b/src/src/local_scan.h index ed9f26d62..11f271d1b 100644 --- a/src/src/local_scan.h +++ b/src/src/local_scan.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2020 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/log.c b/src/src/log.c index 1a77f2cdb..e696b2cc7 100644 --- a/src/src/log.c +++ b/src/src/log.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/lookups/dbmdb.c b/src/src/lookups/dbmdb.c index 96665b6e4..1b3dc031e 100644 --- a/src/src/lookups/dbmdb.c +++ b/src/src/lookups/dbmdb.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/lookups/dnsdb.c b/src/src/lookups/dnsdb.c index af1ad9dab..5d441de43 100644 --- a/src/src/lookups/dnsdb.c +++ b/src/src/lookups/dnsdb.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/lookups/dsearch.c b/src/src/lookups/dsearch.c index 8258a121e..1229368ba 100644 --- a/src/src/lookups/dsearch.c +++ b/src/src/lookups/dsearch.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/lookups/ldap.c b/src/src/lookups/ldap.c index 90cde6c86..0c29b6c9a 100644 --- a/src/src/lookups/ldap.c +++ b/src/src/lookups/ldap.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/lookups/lsearch.c b/src/src/lookups/lsearch.c index 7b78de916..6728fecc7 100644 --- a/src/src/lookups/lsearch.c +++ b/src/src/lookups/lsearch.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/lookups/oracle.c b/src/src/lookups/oracle.c index c25b7c0ee..9eb936b25 100644 --- a/src/src/lookups/oracle.c +++ b/src/src/lookups/oracle.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/lookups/redis.c b/src/src/lookups/redis.c index 53b479142..a97496ba6 100644 --- a/src/src/lookups/redis.c +++ b/src/src/lookups/redis.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/lookups/sqlite.c b/src/src/lookups/sqlite.c index fd7b3e033..9f6c9e5aa 100644 --- a/src/src/lookups/sqlite.c +++ b/src/src/lookups/sqlite.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/macro_predef.c b/src/src/macro_predef.c index 55401e316..5bea91228 100644 --- a/src/src/macro_predef.c +++ b/src/src/macro_predef.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) Jeremy Harris 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/macros.h b/src/src/macros.h index 16d1503f2..1a619b951 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/malware.c b/src/src/malware.c index 760278079..408e2299b 100644 --- a/src/src/malware.c +++ b/src/src/malware.c @@ -3,7 +3,7 @@ *************************************************/ /* - * Copyright (c) The Exim Maintainers 2015 - 2023 + * Copyright (c) The Exim Maintainers 2015 - 2024 * Copyright (c) Tom Kistner 2003 - 2015 * License: GPL * SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/src/match.c b/src/src/match.c index 956ce60ad..4f4f2c777 100644 --- a/src/src/match.c +++ b/src/src/match.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/moan.c b/src/src/moan.c index 641540839..19d29190b 100644 --- a/src/src/moan.c +++ b/src/src/moan.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/os.c b/src/src/os.c index c4c86df95..dce5fca3d 100644 --- a/src/src/os.c +++ b/src/src/os.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2021 - 2023 */ +/* Copyright (c) The Exim Maintainers 2021 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/parse.c b/src/src/parse.c index 4a75d229e..e86dc4130 100644 --- a/src/src/parse.c +++ b/src/src/parse.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index 565ee068e..4fb22a113 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -1,7 +1,7 @@ /* * PDKIM - a RFC4871 (DKIM) implementation * - * Copyright (c) The Exim Maintainers 2021 - 2023 + * Copyright (c) The Exim Maintainers 2021 - 2024 * Copyright (C) 2016 - 2020 Jeremy Harris * Copyright (C) 2009 - 2016 Tom Kistner * SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/src/proxy.c b/src/src/proxy.c index e2d3bace0..46c98c8d0 100644 --- a/src/src/proxy.c +++ b/src/src/proxy.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/queue.c b/src/src/queue.c index 2c3d014f5..6e72a33d5 100644 --- a/src/src/queue.c +++ b/src/src/queue.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/rda.c b/src/src/rda.c index 7d329c580..776805ca4 100644 --- a/src/src/rda.c +++ b/src/src/rda.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim maintainers 2020 - 2022 */ +/* Copyright (c) The Exim maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/readconf.c b/src/src/readconf.c index aef07184a..4ed042df9 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/regex.c b/src/src/regex.c index 285ab8dbd..94e2928b9 100644 --- a/src/src/regex.c +++ b/src/src/regex.c @@ -3,7 +3,7 @@ *************************************************/ /* - * Copyright (c) The Exim Maintainers 2016 - 2023 + * Copyright (c) The Exim Maintainers 2016 - 2024 * Copyright (c) Tom Kistner 2003-2015 * License: GPL * SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/src/retry.c b/src/src/retry.c index 6ccef4f98..f180c779e 100644 --- a/src/src/retry.c +++ b/src/src/retry.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/rewrite.c b/src/src/rewrite.c index ce35c4365..dd3b4fa74 100644 --- a/src/src/rewrite.c +++ b/src/src/rewrite.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2021 - 2023 */ +/* Copyright (c) The Exim Maintainers 2021 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/rfc2047.c b/src/src/rfc2047.c index 1ab25150a..94178af73 100644 --- a/src/src/rfc2047.c +++ b/src/src/rfc2047.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/route.c b/src/src/route.c index afd43d866..887ef9975 100644 --- a/src/src/route.c +++ b/src/src/route.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/routers/accept.c b/src/src/routers/accept.c index 9766e5e10..6eb66e377 100644 --- a/src/src/routers/accept.c +++ b/src/src/routers/accept.c @@ -2,8 +2,8 @@ * Exim - an Internet mail transport agent * *************************************************/ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ -/* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/routers/dnslookup.c b/src/src/routers/dnslookup.c index d3c4349b6..ef8243818 100644 --- a/src/src/routers/dnslookup.c +++ b/src/src/routers/dnslookup.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/routers/ipliteral.c b/src/src/routers/ipliteral.c index 16e096b40..27c7fe6b3 100644 --- a/src/src/routers/ipliteral.c +++ b/src/src/routers/ipliteral.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/routers/iplookup.c b/src/src/routers/iplookup.c index b88212e5c..5078fdc00 100644 --- a/src/src/routers/iplookup.c +++ b/src/src/routers/iplookup.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/routers/manualroute.c b/src/src/routers/manualroute.c index ffd6eeaca..d07ea3490 100644 --- a/src/src/routers/manualroute.c +++ b/src/src/routers/manualroute.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/routers/queryprogram.c b/src/src/routers/queryprogram.c index 39645cd23..267a8ec5f 100644 --- a/src/src/routers/queryprogram.c +++ b/src/src/routers/queryprogram.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/routers/redirect.c b/src/src/routers/redirect.c index 799bee062..e1ee8bc33 100644 --- a/src/src/routers/redirect.c +++ b/src/src/routers/redirect.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/routers/rf_expand_data.c b/src/src/routers/rf_expand_data.c index 6ba613215..a3074f807 100644 --- a/src/src/routers/rf_expand_data.c +++ b/src/src/routers/rf_expand_data.c @@ -2,6 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ +/* Copyright (c) The Exim Maintainers 2024 */ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/routers/rf_functions.h b/src/src/routers/rf_functions.h index 40d4cde1c..597745f96 100644 --- a/src/src/routers/rf_functions.h +++ b/src/src/routers/rf_functions.h @@ -2,6 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ +/* Copyright (c) The Exim Maintainers 2024 */ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/routers/rf_get_errors_address.c b/src/src/routers/rf_get_errors_address.c index 792b9b5f3..6f8f7c7c3 100644 --- a/src/src/routers/rf_get_errors_address.c +++ b/src/src/routers/rf_get_errors_address.c @@ -2,8 +2,8 @@ * Exim - an Internet mail transport agent * *************************************************/ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ -/* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/routers/rf_get_transport.c b/src/src/routers/rf_get_transport.c index d37966865..fa1c2732f 100644 --- a/src/src/routers/rf_get_transport.c +++ b/src/src/routers/rf_get_transport.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2021 - 2022 */ +/* Copyright (c) The Exim Maintainers 2021 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/search.c b/src/src/search.c index d0eb84b3b..6c28b390e 100644 --- a/src/src/search.c +++ b/src/src/search.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 5110ed3d6..c52d3f4d6 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/smtp_out.c b/src/src/smtp_out.c index 81042003f..cfc96c13c 100644 --- a/src/src/smtp_out.c +++ b/src/src/smtp_out.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/spf.c b/src/src/spf.c index cc36463e4..9abe18fc3 100644 --- a/src/src/spf.c +++ b/src/src/spf.c @@ -3,7 +3,7 @@ *************************************************/ /* SPF support. - Copyright (c) The Exim Maintainers 2015 - 2023 + Copyright (c) The Exim Maintainers 2015 - 2024 Copyright (c) Tom Kistner 2004 - 2014 License: GPL SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/src/spf.h b/src/src/spf.h index afee742ed..7e9a6ca2a 100644 --- a/src/src/spf.h +++ b/src/src/spf.h @@ -2,8 +2,8 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Experimental SPF support. - Copyright (c) The Exim Maintainers 2016 - 2022 +/* SPF support. + Copyright (c) The Exim Maintainers 2016 - 2024 Copyright (c) Tom Kistner 2004 License: GPL SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/src/spool_in.c b/src/src/spool_in.c index 3e3b246ab..c788fd8f8 100644 --- a/src/src/spool_in.c +++ b/src/src/spool_in.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/spool_out.c b/src/src/spool_out.c index f3373faed..574aa167f 100644 --- a/src/src/spool_out.c +++ b/src/src/spool_out.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/store.c b/src/src/store.c index 03322a097..181e7fd67 100644 --- a/src/src/store.c +++ b/src/src/store.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim maintainers 2019 - 2023 */ +/* Copyright (c) The Exim maintainers 2019 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/string.c b/src/src/string.c index 170b47b23..113c05754 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/structs.h b/src/src/structs.h index 8aab54055..3c85f6ae5 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 3e8ec6d84..f443a6e27 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) Phil Pennock 2012 */ /* See the file NOTICE for conditions of use and distribution. */ diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index ca8e9b0de..c97106fe0 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2019 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/tls.c b/src/src/tls.c index 0e9a7c534..a1ae1abd1 100644 --- a/src/src/tls.c +++ b/src/src/tls.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/transport.c b/src/src/transport.c index 3609b78bb..6768e85de 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/transports/appendfile.c b/src/src/transports/appendfile.c index f495f9571..eeb726ae1 100644 --- a/src/src/transports/appendfile.c +++ b/src/src/transports/appendfile.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim maintainers 2020 - 2023 */ +/* Copyright (c) The Exim maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2020 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/transports/autoreply.c b/src/src/transports/autoreply.c index 803202b52..5ffc07215 100644 --- a/src/src/transports/autoreply.c +++ b/src/src/transports/autoreply.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/transports/lmtp.c b/src/src/transports/lmtp.c index eff0dc79d..7f82ef708 100644 --- a/src/src/transports/lmtp.c +++ b/src/src/transports/lmtp.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/transports/pipe.c b/src/src/transports/pipe.c index f8a1427b9..faed1d00f 100644 --- a/src/src/transports/pipe.c +++ b/src/src/transports/pipe.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim maintainers 2020 - 2023 */ +/* Copyright (c) The Exim maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/transports/queuefile.c b/src/src/transports/queuefile.c index 21c98eeef..79020217b 100644 --- a/src/src/transports/queuefile.c +++ b/src/src/transports/queuefile.c @@ -2,9 +2,9 @@ * Exim - an Internet mail transport agent * *************************************************/ +/* Copyright (c) The Exim Maintainers 1995 - 2024 */ /* Copyright (c) Andrew Colin Kissa 2016 */ /* Copyright (c) University of Cambridge 2016 */ -/* Copyright (c) The Exim Maintainers 1995 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 8645c150d..a5caf3de6 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h index fc8a6b126..1d892d567 100644 --- a/src/src/transports/smtp.h +++ b/src/src/transports/smtp.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/transports/smtp_socks.c b/src/src/transports/smtp_socks.c index ac8d0f2da..8848dd7a5 100644 --- a/src/src/transports/smtp_socks.c +++ b/src/src/transports/smtp_socks.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2021 - 2022 */ +/* Copyright (c) The Exim Maintainers 2021 - 2024 */ /* Copyright (c) Jeremy Harris 2015 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/tree.c b/src/src/tree.c index 2c703583e..8f73a46aa 100644 --- a/src/src/tree.c +++ b/src/src/tree.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2021 - 2023 */ +/* Copyright (c) The Exim Maintainers 2021 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/verify.c b/src/src/verify.c index 065deec41..c5b99a97c 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) The Exim Maintainers 2020 - 2024 */ /* Copyright (c) University of Cambridge 1995 - 2023 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/xclient.c b/src/src/xclient.c index fb9d95c5e..cca5dd887 100644 --- a/src/src/xclient.c +++ b/src/src/xclient.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2023 */ +/* Copyright (c) The Exim Maintainers 2023 - 2024 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ commit 50589c35f57a4b9465ad7041db7d5cd67c52d72b Author: Jeremy Harris Date: Fri Jun 7 20:12:27 2024 +0100 sqlite hintsdb: safer create of table diff --git a/src/src/dbfn.c b/src/src/dbfn.c index 6b2027aa9..5f30ccf58 100644 --- a/src/src/dbfn.c +++ b/src/src/dbfn.c @@ -67,6 +67,8 @@ Arguments: name The single-component name of one of Exim's database files. flags Either O_RDONLY or O_RDWR, indicating the type of open required; O_RDWR implies "create if necessary" +XXX this is a mess. hintsdb.h has grown lots of code expecting O_CREAT +XXX with the obvious semantics, and not that described above. dbblock Points to an open_db block to be filled in. lof If TRUE, write to the log for actual open failures (locking failures are always logged). diff --git a/src/src/hintsdb.h b/src/src/hintsdb.h index 545c83dbd..17b5c243c 100644 --- a/src/src/hintsdb.h +++ b/src/src/hintsdb.h @@ -87,7 +87,8 @@ if ((ret = sqlite3_open_v2(CCS name, &dbp, sflags, NULL)) == SQLITE_OK) sqlite3_busy_timeout(dbp, 5000); if (flags & O_CREAT) ret == sqlite3_exec(dbp, - "CREATE TABLE tbl (ky TEXT PRIMARY KEY, dat BLOB);", NULL, NULL, NULL); + "CREATE TABLE IF NOT EXISTS tbl (ky TEXT PRIMARY KEY, dat BLOB);", + NULL, NULL, NULL); } //else // fprintf(stderr, "sqlite3_open_v2: %s\n", sqlite3_errmsg(dbp)); commit 627391cbcaf3376e201be3ab2815b5abc560ce0c Author: Jeremy Harris Date: Fri Jun 7 21:45:57 2024 +0100 Hintsdb: tidy coding for DB create diff --git a/src/src/dbfn.c b/src/src/dbfn.c index 5f30ccf58..f1989d413 100644 --- a/src/src/dbfn.c +++ b/src/src/dbfn.c @@ -67,8 +67,6 @@ Arguments: name The single-component name of one of Exim's database files. flags Either O_RDONLY or O_RDWR, indicating the type of open required; O_RDWR implies "create if necessary" -XXX this is a mess. hintsdb.h has grown lots of code expecting O_CREAT -XXX with the obvious semantics, and not that described above. dbblock Points to an open_db block to be filled in. lof If TRUE, write to the log for actual open failures (locking failures are always logged). @@ -79,17 +77,14 @@ Returns: NULL if the open failed, or the locking failed. After locking On success, dbblock is returned. This contains the dbm pointer and the fd of the locked lock file. - -There are some calls that use O_RDWR|O_CREAT for the flags. Having discovered -this in December 2005, I'm not sure if this is correct or not, but for the -moment I haven't changed them. */ open_db * -dbfn_open(uschar *name, int flags, open_db *dbblock, BOOL lof, BOOL panic) +dbfn_open(const uschar * name, int flags, open_db * dbblock, + BOOL lof, BOOL panic) { int rc, save_errno; -BOOL read_only = flags == O_RDONLY; +BOOL read_only = flags & O_RDONLY; flock_t lock_data; uschar dirname[PATHLEN], filename[PATHLEN]; @@ -165,6 +160,7 @@ 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. */ +flags &= O_RDONLY | O_RDWR; snprintf(CS filename, sizeof(filename), "%s/%s", dirname, name); priv_drop_temp(exim_uid, exim_gid); @@ -202,7 +198,6 @@ DEBUG(D_hints_lookup) debug_printf_indent("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" : "??"); /* Pass back the block containing the opened database handle and the open fd diff --git a/src/src/dbfunctions.h b/src/src/dbfunctions.h index 1f0dec1f7..0b0bcab22 100644 --- a/src/src/dbfunctions.h +++ b/src/src/dbfunctions.h @@ -14,7 +14,7 @@ void dbfn_close(open_db *); int dbfn_delete(open_db *, const uschar *); -open_db *dbfn_open(uschar *, int, open_db *, BOOL, BOOL); +open_db *dbfn_open(const uschar *, int, open_db *, BOOL, BOOL); void *dbfn_read_with_length(open_db *, const uschar *, int *); void *dbfn_read_enforce_length(open_db *, const uschar *, size_t); uschar *dbfn_scan(open_db *, BOOL, EXIM_CURSOR **); diff --git a/src/src/directory.c b/src/src/directory.c index 94303db0b..876d097b5 100644 --- a/src/src/directory.c +++ b/src/src/directory.c @@ -36,7 +36,7 @@ Returns: panic on failure if panic is set; otherwise return FALSE; */ BOOL -directory_make(const uschar *parent, const uschar *name, +directory_make(const uschar * parent, const uschar * name, int mode, BOOL panic) { BOOL use_chown = parent == spool_directory && geteuid() == root_uid; diff --git a/src/src/exim_dbutil.c b/src/src/exim_dbutil.c index 4e8b291c6..b152df145 100644 --- a/src/src/exim_dbutil.c +++ b/src/src/exim_dbutil.c @@ -287,28 +287,23 @@ Returns: NULL if the open failed, or the locking failed. */ open_db * -dbfn_open(uschar *name, int flags, open_db *dbblock, BOOL lof, BOOL panic) +dbfn_open(const uschar * name, int flags, open_db * dbblock, + BOOL lof, BOOL panic) { int rc; struct flock lock_data; -BOOL read_only = flags == O_RDONLY; +BOOL read_only = flags & O_RDONLY; uschar * dirname, * filename; /* 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. */ -#ifdef COMPILE_UTILITY if ( asprintf(CSS &dirname, "%s/db", spool_directory) < 0 || asprintf(CSS &filename, "%s/%s.lockfile", dirname, name) < 0) return NULL; -#else -dirname = string_sprintf("%s/db", spool_directory); -filename = string_sprintf("%s/%s.lockfile", dirname, name); -#endif -dbblock->lockfd = Uopen(filename, flags, 0); -if (dbblock->lockfd < 0) +if ((dbblock->lockfd = Uopen(filename, O_RDWR|O_CREAT, 0)) < 0) { printf("** Failed to open database lock file %s: %s\n", filename, strerror(errno)); @@ -331,7 +326,7 @@ if (sigalrm_seen) errno = ETIMEDOUT; if (rc < 0) { printf("** Failed to get %s lock for %s: %s", - flags & O_WRONLY ? "write" : "read", + read_only ? "read" : "write", filename, errno == ETIMEDOUT ? "timed out" : strerror(errno)); (void)close(dbblock->lockfd); @@ -341,14 +336,11 @@ 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. */ -#ifdef COMPILE_UTILITY if (asprintf(CSS &filename, "%s/%s", dirname, name) < 0) return NULL; -#else -filename = string_sprintf("%s/%s", dirname, name); -#endif -dbblock->dbptr = exim_dbopen(filename, dirname, flags, 0); -if (!dbblock->dbptr) +if (flags & O_RDWR) flags |= O_CREAT; + +if (!(dbblock->dbptr = exim_dbopen(filename, dirname, flags, 0))) { printf("** Failed to open DBM file %s for %s:\n %s%s\n", filename, read_only? "reading" : "writing", strerror(errno), @@ -1427,3 +1419,5 @@ return 0; #endif /* EXIM_TIDYDB */ /* End of exim_dbutil.c */ +/* vi: aw ai sw=2 +*/ diff --git a/src/src/hintsdb.h b/src/src/hintsdb.h index 17b5c243c..3898b0221 100644 --- a/src/src/hintsdb.h +++ b/src/src/hintsdb.h @@ -23,7 +23,7 @@ binary blobs. The API is: Functions: - exim_dbopen + exim_dbopen O_RDONLY/O_RDWR, optionally OR'd with O_CREAT exim_dbclose exim_dbget exim_dbput @@ -542,8 +542,10 @@ if (db_create(&b, dbp, 0) == 0) { dbp->app_private = b; if (b->open(b, NULL, CS name, NULL, - flags == O_RDONLY ? DB_UNKNOWN : DB_HASH, - flags == O_RDONLY ? DB_RDONLY : DB_CREATE, + flags & O_CREAT ? DB_HASH : DB_UNKNOWN, + flags & O_CREAT ? DB_CREATE + : flags & O_RDONLY ? DB_RDONLY + : 0, /*XXX is there a writeable if exists option? */ mode) == 0 ) return dbp; @@ -676,8 +678,10 @@ EXIM_DB * dbp; return db_create(&dbp, NULL, 0) == 0 && ( dbp->set_errcall(dbp, dbfn_bdb_error_callback), dbp->open(dbp, CS name, NULL, - flags == O_RDONLY ? DB_UNKNOWN : DB_HASH, - flags == O_RDONLY ? DB_RDONLY : DB_CREATE, + flags & O_CREAT ? DB_HASH : DB_UNKNOWN, + flags & O_CREAT ? DB_CREATE + : flags & O_RDONLY ? DB_RDONLY + : 0, /*XXX*/ mode) ) == 0 ? dbp : NULL; diff --git a/src/src/verify.c b/src/src/verify.c index c5b99a97c..b1e5d6802 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -294,7 +294,7 @@ implying some kind of I/O error. We don't want to write the cache in that case. Otherwise the value is ccache_accept, ccache_reject, or ccache_reject_mfnull. */ if (dom_rec->result != ccache_unknown) - if (!(dbm_file = dbfn_open(US"callout", O_RDWR|O_CREAT, &dbblock, FALSE, TRUE))) + if (!(dbm_file = dbfn_open(US"callout", O_RDWR, &dbblock, FALSE, TRUE))) { HDEBUG(D_verify) debug_printf_indent("callout cache: not available\n"); } @@ -316,7 +316,7 @@ is disabled. */ if (done && addr_rec->result != ccache_unknown) { if (!dbm_file) - dbm_file = dbfn_open(US"callout", O_RDWR|O_CREAT, &dbblock, FALSE, TRUE); + dbm_file = dbfn_open(US"callout", O_RDWR, &dbblock, FALSE, TRUE); if (!dbm_file) { HDEBUG(D_verify) debug_printf_indent("no callout cache available\n"); @@ -3523,7 +3523,7 @@ dbdata_callout_cache_address cache_address_record; if (!pos_cache && !neg_cache) return; -if (!(dbm_file = dbfn_open(US"callout", O_RDWR|O_CREAT, &dbblock, FALSE, TRUE))) +if (!(dbm_file = dbfn_open(US"callout", O_RDWR, &dbblock, FALSE, TRUE))) { HDEBUG(D_verify) debug_printf_indent("quota cache: not available\n"); return; commit 04c21a8fe3b5027b7430cc9c706196733426bfa6 Author: Jeremy Harris Date: Fri Jun 7 23:42:02 2024 +0100 Fix build with DISABLE_EVENTS diff --git a/src/src/dns.c b/src/src/dns.c index 510011d51..538c0c0b4 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -1135,6 +1135,7 @@ errstr = US"cname_loop"; not_good: { +#ifndef DISABLE_EVENTS const uschar * s = NULL; BOOL save_flag = f.search_find_defer; uschar * save_serr = search_error_message; @@ -1167,6 +1168,7 @@ not_good: /*XXX what other state could an expansion in the eventhandler mess up? */ search_error_message = save_serr; f.search_find_defer = save_flag; +#endif /*EVENTS*/ return rc; } } commit 8769407d774157072e36d23bc6cc831cadacb6bb Author: Jeremy Harris Date: Sat Jun 8 13:17:19 2024 +0100 Hintsdb: fix build with USE_TDB Broken-by: 799253d6a75d diff --git a/src/src/hintsdb.h b/src/src/hintsdb.h index 3898b0221..643395e13 100644 --- a/src/src/hintsdb.h +++ b/src/src/hintsdb.h @@ -427,7 +427,7 @@ exim_datum_data_set(EXIM_DATUM * dp, void * s) static inline unsigned exim_datum_size_get(EXIM_DATUM * dp) -{ return dp->len; } +{ return dp->dsize; } static inline void exim_datum_size_set(EXIM_DATUM * dp, unsigned n) { dp->dsize = n; } commit 3ad9bd22ff9074736f35b60a9f9085ab5c3a5c38 Author: Jeremy Harris Date: Sat Jun 8 14:16:48 2024 +0100 Config conflict checking vs. USE_SQLITE diff --git a/src/src/hintsdb.h b/src/src/hintsdb.h index 643395e13..7335ef842 100644 --- a/src/src/hintsdb.h +++ b/src/src/hintsdb.h @@ -33,8 +33,8 @@ The API is: exim_dbscan (get, and bump cursor) exim_dbdelete_cursor exim_datum_init - exim_datum_size_get - exim_datum_data_get + exim_datum_size_get/set + exim_datum_data_get/set exim_datum_free Defines: EXIM_DB access handle @@ -49,6 +49,17 @@ The users of this API are: hintsdb utilities exim_dbutil.c and exim_dbmvuild.c dbmdb lookup lookups/dbmdb,c autoreply transport transports/autoreply.c + +Note that the dbmdb lookup use, bypassing the dbfn.c layer, +means that no file-locking is done. +XXX This feels like a layering violation; I don't see it commented on +anywhere. + +Future: consider re-architecting to support caching of the open-handle +for hintsdb uses (the dbmdb use gets that already). This would need APIs +for transaction locks. Perhaps merge the implementation with the lookups +layer, in some way, for the open-handle caching (since that manages closes +required by Exim's process transisitions)? */ #ifndef HINTSDB_H @@ -56,6 +67,9 @@ The users of this API are: #ifdef USE_SQLITE +# if defined(USE_DB) || defined(USE_GDBM) || defined(USE_TDB) +# error USE_SQLITE conflict with alternate definition +# endif /* ********************* sqlite3 interface ************************ */ @@ -323,7 +337,7 @@ exim_datum_free(EXIM_DATUM * dp) #elif defined(USE_TDB) -# if defined(USE_DB) || defined(USE_GDBM) +# if defined(USE_DB) || defined(USE_GDBM) || defined(USE_SQLITE) # error USE_TDB conflict with alternate definition # endif @@ -460,7 +474,7 @@ d->dptr = NULL; #elif defined USE_DB -# if defined(USE_TDB) || defined(USE_GDBM) +# if defined(USE_TDB) || defined(USE_GDBM) || defined(USE_SQLITE) # error USE_DB conflict with alternate definition # endif @@ -791,9 +805,9 @@ exim_datum_free(EXIM_DATUM * d) /********************* gdbm interface definitions **********************/ #elif defined USE_GDBM -/*XXX TODO: exim's locfile not needed */ +/*XXX TODO: exim's lockfile not needed? */ -# if defined(USE_TDB) || defined(USE_DB) +# if defined(USE_TDB) || defined(USE_DB) || defined(USE_SQLITE) # error USE_GDBM conflict with alternate definition # endif @@ -935,8 +949,8 @@ exim_datum_free(EXIM_DATUM * d) -/* If none of USE_DB, USG_GDBM, or USE_TDB are set, the default is the NDBM -interface (which seems to be a wrapper for GDBM) */ +/* If none of USE_DB, USG_GDBM, USE_SQLITE or USE_TDB are set, +the default is the NDBM interface (which seems to be a wrapper for GDBM) */ /********************* ndbm interface definitions **********************/ diff --git a/src/src/lookups/dbmdb.c b/src/src/lookups/dbmdb.c index 1b3dc031e..d68110b12 100644 --- a/src/src/lookups/dbmdb.c +++ b/src/src/lookups/dbmdb.c @@ -49,7 +49,7 @@ dbmdb_check(void *handle, const uschar *filename, int modemask, uid_t *owners, { int rc; -#if defined(USE_DB) || defined(USE_TDB) || defined(USE_GDBM) +#if defined(USE_DB) || defined(USE_TDB) || defined(USE_GDBM) || defined(USE_SQLITE) rc = lf_check_file(-1, filename, S_IFREG, modemask, owners, owngroups, "dbm", errmsg); #else commit de531e70dc0e5aa94cf6fba35afa1cd23c36bf1f Author: Jeremy Harris Date: Sun Jun 9 14:22:20 2024 +0100 Hintsbd: fix locking Broken-by: 627391cbcaf3 diff --git a/src/src/exim_dbutil.c b/src/src/exim_dbutil.c index b152df145..c466ae380 100644 --- a/src/src/exim_dbutil.c +++ b/src/src/exim_dbutil.c @@ -292,18 +292,21 @@ dbfn_open(const uschar * name, int flags, open_db * dbblock, { int rc; struct flock lock_data; -BOOL read_only = flags & O_RDONLY; +BOOL read_only = (flags & (O_WRONLY|O_RDWR)) == O_RDONLY; uschar * dirname, * filename; /* 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. */ +open it. If there is a database, there should be a lock file in existence; +if no lockfile we infer there is no database and error out. We open the +lockfile using the r/w mode requested for the DB, users lacking permission +for the DB access mode will error out here. */ if ( asprintf(CSS &dirname, "%s/db", spool_directory) < 0 || asprintf(CSS &filename, "%s/%s.lockfile", dirname, name) < 0) return NULL; -if ((dbblock->lockfd = Uopen(filename, O_RDWR|O_CREAT, 0)) < 0) +if ((dbblock->lockfd = Uopen(filename, flags, 0)) < 0) { printf("** Failed to open database lock file %s: %s\n", filename, strerror(errno)); diff --git a/src/src/hintsdb.h b/src/src/hintsdb.h index 7335ef842..12c370dba 100644 --- a/src/src/hintsdb.h +++ b/src/src/hintsdb.h @@ -558,8 +558,7 @@ if (db_create(&b, dbp, 0) == 0) if (b->open(b, NULL, CS name, NULL, flags & O_CREAT ? DB_HASH : DB_UNKNOWN, flags & O_CREAT ? DB_CREATE - : flags & O_RDONLY ? DB_RDONLY - : 0, /*XXX is there a writeable if exists option? */ + : flags & (O_WRONLY|O_RDWR) ? 0 : DB_RDONLY, mode) == 0 ) return dbp; @@ -694,8 +693,7 @@ return db_create(&dbp, NULL, 0) == 0 dbp->open(dbp, CS name, NULL, flags & O_CREAT ? DB_HASH : DB_UNKNOWN, flags & O_CREAT ? DB_CREATE - : flags & O_RDONLY ? DB_RDONLY - : 0, /*XXX*/ + : flags & (O_WRONLY|O_RDWR) ? 0 : DB_RDONLY, mode) ) == 0 ? dbp : NULL; @@ -842,8 +840,7 @@ if (dbp) dbp->lkey.dptr = NULL; dbp->gdbm = gdbm_open(CS name, 0, flags & O_CREAT ? GDBM_WRCREAT - : flags & (O_RDWR|O_WRONLY) ? GDBM_WRITER - : GDBM_READER, + : flags & (O_RDWR|O_WRONLY) ? GDBM_WRITER : GDBM_READER, mode, 0); if (dbp->gdbm) return dbp; free(dbp); commit f62aa2d04dae708c59988157496a0479f2c57286 Author: Jeremy Harris Date: Sun Jun 9 16:08:38 2024 +0100 Hintsdb: fix build config phase for sqlite diff --git a/src/src/buildconfig.c b/src/src/buildconfig.c index 8f53671a0..bfd267929 100644 --- a/src/src/buildconfig.c +++ b/src/src/buildconfig.c @@ -53,7 +53,7 @@ typedef struct { char *data; } save_item; -static const char *db_opts[] = { "", "USE_DB", "USE_GDBM", "USE_TDB", "USE_NDBM" }; +static const char *db_opts[] = { "", "USE_DB", "USE_GDBM", "USE_TDB", "USE_NDBM", "USE_SQLITE" }; static int have_ipv6 = 0; static int have_iconv = 0; @@ -266,7 +266,7 @@ while (fgets(buffer, sizeof(buffer), base) != NULL) { if (use_which_db_in_local_makefile) { - printf("*** Only one of USE_DB, USE_GDBM, or USE_TDB should be " + printf("*** Only one of USE_DB, USE_GDBM, USE_SQLITE or USE_TDB should be " "defined in Local/Makefile\n"); exit(1); } commit 28c8a333bbff8e7a8796e047e4f921fdb89d710f Author: Jeremy Harris Date: Mon Jun 10 17:18:32 2024 +0100 Refactor hintsdb lockfile acquisition diff --git a/src/src/dbfn.c b/src/src/dbfn.c index f1989d413..88b4d8920 100644 --- a/src/src/dbfn.c +++ b/src/src/dbfn.c @@ -61,6 +61,59 @@ Users: * Open and lock a database file * *************************************************/ +/* Lock a file to protect the DB. Return TRUE for success */ + +static inline BOOL +lockfile_take(open_db * dbblock, const uschar * filename, BOOL rdonly, BOOL panic) +{ +flock_t lock_data; +int rc; + +priv_drop_temp(exim_uid, exim_gid); +if ((dbblock->lockfd = Uopen(filename, O_RDWR, EXIMDB_LOCKFILE_MODE)) < 0) + { + (void)directory_make(spool_directory, US"db", EXIMDB_DIRECTORY_MODE, panic); + dbblock->lockfd = Uopen(filename, O_RDWR|O_CREAT, EXIMDB_LOCKFILE_MODE); + } +priv_restore(); + +if (dbblock->lockfd < 0) + { + log_write(0, LOG_MAIN, "%s", + string_open_failed("database lock file %s", filename)); + errno = 0; /* Indicates locking failure */ + return FALSE; + } + +/* Now we must get a lock on the opened lock file; do this with a blocking +lock that times out. */ + +lock_data.l_type = rdonly ? 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_indent("locking %s\n", filename); + +sigalrm_seen = FALSE; +ALARM(EXIMDB_LOCK_TIMEOUT); +rc = fcntl(dbblock->lockfd, F_SETLKW, &lock_data); +ALARM_CLR(0); + +if (sigalrm_seen) errno = ETIMEDOUT; +if (rc < 0) + { + log_write(0, LOG_MAIN|LOG_PANIC, "Failed to get %s lock for %s: %s", + rdonly ? "read" : "write", filename, + errno == ETIMEDOUT ? "timed out" : strerror(errno)); + (void)close(dbblock->lockfd); + errno = 0; /* Indicates locking failure */ + return FALSE; + } + +DEBUG(D_hints_lookup) debug_printf_indent("locked %s\n", filename); +return TRUE; +} + /* Used for accessing Exim's hints databases. Arguments: @@ -85,7 +138,6 @@ dbfn_open(const uschar * name, int flags, open_db * dbblock, { int rc, save_errno; BOOL read_only = flags & O_RDONLY; -flock_t lock_data; uschar dirname[PATHLEN], filename[PATHLEN]; DEBUG(D_hints_lookup) acl_level++; @@ -106,54 +158,16 @@ exists, there is no error. */ snprintf(CS dirname, sizeof(dirname), "%s/db", spool_directory); snprintf(CS filename, sizeof(filename), "%s/%s.lockfile", dirname, name); -priv_drop_temp(exim_uid, exim_gid); -if ((dbblock->lockfd = Uopen(filename, O_RDWR, EXIMDB_LOCKFILE_MODE)) < 0) +dbblock->lockfd = -1; +if (!lockfile_take(dbblock, filename, flags == O_RDONLY, panic)) { - (void)directory_make(spool_directory, US"db", EXIMDB_DIRECTORY_MODE, panic); - dbblock->lockfd = Uopen(filename, O_RDWR|O_CREAT, EXIMDB_LOCKFILE_MODE); - } -priv_restore(); - -if (dbblock->lockfd < 0) - { - log_write(0, LOG_MAIN, "%s", - string_open_failed("database lock file %s", filename)); - errno = 0; /* Indicates locking failure */ DEBUG(D_hints_lookup) acl_level--; return NULL; } -/* Now we must get a lock on the opened lock file; do this with a blocking -lock that times out. */ - -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_indent("locking %s\n", filename); - -sigalrm_seen = FALSE; -ALARM(EXIMDB_LOCK_TIMEOUT); -rc = fcntl(dbblock->lockfd, F_SETLKW, &lock_data); -ALARM_CLR(0); - -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", filename, - errno == ETIMEDOUT ? "timed out" : strerror(errno)); - (void)close(dbblock->lockfd); - errno = 0; /* Indicates locking failure */ - DEBUG(D_hints_lookup) acl_level--; - return NULL; - } - -DEBUG(D_hints_lookup) debug_printf_indent("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 -expected to create it, don't do so at first, again so that we can detect +/* 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 expected to create it, don't do so at first, again so that we can detect whether we need to change its ownership (see comments about the lock file above.) There have been regular reports of crashes while opening hints databases - often this is caused by non-matching db.h and the library. To make commit 5bb1dfe41e40fa45f85305ccbd7d4533a229d3fc Author: Jeremy Harris Date: Wed Jun 12 22:37:15 2024 +0100 Hintsdb sqlite: use transaction rather than file lock diff --git a/src/src/dbfn.c b/src/src/dbfn.c index 88b4d8920..2b5ec908b 100644 --- a/src/src/dbfn.c +++ b/src/src/dbfn.c @@ -61,23 +61,32 @@ Users: * Open and lock a database file * *************************************************/ +/* Ensure the directory for the DB is present */ + +static inline void +db_dir_make(BOOL panic) +{ +(void) directory_make(spool_directory, US"db", EXIMDB_DIRECTORY_MODE, panic); +} + + /* Lock a file to protect the DB. Return TRUE for success */ static inline BOOL lockfile_take(open_db * dbblock, const uschar * filename, BOOL rdonly, BOOL panic) { flock_t lock_data; -int rc; +int rc, * fdp = &dbblock->lockfd; priv_drop_temp(exim_uid, exim_gid); -if ((dbblock->lockfd = Uopen(filename, O_RDWR, EXIMDB_LOCKFILE_MODE)) < 0) +if ((*fdp = Uopen(filename, O_RDWR, EXIMDB_LOCKFILE_MODE)) < 0) { - (void)directory_make(spool_directory, US"db", EXIMDB_DIRECTORY_MODE, panic); - dbblock->lockfd = Uopen(filename, O_RDWR|O_CREAT, EXIMDB_LOCKFILE_MODE); + db_dir_make(panic); + *fdp = Uopen(filename, O_RDWR|O_CREAT, EXIMDB_LOCKFILE_MODE); } priv_restore(); -if (dbblock->lockfd < 0) +if (*fdp < 0) { log_write(0, LOG_MAIN, "%s", string_open_failed("database lock file %s", filename)); @@ -96,7 +105,7 @@ DEBUG(D_hints_lookup|D_retry|D_route|D_deliver) sigalrm_seen = FALSE; ALARM(EXIMDB_LOCK_TIMEOUT); -rc = fcntl(dbblock->lockfd, F_SETLKW, &lock_data); +rc = fcntl(*fdp, F_SETLKW, &lock_data); ALARM_CLR(0); if (sigalrm_seen) errno = ETIMEDOUT; @@ -105,7 +114,7 @@ if (rc < 0) log_write(0, LOG_MAIN|LOG_PANIC, "Failed to get %s lock for %s: %s", rdonly ? "read" : "write", filename, errno == ETIMEDOUT ? "timed out" : strerror(errno)); - (void)close(dbblock->lockfd); + (void)close(*fdp); *fdp = -1; errno = 0; /* Indicates locking failure */ return FALSE; } @@ -137,7 +146,7 @@ dbfn_open(const uschar * name, int flags, open_db * dbblock, BOOL lof, BOOL panic) { int rc, save_errno; -BOOL read_only = flags & O_RDONLY; +flock_t lock_data; uschar dirname[PATHLEN], filename[PATHLEN]; DEBUG(D_hints_lookup) acl_level++; @@ -159,15 +168,20 @@ snprintf(CS dirname, sizeof(dirname), "%s/db", spool_directory); snprintf(CS filename, sizeof(filename), "%s/%s.lockfile", dirname, name); dbblock->lockfd = -1; -if (!lockfile_take(dbblock, filename, flags == O_RDONLY, panic)) +if (!exim_lockfile_needed()) + db_dir_make(panic); +else { - DEBUG(D_hints_lookup) acl_level--; - return NULL; + if (!lockfile_take(dbblock, filename, flags == O_RDONLY, panic)) + { + DEBUG(D_hints_lookup) acl_level--; + return NULL; + } } -/* 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 expected to create it, don't do so at first, again so that we can detect +/* 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 +expected to create it, don't do so at first, again so that we can detect whether we need to change its ownership (see comments about the lock file above.) There have been regular reports of crashes while opening hints databases - often this is caused by non-matching db.h and the library. To make @@ -203,6 +217,7 @@ if (!dbblock->dbptr) debug_printf_indent("%s\n", CS string_open_failed("DB file %s", filename)); (void)close(dbblock->lockfd); + dbblock->lockfd = -1; errno = save_errno; DEBUG(D_hints_lookup) acl_level--; return NULL; @@ -228,7 +243,7 @@ return dbblock; *************************************************/ /* Closing a file automatically unlocks it, so after closing the database, just -close the lock file. +close the lock file if there was one. Argument: a pointer to an open database block Returns: nothing @@ -237,10 +252,17 @@ Returns: nothing void dbfn_close(open_db *dbblock) { +int * fdp = &dbblock->lockfd; + exim_dbclose(dbblock->dbptr); -(void)close(dbblock->lockfd); +if (*fdp >= 0) (void)close(*fdp); DEBUG(D_hints_lookup) - { debug_printf_indent("closed hints database and lockfile\n"); acl_level--; } + { + debug_printf_indent("closed hints database%s\n", + *fdp < 0 ? "" : " and lockfile"); + acl_level--; + } +*fdp = -1; } diff --git a/src/src/exim_dbmbuild.c b/src/src/exim_dbmbuild.c index d68333079..d07c3e024 100644 --- a/src/src/exim_dbmbuild.c +++ b/src/src/exim_dbmbuild.c @@ -76,14 +76,6 @@ uschar * queue_name; BOOL split_spool_directory; -/* These introduced by the taintwarn handling */ -rmark -store_mark_3(const char *func, int linenumber) -{ return NULL; } -#ifdef ALLOW_INSECURE_TAINTED_DATA -BOOL allow_insecure_tainted_data; -#endif - /******************************************************************************/ @@ -177,7 +169,7 @@ BOOL lowercase = TRUE; BOOL warn = TRUE; BOOL duperr = TRUE; BOOL lastdup = FALSE; -#if !defined (USE_DB) && !defined(USE_TDB) && !defined(USE_GDBM) +#if !defined (USE_DB) && !defined(USE_TDB) && !defined(USE_GDBM) && !defined(USE_SQLITE) int is_db = 0; struct stat statbuf; #endif @@ -221,7 +213,7 @@ else if (!(f = fopen(argv[arg], "rb"))) /* By default Berkeley db does not put extensions on... which can be painful! */ -#if defined(USE_DB) || defined(USE_TDB) || defined(USE_GDBM) +#if defined(USE_DB) || defined(USE_TDB) || defined(USE_GDBM) && !defined(USE_SQLITE) if (Ustrcmp(argv[arg], argv[arg+1]) == 0) { printf("exim_dbmbuild: input and output filenames are the same\n"); @@ -261,7 +253,7 @@ if (!(d = exim_dbopen(temp_dbmname, dirname, O_RDWR|O_CREAT|O_EXCL, 0644))) /* Unless using native db calls, see if we have created .db; if not, assume .dir & .pag */ -#if !defined(USE_DB) && !defined(USE_TDB) && !defined(USE_GDBM) +#if !defined(USE_DB) && !defined(USE_TDB) && !defined(USE_GDBM) && !defined(USE_SQLITE) sprintf(CS real_dbmname, "%s.db", temp_dbmname); is_db = Ustat(real_dbmname, &statbuf) == 0; #endif @@ -333,7 +325,8 @@ while (Ufgets(line, max_insize, f) != NULL) exim_datum_data_set(&content, buffer); exim_datum_size_set(&content, bptr - buffer + add_zero); - switch(rc = exim_dbputb(d, &key, &content)) + rc = exim_dbputb(d, &key, &content); + switch(rc) { case EXIM_DBPUTB_OK: count++; @@ -423,7 +416,8 @@ if (started) exim_datum_data_set(&content, buffer); exim_datum_size_set(&content, bptr - buffer + add_zero); - switch(rc = exim_dbputb(d, &key, &content)) + rc = exim_dbputb(d, &key, &content); + switch(rc) { case EXIM_DBPUTB_OK: count++; @@ -462,7 +456,7 @@ if (yield == 0 || yield == 1) printf("%d duplicate key%s \n", dupcount, (dupcount > 1)? "s" : ""); } - #if defined(USE_DB) || defined(USE_TDB) || defined(USE_GDBM) +#if defined(USE_DB) || defined(USE_TDB) || defined(USE_GDBM) || defined(USE_SQLITE) Ustrcpy(real_dbmname, temp_dbmname); Ustrcpy(buffer, US argv[arg+1]); if (Urename(real_dbmname, buffer) != 0) @@ -470,7 +464,7 @@ if (yield == 0 || yield == 1) printf("Unable to rename %s as %s\n", real_dbmname, buffer); return 1; } - #else +#else /* Rename a single .db file */ @@ -506,7 +500,7 @@ if (yield == 0 || yield == 1) } } - #endif /* USE_DB || USE_TDB || USE_GDBM */ +#endif /* USE_DB || USE_TDB || USE_GDBM || USE_SQLITE */ } /* Otherwise unlink the temporary files. */ @@ -514,7 +508,7 @@ if (yield == 0 || yield == 1) else { printf("dbmbuild abandoned\n"); -#if defined(USE_DB) || defined(USE_TDB) || defined(USE_GDBM) +#if defined(USE_DB) || defined(USE_TDB) || defined(USE_GDBM) || defined(USE_SQLITE) /* We created it, so safe to delete despite the name coming from outside */ /* coverity[tainted_string] */ Uunlink(temp_dbmname); @@ -531,10 +525,12 @@ else sprintf(CS real_dbmname, "%s.pag", temp_dbmname); Uunlink(real_dbmname); } -#endif /* USE_DB || USE_TDB */ +#endif /* USE_DB || USE_TDB || USE_GDBM || USE_SQLITE */ } return yield; } /* End of exim_dbmbuild.c */ +/* se aw ai sw=2 +*/ diff --git a/src/src/exim_dbutil.c b/src/src/exim_dbutil.c index c466ae380..e7d523e88 100644 --- a/src/src/exim_dbutil.c +++ b/src/src/exim_dbutil.c @@ -81,11 +81,6 @@ uschar * queue_name; BOOL split_spool_directory; -/* These introduced by the taintwarn handling */ -#ifdef ALLOW_INSECURE_TAINTED_DATA -BOOL allow_insecure_tainted_data; -#endif - /******************************************************************************/ @@ -306,38 +301,42 @@ if ( asprintf(CSS &dirname, "%s/db", spool_directory) < 0 || asprintf(CSS &filename, "%s/%s.lockfile", dirname, name) < 0) return NULL; -if ((dbblock->lockfd = Uopen(filename, flags, 0)) < 0) +dbblock->lockfd = -1; +if (exim_lockfile_needed()) { - printf("** Failed to open database lock file %s: %s\n", filename, - strerror(errno)); - return NULL; - } + if ((dbblock->lockfd = Uopen(filename, flags, 0)) < 0) + { + printf("** Failed to open database lock file %s: %s\n", filename, + strerror(errno)); + return NULL; + } -/* Now we must get a lock on the opened lock file; do this with a blocking -lock that times out. */ + /* Now we must get a lock on the opened lock file; do this with a blocking + lock that times out. */ -lock_data.l_type = read_only ? F_RDLCK : F_WRLCK; -lock_data.l_whence = lock_data.l_start = lock_data.l_len = 0; + lock_data.l_type = read_only ? F_RDLCK : F_WRLCK; + lock_data.l_whence = lock_data.l_start = lock_data.l_len = 0; -sigalrm_seen = FALSE; -os_non_restarting_signal(SIGALRM, sigalrm_handler); -ALARM(EXIMDB_LOCK_TIMEOUT); -rc = fcntl(dbblock->lockfd, F_SETLKW, &lock_data); -ALARM_CLR(0); + sigalrm_seen = FALSE; + os_non_restarting_signal(SIGALRM, sigalrm_handler); + ALARM(EXIMDB_LOCK_TIMEOUT); + rc = fcntl(dbblock->lockfd, F_SETLKW, &lock_data); + ALARM_CLR(0); -if (sigalrm_seen) errno = ETIMEDOUT; -if (rc < 0) - { - printf("** Failed to get %s lock for %s: %s", - read_only ? "read" : "write", - filename, - errno == ETIMEDOUT ? "timed out" : strerror(errno)); - (void)close(dbblock->lockfd); - return NULL; - } + if (sigalrm_seen) errno = ETIMEDOUT; + if (rc < 0) + { + printf("** Failed to get %s lock for %s: %s", + read_only ? "read" : "write", + filename, + errno == ETIMEDOUT ? "timed out" : strerror(errno)); + (void)close(dbblock->lockfd); + return NULL; + } -/* 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. */ + /* 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 (asprintf(CSS &filename, "%s/%s", dirname, name) < 0) return NULL; @@ -345,15 +344,15 @@ if (flags & O_RDWR) flags |= O_CREAT; if (!(dbblock->dbptr = exim_dbopen(filename, dirname, flags, 0))) { - printf("** Failed to open DBM file %s for %s:\n %s%s\n", filename, - read_only? "reading" : "writing", strerror(errno), - #ifdef USE_DB + printf("** Failed to open DBM file %s for %s: %s%s\n", filename, + read_only ? "reading" : "writing", strerror(errno), +#ifdef USE_DB " (or Berkeley DB error while opening)" - #else +#else "" - #endif +#endif ); - (void)close(dbblock->lockfd); + if (dbblock->lockfd >= 0) (void)close(dbblock->lockfd); return NULL; } @@ -368,17 +367,17 @@ return dbblock; *************************************************/ /* Closing a file automatically unlocks it, so after closing the database, just -close the lock file. +close the lock file if there was one. Argument: a pointer to an open database block Returns: nothing */ void -dbfn_close(open_db *dbblock) +dbfn_close(open_db * dbp) { -exim_dbclose(dbblock->dbptr); -(void)close(dbblock->lockfd); +exim_dbclose(dbp->dbptr); +if (dbp->lockfd >= 0) (void) close(dbp->lockfd); } diff --git a/src/src/hintsdb.h b/src/src/hintsdb.h index 12c370dba..c5a856abc 100644 --- a/src/src/hintsdb.h +++ b/src/src/hintsdb.h @@ -23,14 +23,15 @@ binary blobs. The API is: Functions: - exim_dbopen O_RDONLY/O_RDWR, optionally OR'd with O_CREAT + exim_lockfile_needed API semantics predicate + exim_dbopen exim_dbclose exim_dbget exim_dbput - exim_dbputb (non-overwriting put) + exim_dbputb non-overwriting put exim_dbdel exim_dbcreate_cursor - exim_dbscan (get, and bump cursor) + exim_dbscan get, and bump cursor exim_dbdelete_cursor exim_datum_init exim_datum_size_get/set @@ -88,6 +89,12 @@ required by Exim's process transisitions)? # /* Access functions */ +static inline BOOL +exim_lockfile_needed(void) +{ +return FALSE; /* We do transaction; no extra locking needed */ +} + /* EXIM_DBOPEN - return pointer to an EXIM_DB, NULL if failed */ static inline EXIM_DB * exim_dbopen__(const uschar * name, const uschar * dirname, int flags, @@ -99,10 +106,13 @@ if (flags & O_CREAT) sflags |= SQLITE_OPEN_CREATE; if ((ret = sqlite3_open_v2(CCS name, &dbp, sflags, NULL)) == SQLITE_OK) { sqlite3_busy_timeout(dbp, 5000); - if (flags & O_CREAT) - ret == sqlite3_exec(dbp, + ret = sqlite3_exec(dbp, "BEGIN TRANSACTION;", NULL, NULL, NULL); + if (ret == SQLITE_OK && flags & O_CREAT) + ret = sqlite3_exec(dbp, "CREATE TABLE IF NOT EXISTS tbl (ky TEXT PRIMARY KEY, dat BLOB);", NULL, NULL, NULL); + if (ret != SQLITE_OK) + sqlite3_close(dbp); } //else // fprintf(stderr, "sqlite3_open_v2: %s\n", sqlite3_errmsg(dbp)); @@ -134,7 +144,11 @@ if (sqlite3_step(statement) != SQLITE_ROW) } res->len = sqlite3_column_bytes(statement, 0); -res->data = store_get(res->len + 1, GET_TAINTED); +# ifdef COMPILE_UTILITY +res->data = malloc(res->len); +# else +res->data = store_get(res->len, GET_TAINTED); +# endif memcpy(res->data, sqlite3_column_blob(statement, 0), res->len); res->data[res->len] = '\0'; /* fprintf(stderr, "res %d bytes: '%.*s'\n", (int)res->len, (int)res->len, res->data); */ @@ -173,23 +187,28 @@ return ret; static inline int exim_s_dbp(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data, const uschar * alt) { +int hlen = data->len * 2, off = 0, res; # define FMT "INSERT OR %s INTO tbl (ky,dat) VALUES ('%.*s', X'%.*s');" -uschar * hex = store_get(data->len * 2, data->data), * qry; -int res; +# ifdef COMPILE_UTILITY +uschar * hex = malloc(hlen+1); +# else +uschar * hex = store_get(hlen+1, data->data); +# endif +uschar * qry; -for (const uschar * s = data->data, * t = s + data->len; s < t; s++) - sprintf(CS hex + 2 * (s - data->data), "%02X", *s); +for (const uschar * s = data->data, * t = s + data->len; s < t; s++, off += 2) + sprintf(CS hex + off, "%02X", *s); # ifdef COMPILE_UTILITY -res = snprintf(NULL, 0, FMT, - alt, (int) key->len, key->data, (int)data->len * 2, hex); +res = snprintf(CS hex, 0, FMT, alt, (int) key->len, key->data, hlen, hex); qry = malloc(res); -snprintf(CS qry, res, FMT, alt, (int) key->len, key->data, (int)data->len * 2, hex); +snprintf(CS qry, res, FMT, alt, (int) key->len, key->data, hlen, hex); /* fprintf(stderr, "exim_s_dbp(%s)\n", qry); */ res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL); free(qry); +free(hex); # else -qry = string_sprintf(FMT, alt, (int) key->len, key->data, (int)data->len * 2, hex); +qry = string_sprintf(FMT, alt, (int) key->len, key->data, hlen, hex); /* fprintf(stderr, "exim_s_dbp(%s)\n", qry); */ res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL); /* fprintf(stderr, "exim_s_dbp res %d\n", res); */ @@ -231,7 +250,7 @@ uschar * qry; int res; # ifdef COMPILE_UTILITY -res = snprintf(NULL, 0, FMT, (int) key->len, key->data); +res = snprintf(NULL, 0, FMT, (int) key->len, key->data); /* res excludes nul */ qry = malloc(res); snprintf(CS qry, res, FMT, (int) key->len, key->data); res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL); @@ -252,7 +271,11 @@ return res; static inline EXIM_CURSOR * exim_dbcreate_cursor(EXIM_DB * dbp) { +# ifdef COMPILE_UTILITY +EXIM_CURSOR * c = malloc(sizeof(int)); +# else EXIM_CURSOR * c = store_malloc(sizeof(int)); +# endif *c = 0; return c; } @@ -269,8 +292,8 @@ int i; BOOL ret; # ifdef COMPILE_UTILITY -qry = malloc(i = snprintf(NULL, 0, FMT, *cursor)); -snprintf(CS qry, i, FMT, *cursor); +qry = malloc((i = snprintf(NULL, 0, FMT, *cursor))); +snprintf(CS qry, i-1, FMT, *cursor); /* fprintf(stderr, "exim_dbscan(%s)\n", qry); */ ret = exim_dbget__(dbp, qry, key); free(qry); @@ -289,13 +312,22 @@ return ret; /* EXIM_DBDELETE_CURSOR - terminate scanning operation. */ static inline void exim_dbdelete_cursor(EXIM_CURSOR * cursor) -{ store_free(cursor); } +{ +# ifdef COMPILE_UTILITY +free(cursor); +# else +store_free(cursor); +# endif +} /* EXIM_DBCLOSE */ static void -exim_dbclose__(EXIM_DB * db) -{ sqlite3_close(db); } +exim_dbclose__(EXIM_DB * dbp) +{ +(void) sqlite3_exec(dbp, "COMMIT TRANSACTION;", NULL, NULL, NULL); +sqlite3_close(dbp); +} /* Datum access */ @@ -362,6 +394,12 @@ tdb_traverse to be called) */ /* Access functions */ +static inline BOOL +exim_lockfile_needed(void) +{ +return TRUE; +} + /* EXIM_DBOPEN - return pointer to an EXIM_DB, NULL if failed */ static inline EXIM_DB * exim_dbopen__(const uschar * name, const uschar * dirname, int flags, @@ -402,7 +440,11 @@ exim_dbdel(EXIM_DB * dbp, EXIM_DATUM * key) static inline EXIM_CURSOR * exim_dbcreate_cursor(EXIM_DB * dbp) { +# ifdef COMPILE_UTILITY +EXIM_CURSOR * c = malloc(sizeof(TDB_DATA)); +# else EXIM_CURSOR * c = store_malloc(sizeof(TDB_DATA)); +# endif c->dptr = NULL; return c; } @@ -533,7 +575,13 @@ log_write(0, LOG_MAIN, "Berkeley DB error: %s", msg); -/* Access functions */ +/* Access functions (BDB 4.1+) */ + +static inline BOOL +exim_lockfile_needed(void) +{ +return TRUE; +} /* EXIM_DBOPEN - return pointer to an EXIM_DB, NULL if failed */ /* The API changed for DB 4.1. - and we also starting using the "env" with a @@ -680,7 +728,13 @@ exim_datum_free(EXIM_DATUM * d) /* Some text for messages */ # define EXIM_DBTYPE "db (v3/4)" -/* Access functions */ +/* Access functions (BDB 3/4) */ + +static inline BOOL +exim_lockfile_needed(void) +{ +return TRUE; +} /* EXIM_DBOPEN - return pointer to an EXIM_DB, NULL if failed */ static inline EXIM_DB * @@ -827,7 +881,13 @@ typedef struct { # define EXIM_DBTYPE "gdbm" -/* Access functions */ +/* Access functions (gdbm) */ + +static inline BOOL +exim_lockfile_needed(void) +{ +return TRUE; +} /* EXIM_DBOPEN - return pointer to an EXIM_DB, NULL if failed */ static inline EXIM_DB * @@ -967,7 +1027,13 @@ the default is the NDBM interface (which seems to be a wrapper for GDBM) */ # define EXIM_DBTYPE "ndbm" -/* Access functions */ +/* Access functions (ndbm) */ + +static inline BOOL +exim_lockfile_needed(void) +{ +return TRUE; +} /* EXIM_DBOPEN - returns a EXIM_DB *, NULL if failed */ /* Check that the name given is not present. This catches diff --git a/src/src/macro_predef.c b/src/src/macro_predef.c index 5bea91228..29ecfdcc8 100644 --- a/src/src/macro_predef.c +++ b/src/src/macro_predef.c @@ -221,6 +221,18 @@ due to conflicts with other common macros. */ builtin_macro_create(US"_HAVE_XCLIENT"); #endif +#ifdef USE_SQLITE + builtin_macro_create(US"_HAVE_HINTS_SQLITE"); +#elif defined(USE_TDB) + builtin_macro_create(US"_HAVE_HINTS_TDB"); +#elif defined(USE_DB) + builtin_macro_create(US"_HAVE_HINTS_BDB"); +#elif defined(USE_GDBM) + builtin_macro_create(US"_HAVE_HINTS_GDBM"); +#else + builtin_macro_create(US"_HAVE_HINTS_NDBM"); +#endif + #ifdef LOOKUP_LSEARCH builtin_macro_create(US"_HAVE_LOOKUP_LSEARCH"); #endif diff --git a/src/src/queue.c b/src/src/queue.c index 6e72a33d5..a4afdcc98 100644 --- a/src/src/queue.c +++ b/src/src/queue.c @@ -394,20 +394,19 @@ f.queue_running = TRUE; if (!recurse) { - uschar extras[8]; - uschar *p = extras; + uschar extras[8], * p = extras; if (q->queue_2stage) *p++ = 'q'; if (q->queue_run_first_delivery) *p++ = 'i'; if (q->queue_run_force) *p++ = 'f'; if (q->deliver_force_thaw) *p++ = 'f'; if (q->queue_run_local) *p++ = 'l'; - *p = 0; + *p = '\0'; p = big_buffer; p += sprintf(CS p, "pid=%d", (int)queue_run_pid); - if (extras[0] != 0) + if (*extras) p += sprintf(CS p, " -q%s", extras); if (deliver_selectstring) @@ -444,6 +443,15 @@ if (deliver_selectstring_sender && f.deliver_selectstring_sender_regex) selectstring_regex_sender = regex_must_compile(deliver_selectstring_sender, MCS_CASELESS, FALSE); +#ifndef DISABLE_TLS +if (!queue_tls_init) + { + queue_tls_init = TRUE; + /* Preload TLS library info for smtp transports. */ + tls_client_creds_reload(FALSE); + } +#endif + /* If the spool is split into subdirectories, we want to process it one directory at a time, so as to spread out the directory scanning and the delivering when there are lots of messages involved, except when @@ -669,16 +677,6 @@ for (int i = queue_run_in_order ? -1 : 0; report_time_since(×tamp_startup, US"queue msg selected"); #endif -#ifndef DISABLE_TLS - if (!queue_tls_init) - { - queue_tls_init = TRUE; - /* Preload TLS library info for smtp transports. Once, and only if we - have a delivery to do. */ - tls_client_creds_reload(FALSE); - } -#endif - single_item_retry: if ((pid = exim_fork(US"qrun-delivery")) == 0) { @@ -1619,3 +1617,5 @@ else DEBUG(D_queue_run) debug_printf(" socket: %s\n", strerror(errno)); #endif /*!COMPILE_UTILITY*/ /* End of queue.c */ +/* vi: aw ai sw=2 +*/ diff --git a/src/src/retry.c b/src/src/retry.c index f180c779e..9dfe1d587 100644 --- a/src/src/retry.c +++ b/src/src/retry.c @@ -857,7 +857,7 @@ for (int i = 0; i < 3; i++) retry_record->basic_errno = rti->basic_errno; retry_record->more_errno = rti->more_errno; Ustrncpy(retry_record->text, message, message_length); - retry_record->text[message_length] = 0; + retry_record->text[message_length] = 0; /* nul-term string in db */ DEBUG(D_retry) { diff --git a/src/src/transport.c b/src/src/transport.c index 6768e85de..b5ed563a5 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -1895,8 +1895,8 @@ while (1) } /* we were not able to find an acceptable message, nor was there a - * continuation record. So bug out, outer logic will clean this up. - */ + continuation record. So bug out, outer logic will clean this up. + */ if (!bContinuation) { @@ -1918,7 +1918,11 @@ if (host_length > 0) } dbfn_close(dbm_file); -DEBUG(D_transport) {acl_level--; debug_printf("transport_check_waiting: TRUE\n"); } +DEBUG(D_transport) + { + acl_level--; + debug_printf("transport_check_waiting: TRUE (found %s)\n", new_message_id); + } return TRUE; retfalse: commit b6bc7293f473a99a5023698741200c79a375a915 Author: Jeremy Harris Date: Thu Jun 13 13:06:05 2024 +0100 Debug: process exits diff --git a/src/src/daemon.c b/src/src/daemon.c index 2ef632065..16137f9f6 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -2157,7 +2157,7 @@ if (f.background_daemon) pid_t pid = exim_fork(US"daemon"); if (pid < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "fork() failed when starting daemon: %s", strerror(errno)); - if (pid > 0) exit(EXIT_SUCCESS); /* in parent process, just exit */ + if (pid > 0) exim_exit(EXIT_SUCCESS); /* in parent process, just exit */ (void)setsid(); /* release controlling terminal */ f.daemon_listen = daemon_listen; } diff --git a/src/src/deliver.c b/src/src/deliver.c index 767a75fdc..eadc96d22 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -2462,8 +2462,7 @@ if ((pid = exim_fork(US"delivery-local")) == 0) and close the pipe we were writing down before exiting. */ (void)close(pfd[pipe_write]); - search_tidyup(); - exit(EXIT_SUCCESS); + exim_exit(EXIT_SUCCESS); } /* Back in the main process: panic if the fork did not succeed. This seems @@ -5026,7 +5025,7 @@ all pipes, so I do not see a reason to use non-blocking IO here big_buffer[0] = continue_transport ? '1' : '0'; rmt_dlv_checked_write(fd, 'Z', '0', big_buffer, 1); (void)close(fd); - exit(EXIT_SUCCESS); + exim_exit(EXIT_SUCCESS); } /* Back in the mainline: close the unwanted half of the pipe. */ diff --git a/src/src/exim.c b/src/src/exim.c index d9039ad0c..8111a4489 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -237,7 +237,7 @@ va_end(ap); static void term_handler(int sig) { -exit(1); +exim_exit(EXIT_FAILURE); } @@ -4692,12 +4692,12 @@ if (malware_test_file) if ((result = malware_in_file(malware_test_file)) == FAIL) { printf("No malware found.\n"); - exit(EXIT_SUCCESS); + exim_exit(EXIT_SUCCESS); } if (result != OK) { printf("Malware lookup returned non-okay/fail: %d\n", result); - exit(EXIT_FAILURE); + exim_exit(EXIT_FAILURE); } if (malware_name) printf("Malware found: %s\n", malware_name); @@ -4706,7 +4706,7 @@ if (malware_test_file) #else printf("Malware scanning not enabled at compile time.\n"); #endif - exit(EXIT_FAILURE); + exim_exit(EXIT_FAILURE); } /* Handle a request to list the delivery queue */ @@ -4715,7 +4715,7 @@ if (list_queue) { set_process_info("listing the queue"); queue_list(list_queue_option, argv + recipients_arg, argc - recipients_arg); - exit(EXIT_SUCCESS); + exim_exit(EXIT_SUCCESS); } /* Handle a request to count the delivery queue */ @@ -4724,7 +4724,7 @@ if (count_queue) { set_process_info("counting the queue"); fprintf(stdout, "%u\n", queue_count()); - exit(EXIT_SUCCESS); + exim_exit(EXIT_SUCCESS); } /* Handle actions on specific messages, except for the force delivery and @@ -4763,7 +4763,7 @@ if (msg_action_arg > 0 && msg_action != MSG_DELIVER && msg_action != MSG_LOAD) else if (!queue_action(argv[msg_action_arg], msg_action, argv, argc, recipients_arg)) yield = EXIT_FAILURE; - exit(yield); + exim_exit(yield); } /* We used to set up here to skip reading the ACL section, on @@ -5166,7 +5166,7 @@ if (f.daemon_listen || f.inetd_wait_mode || is_multiple_qrun()) (void)gettimeofday(&t0, NULL); # endif if (!tls_dropprivs_validate_require_cipher(FALSE)) - exit(1); + exim_exit(EXIT_FAILURE); # ifdef MEASURE_TIMING report_time_since(&t0, US"validate_ciphers (delta)"); # endif diff --git a/src/src/exim_dbmbuild.c b/src/src/exim_dbmbuild.c index d07c3e024..05387aa3f 100644 --- a/src/src/exim_dbmbuild.c +++ b/src/src/exim_dbmbuild.c @@ -199,7 +199,7 @@ while (argc > 1) if (argc != 3) { printf("usage: exim_dbmbuild [-nolc] \n"); - exit(1); + exit(EXIT_FAILURE); } if (Ustrcmp(argv[arg], "-") == 0) @@ -207,7 +207,7 @@ if (Ustrcmp(argv[arg], "-") == 0) else if (!(f = fopen(argv[arg], "rb"))) { printf("exim_dbmbuild: unable to open %s: %s\n", argv[arg], strerror(errno)); - exit(1); + exit(EXIT_FAILURE); } /* By default Berkeley db does not put extensions on... which @@ -217,7 +217,7 @@ can be painful! */ if (Ustrcmp(argv[arg], argv[arg+1]) == 0) { printf("exim_dbmbuild: input and output filenames are the same\n"); - exit(1); + exit(EXIT_FAILURE); } #endif @@ -227,7 +227,7 @@ if (Ustrcmp(argv[arg], argv[arg+1]) == 0) if (strlen(argv[arg+1]) > sizeof(temp_dbmname) - 20) { printf("exim_dbmbuild: output filename is ridiculously long\n"); - exit(1); + exit(EXIT_FAILURE); } Ustrcpy(temp_dbmname, US argv[arg+1]); @@ -247,7 +247,7 @@ if (!(d = exim_dbopen(temp_dbmname, dirname, O_RDWR|O_CREAT|O_EXCL, 0644))) printf("exim_dbmbuild: unable to create %s: %s\n", temp_dbmname, strerror(errno)); (void)fclose(f); - exit(1); + exit(EXIT_FAILURE); } /* Unless using native db calls, see if we have created .db; if not, diff --git a/src/src/exim_dbutil.c b/src/src/exim_dbutil.c index e7d523e88..397b1162e 100644 --- a/src/src/exim_dbutil.c +++ b/src/src/exim_dbutil.c @@ -569,7 +569,7 @@ argc -= optind; argv += optind; spool_directory = argv[0]; if (!(dbm = dbfn_open(argv[1], O_RDONLY, &dbblock, FALSE, TRUE))) - exit(1); + exit(EXIT_FAILURE); /* Scan the file, formatting the information for each entry. Note that data is returned in a malloc'ed block, in order that it be @@ -1210,7 +1210,7 @@ printf("Tidying Exim hints database %s/db/%s\n", argv[1], argv[2]); spool_directory = argv[1]; if (!(dbm = dbfn_open(argv[2], O_RDWR, &dbblock, FALSE, TRUE))) - exit(1); + exit(EXIT_FAILURE); /* Prepare for building file names */ diff --git a/src/src/exim_lock.c b/src/src/exim_lock.c index 363c1bc71..b37f962d2 100644 --- a/src/src/exim_lock.c +++ b/src/src/exim_lock.c @@ -103,7 +103,7 @@ usage(void) printf("usage: exim_lock [-v] [-q] [-lockfile] [-fcntl] [-flock] [-mbx]\n" " [-retries ] [-interval ] [-timeout ] [-restore-times]\n" " [command]\n"); -exit(1); +exit(EXIT_FAILURE); } @@ -227,7 +227,7 @@ if (use_flock) { printf("exim_lock: can't use flock() because it was not available in the\n" " operating system when exim_lock was compiled\n"); - exit(1); + exit(EXIT_FAILURE); } #endif @@ -271,14 +271,14 @@ if (*filename == '~') if (pw == NULL) { printf("exim_lock: unable to expand file name %s\n", argv[i-1]); - exit(1); + exit(EXIT_FAILURE); } if ((int)strlen(pw->pw_dir) + (int)strlen(filename) + 1 > sizeof(buffer)) { printf("exim_lock: expanded file name %s%s is too long", pw->pw_dir, filename); - exit(1); + exit(EXIT_FAILURE); } strcpy(buffer, pw->pw_dir); @@ -294,7 +294,7 @@ if (use_lockfile) if (uname(&s) < 0) { printf("exim_lock: failed to find host name using uname()\n"); - exit(1); + exit(EXIT_FAILURE); } primary_hostname = s.nodename; @@ -331,7 +331,7 @@ for (j = 0; j < lock_retries; j++) { printf("exim_lock: failed to create hitching post %s: %s\n", hitchname, strerror(errno)); - exit(1); + exit(EXIT_FAILURE); } /* Apply hitching post algorithm. */ diff --git a/src/src/log.c b/src/src/log.c index e696b2cc7..f12721cf5 100644 --- a/src/src/log.c +++ b/src/src/log.c @@ -1464,7 +1464,7 @@ if (Ustrcmp(which, "debug") == 0) return; } fprintf(stderr, "exim: %s\n", errmsg); - exit(EXIT_FAILURE); + exim_exit(EXIT_FAILURE); } else log_write(0, LOG_CONFIG|LOG_PANIC_DIE, "%s", errmsg); } diff --git a/src/src/pdkim/signing.c b/src/src/pdkim/signing.c index e72d4b53e..b4754c1a1 100644 --- a/src/src/pdkim/signing.c +++ b/src/src/pdkim/signing.c @@ -308,7 +308,7 @@ makes sure that important subsystems are initialized. */ if (!gcry_check_version (GCRYPT_VERSION)) { fputs ("libgcrypt version mismatch\n", stderr); - exit (2); + exim_exit(2); } /* We don't want to see any warnings, e.g. because we have not yet diff --git a/src/src/readconf.c b/src/src/readconf.c index 4ed042df9..940c5d4d3 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -3271,7 +3271,7 @@ if (config_file) if (os_getcwd(buf, PATH_MAX) == NULL) { perror("exim: getcwd"); - exit(EXIT_FAILURE); + exim_exit(EXIT_FAILURE); } g = string_cat(NULL, buf); @@ -3301,7 +3301,7 @@ to a safe place. Later we change to $spool_directory. */ if (Uchdir("/") < 0) { perror("exim: chdir `/': "); - exit(EXIT_FAILURE); + exim_exit(EXIT_FAILURE); } /* Check the status of the file we have opened, if we have retained root diff --git a/src/src/route.c b/src/src/route.c index 887ef9975..f42afd2ef 100644 --- a/src/src/route.c +++ b/src/src/route.c @@ -1869,7 +1869,7 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) if (yield == PASS) { - if (r->pass_router != NULL) nextr = r->pass_router; + if (r->pass_router) nextr = r->pass_router; } else { @@ -2016,7 +2016,6 @@ if (r->translate_ip_address) /* See if this is an unseen routing; first expand the option if necessary. DEFER can be given if the expansion fails */ -GET_OPTION("unseen"); yield = exp_bool(addr, US"router", r->name, D_route, US"unseen", r->unseen, r->expand_unseen, &unseen); if (yield != OK) goto ROUTE_EXIT; diff --git a/src/src/routers/accept.c b/src/src/routers/accept.c index 6eb66e377..c747a8ba3 100644 --- a/src/src/routers/accept.c +++ b/src/src/routers/accept.c @@ -115,8 +115,8 @@ DEBUG(D_route) debug_printf("%s router called for %s\n domain = %s\n", /* Set up the errors address, if any. */ -rc = rf_get_errors_address(addr, rblock, verify, &errors_to); -if (rc != OK) return rc; +if ((rc = rf_get_errors_address(addr, rblock, verify, &errors_to)) != OK) + return rc; /* Set up the additional and removable headers for the address. */ @@ -135,9 +135,12 @@ addr->prop.errors_address = errors_to; addr->prop.extra_headers = extra_headers; addr->prop.remove_headers = remove_headers; -return rf_queue_add(addr, addr_local, addr_remote, rblock, pw)? OK : DEFER; +return rf_queue_add(addr, addr_local, addr_remote, rblock, pw) ? OK : DEFER; } #endif /*!MACRO_PREDEF*/ #endif /*ROUTER_ACCEPT*/ + /* End of routers/accept.c */ +/* vi: aw ai sw=2 +*/ diff --git a/src/src/routers/rf_get_transport.c b/src/src/routers/rf_get_transport.c index fa1c2732f..e6acca1b8 100644 --- a/src/src/routers/rf_get_transport.c +++ b/src/src/routers/rf_get_transport.c @@ -58,7 +58,7 @@ if (!tpname) } expandable = Ustrchr(tpname, '$') != NULL; -if (*tpptr != NULL && !expandable) return TRUE; +if (*tpptr && !expandable) return TRUE; if (expandable) { commit b9fd0b2293f8e24978655161662565290792ec41 Author: Jeremy Harris Date: Sat Jun 15 13:20:07 2024 +0100 Fix build with DISABLE_EVENT (proper spelling) cf. 04c21a8fe3b5 diff --git a/src/src/dns.c b/src/src/dns.c index 538c0c0b4..45dc1574d 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -1135,7 +1135,7 @@ errstr = US"cname_loop"; not_good: { -#ifndef DISABLE_EVENTS +#ifndef DISABLE_EVENT const uschar * s = NULL; BOOL save_flag = f.search_find_defer; uschar * save_serr = search_error_message; @@ -1168,7 +1168,7 @@ not_good: /*XXX what other state could an expansion in the eventhandler mess up? */ search_error_message = save_serr; f.search_find_defer = save_flag; -#endif /*EVENTS*/ +#endif /*EVENT*/ return rc; } } commit eaf2c50512d41a9a4a668c13069e083057e12e4f Author: Jeremy Harris Date: Tue Jun 18 09:23:51 2024 +0100 Fix 2-phase, in-order queue run delivery order Broken-by: a39bd74d3e94 diff --git a/src/src/transport.c b/src/src/transport.c index b5ed563a5..658fc6235 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -1702,7 +1702,7 @@ DEBUG(D_transport) acl_level++; } -/* Do nothing if we have hit the maximum number that can be send down one +/* Do nothing if we have hit the maximum number that can be sent down one connection. */ if (connection_max_messages >= 0) local_message_max = connection_max_messages; @@ -1739,7 +1739,7 @@ if (host_record->count > WAIT_NAME_MAX) goto retfalse; } -/* Scan the message ids in the record from the end towards the beginning, +/* Scan the message ids in the record in order until one is found for which a spool file actually exists. If the record gets emptied, delete it and continue with any continuation records that may exist. */ @@ -1752,17 +1752,14 @@ host_length = host_record->count * MESSAGE_ID_LENGTH; while (1) { - msgq_t *msgq; - int msgq_count = 0; - int msgq_actual = 0; - BOOL bFound = FALSE; - BOOL bContinuation = FALSE; + msgq_t * msgq; + int msgq_count = 0, msgq_actual = 0; + BOOL bFound = FALSE, bContinuation = FALSE; /* create an array to read entire message queue into memory for processing */ msgq = store_get(sizeof(msgq_t) * host_record->count, GET_UNTAINTED); - msgq_count = host_record->count; - msgq_actual = msgq_count; + msgq_actual = msgq_count = host_record->count; for (i = 0; i < host_record->count; ++i) { @@ -1776,10 +1773,9 @@ while (1) debug_printf_indent("NOTE: old or corrupt message-id found in wait=%.200s" " hints DB; deleting records for %s\n", transport_name, hostname); (void) dbfn_delete(dbm_file, hostname); - for (int i = host_record->sequence - 1; i >= 0; i--) + for (int j = host_record->sequence - 1; j >= 0; j--) (void) dbfn_delete(dbm_file, - (sprintf(CS buffer, "%.200s:%d", hostname, i), buffer)); - dbfn_close(dbm_file); + (sprintf(CS buffer, "%.200s:%d", hostname, j), buffer)); goto retfalse; } msgq[i].bKeep = TRUE; @@ -1801,7 +1797,7 @@ while (1) /* now find the next acceptable message_id */ - for (i = msgq_count - 1; i >= 0; --i) if (msgq[i].bKeep) + for (i = 0; i < msgq_count; i++) if (msgq[i].bKeep) { uschar subdir[2]; uschar * mid = msgq[i].message_id; commit f3fe5970b320bd6f8c9d01f11881fa6d6b5e7384 Author: Jeremy Harris Date: Fri Jun 28 14:01:54 2024 +0100 Hintsdb: fix dumpdb for sqlite diff --git a/src/src/hintsdb.h b/src/src/hintsdb.h index c5a856abc..3fedfabca 100644 --- a/src/src/hintsdb.h +++ b/src/src/hintsdb.h @@ -292,8 +292,8 @@ int i; BOOL ret; # ifdef COMPILE_UTILITY -qry = malloc((i = snprintf(NULL, 0, FMT, *cursor))); -snprintf(CS qry, i-1, FMT, *cursor); +if (!(qry = malloc((i = snprintf(NULL, 0, FMT, *cursor))+1))) return FALSE; +snprintf(CS qry, i, FMT, *cursor); /* fprintf(stderr, "exim_dbscan(%s)\n", qry); */ ret = exim_dbget__(dbp, qry, key); free(qry); commit 9bf74b9c298f6968417fe88a68ccdf2179db1403 Author: Jeremy Harris Date: Sat Jun 29 09:28:25 2024 +0100 Hintsdb: fix dumpdb for sqlite diff --git a/src/src/exim_dbutil.c b/src/src/exim_dbutil.c index 397b1162e..d3d854dd5 100644 --- a/src/src/exim_dbutil.c +++ b/src/src/exim_dbutil.c @@ -344,7 +344,7 @@ if (flags & O_RDWR) flags |= O_CREAT; if (!(dbblock->dbptr = exim_dbopen(filename, dirname, flags, 0))) { - printf("** Failed to open DBM file %s for %s: %s%s\n", filename, + printf("** Failed to open hintsdb file %s for %s: %s%s\n", filename, read_only ? "reading" : "writing", strerror(errno), #ifdef USE_DB " (or Berkeley DB error while opening)" diff --git a/src/src/hintsdb.h b/src/src/hintsdb.h index 3fedfabca..a6555ed25 100644 --- a/src/src/hintsdb.h +++ b/src/src/hintsdb.h @@ -145,9 +145,10 @@ if (sqlite3_step(statement) != SQLITE_ROW) res->len = sqlite3_column_bytes(statement, 0); # ifdef COMPILE_UTILITY -res->data = malloc(res->len); +if (!(res->data = malloc(res->len +1))) + { sqlite3_finalize(statement); return FALSE; } # else -res->data = store_get(res->len, GET_TAINTED); +res->data = store_get(res->len +1, GET_TAINTED); # endif memcpy(res->data, sqlite3_column_blob(statement, 0), res->len); res->data[res->len] = '\0'; @@ -166,7 +167,9 @@ BOOL ret; # ifdef COMPILE_UTILITY /* fprintf(stderr, "exim_dbget(k len %d '%.*s')\n", (int)key->len, (int)key->len, key->data); */ -qry = malloc(i = snprintf(NULL, 0, FMT, (int) key->len, key->data)); +i = snprintf(NULL, 0, FMT, (int) key->len, key->data)+1; +if (!(qry = malloc(i))) + return FALSE; snprintf(CS qry, i, FMT, (int) key->len, key->data); ret = exim_dbget__(dbp, qry, res); free(qry); @@ -189,19 +192,20 @@ exim_s_dbp(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data, const uschar * al { int hlen = data->len * 2, off = 0, res; # define FMT "INSERT OR %s INTO tbl (ky,dat) VALUES ('%.*s', X'%.*s');" +uschar * qry; # ifdef COMPILE_UTILITY uschar * hex = malloc(hlen+1); +if (!hex) return EXIM_DBPUTB_DUP; /* best we can do */ # else uschar * hex = store_get(hlen+1, data->data); # endif -uschar * qry; for (const uschar * s = data->data, * t = s + data->len; s < t; s++, off += 2) sprintf(CS hex + off, "%02X", *s); # ifdef COMPILE_UTILITY -res = snprintf(CS hex, 0, FMT, alt, (int) key->len, key->data, hlen, hex); -qry = malloc(res); +res = snprintf(CS hex, 0, FMT, alt, (int) key->len, key->data, hlen, hex) +1; +if (!(qry = malloc(res))) return EXIM_DBPUTB_DUP; snprintf(CS qry, res, FMT, alt, (int) key->len, key->data, hlen, hex); /* fprintf(stderr, "exim_s_dbp(%s)\n", qry); */ res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL); @@ -250,8 +254,8 @@ uschar * qry; int res; # ifdef COMPILE_UTILITY -res = snprintf(NULL, 0, FMT, (int) key->len, key->data); /* res excludes nul */ -qry = malloc(res); +res = snprintf(NULL, 0, FMT, (int) key->len, key->data) +1; /* res includes nul */ +if (!(qry = malloc(res))) return SQLITE_NOMEM; snprintf(CS qry, res, FMT, (int) key->len, key->data); res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL); free(qry); @@ -273,6 +277,7 @@ exim_dbcreate_cursor(EXIM_DB * dbp) { # ifdef COMPILE_UTILITY EXIM_CURSOR * c = malloc(sizeof(int)); +if (!c) return NULL; # else EXIM_CURSOR * c = store_malloc(sizeof(int)); # endif @@ -292,7 +297,8 @@ int i; BOOL ret; # ifdef COMPILE_UTILITY -if (!(qry = malloc((i = snprintf(NULL, 0, FMT, *cursor))+1))) return FALSE; +i = snprintf(NULL, 0, FMT, *cursor)+1; +if (!(qry = malloc(i))) return FALSE; snprintf(CS qry, i, FMT, *cursor); /* fprintf(stderr, "exim_dbscan(%s)\n", qry); */ ret = exim_dbget__(dbp, qry, key); commit 6ce5c70cff8989418e05d01fd2a57703007a6357 Author: Jeremy Harris Date: Mon Jul 1 19:35:12 2024 +0100 Fix MIME parsing of filenames specified using multiple parameters. Bug 3099 diff --git a/src/src/mime.c b/src/src/mime.c index 975ddca85..5f9e1ade7 100644 --- a/src/src/mime.c +++ b/src/src/mime.c @@ -587,10 +587,10 @@ while(1) while (*p) { - DEBUG(D_acl) debug_printf_indent("MIME: considering paramlist '%s'\n", p); + DEBUG(D_acl) + debug_printf_indent("MIME: considering paramlist '%s'\n", p); - if ( !mime_filename - && strncmpic(CUS"content-disposition:", header, 20) == 0 + if ( strncmpic(CUS"content-disposition:", header, 20) == 0 && strncmpic(CUS"filename*", p, 9) == 0 ) { /* RFC 2231 filename */ @@ -604,11 +604,12 @@ while(1) if (q && *q) { - uschar * temp_string, * err_msg; + uschar * temp_string, * err_msg, * fname = q; int slen; /* build up an un-decoded filename over successive filename*= parameters (for use when 2047 decode fails) */ +/*XXX could grow a gstring here */ mime_fname_rfc2231 = string_sprintf("%#s%s", mime_fname_rfc2231, q); @@ -623,26 +624,32 @@ while(1) /* look for a ' in the "filename" */ while(*s != '\'' && *s) s++; /* s is 1st ' or NUL */ - if ((size = s-q) > 0) - mime_filename_charset = string_copyn(q, size); + if (*s) /* there was a ' */ + { + if ((size = s-q) > 0) + mime_filename_charset = string_copyn(q, size); - if (*(p = s)) p++; - while(*p == '\'') p++; /* p is after 2nd ' */ + if (*(fname = s)) fname++; + while(*fname == '\'') fname++; /* fname is after 2nd ' */ + } } - else - p = q; - DEBUG(D_acl) debug_printf_indent("MIME: charset %s fname '%s'\n", - mime_filename_charset ? mime_filename_charset : US"", p); + DEBUG(D_acl) + debug_printf_indent("MIME: charset %s fname '%s'\n", + mime_filename_charset ? mime_filename_charset : US"", + fname); - temp_string = rfc2231_to_2047(p, mime_filename_charset, &slen); - DEBUG(D_acl) debug_printf_indent("MIME: 2047-name %s\n", temp_string); + temp_string = rfc2231_to_2047(fname, mime_filename_charset, + &slen); + DEBUG(D_acl) + debug_printf_indent("MIME: 2047-name %s\n", temp_string); temp_string = rfc2047_decode(temp_string, FALSE, NULL, ' ', - NULL, &err_msg); - DEBUG(D_acl) debug_printf_indent("MIME: plain-name %s\n", temp_string); + NULL, &err_msg); + DEBUG(D_acl) + debug_printf_indent("MIME: plain-name %s\n", temp_string); - if (!temp_string || (size = Ustrlen(temp_string)) == slen) + if (!temp_string || (size = Ustrlen(temp_string)) == slen) decoding_failed = TRUE; else /* build up a decoded filename over successive @@ -651,9 +658,9 @@ while(1) mime_filename = mime_fname = mime_fname ? string_sprintf("%s%s", mime_fname, temp_string) : temp_string; - } - } - } + } /*!decoding_failed*/ + } /*q*/ + } /*2231 filename*/ else /* look for interesting parameters */ @@ -682,7 +689,7 @@ while(1) /* There is something, but not one of our interesting parameters. - Advance past the next semicolon */ + Advance past the next semicolon */ p = mime_next_semicolon(p); if (*p) p++; } /* param scan on line */ @@ -800,5 +807,5 @@ return rc; #endif /*WITH_CONTENT_SCAN*/ -/* vi: sw ai sw=2 +/* vi: aw ai sw=2 */ diff --git a/src/src/string.c b/src/src/string.c index 113c05754..aa768d03c 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -1342,6 +1342,7 @@ Field width: decimal digits, or * Precision: dot, followed by decimal digits or * Length modifiers: h L l ll z Conversion specifiers: n d o u x X p f e E g G % c s S T W V Y D M +Alternate-form: %#s is silent about a null string Returns the possibly-new (if copy for growth or taint-handling was needed) string, not nul-terminated. commit 1b3209b0577a9327ebb076f3b32b8a159c253f7b Author: Jeremy Harris Date: Tue Jul 2 14:41:19 2024 +0100 MIME: support RFC 2331 for name=. Bug 3099 diff --git a/src/src/mime.c b/src/src/mime.c index 5f9e1ade7..8044bb3fd 100644 --- a/src/src/mime.c +++ b/src/src/mime.c @@ -30,10 +30,10 @@ static int mime_header_list_size = nelem(mime_header_list); static mime_parameter mime_parameter_list[] = { /* name namelen value */ - { US"name=", 5, &mime_filename }, - { US"filename=", 9, &mime_filename }, - { US"charset=", 8, &mime_charset }, - { US"boundary=", 9, &mime_boundary } + { US"name", 4, &mime_filename }, + { US"filename", 8, &mime_filename }, + { US"charset", 7, &mime_charset }, + { US"boundary", 8, &mime_boundary } }; @@ -577,8 +577,8 @@ while(1) if (*(p = q)) p++; /* jump past the ; */ { - uschar * mime_fname = NULL; - uschar * mime_fname_rfc2231 = NULL; + gstring * mime_fname = NULL; + gstring * mime_fname_rfc2231 = NULL; uschar * mime_filename_charset = NULL; BOOL decoding_failed = FALSE; @@ -590,90 +590,92 @@ while(1) DEBUG(D_acl) debug_printf_indent("MIME: considering paramlist '%s'\n", p); - if ( strncmpic(CUS"content-disposition:", header, 20) == 0 - && strncmpic(CUS"filename*", p, 9) == 0 - ) - { /* RFC 2231 filename */ - uschar * q; - - /* find value of the filename */ - p += 9; - while(*p != '=' && *p) p++; - if (*p) p++; /* p is filename or NUL */ - q = mime_param_val(&p); /* p now trailing ; or NUL */ - - if (q && *q) + /* look for interesting parameters */ + for (mime_parameter * mp = mime_parameter_list; + mp < mime_parameter_list + nelem(mime_parameter_list); + mp++ + ) if (strncmpic(mp->name, p, mp->namelen) == 0) + { + p += mp->namelen; + if (*p == '*') /* RFC 2231 */ { - uschar * temp_string, * err_msg, * fname = q; - int slen; - - /* build up an un-decoded filename over successive - filename*= parameters (for use when 2047 decode fails) */ -/*XXX could grow a gstring here */ - - mime_fname_rfc2231 = string_sprintf("%#s%s", - mime_fname_rfc2231, q); - - if (!decoding_failed) + while (isdigit(*++p)) ; /* ignore cont-cnt values */ + if (*p == '*') p++; /* step over sep chset mark */ + if (*p == '=') { - int size; - if (!mime_filename_charset) + uschar * q; + p++; /* step over = */ + q = mime_param_val(&p); /* p now trailing ; or NUL */ + + if (q && *q) /* q is the dequoted value */ { - uschar * s = q; + uschar * err_msg, * fname = q; + int slen; + + /* build up an un-decoded filename over successive + filename*= parameters (for use when 2047 decode fails) */ - /* look for a ' in the "filename" */ - while(*s != '\'' && *s) s++; /* s is 1st ' or NUL */ + mime_fname_rfc2231 = string_cat(mime_fname_rfc2231, q); - if (*s) /* there was a ' */ + if (!decoding_failed) { - if ((size = s-q) > 0) - mime_filename_charset = string_copyn(q, size); - - if (*(fname = s)) fname++; - while(*fname == '\'') fname++; /* fname is after 2nd ' */ - } - } - - DEBUG(D_acl) - debug_printf_indent("MIME: charset %s fname '%s'\n", - mime_filename_charset ? mime_filename_charset : US"", - fname); - - temp_string = rfc2231_to_2047(fname, mime_filename_charset, - &slen); - DEBUG(D_acl) - debug_printf_indent("MIME: 2047-name %s\n", temp_string); - - temp_string = rfc2047_decode(temp_string, FALSE, NULL, ' ', - NULL, &err_msg); - DEBUG(D_acl) - debug_printf_indent("MIME: plain-name %s\n", temp_string); - - if (!temp_string || (size = Ustrlen(temp_string)) == slen) - decoding_failed = TRUE; - else - /* build up a decoded filename over successive - filename*= parameters */ - - mime_filename = mime_fname = mime_fname - ? string_sprintf("%s%s", mime_fname, temp_string) - : temp_string; - } /*!decoding_failed*/ - } /*q*/ - } /*2231 filename*/ - - else - /* look for interesting parameters */ - for (mime_parameter * mp = mime_parameter_list; - mp < mime_parameter_list + nelem(mime_parameter_list); - mp++ - ) if (strncmpic(mp->name, p, mp->namelen) == 0) - { - uschar * q; - uschar * dummy_errstr; + if (!mime_filename_charset) + { /* try for RFC 2231 chset/lang */ + uschar * s = q; + + /* look for a ' in the raw paramval */ + while(*s != '\'' && *s) s++; /* s is 1st ' or NUL */ + + if (*s) /* there was a ' */ + { + int size; + if ((size = s-q) > 0) + mime_filename_charset = string_copyn(q, size); + + if (*(fname = s)) fname++; + while(*fname == '\'') fname++; /*fname is after 2nd '*/ + } + } + + DEBUG(D_acl) + debug_printf_indent("MIME: charset %s fname '%s'\n", + mime_filename_charset ? mime_filename_charset : US"", + fname); + + fname = rfc2231_to_2047(fname, mime_filename_charset, + &slen); + DEBUG(D_acl) + debug_printf_indent("MIME: 2047-name %s\n", fname); + + fname = rfc2047_decode(fname, FALSE, NULL, ' ', + NULL, &err_msg); + DEBUG(D_acl) debug_printf_indent( + "MIME: plain-name %s\n", fname); + + if (!fname || Ustrlen(fname) == slen) + decoding_failed = TRUE; + else if (mp->value == &mime_filename) + { + /* build up a decoded filename over successive + filename*= parameters */ + + mime_fname = string_cat(mime_fname, fname); + mime_filename = string_from_gstring(mime_fname); + } + } /*!decoding_failed*/ + } /*q*/ + + if (*p) p++; /* p is past ; */ + goto param_done; /* done matching param names */ + } /*2231 param coding extension*/ + } + else if (*p == '=') + { /* non-2231 param */ + uschar * q, * dummy_errstr; /* grab the value and copy to its expansion variable */ - p += mp->namelen; + + if (*p) p++; /* step over = */ q = mime_param_val(&p); /* p now trailing ; or NUL */ *mp->value = q && *q @@ -684,26 +686,31 @@ while(1) "MIME: found %s parameter in %s header, value '%s'\n", mp->name, mh->name, *mp->value); - break; /* done matching param names */ + if (*p) p++; /* p is past ; */ + goto param_done; /* done matching param names */ } - + } /* interesting parameters */ /* There is something, but not one of our interesting parameters. Advance past the next semicolon */ + p = mime_next_semicolon(p); if (*p) p++; - } /* param scan on line */ + param_done: + } /* param scan on line */ if (strncmpic(CUS"content-disposition:", header, 20) == 0) { - if (decoding_failed) mime_filename = mime_fname_rfc2231; + if (decoding_failed) + mime_filename = string_from_gstring(mime_fname_rfc2231); DEBUG(D_acl) debug_printf_indent( "MIME: found %s parameter in %s header, value is '%s'\n", "filename", mh->name, mime_filename); } } - } + break; + } /* interesting headers */ /* set additional flag variables (easier access) */ if ( mime_content_type commit 1a088aa5978ee4ce4427633de8262ab7050d1a56 Author: Jeremy Harris Date: Wed Jul 3 09:23:45 2024 +0100 Compiler quietening diff --git a/src/src/mime.c b/src/src/mime.c index 8044bb3fd..2c107e75f 100644 --- a/src/src/mime.c +++ b/src/src/mime.c @@ -696,7 +696,7 @@ while(1) p = mime_next_semicolon(p); if (*p) p++; - param_done: + param_done: ; } /* param scan on line */ if (strncmpic(CUS"content-disposition:", header, 20) == 0)