/src/src/exim.c
C | 5670 lines | 5194 code | 164 blank | 312 comment | 133 complexity | fc7ec81c449b6f2bf8adf30630933d1f MD5 | raw file
Possible License(s): GPL-2.0
Large files files are truncated, but you can click here to view the full file
- /*************************************************
- * Exim - an Internet mail transport agent *
- *************************************************/
- /* Copyright (c) University of Cambridge 1995 - 2014 */
- /* See the file NOTICE for conditions of use and distribution. */
- /* The main function: entry point, initialization, and high-level control.
- Also a few functions that don't naturally fit elsewhere. */
- #include "exim.h"
- #ifdef USE_GNUTLS
- # include <gnutls/gnutls.h>
- # if GNUTLS_VERSION_NUMBER < 0x030103 && !defined(DISABLE_OCSP)
- # define DISABLE_OCSP
- # endif
- #endif
- extern void init_lookup_list(void);
- /*************************************************
- * Function interface to store functions *
- *************************************************/
- /* We need some real functions to pass to the PCRE regular expression library
- for store allocation via Exim's store manager. The normal calls are actually
- macros that pass over location information to make tracing easier. These
- functions just interface to the standard macro calls. A good compiler will
- optimize out the tail recursion and so not make them too expensive. There
- are two sets of functions; one for use when we want to retain the compiled
- regular expression for a long time; the other for short-term use. */
- static void *
- function_store_get(size_t size)
- {
- return store_get((int)size);
- }
- static void
- function_dummy_free(void *block) { block = block; }
- static void *
- function_store_malloc(size_t size)
- {
- return store_malloc((int)size);
- }
- static void
- function_store_free(void *block)
- {
- store_free(block);
- }
- /*************************************************
- * Enums for cmdline interface *
- *************************************************/
- enum commandline_info { CMDINFO_NONE=0,
- CMDINFO_HELP, CMDINFO_SIEVE, CMDINFO_DSCP };
- /*************************************************
- * Compile regular expression and panic on fail *
- *************************************************/
- /* This function is called when failure to compile a regular expression leads
- to a panic exit. In other cases, pcre_compile() is called directly. In many
- cases where this function is used, the results of the compilation are to be
- placed in long-lived store, so we temporarily reset the store management
- functions that PCRE uses if the use_malloc flag is set.
- Argument:
- pattern the pattern to compile
- caseless TRUE if caseless matching is required
- use_malloc TRUE if compile into malloc store
- Returns: pointer to the compiled pattern
- */
- const pcre *
- regex_must_compile(const uschar *pattern, BOOL caseless, BOOL use_malloc)
- {
- int offset;
- int options = PCRE_COPT;
- const pcre *yield;
- const uschar *error;
- if (use_malloc)
- {
- pcre_malloc = function_store_malloc;
- pcre_free = function_store_free;
- }
- if (caseless) options |= PCRE_CASELESS;
- yield = pcre_compile(CCS pattern, options, (const char **)&error, &offset, NULL);
- pcre_malloc = function_store_get;
- pcre_free = function_dummy_free;
- if (yield == NULL)
- log_write(0, LOG_MAIN|LOG_PANIC_DIE, "regular expression error: "
- "%s at offset %d while compiling %s", error, offset, pattern);
- return yield;
- }
- /*************************************************
- * Execute regular expression and set strings *
- *************************************************/
- /* This function runs a regular expression match, and sets up the pointers to
- the matched substrings.
- Arguments:
- re the compiled expression
- subject the subject string
- options additional PCRE options
- setup if < 0 do full setup
- if >= 0 setup from setup+1 onwards,
- excluding the full matched string
- Returns: TRUE or FALSE
- */
- BOOL
- regex_match_and_setup(const pcre *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, sizeof(ovector)/sizeof(int));
- BOOL yield = n >= 0;
- if (n == 0) n = EXPAND_MAXN + 1;
- if (yield)
- {
- int nn;
- expand_nmax = (setup < 0)? 0 : setup + 1;
- for (nn = (setup < 0)? 0 : 2; nn < n*2; nn += 2)
- {
- expand_nstring[expand_nmax] = s + ovector[nn];
- expand_nlength[expand_nmax++] = ovector[nn+1] - ovector[nn];
- }
- expand_nmax--;
- }
- return yield;
- }
- /*************************************************
- * Set up processing details *
- *************************************************/
- /* Save a text string for dumping when SIGUSR1 is received.
- Do checks for overruns.
- Arguments: format and arguments, as for printf()
- Returns: nothing
- */
- void
- set_process_info(const char *format, ...)
- {
- int len;
- va_list ap;
- sprintf(CS process_info, "%5d ", (int)getpid());
- len = Ustrlen(process_info);
- va_start(ap, format);
- if (!string_vformat(process_info + len, PROCESS_INFO_SIZE - len - 2, format, ap))
- Ustrcpy(process_info + len, "**** string overflowed buffer ****");
- len = Ustrlen(process_info);
- process_info[len+0] = '\n';
- process_info[len+1] = '\0';
- process_info_len = len + 1;
- DEBUG(D_process_info) debug_printf("set_process_info: %s", process_info);
- va_end(ap);
- }
- /*************************************************
- * Handler for SIGUSR1 *
- *************************************************/
- /* SIGUSR1 causes any exim process to write to the process log details of
- what it is currently doing. It will only be used if the OS is capable of
- setting up a handler that causes automatic restarting of any system call
- that is in progress at the time.
- This function takes care to be signal-safe.
- Argument: the signal number (SIGUSR1)
- Returns: nothing
- */
- static void
- usr1_handler(int sig)
- {
- int fd;
- os_restarting_signal(sig, usr1_handler);
- fd = Uopen(process_log_path, O_APPEND|O_WRONLY, LOG_MODE);
- if (fd < 0)
- {
- /* If we are already running as the Exim user, try to create it in the
- current process (assuming spool_directory exists). Otherwise, if we are
- root, do the creation in an exim:exim subprocess. */
- int euid = geteuid();
- if (euid == exim_uid)
- fd = Uopen(process_log_path, O_CREAT|O_APPEND|O_WRONLY, LOG_MODE);
- else if (euid == root_uid)
- fd = log_create_as_exim(process_log_path);
- }
- /* If we are neither exim nor root, or if we failed to create the log file,
- give up. There is not much useful we can do with errors, since we don't want
- to disrupt whatever is going on outside the signal handler. */
- if (fd < 0) return;
- {int dummy = write(fd, process_info, process_info_len); dummy = dummy; }
- (void)close(fd);
- }
- /*************************************************
- * Timeout handler *
- *************************************************/
- /* This handler is enabled most of the time that Exim is running. The handler
- doesn't actually get used unless alarm() has been called to set a timer, to
- place a time limit on a system call of some kind. When the handler is run, it
- re-enables itself.
- There are some other SIGALRM handlers that are used in special cases when more
- than just a flag setting is required; for example, when reading a message's
- input. These are normally set up in the code module that uses them, and the
- SIGALRM handler is reset to this one afterwards.
- Argument: the signal value (SIGALRM)
- Returns: nothing
- */
- void
- sigalrm_handler(int sig)
- {
- sig = sig; /* Keep picky compilers happy */
- sigalrm_seen = TRUE;
- os_non_restarting_signal(SIGALRM, sigalrm_handler);
- }
- /*************************************************
- * Sleep for a fractional time interval *
- *************************************************/
- /* This function is called by millisleep() and exim_wait_tick() to wait for a
- period of time that may include a fraction of a second. The coding is somewhat
- tedious. We do not expect setitimer() ever to fail, but if it does, the process
- will wait for ever, so we panic in this instance. (There was a case of this
- when a bug in a function that calls milliwait() caused it to pass invalid data.
- That's when I added the check. :-)
- We assume it to be not worth sleeping for under 100us; this value will
- require revisiting as hardware advances. This avoids the issue of
- a zero-valued timer setting meaning "never fire".
- Argument: an itimerval structure containing the interval
- Returns: nothing
- */
- static void
- milliwait(struct itimerval *itval)
- {
- sigset_t sigmask;
- sigset_t old_sigmask;
- if (itval->it_value.tv_usec < 100 && itval->it_value.tv_sec == 0)
- return;
- (void)sigemptyset(&sigmask); /* Empty mask */
- (void)sigaddset(&sigmask, SIGALRM); /* Add SIGALRM */
- (void)sigprocmask(SIG_BLOCK, &sigmask, &old_sigmask); /* Block SIGALRM */
- if (setitimer(ITIMER_REAL, itval, NULL) < 0) /* Start timer */
- log_write(0, LOG_MAIN|LOG_PANIC_DIE,
- "setitimer() failed: %s", strerror(errno));
- (void)sigfillset(&sigmask); /* All signals */
- (void)sigdelset(&sigmask, SIGALRM); /* Remove SIGALRM */
- (void)sigsuspend(&sigmask); /* Until SIGALRM */
- (void)sigprocmask(SIG_SETMASK, &old_sigmask, NULL); /* Restore mask */
- }
- /*************************************************
- * Millisecond sleep function *
- *************************************************/
- /* The basic sleep() function has a granularity of 1 second, which is too rough
- in some cases - for example, when using an increasing delay to slow down
- spammers.
- Argument: number of millseconds
- Returns: nothing
- */
- void
- millisleep(int msec)
- {
- struct itimerval itval;
- itval.it_interval.tv_sec = 0;
- itval.it_interval.tv_usec = 0;
- itval.it_value.tv_sec = msec/1000;
- itval.it_value.tv_usec = (msec % 1000) * 1000;
- milliwait(&itval);
- }
- /*************************************************
- * Compare microsecond times *
- *************************************************/
- /*
- Arguments:
- tv1 the first time
- tv2 the second time
- Returns: -1, 0, or +1
- */
- int
- exim_tvcmp(struct timeval *t1, struct timeval *t2)
- {
- if (t1->tv_sec > t2->tv_sec) return +1;
- if (t1->tv_sec < t2->tv_sec) return -1;
- if (t1->tv_usec > t2->tv_usec) return +1;
- if (t1->tv_usec < t2->tv_usec) return -1;
- return 0;
- }
- /*************************************************
- * Clock tick wait function *
- *************************************************/
- /* Exim uses a time + a pid to generate a unique identifier in two places: its
- message IDs, and in file names for maildir deliveries. Because some OS now
- re-use pids within the same second, sub-second times are now being used.
- However, for absolute certaintly, we must ensure the clock has ticked before
- allowing the relevant process to complete. At the time of implementation of
- this code (February 2003), the speed of processors is such that the clock will
- invariably have ticked already by the time a process has done its job. This
- function prepares for the time when things are faster - and it also copes with
- clocks that go backwards.
- Arguments:
- then_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.
- resolution The resolution that was used to divide the microseconds
- (1 for maildir, larger for message ids)
- Returns: nothing
- */
- void
- exim_wait_tick(struct timeval *then_tv, int resolution)
- {
- struct timeval now_tv;
- long int now_true_usec;
- (void)gettimeofday(&now_tv, NULL);
- now_true_usec = now_tv.tv_usec;
- now_tv.tv_usec = (now_true_usec/resolution) * resolution;
- if (exim_tvcmp(&now_tv, then_tv) <= 0)
- {
- struct itimerval itval;
- itval.it_interval.tv_sec = 0;
- itval.it_interval.tv_usec = 0;
- itval.it_value.tv_sec = then_tv->tv_sec - now_tv.tv_sec;
- itval.it_value.tv_usec = then_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"
- is more than a second less than "then". That means that itval.it_value.tv_sec
- is greater than zero. The following correction is therefore safe. */
- if (itval.it_value.tv_usec < 0)
- {
- itval.it_value.tv_usec += 1000000;
- itval.it_value.tv_sec -= 1;
- }
- DEBUG(D_transport|D_receive)
- {
- if (!running_in_test_harness)
- {
- debug_printf("tick check: " TIME_T_FMT ".%06lu " TIME_T_FMT ".%06lu\n",
- then_tv->tv_sec, (long) then_tv->tv_usec,
- now_tv.tv_sec, (long) now_tv.tv_usec);
- debug_printf("waiting " TIME_T_FMT ".%06lu\n",
- itval.it_value.tv_sec, (long) itval.it_value.tv_usec);
- }
- }
- milliwait(&itval);
- }
- }
- /*************************************************
- * Call fopen() with umask 777 and adjust mode *
- *************************************************/
- /* Exim runs with umask(0) so that files created with open() have the mode that
- is specified in the open() call. However, there are some files, typically in
- the spool directory, that are created with fopen(). They end up world-writeable
- if no precautions are taken. Although the spool directory is not accessible to
- the world, this is an untidiness. So this is a wrapper function for fopen()
- that sorts out the mode of the created file.
- Arguments:
- filename the file name
- options the fopen() options
- mode the required mode
- Returns: the fopened FILE or NULL
- */
- FILE *
- modefopen(const uschar *filename, const char *options, mode_t mode)
- {
- mode_t saved_umask = umask(0777);
- FILE *f = Ufopen(filename, options);
- (void)umask(saved_umask);
- if (f != NULL) (void)fchmod(fileno(f), mode);
- return f;
- }
- /*************************************************
- * Ensure stdin, stdout, and stderr exist *
- *************************************************/
- /* Some operating systems grumble if an exec() happens without a standard
- input, output, and error (fds 0, 1, 2) being defined. The worry is that some
- file will be opened and will use these fd values, and then some other bit of
- code will assume, for example, that it can write error messages to stderr.
- This function ensures that fds 0, 1, and 2 are open if they do not already
- exist, by connecting them to /dev/null.
- This function is also used to ensure that std{in,out,err} exist at all times,
- so that if any library that Exim calls tries to use them, it doesn't crash.
- Arguments: None
- Returns: Nothing
- */
- void
- exim_nullstd(void)
- {
- int i;
- int devnull = -1;
- struct stat statbuf;
- for (i = 0; i <= 2; i++)
- {
- if (fstat(i, &statbuf) < 0 && errno == EBADF)
- {
- if (devnull < 0) devnull = open("/dev/null", O_RDWR);
- if (devnull < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s",
- string_open_failed(errno, "/dev/null"));
- if (devnull != i) (void)dup2(devnull, i);
- }
- }
- if (devnull > 2) (void)close(devnull);
- }
- /*************************************************
- * Close unwanted file descriptors for delivery *
- *************************************************/
- /* This function is called from a new process that has been forked to deliver
- an incoming message, either directly, or using exec.
- We want any smtp input streams to be closed in this new process. However, it
- has been observed that using fclose() here causes trouble. When reading in -bS
- input, duplicate copies of messages have been seen. The files will be sharing a
- file pointer with the parent process, and it seems that fclose() (at least on
- some systems - I saw this on Solaris 2.5.1) messes with that file pointer, at
- least sometimes. Hence we go for closing the underlying file descriptors.
- If TLS is active, we want to shut down the TLS library, but without molesting
- the parent's SSL connection.
- For delivery of a non-SMTP message, we want to close stdin and stdout (and
- stderr unless debugging) because the calling process might have set them up as
- pipes and be waiting for them to close before it waits for the submission
- process to terminate. If they aren't closed, they hold up the calling process
- until the initial delivery process finishes, which is not what we want.
- Exception: We do want it for synchronous delivery!
- And notwithstanding all the above, if D_resolver is set, implying resolver
- debugging, leave stdout open, because that's where the resolver writes its
- debugging output.
- When we close stderr (which implies we've also closed stdout), we also get rid
- of any controlling terminal.
- Arguments: None
- Returns: Nothing
- */
- static void
- close_unwanted(void)
- {
- if (smtp_input)
- {
- #ifdef SUPPORT_TLS
- tls_close(TRUE, FALSE); /* Shut down the TLS library */
- #endif
- (void)close(fileno(smtp_in));
- (void)close(fileno(smtp_out));
- smtp_in = NULL;
- }
- else
- {
- (void)close(0); /* stdin */
- if ((debug_selector & D_resolver) == 0) (void)close(1); /* stdout */
- if (debug_selector == 0) /* stderr */
- {
- if (!synchronous_delivery)
- {
- (void)close(2);
- log_stderr = NULL;
- }
- (void)setsid();
- }
- }
- }
- /*************************************************
- * Set uid and gid *
- *************************************************/
- /* This function sets a new uid and gid permanently, optionally calling
- initgroups() to set auxiliary groups. There are some special cases when running
- Exim in unprivileged modes. In these situations the effective uid will not be
- root; if we already have the right effective uid/gid, and don't need to
- initialize any groups, leave things as they are.
- Arguments:
- uid the uid
- gid the gid
- igflag TRUE if initgroups() wanted
- msg text to use in debugging output and failure log
- Returns: nothing; bombs out on failure
- */
- void
- exim_setugid(uid_t uid, gid_t gid, BOOL igflag, uschar *msg)
- {
- uid_t euid = geteuid();
- gid_t egid = getegid();
- if (euid == root_uid || euid != uid || egid != gid || igflag)
- {
- /* At least one OS returns +1 for initgroups failure, so just check for
- non-zero. */
- if (igflag)
- {
- struct passwd *pw = getpwuid(uid);
- if (pw != NULL)
- {
- if (initgroups(pw->pw_name, gid) != 0)
- log_write(0,LOG_MAIN|LOG_PANIC_DIE,"initgroups failed for uid=%ld: %s",
- (long int)uid, strerror(errno));
- }
- else log_write(0, LOG_MAIN|LOG_PANIC_DIE, "cannot run initgroups(): "
- "no passwd entry for uid=%ld", (long int)uid);
- }
- if (setgid(gid) < 0 || setuid(uid) < 0)
- {
- log_write(0, LOG_MAIN|LOG_PANIC_DIE, "unable to set gid=%ld or uid=%ld "
- "(euid=%ld): %s", (long int)gid, (long int)uid, (long int)euid, msg);
- }
- }
- /* Debugging output included uid/gid and all groups */
- DEBUG(D_uid)
- {
- int group_count, save_errno;
- gid_t group_list[NGROUPS_MAX];
- debug_printf("changed uid/gid: %s\n uid=%ld gid=%ld pid=%ld\n", msg,
- (long int)geteuid(), (long int)getegid(), (long int)getpid());
- group_count = getgroups(NGROUPS_MAX, group_list);
- save_errno = errno;
- debug_printf(" auxiliary group list:");
- if (group_count > 0)
- {
- int i;
- for (i = 0; i < group_count; i++) debug_printf(" %d", (int)group_list[i]);
- }
- else if (group_count < 0)
- debug_printf(" <error: %s>", strerror(save_errno));
- else debug_printf(" <none>");
- debug_printf("\n");
- }
- }
- /*************************************************
- * Exit point *
- *************************************************/
- /* Exim exits via this function so that it always clears up any open
- databases.
- Arguments:
- rc return code
- Returns: does not return
- */
- void
- exim_exit(int rc)
- {
- search_tidyup();
- DEBUG(D_any)
- debug_printf(">>>>>>>>>>>>>>>> Exim pid=%d terminating with rc=%d "
- ">>>>>>>>>>>>>>>>\n", (int)getpid(), rc);
- exit(rc);
- }
- /*************************************************
- * Extract port from host address *
- *************************************************/
- /* Called to extract the port from the values given to -oMa and -oMi.
- It also checks the syntax of the address, and terminates it before the
- port data when a port is extracted.
- Argument:
- address the address, with possible port on the end
- Returns: the port, or zero if there isn't one
- bombs out on a syntax error
- */
- static int
- check_port(uschar *address)
- {
- int port = host_address_extract_port(address);
- if (string_is_ip_address(address, NULL) == 0)
- {
- fprintf(stderr, "exim abandoned: \"%s\" is not an IP address\n", address);
- exit(EXIT_FAILURE);
- }
- return port;
- }
- /*************************************************
- * Test/verify an address *
- *************************************************/
- /* This function is called by the -bv and -bt code. It extracts a working
- address from a full RFC 822 address. This isn't really necessary per se, but it
- has the effect of collapsing source routes.
- Arguments:
- s the address string
- flags flag bits for verify_address()
- exit_value to be set for failures
- Returns: nothing
- */
- static void
- test_address(uschar *s, int flags, int *exit_value)
- {
- int start, end, domain;
- uschar *parse_error = NULL;
- uschar *address = parse_extract_address(s, &parse_error, &start, &end, &domain,
- FALSE);
- if (address == NULL)
- {
- fprintf(stdout, "syntax error: %s\n", parse_error);
- *exit_value = 2;
- }
- else
- {
- int rc = verify_address(deliver_make_addr(address,TRUE), stdout, flags, -1,
- -1, -1, NULL, NULL, NULL);
- if (rc == FAIL) *exit_value = 2;
- else if (rc == DEFER && *exit_value == 0) *exit_value = 1;
- }
- }
- /*************************************************
- * Show supported features *
- *************************************************/
- /* This function is called for -bV/--version and for -d to output the optional
- features of the current Exim binary.
- Arguments: a FILE for printing
- Returns: nothing
- */
- static void
- show_whats_supported(FILE *f)
- {
- auth_info *authi;
- #ifdef DB_VERSION_STRING
- fprintf(f, "Berkeley DB: %s\n", DB_VERSION_STRING);
- #elif defined(BTREEVERSION) && defined(HASHVERSION)
- #ifdef USE_DB
- fprintf(f, "Probably Berkeley DB version 1.8x (native mode)\n");
- #else
- fprintf(f, "Probably Berkeley DB version 1.8x (compatibility mode)\n");
- #endif
- #elif defined(_DBM_RDONLY) || defined(dbm_dirfno)
- fprintf(f, "Probably ndbm\n");
- #elif defined(USE_TDB)
- fprintf(f, "Using tdb\n");
- #else
- #ifdef USE_GDBM
- fprintf(f, "Probably GDBM (native mode)\n");
- #else
- fprintf(f, "Probably GDBM (compatibility mode)\n");
- #endif
- #endif
- fprintf(f, "Support for:");
- #ifdef SUPPORT_CRYPTEQ
- fprintf(f, " crypteq");
- #endif
- #if HAVE_ICONV
- fprintf(f, " iconv()");
- #endif
- #if HAVE_IPV6
- fprintf(f, " IPv6");
- #endif
- #ifdef HAVE_SETCLASSRESOURCES
- fprintf(f, " use_setclassresources");
- #endif
- #ifdef SUPPORT_PAM
- fprintf(f, " PAM");
- #endif
- #ifdef EXIM_PERL
- fprintf(f, " Perl");
- #endif
- #ifdef EXPAND_DLFUNC
- fprintf(f, " Expand_dlfunc");
- #endif
- #ifdef USE_TCP_WRAPPERS
- fprintf(f, " TCPwrappers");
- #endif
- #ifdef SUPPORT_TLS
- #ifdef USE_GNUTLS
- fprintf(f, " GnuTLS");
- #else
- fprintf(f, " OpenSSL");
- #endif
- #endif
- #ifdef SUPPORT_TRANSLATE_IP_ADDRESS
- fprintf(f, " translate_ip_address");
- #endif
- #ifdef SUPPORT_MOVE_FROZEN_MESSAGES
- fprintf(f, " move_frozen_messages");
- #endif
- #ifdef WITH_CONTENT_SCAN
- fprintf(f, " Content_Scanning");
- #endif
- #ifndef DISABLE_DKIM
- fprintf(f, " DKIM");
- #endif
- #ifdef WITH_OLD_DEMIME
- fprintf(f, " Old_Demime");
- #endif
- #ifndef DISABLE_DNSSEC
- fprintf(f, " DNSSEC");
- #endif
- #ifndef DISABLE_PRDR
- fprintf(f, " PRDR");
- #endif
- #ifndef DISABLE_OCSP
- fprintf(f, " OCSP");
- #endif
- #ifdef EXPERIMENTAL_SPF
- fprintf(f, " Experimental_SPF");
- #endif
- #ifdef EXPERIMENTAL_SRS
- fprintf(f, " Experimental_SRS");
- #endif
- #ifdef EXPERIMENTAL_BRIGHTMAIL
- fprintf(f, " Experimental_Brightmail");
- #endif
- #ifdef EXPERIMENTAL_DANE
- fprintf(f, " Experimental_DANE");
- #endif
- #ifdef EXPERIMENTAL_DCC
- fprintf(f, " Experimental_DCC");
- #endif
- #ifdef EXPERIMENTAL_DMARC
- fprintf(f, " Experimental_DMARC");
- #endif
- #ifdef EXPERIMENTAL_PROXY
- fprintf(f, " Experimental_Proxy");
- #endif
- #ifdef EXPERIMENTAL_EVENT
- fprintf(f, " Experimental_Event");
- #endif
- #ifdef EXPERIMENTAL_REDIS
- fprintf(f, " Experimental_Redis");
- #endif
- #ifdef EXPERIMENTAL_SOCKS
- fprintf(f, " Experimental_SOCKS");
- #endif
- #ifdef EXPERIMENTAL_INTERNATIONAL
- fprintf(f, " Experimental_International");
- #endif
- fprintf(f, "\n");
- fprintf(f, "Lookups (built-in):");
- #if defined(LOOKUP_LSEARCH) && LOOKUP_LSEARCH!=2
- fprintf(f, " lsearch wildlsearch nwildlsearch iplsearch");
- #endif
- #if defined(LOOKUP_CDB) && LOOKUP_CDB!=2
- fprintf(f, " cdb");
- #endif
- #if defined(LOOKUP_DBM) && LOOKUP_DBM!=2
- fprintf(f, " dbm dbmjz dbmnz");
- #endif
- #if defined(LOOKUP_DNSDB) && LOOKUP_DNSDB!=2
- fprintf(f, " dnsdb");
- #endif
- #if defined(LOOKUP_DSEARCH) && LOOKUP_DSEARCH!=2
- fprintf(f, " dsearch");
- #endif
- #if defined(LOOKUP_IBASE) && LOOKUP_IBASE!=2
- fprintf(f, " ibase");
- #endif
- #if defined(LOOKUP_LDAP) && LOOKUP_LDAP!=2
- fprintf(f, " ldap ldapdn ldapm");
- #endif
- #if defined(LOOKUP_MYSQL) && LOOKUP_MYSQL!=2
- fprintf(f, " mysql");
- #endif
- #if defined(LOOKUP_NIS) && LOOKUP_NIS!=2
- fprintf(f, " nis nis0");
- #endif
- #if defined(LOOKUP_NISPLUS) && LOOKUP_NISPLUS!=2
- fprintf(f, " nisplus");
- #endif
- #if defined(LOOKUP_ORACLE) && LOOKUP_ORACLE!=2
- fprintf(f, " oracle");
- #endif
- #if defined(LOOKUP_PASSWD) && LOOKUP_PASSWD!=2
- fprintf(f, " passwd");
- #endif
- #if defined(LOOKUP_PGSQL) && LOOKUP_PGSQL!=2
- fprintf(f, " pgsql");
- #endif
- #if defined(LOOKUP_SQLITE) && LOOKUP_SQLITE!=2
- fprintf(f, " sqlite");
- #endif
- #if defined(LOOKUP_TESTDB) && LOOKUP_TESTDB!=2
- fprintf(f, " testdb");
- #endif
- #if defined(LOOKUP_WHOSON) && LOOKUP_WHOSON!=2
- fprintf(f, " whoson");
- #endif
- fprintf(f, "\n");
- fprintf(f, "Authenticators:");
- #ifdef AUTH_CRAM_MD5
- fprintf(f, " cram_md5");
- #endif
- #ifdef AUTH_CYRUS_SASL
- fprintf(f, " cyrus_sasl");
- #endif
- #ifdef AUTH_DOVECOT
- fprintf(f, " dovecot");
- #endif
- #ifdef AUTH_GSASL
- fprintf(f, " gsasl");
- #endif
- #ifdef AUTH_HEIMDAL_GSSAPI
- fprintf(f, " heimdal_gssapi");
- #endif
- #ifdef AUTH_PLAINTEXT
- fprintf(f, " plaintext");
- #endif
- #ifdef AUTH_SPA
- fprintf(f, " spa");
- #endif
- fprintf(f, "\n");
- fprintf(f, "Routers:");
- #ifdef ROUTER_ACCEPT
- fprintf(f, " accept");
- #endif
- #ifdef ROUTER_DNSLOOKUP
- fprintf(f, " dnslookup");
- #endif
- #ifdef ROUTER_IPLITERAL
- fprintf(f, " ipliteral");
- #endif
- #ifdef ROUTER_IPLOOKUP
- fprintf(f, " iplookup");
- #endif
- #ifdef ROUTER_MANUALROUTE
- fprintf(f, " manualroute");
- #endif
- #ifdef ROUTER_QUERYPROGRAM
- fprintf(f, " queryprogram");
- #endif
- #ifdef ROUTER_REDIRECT
- fprintf(f, " redirect");
- #endif
- fprintf(f, "\n");
- fprintf(f, "Transports:");
- #ifdef TRANSPORT_APPENDFILE
- fprintf(f, " appendfile");
- #ifdef SUPPORT_MAILDIR
- fprintf(f, "/maildir");
- #endif
- #ifdef SUPPORT_MAILSTORE
- fprintf(f, "/mailstore");
- #endif
- #ifdef SUPPORT_MBX
- fprintf(f, "/mbx");
- #endif
- #endif
- #ifdef TRANSPORT_AUTOREPLY
- fprintf(f, " autoreply");
- #endif
- #ifdef TRANSPORT_LMTP
- fprintf(f, " lmtp");
- #endif
- #ifdef TRANSPORT_PIPE
- fprintf(f, " pipe");
- #endif
- #ifdef TRANSPORT_SMTP
- fprintf(f, " smtp");
- #endif
- fprintf(f, "\n");
- if (fixed_never_users[0] > 0)
- {
- int i;
- fprintf(f, "Fixed never_users: ");
- for (i = 1; i <= (int)fixed_never_users[0] - 1; i++)
- fprintf(f, "%d:", (unsigned int)fixed_never_users[i]);
- fprintf(f, "%d\n", (unsigned int)fixed_never_users[i]);
- }
- fprintf(f, "Size of off_t: " SIZE_T_FMT "\n", sizeof(off_t));
- /* Everything else is details which are only worth reporting when debugging.
- Perhaps the tls_version_report should move into this too. */
- DEBUG(D_any) do {
- int i;
- /* clang defines __GNUC__ (at least, for me) so test for it first */
- #if defined(__clang__)
- fprintf(f, "Compiler: CLang [%s]\n", __clang_version__);
- #elif defined(__GNUC__)
- fprintf(f, "Compiler: GCC [%s]\n",
- # ifdef __VERSION__
- __VERSION__
- # else
- "? unknown version ?"
- # endif
- );
- #else
- fprintf(f, "Compiler: <unknown>\n");
- #endif
- #ifdef SUPPORT_TLS
- tls_version_report(f);
- #endif
- #ifdef EXPERIMENTAL_INTERNATIONAL
- utf8_version_report(f);
- #endif
- for (authi = auths_available; *authi->driver_name != '\0'; ++authi)
- if (authi->version_report)
- (*authi->version_report)(f);
- /* PCRE_PRERELEASE is either defined and empty or a bare sequence of
- characters; unless it's an ancient version of PCRE in which case it
- is not defined. */
- #ifndef PCRE_PRERELEASE
- #define PCRE_PRERELEASE
- #endif
- #define QUOTE(X) #X
- #define EXPAND_AND_QUOTE(X) QUOTE(X)
- fprintf(f, "Library version: PCRE: Compile: %d.%d%s\n"
- " Runtime: %s\n",
- PCRE_MAJOR, PCRE_MINOR,
- EXPAND_AND_QUOTE(PCRE_PRERELEASE) "",
- pcre_version());
- #undef QUOTE
- #undef EXPAND_AND_QUOTE
- init_lookup_list();
- for (i = 0; i < lookup_list_count; i++)
- if (lookup_list[i]->version_report)
- lookup_list[i]->version_report(f);
- #ifdef WHITELIST_D_MACROS
- fprintf(f, "WHITELIST_D_MACROS: \"%s\"\n", WHITELIST_D_MACROS);
- #else
- fprintf(f, "WHITELIST_D_MACROS unset\n");
- #endif
- #ifdef TRUSTED_CONFIG_LIST
- fprintf(f, "TRUSTED_CONFIG_LIST: \"%s\"\n", TRUSTED_CONFIG_LIST);
- #else
- fprintf(f, "TRUSTED_CONFIG_LIST unset\n");
- #endif
- } while (0);
- }
- /*************************************************
- * Show auxiliary information about Exim *
- *************************************************/
- static void
- show_exim_information(enum commandline_info request, FILE *stream)
- {
- const uschar **pp;
- switch(request)
- {
- case CMDINFO_NONE:
- fprintf(stream, "Oops, something went wrong.\n");
- return;
- case CMDINFO_HELP:
- fprintf(stream,
- "The -bI: flag takes a string indicating which information to provide.\n"
- "If the string is not recognised, you'll get this help (on stderr).\n"
- "\n"
- " exim -bI:help this information\n"
- " exim -bI:dscp dscp value keywords known\n"
- " exim -bI:sieve list of supported sieve extensions, one per line.\n"
- );
- return;
- case CMDINFO_SIEVE:
- for (pp = exim_sieve_extension_list; *pp; ++pp)
- fprintf(stream, "%s\n", *pp);
- return;
- case CMDINFO_DSCP:
- dscp_list_to_stream(stream);
- return;
- }
- }
- /*************************************************
- * Quote a local part *
- *************************************************/
- /* This function is used when a sender address or a From: or Sender: header
- line is being created from the caller's login, or from an authenticated_id. It
- applies appropriate quoting rules for a local part.
- Argument: the local part
- Returns: the local part, quoted if necessary
- */
- uschar *
- local_part_quote(uschar *lpart)
- {
- BOOL needs_quote = FALSE;
- int size, ptr;
- uschar *yield;
- uschar *t;
- for (t = lpart; !needs_quote && *t != 0; t++)
- {
- needs_quote = !isalnum(*t) && strchr("!#$%&'*+-/=?^_`{|}~", *t) == NULL &&
- (*t != '.' || t == lpart || t[1] == 0);
- }
- if (!needs_quote) return lpart;
- size = ptr = 0;
- yield = string_cat(NULL, &size, &ptr, US"\"", 1);
- for (;;)
- {
- uschar *nq = US Ustrpbrk(lpart, "\\\"");
- if (nq == NULL)
- {
- yield = string_cat(yield, &size, &ptr, lpart, Ustrlen(lpart));
- break;
- }
- yield = string_cat(yield, &size, &ptr, lpart, nq - lpart);
- yield = string_cat(yield, &size, &ptr, US"\\", 1);
- yield = string_cat(yield, &size, &ptr, nq, 1);
- lpart = nq + 1;
- }
- yield = string_cat(yield, &size, &ptr, US"\"", 1);
- yield[ptr] = 0;
- return yield;
- }
- #ifdef USE_READLINE
- /*************************************************
- * Load readline() functions *
- *************************************************/
- /* This function is called from testing executions that read data from stdin,
- but only when running as the calling user. Currently, only -be does this. The
- function loads the readline() function library and passes back the functions.
- On some systems, it needs the curses library, so load that too, but try without
- it if loading fails. All this functionality has to be requested at build time.
- Arguments:
- fn_readline_ptr pointer to where to put the readline pointer
- fn_addhist_ptr pointer to where to put the addhistory function
- Returns: the dlopen handle or NULL on failure
- */
- static void *
- set_readline(char * (**fn_readline_ptr)(const char *),
- void (**fn_addhist_ptr)(const char *))
- {
- void *dlhandle;
- void *dlhandle_curses = dlopen("libcurses." DYNLIB_FN_EXT, RTLD_GLOBAL|RTLD_LAZY);
- dlhandle = dlopen("libreadline." DYNLIB_FN_EXT, RTLD_GLOBAL|RTLD_NOW);
- if (dlhandle_curses != NULL) dlclose(dlhandle_curses);
- if (dlhandle != NULL)
- {
- /* Checked manual pages; at least in GNU Readline 6.1, the prototypes are:
- * char * readline (const char *prompt);
- * void add_history (const char *string);
- */
- *fn_readline_ptr = (char *(*)(const char*))dlsym(dlhandle, "readline");
- *fn_addhist_ptr = (void(*)(const char*))dlsym(dlhandle, "add_history");
- }
- else
- {
- DEBUG(D_any) debug_printf("failed to load readline: %s\n", dlerror());
- }
- return dlhandle;
- }
- #endif
- /*************************************************
- * Get a line from stdin for testing things *
- *************************************************/
- /* This function is called when running tests that can take a number of lines
- of input (for example, -be and -bt). It handles continuations and trailing
- spaces. And prompting and a blank line output on eof. If readline() is in use,
- the arguments are non-NULL and provide the relevant functions.
- Arguments:
- fn_readline readline function or NULL
- fn_addhist addhist function or NULL
- Returns: pointer to dynamic memory, or NULL at end of file
- */
- static uschar *
- get_stdinput(char *(*fn_readline)(const char *), void(*fn_addhist)(const char *))
- {
- int i;
- int size = 0;
- int ptr = 0;
- uschar *yield = NULL;
- if (fn_readline == NULL) { printf("> "); fflush(stdout); }
- for (i = 0;; i++)
- {
- uschar buffer[1024];
- uschar *p, *ss;
- #ifdef USE_READLINE
- char *readline_line = NULL;
- if (fn_readline != NULL)
- {
- if ((readline_line = fn_readline((i > 0)? "":"> ")) == NULL) break;
- if (*readline_line != 0 && fn_addhist != NULL) fn_addhist(readline_line);
- p = US readline_line;
- }
- else
- #endif
- /* readline() not in use */
- {
- if (Ufgets(buffer, sizeof(buffer), stdin) == NULL) break;
- p = buffer;
- }
- /* Handle the line */
- ss = p + (int)Ustrlen(p);
- while (ss > p && isspace(ss[-1])) ss--;
- if (i > 0)
- {
- while (p < ss && isspace(*p)) p++; /* leading space after cont */
- }
- yield = string_cat(yield, &size, &ptr, p, ss - p);
- #ifdef USE_READLINE
- if (fn_readline != NULL) free(readline_line);
- #endif
- if (ss == p || yield[ptr-1] != '\\')
- {
- yield[ptr] = 0;
- break;
- }
- yield[--ptr] = 0;
- }
- if (yield == NULL) printf("\n");
- return yield;
- }
- /*************************************************
- * Output usage information for the program *
- *************************************************/
- /* This function is called when there are no recipients
- or a specific --help argument was added.
- Arguments:
- progname information on what name we were called by
- Returns: DOES NOT RETURN
- */
- static void
- exim_usage(uschar *progname)
- {
- /* Handle specific program invocation varients */
- if (Ustrcmp(progname, US"-mailq") == 0)
- {
- fprintf(stderr,
- "mailq - list the contents of the mail queue\n\n"
- "For a list of options, see the Exim documentation.\n");
- exit(EXIT_FAILURE);
- }
- /* Generic usage - we output this whatever happens */
- fprintf(stderr,
- "Exim is a Mail Transfer Agent. It is normally called by Mail User Agents,\n"
- "not directly from a shell command line. Options and/or arguments control\n"
- "what it does when called. For a list of options, see the Exim documentation.\n");
- exit(EXIT_FAILURE);
- }
- /*************************************************
- * Validate that the macros given are okay *
- *************************************************/
- /* Typically, Exim will drop privileges if macros are supplied. In some
- cases, we want to not do so.
- Arguments: none (macros is a global)
- Returns: true if trusted, false otherwise
- */
- static BOOL
- macros_trusted(void)
- {
- #ifdef WHITELIST_D_MACROS
- macro_item *m;
- uschar *whitelisted, *end, *p, **whites, **w;
- int white_count, i, n;
- size_t len;
- BOOL prev_char_item, found;
- #endif
- if (macros == NULL)
- return TRUE;
- #ifndef WHITELIST_D_MACROS
- return FALSE;
- #else
- /* We only trust -D overrides for some invoking users:
- root, the exim run-time user, the optional config owner user.
- I don't know why config-owner would be needed, but since they can own the
- config files anyway, there's no security risk to letting them override -D. */
- if ( ! ((real_uid == root_uid)
- || (real_uid == exim_uid)
- #ifdef CONFIGURE_OWNER
- || (real_uid == config_uid)
- #endif
- ))
- {
- debug_printf("macros_trusted rejecting macros for uid %d\n", (int) real_uid);
- return FALSE;
- }
- /* Get a list of macros which are whitelisted */
- whitelisted = string_copy_malloc(US WHITELIST_D_MACROS);
- prev_char_item = FALSE;
- white_count = 0;
- for (p = whitelisted; *p != '\0'; ++p)
- {
- if (*p == ':' || isspace(*p))
- {
- *p = '\0';
- if (prev_char_item)
- ++white_count;
- prev_char_item = FALSE;
- continue;
- }
- if (!prev_char_item)
- prev_char_item = TRUE;
- }
- end = p;
- if (prev_char_item)
- ++white_count;
- if (!white_count)
- return FALSE;
- whites = store_malloc(sizeof(uschar *) * (white_count+1));
- for (p = whitelisted, i = 0; (p != end) && (i < white_count); ++p)
- {
- if (*p != '\0')
- {
- whites[i++] = p;
- if (i == white_count)
- break;
- while (*p != '\0' && p < end)
- ++p;
- }
- }
- whites[i] = NULL;
- /* The list of macros should be very short. Accept the N*M complexity. */
- for (m = macros; m != NULL; m = m->next)
- {
- found = FALSE;
- for (w = whites; *w; ++w)
- if (Ustrcmp(*w, m->name) == 0)
- {
- found = TRUE;
- break;
- }
- if (!found)
- return FALSE;
- if (m->replacement == NULL)
- continue;
- len = Ustrlen(m->replacement);
- if (len == 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);
- return FALSE;
- }
- }
- DEBUG(D_any) debug_printf("macros_trusted overridden to true by whitelisting\n");
- return TRUE;
- #endif
- }
- /*************************************************
- * Entry point and high-level code *
- *************************************************/
- /* Entry point for the Exim mailer. Analyse the arguments and arrange to take
- the appropriate action. All the necessary functions are present in the one
- binary. I originally thought one should split it up, but it turns out that so
- much of the apparatus is needed in each chunk that one might as well just have
- it all available all the time, which then makes the coding easier as well.
- Arguments:
- argc count of entries in argv
- argv argument strings, with argv[0] being the program name
- Returns: EXIT_SUCCESS if terminated successfully
- EXIT_FAILURE otherwise, except when a message has been sent
- to the sender, and -oee was given
- */
- int
- main(int argc, char **cargv)
- {
- uschar **argv = USS cargv;
- int arg_receive_timeout = -1;
- int arg_smtp_receive_timeout = -1;
- int arg_error_handling = error_handling;
- int filter_sfd = -1;
- int filter_ufd = -1;
- int group_count;
- int i, rv;
- int list_queue_option = 0;
- int msg_action = 0;
- int msg_action_arg = -1;
- int namelen = (argv[0] == NULL)? 0 : Ustrlen(argv[0]);
- int queue_only_reason = 0;
- #ifdef EXIM_PERL
- int perl_start_option = 0;
- #endif
- int recipients_arg = argc;
- int sender_address_domain = 0;
- int test_retry_arg = -1;
- int test_rewrite_arg = -1;
- BOOL arg_queue_only = FALSE;
- BOOL bi_option = FALSE;
- BOOL checking = FALSE;
- BOOL count_queue = FALSE;
- BOOL expansion_test = FALSE;
- BOOL extract_recipients = FALSE;
- BOOL flag_G = FALSE;
- BOOL flag_n = FALSE;
- BOOL forced_delivery = FALSE;
- BOOL f_end_dot = FALSE;
- BOOL deliver_give_up = FALSE;
- BOOL list_queue = FALSE;
- BOOL list_options = FALSE;
- BOOL local_queue_only;
- BOOL more = TRUE;
- BOOL one_msg_action = FALSE;
- BOOL queue_only_set = FALSE;
- BOOL receiving_message = TRUE;
- BOOL sender_ident_set = FALSE;
- BOOL session_local_queue_only;
- BOOL unprivileged;
- BOOL removed_privilege = FALSE;
- BOOL usage_wanted = FALSE;
- BOOL verify_address_mode = FALSE;
- BOOL verify_as_sender = 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;
- uschar *ftest_domain = NULL;
- uschar *ftest_localpart = NULL;
- uschar *ftest_prefix = NULL;
- uschar *ftest_suffix = NULL;
- uschar *log_oneline = NULL;
- uschar *malware_test_file = NULL;
- uschar *real_sender_address;
- uschar *originator_home = US"/";
- size_t sz;
- void *reset_point;
- struct passwd *pw;
- struct stat statbuf;
- pid_t passed_qr_pid = (pid_t)0;
- int passed_qr_pipe = -1;
- gid_t group_list[NGROUPS_MAX];
- /* For the -bI: flag */
- enum commandline_info info_flag = CMDINFO_NONE;
- BOOL info_stdout = FALSE;
- /* Possible options for -R and -S */
- static uschar *rsopts[] = { US"f", US"ff", US"r", US"rf", US"rff" };
- /* Need to define this in case we need to change the environment in order
- to get rid of a bogus time zone. We have to make it char rather than uschar
- because some OS define it in /usr/include/unistd.h. */
- extern char **environ;
- /* 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.
- This is a feature to make the lives of binary distributors easier. */
- #ifdef EXIM_USERNAME
- if (route_finduser(US EXIM_USERNAME, &pw, &exim_uid))
- {
- if (exim_uid == 0)
- {
- fprintf(stderr, "exim: refusing to run with uid 0 for \"%s\"\n",
- EXIM_USERNAME);
- exit(EXIT_FAILURE);
- }
- /* If ref:name uses a number as the name, route_finduser() returns
- TRUE with exim_uid set and pw coerced to NULL. */
- if (pw)
- exim_gid = pw->pw_gid;
- #ifndef EXIM_GROUPNAME
- else
- {
- fprintf(stderr,
- "exim: ref:name should specify a usercode, not a group.\n"
- "exim: can't let you get away with it unless you also specify a group.\n");
- exit(EXIT_FAILURE);
- }
- #endif
- }
- else
- {
- fprintf(stderr, "exim: failed to find uid for user name \"%s\"\n",
- EXIM_USERNAME);
- exit(EXIT_FAILURE);
- }
- #endif
- #ifdef EXIM_GROUPNAME
- if (!route_findgroup(US EXIM_GROUPNAME, &exim_gid))
- {
- fprintf(stderr, "exim: failed to find gid for group name \"%s\"\n",
- EXIM_GROUPNAME);
- exit(EXIT_FAILURE);
- }
- #endif
- #ifdef CONFIGURE_OWNERNAME
- if (!route_finduser(US CONFIGURE_OWNERNAME, NULL, &config_uid))
- {
- fprintf(stderr, "exim: failed to find uid for user name \"%s\"\n",
- CONFIGURE_OWNERNAME);
- exit(EXIT_FAILURE);
- }
- #endif
- /* We default the system_filter_user to be the Exim run-time user, as a
- sane non-root value. */
- system_filter_uid = exim_uid;
- #ifdef CONFIGURE_GROUPNAME
- if (!route_findgroup(US CONFIGURE_GROUPNAME, &config_gid))
- {
- fprintf(stderr, "exim: failed to find gid for group name \"%s\"\n",
- CONFIGURE_GROUPNAME);
- exit(EXIT_FAILURE);
- }
- #endif
- /* In the Cygwin environment, some initialization needs doing. It is fudged
- in by means of this macro. */
- #ifdef OS_INIT
- OS_INIT
- #endif
- /* Check a field which is patched when we are running Exim within its
- testing harness; do a fast initial check, and then the whole thing. */
- running_in_test_harness =
- *running_status == '<' && Ustrcmp(running_status, "<<<testing>>>") == 0;
- /* The C standard says that the equivalent of setlocale(LC_ALL, "C") is obeyed
- at the start of a program; however, it seems that some environments do not
- follow this. A "strange" locale can affect the formatting of timestamps, so we
- make quite sure. */
- setlocale(LC_ALL, "C");
- /* Set up the default handler for timing using alarm(). */
- os_non_restarting_signal(SIGALRM, sigalrm_handler);
- /* Ensure we have a buffer for constructing log entries. Use malloc directly,
- because store_malloc writes a log entry on failure. */
- log_buffer = (uschar *)malloc(LOG_BUFFER_SIZE);
- if (log_buffer == NULL)
- {
- fprintf(stderr, "exim: failed to get store for log buffer\n");
- exit(EXIT_FAILURE);
- }
- /* Set log_stderr to stderr, provided that stderr exists. This gets reset to
- NULL when the daemon is run and the file is closed. We have to use this
- indirection, because some systems don't allow writing to the variable "stderr".
- */
- 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. */
- big_buffer = store_malloc(big_buffer_size);
- /* Set up the handler for the data request signal, and set the initial
- descriptive text. */
- set_process_info("initializing");
- os_restarting_signal(SIGUSR1, usr1_handler);
- /* SIGHUP is used to get the daemon to reconfigure. It gets set as appropriate
- in the daemon code. For the rest of Exim's uses, we ignore it. */
- signal(SIGHUP, SIG_IGN);
- /* We don't want to die on pipe errors as the code is written to handle
- the write error instead. */
- signal(SIGPIPE, SIG_IGN);
- /* Under some circumstance on some OS, Exim can get called with SIGCHLD
- set to SIG_IGN. This causes subprocesses that complete before the parent
- process waits for them not to hang around, so when Exim calls wait(), nothing
- is there. The wait() code has been made robust against this, but let's ensure
- that SIGCHLD is set to SIG_DFL, because it's tidier to wait and get a process
- ending status. We use sigaction rather than plain signal() on those OS where
- SA_NOCLDWAIT exists, because we want to be sure it is turned off. (There was a
- problem on AIX with this.) */
- #ifdef SA_NOCLDWAIT
- {
- struct sigaction act;
- act.sa_handler = SIG_DFL;
- sigemptyset(&(act.sa_mask));
- act.sa_flags = 0;
- sigaction(SIGCHLD, &act, NULL);
- }
- #else
- signal(SIGCHLD, SIG_DFL);
- #endif
- /* Save the arguments for use if we re-exec exim as a daemon after receiving
- SIGHUP. */
- sighup_argv = argv;
- /* Set up the version number. Set up the leading 'E' for the external form of
- message ids, set the pointer to the internal form, and initialize it to
- indicate no message being processed. */
- version_init();
- message_id_option[0] = '-';
- message_id_external = message_id_option + 1;
- message_id_external[0] = 'E';
- message_id = message_id_external + 1;
- message_id[0] = 0;
- /* Set the umask to zero so that any files Exim creates using open() are
- created with the modes that it specifies. NOTE: Files created with fopen() have
- a problem, which was not recognized till rather late (February 2006). With this
- umask, such files will be world writeable. (They are all content scanning files
- in the spool directory, which isn't world-accessible, so this is not a
- disaster, but it's untidy.) I don't want to change this overall setting,
- however, because it will interact badly with the open() calls. Instead, there's
- now a function called modefopen() that fiddles with the umask while calling
- fopen(). */
- (void)umask(0);
- /* Precompile the regular expression for matching a message id. Keep this in
- step with the code that generates ids in the accept.c module. We need to do
- this here, because the -M options check their arguments for syntactic validity
- using mac_ismsgid, which uses this. */
- regex_ismsgid =
- regex_must_compile(US"^(?:[^\\W_]{6}-){2}[^\\W_]{2}$", FALSE, TRUE);
- /* Precompile the regular expression that is used for matching an SMTP error
- code, possibly extended, at the start of an error message. Note that the
- terminating whitespace character is included. */
- regex_smtp_code =
- regex_must_compile(US"^\\d\\d\\d\\s(?:\\d\\.\\d\\d?\\d?\\.\\d\\d?\\d?\\s)?",
- FALSE, TRUE);
- #ifdef WHITELIST_D_MACROS
- /* Precompile the regular expression used to filter the content of macros
- given to -D for permissibility. */
- regex_whitelisted_macro =
- regex_must_compile(US"^[A-Za-z0-9_/.-]*$", FALSE, TRUE);
- #endif
- /* If the program is called as "mailq" treat it as equivalent to "exim -bp";
- this seems to be a generally accepted convention, since one finds symbolic
- links called "mailq" in standard OS configurations. */
- if ((namelen == 5 && Ustrcmp(argv[0], "mailq") == 0) ||
- (namelen > 5 && Ustrncmp(argv[0] + namelen - 6, "/mailq", 6) == 0))
- {
- list_queue = TRUE;
- receiving_message = FALSE;
- called_as = US"-mailq";
- }
- /* If the program is called as "rmail" treat it as equivalent to
- "exim -i -oee", thus allowing UUCP messages to be input using non-SMTP mode,
- i.e. preventing a single dot on a line from terminating the message, and
- returning with zero return code, even in cases of error (provided an error
- message has been sent). */
- if ((namelen == 5 && Ustrcmp(argv[0], "rmail") == 0) ||
- (namelen > 5 && Ustrncmp(argv[0] + namelen - 6, "/rmail", 6) == 0))
- {
- dot_ends = FALSE;
- called_as = US"-rmail";
- errors_sender_rc = EXIT_SUCCESS;
- }
- /* If the program is called as "rsmtp" treat it as equivalent to "exim -bS";
- this is a smail convention. */
- if ((namelen == 5 && Ustrcmp(argv[0], "rsmtp") == 0) ||
- (namelen > …
Large files files are truncated, but you can click here to view the full file