regular expression for a long time; the other for short-term use. */
static void *
-function_store_get(size_t size)
+function_store_get(PCRE2_SIZE size, void * tag)
{
/* For now, regard all RE results as potentially tainted. We might need
more intelligence on this point. */
}
static void
-function_dummy_free(void * block) {}
+function_dummy_free(void * block, void * tag) {}
static void *
-function_store_malloc(size_t size)
+function_store_malloc(PCRE2_SIZE size, void * tag)
{
return store_malloc((int)size);
}
static void
-function_store_free(void * block)
+function_store_free(void * block, void * tag)
{
store_free(block);
}
Returns: pointer to the compiled pattern
*/
-const pcre *
-regex_must_compile(const uschar *pattern, BOOL caseless, BOOL use_malloc)
+const pcre2_code *
+regex_must_compile(const uschar * pattern, BOOL caseless, BOOL use_malloc)
{
-int offset;
-int options = PCRE_COPT;
-const pcre *yield;
-const uschar *error;
+size_t offset;
+int options = caseless ? PCRE_COPT|PCRE2_CASELESS : PCRE_COPT;
+const pcre2_code * yield;
+int err;
+pcre2_general_context * gctx;
+pcre2_compile_context * cctx;
+
if (use_malloc)
{
- pcre_malloc = function_store_malloc;
- pcre_free = function_store_free;
+ gctx = pcre2_general_context_create(function_store_malloc, function_store_free, NULL);
+ cctx = pcre2_compile_context_create(gctx);
}
-if (caseless) options |= PCRE_CASELESS;
-yield = pcre_compile(CCS pattern, options, CCSS &error, &offset, NULL);
-pcre_malloc = function_store_get;
-pcre_free = function_dummy_free;
-if (yield == NULL)
+else
+ cctx = pcre_cmp_ctx;
+
+if (!(yield = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, options,
+ &err, &offset, cctx)))
+ {
+ uschar errbuf[128];
+ pcre2_get_error_message(err, errbuf, sizeof(errbuf));
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "regular expression error: "
- "%s at offset %d while compiling %s", error, offset, pattern);
+ "%s at offset %d while compiling %s", errbuf, (long)offset, pattern);
+ }
+
+if (use_malloc)
+ {
+ pcre2_compile_context_free(cctx);
+ pcre2_general_context_free(gctx);
+ }
return yield;
}
+static void
+pcre_init(void)
+{
+pcre_gen_ctx = pcre2_general_context_create(function_store_malloc, function_store_free, NULL);
+pcre_cmp_ctx = pcre2_compile_context_create(pcre_gen_ctx);
+pcre_mtc_ctx = pcre2_match_context_create(pcre_gen_ctx);
+}
+
+
/*************************************************
*************************************************/
/* This function runs a regular expression match, and sets up the pointers to
-the matched substrings.
+the matched substrings. The matched strings are copied so the lifetime of
+the subject is not a problem.
Arguments:
re the compiled expression
if >= 0 setup from setup+1 onwards,
excluding the full matched string
-Returns: TRUE or FALSE
+Returns: TRUE if matched, or FALSE
*/
BOOL
-regex_match_and_setup(const pcre *re, const uschar *subject, int options, int setup)
+regex_match_and_setup(const pcre2_code * re, const uschar * subject, int options, int setup)
{
-int ovector[3*(EXPAND_MAXN+1)];
-uschar * s = string_copy(subject); /* de-constifying */
-int n = pcre_exec(re, NULL, CS s, Ustrlen(s), 0,
- PCRE_EOPT | options, ovector, nelem(ovector));
-BOOL yield = n >= 0;
-if (n == 0) n = EXPAND_MAXN + 1;
-if (yield)
+pcre2_match_data * md = pcre2_match_data_create_from_pattern(re, pcre_gen_ctx);
+int res = pcre2_match(re, (PCRE2_SPTR)subject, PCRE2_ZERO_TERMINATED, 0,
+ PCRE_EOPT | options, md, pcre_mtc_ctx);
+BOOL yield;
+
+if ((yield = (res >= 0)))
{
+ res = pcre2_get_ovector_count(md);
expand_nmax = setup < 0 ? 0 : setup + 1;
- for (int nn = setup < 0 ? 0 : 2; nn < n*2; nn += 2)
+ for (int matchnum = setup < 0 ? 0 : 1; matchnum < res; matchnum++)
{
- expand_nstring[expand_nmax] = s + ovector[nn];
- expand_nlength[expand_nmax++] = ovector[nn+1] - ovector[nn];
+ PCRE2_SIZE len;
+ pcre2_substring_get_bynumber(md, matchnum,
+ (PCRE2_UCHAR **)&expand_nstring[expand_nmax], &len);
+ expand_nlength[expand_nmax++] = (int)len;
}
expand_nmax--;
}
+else if (res != PCRE2_ERROR_NOMATCH) DEBUG(D_any)
+ {
+ uschar errbuf[128];
+ pcre2_get_error_message(res, errbuf, sizeof(errbuf));
+ debug_printf_indent("pcre2: %s\n", errbuf);
+ }
+pcre2_match_data_free(md);
return yield;
}
+/* Check just for match with regex. Uses the common memory-handling.
+
+Arguments:
+ re compiled regex
+ subject string to be checked
+ slen length of subject; -1 for nul-terminated
+ rptr pointer for matched string, copied, or NULL
+
+Return: TRUE for a match.
+*/
+
+BOOL
+regex_match(const pcre2_code * re, const uschar * subject, int slen, uschar ** rptr)
+{
+pcre2_match_data * md = pcre2_match_data_create(1, pcre_gen_ctx);
+int rc = pcre2_match(re, (PCRE2_SPTR)subject,
+ slen >= 0 ? slen : PCRE2_ZERO_TERMINATED,
+ 0, PCRE_EOPT, md, pcre_mtc_ctx);
+PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md);
+if (rc < 0)
+ return FALSE;
+if (rptr)
+ *rptr = string_copyn(subject + ovec[0], ovec[1] - ovec[0]);
+return TRUE;
+}
+
/*************************************************
}
+/***********************************************
+* Handler for SIGSEGV *
+***********************************************/
+
+static void
+segv_handler(int sig)
+{
+log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (maybe attempt to write to immutable memory)");
+signal(SIGSEGV, SIG_DFL);
+kill(getpid(), sig);
+}
+
+
/*************************************************
* Handler for SIGUSR1 *
*************************************************/
clocks that go backwards.
Arguments:
- tgt_tv A timeval which was used to create uniqueness; its usec field
+ prev_tv A timeval which was used to create uniqueness; its usec field
has been rounded down to the value of the resolution.
We want to be sure the current time is greater than this.
+ On return, updated to current (rounded down).
resolution The resolution that was used to divide the microseconds
(1 for maildir, larger for message ids)
*/
void
-exim_wait_tick(struct timeval * tgt_tv, int resolution)
+exim_wait_tick(struct timeval * prev_tv, int resolution)
{
struct timeval now_tv;
long int now_true_usec;
now_true_usec = now_tv.tv_usec;
now_tv.tv_usec = (now_true_usec/resolution) * resolution;
-while (exim_tvcmp(&now_tv, tgt_tv) <= 0)
+while (exim_tvcmp(&now_tv, prev_tv) <= 0)
{
struct itimerval itval;
itval.it_interval.tv_sec = 0;
itval.it_interval.tv_usec = 0;
- itval.it_value.tv_sec = tgt_tv->tv_sec - now_tv.tv_sec;
- itval.it_value.tv_usec = tgt_tv->tv_usec + resolution - now_true_usec;
+ itval.it_value.tv_sec = prev_tv->tv_sec - now_tv.tv_sec;
+ itval.it_value.tv_usec = prev_tv->tv_usec + resolution - now_true_usec;
/* We know that, overall, "now" is less than or equal to "then". Therefore, a
negative value for the microseconds is possible only in the case when "now"
if (!f.running_in_test_harness)
{
debug_printf("tick check: " TIME_T_FMT ".%06lu " TIME_T_FMT ".%06lu\n",
- tgt_tv->tv_sec, (long) tgt_tv->tv_usec,
+ prev_tv->tv_sec, (long) prev_tv->tv_usec,
now_tv.tv_sec, (long) now_tv.tv_usec);
debug_printf("waiting " TIME_T_FMT ".%06lu sec\n",
itval.it_value.tv_sec, (long) itval.it_value.tv_usec);
now_true_usec = now_tv.tv_usec;
now_tv.tv_usec = (now_true_usec/resolution) * resolution;
}
+*prev_tv = now_tv;
}
#endif
#define QUOTE(X) #X
#define EXPAND_AND_QUOTE(X) QUOTE(X)
- fprintf(fp, "Library version: PCRE: Compile: %d.%d%s\n"
- " Runtime: %s\n",
- PCRE_MAJOR, PCRE_MINOR,
- EXPAND_AND_QUOTE(PCRE_PRERELEASE) "",
- pcre_version());
+ {
+ uschar buf[24];
+ pcre2_config(PCRE2_CONFIG_VERSION, buf);
+ fprintf(fp, "Library version: PCRE2: Compile: %d.%d%s\n"
+ " Runtime: %s\n",
+ PCRE2_MAJOR, PCRE2_MINOR,
+ EXPAND_AND_QUOTE(PCRE2_PRERELEASE) "",
+ buf);
+ }
#undef QUOTE
#undef EXPAND_AND_QUOTE
continue;
if ((len = m->replen) == 0)
continue;
- n = pcre_exec(regex_whitelisted_macro, NULL, CS m->replacement, len,
- 0, PCRE_EOPT, NULL, 0);
- if (n < 0)
- {
- if (n != PCRE_ERROR_NOMATCH)
- debug_printf("macros_trusted checking %s returned %d\n", m->name, n);
+ if (!regex_match(regex_whitelisted_macro, m->replacement, len, NULL))
return FALSE;
- }
}
DEBUG(D_any) debug_printf("macros_trusted overridden to true by whitelisting\n");
return TRUE;
BOOL list_options = FALSE;
BOOL list_config = FALSE;
BOOL local_queue_only;
-BOOL more = TRUE;
BOOL one_msg_action = FALSE;
BOOL opt_D_used = FALSE;
BOOL queue_only_set = FALSE;
#endif
store_init(); /* Initialise the memory allocation susbsystem */
+pcre_init(); /* Set up memory handling for pcre */
/* If the Exim user and/or group and/or the configuration file owner/group were
defined by ref:name at build time, we must now find the actual uid/gid values.
if (fstat(fileno(stderr), &statbuf) >= 0) log_stderr = stderr;
-/* Arrange for the PCRE regex library to use our store functions. Note that
-the normal calls are actually macros that add additional arguments for
-debugging purposes so we have to assign specially constructed functions here.
-The default is to use store in the stacking pool, but this is overridden in the
-regex_must_compile() function. */
-
-pcre_malloc = function_store_get;
-pcre_free = function_dummy_free;
-
/* Ensure there is a big buffer for temporary use in several places. It is put
in malloc store so that it can be freed for enlargement if necessary. */
process_info = store_get(PROCESS_INFO_SIZE, TRUE); /* tainted */
set_process_info("initializing");
-os_restarting_signal(SIGUSR1, usr1_handler);
+os_restarting_signal(SIGUSR1, usr1_handler); /* exiwhat */
+signal(SIGSEGV, segv_handler); /* log faults */
/* If running in a dockerized environment, the TERM signal is only
delegated to the PID 1 if we request it by setting an signal handler */
{
struct rlimit rlp;
- #ifdef RLIMIT_NOFILE
+#ifdef RLIMIT_NOFILE
if (getrlimit(RLIMIT_NOFILE, &rlp) < 0)
{
log_write(0, LOG_MAIN|LOG_PANIC, "getrlimit(RLIMIT_NOFILE) failed: %s",
strerror(errno));
}
}
- #endif
+#endif
- #ifdef RLIMIT_NPROC
+#ifdef RLIMIT_NPROC
if (getrlimit(RLIMIT_NPROC, &rlp) < 0)
{
log_write(0, LOG_MAIN|LOG_PANIC, "getrlimit(RLIMIT_NPROC) failed: %s",
rlp.rlim_cur = rlp.rlim_max = 0;
}
- #ifdef RLIM_INFINITY
+# ifdef RLIM_INFINITY
if (rlp.rlim_cur != RLIM_INFINITY && rlp.rlim_cur < 1000)
{
rlp.rlim_cur = rlp.rlim_max = RLIM_INFINITY;
- #else
+# else
if (rlp.rlim_cur < 1000)
{
rlp.rlim_cur = rlp.rlim_max = 1000;
- #endif
+# endif
if (setrlimit(RLIMIT_NPROC, &rlp) < 0)
log_write(0, LOG_MAIN|LOG_PANIC, "setrlimit(RLIMIT_NPROC) failed: %s",
strerror(errno));
}
- #endif
+#endif
}
/* Exim is normally entered as root (but some special configurations are
This needs to happen before we read the main configuration. */
init_lookup_list();
+/*XXX this excrescence could move to the testsuite standard config setup file */
#ifdef SUPPORT_I18N
if (f.running_in_test_harness) smtputf8_advertise_hosts = NULL;
#endif
defined) */
{
+ int old_pool = store_pool;
#ifdef MEASURE_TIMING
struct timeval t0, diff;
(void)gettimeofday(&t0, NULL);
#endif
+ store_pool = POOL_CONFIG;
readconf_main(checking || list_options);
+ store_pool = old_pool;
#ifdef MEASURE_TIMING
report_time_since(&t0, US"readconf_main (delta)");
#endif
}
-
/* Now in directory "/" */
if (cleanup_environment() == FALSE)
event_action gets expanded */
if (msg_action == MSG_REMOVE)
+ {
+ int old_pool = store_pool;
+ store_pool = POOL_CONFIG;
readconf_rest();
+ store_pool = old_pool;
+ store_writeprotect(POOL_CONFIG);
+ }
if (!one_msg_action)
{
needed in transports so we lost the optimisation. */
{
+ int old_pool = store_pool;
#ifdef MEASURE_TIMING
struct timeval t0, diff;
(void)gettimeofday(&t0, NULL);
#endif
+ store_pool = POOL_CONFIG;
readconf_rest();
+ store_pool = old_pool;
+ store_writeprotect(POOL_CONFIG);
#ifdef MEASURE_TIMING
report_time_since(&t0, US"readconf_rest (delta)");
if (gecos_pattern && gecos_name)
{
- const pcre *re;
+ const pcre2_code *re;
re = regex_must_compile(gecos_pattern, FALSE, TRUE); /* Use malloc */
if (regex_match_and_setup(re, name, 0, -1))
if (!f.synchronous_delivery)
{
- #ifdef SA_NOCLDWAIT
+#ifdef SA_NOCLDWAIT
struct sigaction act;
act.sa_handler = SIG_IGN;
sigemptyset(&(act.sa_mask));
act.sa_flags = SA_NOCLDWAIT;
sigaction(SIGCHLD, &act, NULL);
- #else
+#else
signal(SIGCHLD, SIG_IGN);
- #endif
+#endif
}
/* Save the current store pool point, for resetting at the start of
messages to be read (SMTP input), or FALSE otherwise (not SMTP, or SMTP channel
collapsed). */
-while (more)
+for (BOOL more = TRUE; more; )
{
rmark reset_point = store_mark();
message_id[0] = 0;
/* Now get the data for the message */
more = receive_msg(extract_recipients);
- if (message_id[0] == 0)
+ if (!message_id[0])
{
cancel_cutthrough_connection(TRUE, US"receive dropped");
- if (more) goto moreloop;
+ if (more) goto MORELOOP;
smtp_log_no_mail(); /* Log no mail if configured */
exim_exit(EXIT_FAILURE);
}
parse_extract_address(s, &errmess, &start, &end, &domain, FALSE);
#ifdef SUPPORT_I18N
- if (string_is_utf8(recipient))
- message_smtputf8 = TRUE;
- else
- allow_utf8_domains = b;
+ if (recipient)
+ if (string_is_utf8(recipient)) message_smtputf8 = TRUE;
+ else allow_utf8_domains = b;
}
+#else
+ ;
#endif
if (domain == 0 && !f.allow_unqualified_recipient)
{
for real; when reading the headers of a message for filter testing,
it is TRUE if the headers were terminated by '.' and FALSE otherwise. */
- if (message_id[0] == 0) exim_exit(EXIT_FAILURE);
+ if (!message_id[0]) exim_exit(EXIT_FAILURE);
} /* Non-SMTP message reception */
/* If this is a filter testing run, there are headers in store, but
finished subprocesses here, in case there are lots of messages coming in
from the same source. */
- #ifndef SIG_IGN_WORKS
+#ifndef SIG_IGN_WORKS
while (waitpid(-1, NULL, WNOHANG) > 0);
- #endif
+#endif
-moreloop:
+MORELOOP:
return_path = sender_address = NULL;
authenticated_sender = NULL;
deliver_localpart_orig = NULL;