-int len = 3;
-smtp_message_code(&code, &len, &user_msg, NULL);
-smtp_respond(code, len, TRUE, user_msg);
-}
-
-
-
-
-/*************************************************
-* Initialize for SMTP incoming message *
-*************************************************/
-
-/* This function conducts the initial dialogue at the start of an incoming SMTP
-message, and builds a list of recipients. However, if the incoming message
-is part of a batch (-bS option) a separate function is called since it would
-be messy having tests splattered about all over this function. This function
-therefore handles the case where interaction is occurring. The input and output
-files are set up in smtp_in and smtp_out.
-
-The global recipients_list is set to point to a vector of recipient_item
-blocks, whose number is given by recipients_count. This is extended by the
-receive_add_recipient() function. The global variable sender_address is set to
-the sender's address. The yield is +1 if a message has been successfully
-started, 0 if a QUIT command was encountered or the connection was refused from
-the particular host, or -1 if the connection was lost.
-
-Argument: none
-
-Returns: > 0 message successfully started (reached DATA)
- = 0 QUIT read or end of file reached or call refused
- < 0 lost connection
-*/
-
-int
-smtp_setup_msg(void)
-{
-int done = 0;
-BOOL toomany = FALSE;
-BOOL discarded = FALSE;
-BOOL last_was_rej_mail = FALSE;
-BOOL last_was_rcpt = FALSE;
-void *reset_point = store_get(0);
-
-DEBUG(D_receive) debug_printf("smtp_setup_msg entered\n");
-
-/* Reset for start of new message. We allow one RSET not to be counted as a
-nonmail command, for those MTAs that insist on sending it between every
-message. Ditto for EHLO/HELO and for STARTTLS, to allow for going in and out of
-TLS between messages (an Exim client may do this if it has messages queued up
-for the host). Note: we do NOT reset AUTH at this point. */
-
-smtp_reset(reset_point);
-message_ended = END_NOTSTARTED;
-
-cmd_list[CMD_LIST_RSET].is_mail_cmd = TRUE;
-cmd_list[CMD_LIST_HELO].is_mail_cmd = TRUE;
-cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE;
-#ifdef SUPPORT_TLS
-cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = TRUE;
-#endif
-
-/* Set the local signal handler for SIGTERM - it tries to end off tidily */
-
-os_non_restarting_signal(SIGTERM, command_sigterm_handler);
-
-/* Batched SMTP is handled in a different function. */
-
-if (smtp_batched_input) return smtp_setup_batch_msg();
-
-/* Deal with SMTP commands. This loop is exited by setting done to a POSITIVE
-value. The values are 2 larger than the required yield of the function. */
-
-while (done <= 0)
- {
- uschar **argv;
- uschar *etrn_command;
- uschar *etrn_serialize_key;
- uschar *errmess;
- uschar *log_msg, *smtp_code;
- uschar *user_msg = NULL;
- uschar *recipient = NULL;
- uschar *hello = NULL;
- uschar *set_id = NULL;
- uschar *s, *ss;
- BOOL was_rej_mail = FALSE;
- BOOL was_rcpt = FALSE;
- void (*oldsignal)(int);
- pid_t pid;
- int start, end, sender_domain, recipient_domain;
- int ptr, size, rc;
- int c, i;
- auth_instance *au;
-
- switch(smtp_read_command(TRUE))
- {
- /* The AUTH command is not permitted to occur inside a transaction, and may
- occur successfully only once per connection. Actually, that isn't quite
- true. When TLS is started, all previous information about a connection must
- be discarded, so a new AUTH is permitted at that time.
-
- AUTH may only be used when it has been advertised. However, it seems that
- there are clients that send AUTH when it hasn't been advertised, some of
- them even doing this after HELO. And there are MTAs that accept this. Sigh.
- So there's a get-out that allows this to happen.
-
- AUTH is initially labelled as a "nonmail command" so that one occurrence
- doesn't get counted. We change the label here so that multiple failing
- AUTHS will eventually hit the nonmail threshold. */
-
- case AUTH_CMD:
- HAD(SCH_AUTH);
- authentication_failed = TRUE;
- cmd_list[CMD_LIST_AUTH].is_mail_cmd = FALSE;
-
- if (!auth_advertised && !allow_auth_unadvertised)
- {
- done = synprot_error(L_smtp_protocol_error, 503, NULL,
- US"AUTH command used when not advertised");
- break;
- }
- if (sender_host_authenticated != NULL)
- {
- done = synprot_error(L_smtp_protocol_error, 503, NULL,
- US"already authenticated");
- break;
- }
- if (sender_address != NULL)
- {
- done = synprot_error(L_smtp_protocol_error, 503, NULL,
- US"not permitted in mail transaction");
- break;
- }
-
- /* Check the ACL */
-
- if (acl_smtp_auth != NULL)
- {
- rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth, &user_msg, &log_msg);
- if (rc != OK)
- {
- done = smtp_handle_acl_fail(ACL_WHERE_AUTH, rc, user_msg, log_msg);
- break;
- }
- }
-
- /* Find the name of the requested authentication mechanism. */
-
- s = smtp_cmd_data;
- while ((c = *smtp_cmd_data) != 0 && !isspace(c))
- {
- if (!isalnum(c) && c != '-' && c != '_')
- {
- done = synprot_error(L_smtp_syntax_error, 501, NULL,
- US"invalid character in authentication mechanism name");
- goto COMMAND_LOOP;
- }
- smtp_cmd_data++;
- }
-
- /* If not at the end of the line, we must be at white space. Terminate the
- name and move the pointer on to any data that may be present. */
-
- if (*smtp_cmd_data != 0)
- {
- *smtp_cmd_data++ = 0;
- while (isspace(*smtp_cmd_data)) smtp_cmd_data++;
- }
-
- /* Search for an authentication mechanism which is configured for use
- as a server and which has been advertised (unless, sigh, allow_auth_
- unadvertised is set). */
-
- for (au = auths; au != NULL; au = au->next)
- {
- if (strcmpic(s, au->public_name) == 0 && au->server &&
- (au->advertised || allow_auth_unadvertised)) break;
- }
-
- if (au == NULL)
- {
- done = synprot_error(L_smtp_protocol_error, 504, NULL,
- string_sprintf("%s authentication mechanism not supported", s));
- break;
- }
-
- /* Run the checking code, passing the remainder of the command line as
- data. Initials the $auth<n> variables as empty. Initialize $0 empty and set
- it as the only set numerical variable. The authenticator may set $auth<n>
- and also set other numeric variables. The $auth<n> variables are preferred
- nowadays; the numerical variables remain for backwards compatibility.
-
- Afterwards, have a go at expanding the set_id string, even if
- authentication failed - for bad passwords it can be useful to log the
- userid. On success, require set_id to expand and exist, and put it in
- authenticated_id. Save this in permanent store, as the working store gets
- reset at HELO, RSET, etc. */
-
- for (i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
- expand_nmax = 0;
- expand_nlength[0] = 0; /* $0 contains nothing */
-
- c = (au->info->servercode)(au, smtp_cmd_data);
- if (au->set_id != NULL) set_id = expand_string(au->set_id);
- expand_nmax = -1; /* Reset numeric variables */
- for (i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL; /* Reset $auth<n> */
-
- /* The value of authenticated_id is stored in the spool file and printed in
- log lines. It must not contain binary zeros or newline characters. In
- normal use, it never will, but when playing around or testing, this error
- can (did) happen. To guard against this, ensure that the id contains only
- printing characters. */
-
- if (set_id != NULL) set_id = string_printing(set_id);
-
- /* For the non-OK cases, set up additional logging data if set_id
- is not empty. */
-
- if (c != OK)
- {
- if (set_id != NULL && *set_id != 0)
- set_id = string_sprintf(" (set_id=%s)", set_id);
- else set_id = US"";
- }
-
- /* Switch on the result */
-
- switch(c)
- {
- case OK:
- if (au->set_id == NULL || set_id != NULL) /* Complete success */
- {
- if (set_id != NULL) authenticated_id = string_copy_malloc(set_id);
- sender_host_authenticated = au->name;
- authentication_failed = FALSE;
- received_protocol =
- protocols[pextend + pauthed + ((tls_active >= 0)? pcrpted:0)] +
- ((sender_host_address != NULL)? pnlocal : 0);
- s = ss = US"235 Authentication succeeded";
- authenticated_by = au;
- break;
- }
-
- /* 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:
- 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;
-
- case CANCELLED:
- s = ss = US"501 Authentication cancelled";
- break;
-
- case UNEXPECTED:
- s = ss = US"553 Initial data not expected";
- break;
-
- case FAIL:
- s = US"535 Incorrect authentication data";
- ss = string_sprintf("535 Incorrect authentication data%s", set_id);
- break;
-
- default:
- s = US"435 Internal error";
- ss = string_sprintf("435 Internal error%s: return %d from authentication "
- "check", set_id, c);
- break;
- }
-
- smtp_printf("%s\r\n", s);
- if (c != OK)
- log_write(0, LOG_MAIN|LOG_REJECT, "%s authenticator failed for %s: %s",
- au->name, host_and_ident(FALSE), ss);
-
- break; /* AUTH_CMD */
-
- /* The HELO/EHLO commands are permitted to appear in the middle of a
- session as well as at the beginning. They have the effect of a reset in
- addition to their other functions. Their absence at the start cannot be
- taken to be an error.
-
- RFC 2821 says:
-
- If the EHLO command is not acceptable to the SMTP server, 501, 500,
- or 502 failure replies MUST be returned as appropriate. The SMTP
- server MUST stay in the same state after transmitting these replies
- that it was in before the EHLO was received.
-
- Therefore, we do not do the reset until after checking the command for
- acceptability. This change was made for Exim release 4.11. Previously
- it did the reset first. */
-
- case HELO_CMD:
- HAD(SCH_HELO);
- hello = US"HELO";
- esmtp = FALSE;
- goto HELO_EHLO;
-
- case EHLO_CMD:
- HAD(SCH_EHLO);
- hello = US"EHLO";
- esmtp = TRUE;
-
- HELO_EHLO: /* Common code for HELO and EHLO */
- cmd_list[CMD_LIST_HELO].is_mail_cmd = FALSE;
- cmd_list[CMD_LIST_EHLO].is_mail_cmd = FALSE;
-
- /* Reject the HELO if its argument was invalid or non-existent. A
- successful check causes the argument to be saved in malloc store. */
-
- if (!check_helo(smtp_cmd_data))
- {
- smtp_printf("501 Syntactically invalid %s argument(s)\r\n", hello);
-
- log_write(0, LOG_MAIN|LOG_REJECT, "rejected %s from %s: syntactically "
- "invalid argument(s): %s", hello, host_and_ident(FALSE),
- (*smtp_cmd_argument == 0)? US"(no argument given)" :
- string_printing(smtp_cmd_argument));
-
- if (++synprot_error_count > smtp_max_synprot_errors)
- {
- log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many "
- "syntax or protocol errors (last command was \"%s\")",
- host_and_ident(FALSE), smtp_cmd_buffer);
- done = 1;
- }
-
- break;
- }
-
- /* If sender_host_unknown is true, we have got here via the -bs interface,
- not called from inetd. Otherwise, we are running an IP connection and the
- host address will be set. If the helo name is the primary name of this
- host and we haven't done a reverse lookup, force one now. If helo_required
- is set, ensure that the HELO name matches the actual host. If helo_verify
- is set, do the same check, but softly. */
-
- if (!sender_host_unknown)
- {
- BOOL old_helo_verified = helo_verified;
- uschar *p = smtp_cmd_data;
-
- while (*p != 0 && !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. */
-
- if (sender_host_name == NULL &&
- (deliver_domain = sender_helo_name, /* set $domain */
- match_isinlist(sender_helo_name, &helo_lookup_domains, 0,
- &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL)) == OK)
- (void)host_name_lookup();
-
- /* Rebuild the fullhost info to include the HELO name (and the real name
- if it was looked up.) */
-
- host_build_sender_fullhost(); /* Rebuild */
- set_process_info("handling%s incoming connection from %s",
- (tls_active >= 0)? " TLS" : "", host_and_ident(FALSE));
-
- /* Verify if configured. This doesn't give much security, but it does
- make some people happy to be able to do it. If helo_required is set,
- (host matches helo_verify_hosts) failure forces rejection. If helo_verify
- is set (host matches helo_try_verify_hosts), it does not. This is perhaps
- now obsolescent, since the verification can now be requested selectively
- at ACL time. */
-
- helo_verified = helo_verify_failed = FALSE;
- if (helo_required || helo_verify)
- {
- BOOL tempfail = !smtp_verify_helo();
- if (!helo_verified)
- {
- if (helo_required)
- {
- smtp_printf("%d %s argument does not match calling host\r\n",
- tempfail? 451 : 550, hello);
- log_write(0, LOG_MAIN|LOG_REJECT, "%srejected \"%s %s\" from %s",
- tempfail? "temporarily " : "",
- hello, sender_helo_name, host_and_ident(FALSE));
- helo_verified = old_helo_verified;
- break; /* End of HELO/EHLO processing */
- }
- HDEBUG(D_all) debug_printf("%s verification failed but host is in "
- "helo_try_verify_hosts\n", hello);
- }
- }
- }
-
-#ifdef EXPERIMENTAL_SPF
- /* set up SPF context */
- spf_init(sender_helo_name, sender_host_address);
-#endif
-
- /* Apply an ACL check if one is defined; afterwards, recheck
- synchronization in case the client started sending in a delay. */
-
- if (acl_smtp_helo != NULL)
- {
- rc = acl_check(ACL_WHERE_HELO, NULL, acl_smtp_helo, &user_msg, &log_msg);
- if (rc != OK)
- {
- done = smtp_handle_acl_fail(ACL_WHERE_HELO, rc, user_msg, log_msg);
- sender_helo_name = NULL;
- host_build_sender_fullhost(); /* Rebuild */
- break;
- }
- else if (!check_sync()) goto SYNC_FAILURE;
- }
-
- /* Generate an OK reply. The default string includes the ident if present,
- and also the IP address if present. Reflecting back the ident is intended
- as a deterrent to mail forgers. For maximum efficiency, and also because
- some broken systems expect each response to be in a single packet, arrange
- that the entire reply is sent in one write(). */
-
- auth_advertised = FALSE;
- pipelining_advertised = FALSE;
- #ifdef SUPPORT_TLS
- tls_advertised = FALSE;
- #endif
-
- smtp_code = US"250 "; /* Default response code plus space*/
- if (user_msg == NULL)
- {
- s = string_sprintf("%.3s %s Hello %s%s%s",
- smtp_code,
- smtp_active_hostname,
- (sender_ident == NULL)? US"" : sender_ident,
- (sender_ident == NULL)? US"" : US" at ",
- (sender_host_name == NULL)? sender_helo_name : sender_host_name);
-
- ptr = Ustrlen(s);
- size = ptr + 1;
-
- if (sender_host_address != NULL)
- {
- s = string_cat(s, &size, &ptr, US" [", 2);
- s = string_cat(s, &size, &ptr, sender_host_address,
- Ustrlen(sender_host_address));
- s = string_cat(s, &size, &ptr, US"]", 1);
- }
- }
-
- /* A user-supplied EHLO greeting may not contain more than one line. Note
- that the code returned by smtp_message_code() includes the terminating
- whitespace character. */
-
- else
- {
- char *ss;
- int codelen = 4;
- smtp_message_code(&smtp_code, &codelen, &user_msg, NULL);
- s = string_sprintf("%.*s%s", codelen, smtp_code, user_msg);
- if ((ss = strpbrk(CS s, "\r\n")) != NULL)
- {
- log_write(0, LOG_MAIN|LOG_PANIC, "EHLO/HELO response must not contain "
- "newlines: message truncated: %s", string_printing(s));
- *ss = 0;
- }
- ptr = Ustrlen(s);
- size = ptr + 1;
- }