Copyright updates:
[exim.git] / src / src / smtp_in.c
index 67179864191c82f8e3a0d5fa3e3d94d1177adf3a..d72adc15c698e42b335f5329860f455c0b2fce48 100644 (file)
@@ -3,6 +3,7 @@
 *************************************************/
 
 /* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim Maintainers 2020 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions for handling an incoming SMTP call. */
@@ -142,7 +143,7 @@ static struct {
   BOOL helo_verify                     :1;
   BOOL helo_seen                       :1;
   BOOL helo_accept_junk                        :1;
-#ifdef SUPPORT_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
   BOOL pipe_connect_acceptable         :1;
 #endif
   BOOL rcpt_smtp_response_same         :1;
@@ -397,7 +398,7 @@ return TRUE;
 }
 
 
-#ifdef SUPPORT_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
 static BOOL
 pipeline_connect_sends(void)
 {
@@ -457,7 +458,7 @@ if (smtp_batched_input)
 smtp_notquit_exit(US"command-timeout", US"421",
   US"%s: SMTP command timeout - closing connection",
   smtp_active_hostname);
-exim_exit(EXIT_FAILURE, US"receiving");
+exim_exit(EXIT_FAILURE);
 }
 
 void
@@ -468,7 +469,7 @@ if (smtp_batched_input)
   moan_smtp_batch(NULL, "421 SIGTERM received");  /* Does not return */
 smtp_notquit_exit(US"signal-exit", US"421",
   US"%s: Service not available - closing connection", smtp_active_hostname);
-exim_exit(EXIT_FAILURE, US"receiving");
+exim_exit(EXIT_FAILURE);
 }
 
 void
@@ -877,6 +878,8 @@ flush for non-TLS connections. The smtp_fflush() function is available for
 checking that: for convenience, TLS output errors are remembered here so that
 they are also picked up later by smtp_fflush().
 
+This function is exposed to the local_scan API; do not change the signature.
+
 Arguments:
   format      format string
   more       further data expected
@@ -897,7 +900,10 @@ va_end(ap);
 
 /* This is split off so that verify.c:respond_printf() can, in effect, call
 smtp_printf(), bearing in mind that in C a vararg function can't directly
-call another vararg function, only a function which accepts a va_list. */
+call another vararg function, only a function which accepts a va_list.
+
+This function is exposed to the local_scan API; do not change the signature.
+*/
 /*XXX consider passing caller-info in, for string_vformat-onward */
 
 void
@@ -926,7 +932,7 @@ if (!yield)
   {
   log_write(0, LOG_MAIN|LOG_PANIC, "string too large in smtp_printf()");
   smtp_closedown(US"Unexpected error");
-  exim_exit(EXIT_FAILURE, NULL);
+  exim_exit(EXIT_FAILURE);
   }
 
 /* If this is the first output for a (non-batch) RCPT command, see if all RCPTs
@@ -947,16 +953,13 @@ if (fl.rcpt_in_progress)
 
 /* Now write the string */
 
+if (
 #ifndef DISABLE_TLS
-if (tls_in.active.sock >= 0)
-  {
-  if (tls_write(NULL, gs.s, gs.ptr, more) < 0)
-    smtp_write_error = -1;
-  }
-else
+    tls_in.active.sock >= 0 ? (tls_write(NULL, gs.s, gs.ptr, more) < 0) :
 #endif
-
-if (fprintf(smtp_out, "%s", gs.s) < 0) smtp_write_error = -1;
+    (fwrite(gs.s, gs.ptr, 1, smtp_out) == 0)
+   )
+    smtp_write_error = -1;
 }
 
 
@@ -967,8 +970,7 @@ if (fprintf(smtp_out, "%s", gs.s) < 0) smtp_write_error = -1;
 
 /* This function isn't currently used within Exim (it detects errors when it
 tries to read the next SMTP input), but is available for use in local_scan().
-For non-TLS connections, it flushes the output and checks for errors. For
-TLS-connections, it checks for a previously-detected TLS write error.
+It flushes the output and checks for errors.
 
 Arguments:  none
 Returns:    0 for no error; -1 after an error
@@ -978,6 +980,15 @@ int
 smtp_fflush(void)
 {
 if (tls_in.active.sock < 0 && fflush(smtp_out) != 0) smtp_write_error = -1;
+
+if (
+#ifndef DISABLE_TLS
+    tls_in.active.sock >= 0 ? (tls_write(NULL, NULL, 0, FALSE) < 0) :
+#endif
+    (fflush(smtp_out) != 0)
+   )
+    smtp_write_error = -1;
+
 return smtp_write_error;
 }
 
@@ -2082,6 +2093,7 @@ dmarc_used_domain = NULL;
 #endif
 #ifdef EXPERIMENTAL_ARC
 arc_state = arc_state_reason = NULL;
+arc_received_instance = 0;
 #endif
 dsn_ret = 0;
 dsn_envid = NULL;
@@ -2395,24 +2407,47 @@ return FALSE;
 static void
 tfo_in_check(void)
 {
-# ifdef TCP_INFO
+# ifdef __FreeBSD__
+int is_fastopen;
+socklen_t len = sizeof(is_fastopen);
+
+/* The tinfo TCPOPT_FAST_OPEN bit seems unreliable, and we don't see state
+TCP_SYN_RCV (as of 12.1) so no idea about data-use. */
+
+if (getsockopt(fileno(smtp_out), IPPROTO_TCP, TCP_FASTOPEN, &is_fastopen, &len) == 0)
+  {
+  if (is_fastopen) 
+    {
+    DEBUG(D_receive)
+      debug_printf("TFO mode connection (TCP_FASTOPEN getsockopt)\n");
+    f.tcp_in_fastopen = TRUE;
+    }
+  }
+else DEBUG(D_receive)
+  debug_printf("TCP_INFO getsockopt: %s\n", strerror(errno));
+
+# elif defined(TCP_INFO)
 struct tcp_info tinfo;
 socklen_t len = sizeof(tinfo);
 
 if (getsockopt(fileno(smtp_out), IPPROTO_TCP, TCP_INFO, &tinfo, &len) == 0)
-#ifdef TCPI_OPT_SYN_DATA       /* FreeBSD 11 does not seem to have this yet */
+#  ifdef TCPI_OPT_SYN_DATA     /* FreeBSD 11,12 do not seem to have this yet */
   if (tinfo.tcpi_options & TCPI_OPT_SYN_DATA)
     {
-    DEBUG(D_receive) debug_printf("TCP_FASTOPEN mode connection (ACKd data-on-SYN)\n");
+    DEBUG(D_receive)
+      debug_printf("TFO mode connection (ACKd data-on-SYN)\n");
     f.tcp_in_fastopen_data = f.tcp_in_fastopen = TRUE;
     }
   else
-#endif
-    if (tinfo.tcpi_state == TCP_SYN_RECV)
+#  endif
+    if (tinfo.tcpi_state == TCP_SYN_RECV)      /* Not seen on FreeBSD 12.1 */
     {
-    DEBUG(D_receive) debug_printf("TCP_FASTOPEN mode connection (state TCP_SYN_RECV)\n");
+    DEBUG(D_receive)
+      debug_printf("TFO mode connection (state TCP_SYN_RECV)\n");
     f.tcp_in_fastopen = TRUE;
     }
+else DEBUG(D_receive)
+  debug_printf("TCP_INFO getsockopt: %s\n", strerror(errno));
 # endif
 }
 #endif
@@ -2466,7 +2501,7 @@ if (!host_checking && !f.sender_host_notsocket)
 authenticated_by = NULL;
 
 #ifndef DISABLE_TLS
-tls_in.cipher = tls_in.peerdn = NULL;
+tls_in.ver = tls_in.cipher = tls_in.peerdn = NULL;
 tls_in.ourcert = tls_in.peercert = NULL;
 tls_in.sni = NULL;
 tls_in.ocsp = OCSP_NOT_REQ;
@@ -2992,7 +3027,7 @@ while (*p);
 /* Before we write the banner, check that there is no input pending, unless
 this synchronisation check is disabled. */
 
-#ifdef SUPPORT_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
 fl.pipe_connect_acceptable =
   sender_host_address && verify_check_host(&pipe_connect_advertise_hosts) == OK;
 
@@ -3005,7 +3040,7 @@ if (!check_sync())
 #endif
     {
     unsigned n = smtp_inend - smtp_inptr;
-    if (n > 32) n = 32;
+    if (n > 128) n = 128;
 
     log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol "
       "synchronization error (input sent without waiting for greeting): "
@@ -3019,7 +3054,7 @@ if (!check_sync())
 /*XXX the ehlo-resp code does its own tls/nontls bit.  Maybe subroutine that? */
 
 smtp_printf("%s",
-#ifdef SUPPORT_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
   fl.pipe_connect_acceptable && pipeline_connect_sends(),
 #else
   FALSE,
@@ -3030,7 +3065,7 @@ smtp_printf("%s",
 handshake arrived.  If so we must have managed a TFO. */
 
 #ifdef TCP_FASTOPEN
-tfo_in_check();
+if (sender_host_address && !f.sender_host_notsocket) tfo_in_check();
 #endif
 
 return TRUE;
@@ -3160,7 +3195,7 @@ for (;;)
     {
     smtp_printf("%.3s-%.*s%.*s\r\n", TRUE, code, esclen, esc, (int)(nl - msg), msg);
     msg = nl + 1;
-    while (isspace(*msg)) msg++;
+    Uskip_whitespace(&msg);
     }
   }
 }
@@ -3714,60 +3749,60 @@ if (rc != OK)
 switch(rc)
   {
   case OK:
-  if (!au->set_id || set_id)    /* Complete success */
-    {
-    if (set_id) authenticated_id = string_copy_perm(set_id, TRUE);
-    sender_host_authenticated = au->name;
-    sender_host_auth_pubname  = au->public_name;
-    authentication_failed = FALSE;
-    authenticated_fail_id = NULL;   /* Impossible to already be set? */
-
-    received_protocol =
-      (sender_host_address ? protocols : protocols_local)
-       [pextend + pauthed + (tls_in.active.sock >= 0 ? pcrpted:0)];
-    *s = *ss = US"235 Authentication succeeded";
-    authenticated_by = au;
-    break;
-    }
+    if (!au->set_id || set_id)    /* Complete success */
+      {
+      if (set_id) authenticated_id = string_copy_perm(set_id, TRUE);
+      sender_host_authenticated = au->name;
+      sender_host_auth_pubname  = au->public_name;
+      authentication_failed = FALSE;
+      authenticated_fail_id = NULL;   /* Impossible to already be set? */
 
-  /* Authentication succeeded, but we failed to expand the set_id string.
-  Treat this as a temporary error. */
+      received_protocol =
+       (sender_host_address ? protocols : protocols_local)
+         [pextend + pauthed + (tls_in.active.sock >= 0 ? pcrpted:0)];
+      *s = *ss = US"235 Authentication succeeded";
+      authenticated_by = au;
+      break;
+      }
 
-  auth_defer_msg = expand_string_message;
-  /* Fall through */
+    /* Authentication succeeded, but we failed to expand the set_id string.
+    Treat this as a temporary error. */
+
+    auth_defer_msg = expand_string_message;
+    /* Fall through */
 
   case DEFER:
-  if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE);
-  *s = string_sprintf("435 Unable to authenticate at present%s",
-    auth_defer_user_msg);
-  *ss = string_sprintf("435 Unable to authenticate at present%s: %s",
-    set_id, auth_defer_msg);
-  break;
+    if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE);
+    *s = string_sprintf("435 Unable to authenticate at present%s",
+      auth_defer_user_msg);
+    *ss = string_sprintf("435 Unable to authenticate at present%s: %s",
+      set_id, auth_defer_msg);
+    break;
 
   case BAD64:
-  *s = *ss = US"501 Invalid base64 data";
-  break;
+    *s = *ss = US"501 Invalid base64 data";
+    break;
 
   case CANCELLED:
-  *s = *ss = US"501 Authentication cancelled";
-  break;
+    *s = *ss = US"501 Authentication cancelled";
+    break;
 
   case UNEXPECTED:
-  *s = *ss = US"553 Initial data not expected";
-  break;
+    *s = *ss = US"553 Initial data not expected";
+    break;
 
   case FAIL:
-  if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE);
-  *s = US"535 Incorrect authentication data";
-  *ss = string_sprintf("535 Incorrect authentication data%s", set_id);
-  break;
+    if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE);
+    *s = US"535 Incorrect authentication data";
+    *ss = string_sprintf("535 Incorrect authentication data%s", set_id);
+    break;
 
   default:
-  if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE);
-  *s = US"435 Internal error";
-  *ss = string_sprintf("435 Internal error%s: return %d from authentication "
-    "check", set_id, rc);
-  break;
+    if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE);
+    *s = US"435 Internal error";
+    *ss = string_sprintf("435 Internal error%s: return %d from authentication "
+      "check", set_id, rc);
+    break;
   }
 
 return rc;
@@ -3970,7 +4005,7 @@ while (done <= 0)
 #endif
 
   switch(smtp_read_command(
-#ifdef SUPPORT_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
          !fl.pipe_connect_acceptable,
 #else
          TRUE,
@@ -4206,7 +4241,7 @@ while (done <= 0)
          host_build_sender_fullhost();  /* Rebuild */
          break;
          }
-#ifdef SUPPORT_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
        else if (!fl.pipe_connect_acceptable && !check_sync())
 #else
        else if (!check_sync())
@@ -4339,7 +4374,7 @@ while (done <= 0)
          sync_cmd_limit = NON_SYNC_CMD_PIPELINING;
          f.smtp_in_pipelining_advertised = TRUE;
 
-#ifdef SUPPORT_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
          if (fl.pipe_connect_acceptable)
            {
            f.smtp_in_early_pipe_advertised = TRUE;
@@ -4374,8 +4409,8 @@ while (done <= 0)
            if (au->server)
              {
              DEBUG(D_auth+D_expand) debug_printf_indent(
-               "Evaluating advertise_condition for %s athenticator\n",
-               au->public_name);
+               "Evaluating advertise_condition for %s %s athenticator\n",
+               au->name, au->public_name);
              if (  !au->advertise_condition
                 || expand_check_condition(au->advertise_condition, au->name,
                        US"authenticator")
@@ -4457,7 +4492,7 @@ while (done <= 0)
 #ifndef DISABLE_TLS
       if (tls_in.active.sock >= 0)
        (void)tls_write(NULL, g->s, g->ptr,
-# ifdef SUPPORT_PIPE_CONNECT
+# ifndef DISABLE_PIPE_CONNECT
                        fl.pipe_connect_acceptable && pipeline_connect_sends());
 # else
                        FALSE);
@@ -4827,8 +4862,8 @@ while (done <= 0)
       and EXPN etc. to be used when space is short. */
 
       if (!receive_check_fs(
-          (smtp_check_spool_space && message_size >= 0)?
-             message_size + 5000 : 0))
+          smtp_check_spool_space && message_size >= 0
+             message_size + 5000 : 0))
        {
        smtp_printf("452 Space shortage, please try later\r\n", FALSE);
        sender_address = NULL;
@@ -5235,7 +5270,7 @@ while (done <= 0)
       f.dot_ends = TRUE;
 
     DATA_BDAT:         /* Common code for DATA and BDAT */
-#ifdef SUPPORT_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
       fl.pipe_connect_acceptable = FALSE;
 #endif
       if (!discarded && recipients_count <= 0)
@@ -5725,7 +5760,7 @@ while (done <= 0)
 
       oldsignal = signal(SIGCHLD, SIG_IGN);
 
-      if ((pid = fork()) == 0)
+      if ((pid = exim_fork(US"etrn-command")) == 0)
        {
        smtp_input = FALSE;       /* This process is not associated with the */
        (void)fclose(smtp_in);    /* SMTP call any more. */
@@ -5736,7 +5771,8 @@ while (done <= 0)
        /* If not serializing, do the exec right away. Otherwise, fork down
        into another process. */
 
-       if (!smtp_etrn_serialize || (pid = fork()) == 0)
+       if (  !smtp_etrn_serialize 
+          || (pid = exim_fork(US"etrn-serialised-command")) == 0)
          {
          DEBUG(D_exec) debug_print_argv(argv);
          exim_nullstd();                   /* Ensure std{in,out,err} exist */