NetBSD/gnu/dist/postfix/smtp/smtp.c

366 lines
12 KiB
C

/*++
/* NAME
/* smtp 8
/* SUMMARY
/* Postfix remote delivery via SMTP
/* SYNOPSIS
/* \fBsmtp\fR [generic Postfix daemon options]
/* DESCRIPTION
/* The SMTP client processes message delivery requests from
/* the queue manager. Each request specifies a queue file, a sender
/* address, a domain or host to deliver to, and recipient information.
/* This program expects to be run from the \fBmaster\fR(8) process
/* manager.
/*
/* The SMTP client updates the queue file and marks recipients
/* as finished, or it informs the queue manager that delivery should
/* be tried again at a later time. Delivery problem reports are sent
/* to the \fBbounce\fR(8) or \fBdefer\fR(8) daemon as appropriate.
/*
/* The SMTP client looks up a list of mail exchanger addresses for
/* the destination host, sorts the list by preference, and connects
/* to each listed address until it finds a server that responds.
/*
/* Once the SMTP client has received the server greeting banner, no
/* error will cause it to proceed to the next address on the mail
/* exchanger list. Instead, the message is either bounced, or its
/* delivery is deferred until later.
/* SECURITY
/* .ad
/* .fi
/* The SMTP client is moderately security-sensitive. It talks to SMTP
/* servers and to DNS servers on the network. The SMTP client can be
/* run chrooted at fixed low privilege.
/* STANDARDS
/* RFC 821 (SMTP protocol)
/* RFC 1651 (SMTP service extensions)
/* RFC 1870 (Message Size Declaration)
/* RFC 2197 (Pipelining)
/* DIAGNOSTICS
/* Problems and transactions are logged to \fBsyslogd\fR(8).
/* Corrupted message files are marked so that the queue manager can
/* move them to the \fBcorrupt\fR queue for further inspection.
/*
/* Depending on the setting of the \fBnotify_classes\fR parameter,
/* the postmaster is notified of bounces, protocol problems, and of
/* other trouble.
/* BUGS
/* CONFIGURATION PARAMETERS
/* .ad
/* .fi
/* The following \fBmain.cf\fR parameters are especially relevant to
/* this program. See the Postfix \fBmain.cf\fR file for syntax details
/* and for default values. Use the \fBpostfix reload\fR command after
/* a configuration change.
/* .SH Miscellaneous
/* .ad
/* .fi
/* .IP \fBbest_mx_transport\fR
/* Name of the delivery transport to use when the local machine
/* is the most-preferred mail exchanger (by default, a mailer
/* loop is reported, and the message is bounced).
/* .IP \fBdebug_peer_level\fR
/* Verbose logging level increment for hosts that match a
/* pattern in the \fBdebug_peer_list\fR parameter.
/* .IP \fBdebug_peer_list\fR
/* List of domain or network patterns. When a remote host matches
/* a pattern, increase the verbose logging level by the amount
/* specified in the \fBdebug_peer_level\fR parameter.
/* .IP \fBdisable_dns_lookups\fR
/* Disable DNS lookups. This means that mail must be forwarded
/* via a smart relay host.
/* .IP \fBerror_notice_recipient\fR
/* Recipient of protocol/policy/resource/software error notices.
/* .IP \fBfallback_relay\fR
/* Hosts to hand off mail to if a message destination is not found
/* or if a destination is unreachable.
/* .IP \fBignore_mx_lookup_error\fR
/* When a name server fails to respond to an MX query, search for an
/* A record instead of assuming that the name server will recover.
/* .IP \fBinet_interfaces\fR
/* The network interface addresses that this mail system receives
/* mail on. When any of those addresses appears in the list of mail
/* exchangers for a remote destination, the list is truncated to
/* avoid mail delivery loops.
/* .IP \fBnotify_classes\fR
/* When this parameter includes the \fBprotocol\fR class, send mail to the
/* postmaster with transcripts of SMTP sessions with protocol errors.
/* .IP \fBsmtp_skip_4xx_greeting\fR
/* Skip servers that greet us with a 4xx status code.
/* .IP \fBsmtp_skip_quit_response\fR
/* Do not wait for the server response after sending QUIT.
/* .SH "Resource controls"
/* .ad
/* .fi
/* .IP \fBsmtp_destination_concurrency_limit\fR
/* Limit the number of parallel deliveries to the same destination.
/* The default limit is taken from the
/* \fBdefault_destination_concurrency_limit\fR parameter.
/* .IP \fBsmtp_destination_recipient_limit\fR
/* Limit the number of recipients per message delivery.
/* The default limit is taken from the
/* \fBdefault_destination_recipient_limit\fR parameter.
/* .SH "Timeout controls"
/* .ad
/* .fi
/* .IP \fBsmtp_connect_timeout\fR
/* Timeout in seconds for completing a TCP connection. When no
/* connection can be made within the deadline, the SMTP client
/* tries the next address on the mail exchanger list.
/* .IP \fBsmtp_helo_timeout\fR
/* Timeout in seconds for receiving the SMTP greeting banner.
/* When the server drops the connection without sending a
/* greeting banner, or when it sends no greeting banner within the
/* deadline, the SMTP client tries the next address on the mail
/* exchanger list.
/* .IP \fBsmtp_helo_timeout\fR
/* Timeout in seconds for sending the \fBHELO\fR command, and for
/* receiving the server response.
/* .IP \fBsmtp_mail_timeout\fR
/* Timeout in seconds for sending the \fBMAIL FROM\fR command, and for
/* receiving the server response.
/* .IP \fBsmtp_rcpt_timeout\fR
/* Timeout in seconds for sending the \fBRCPT TO\fR command, and for
/* receiving the server response.
/* .IP \fBsmtp_data_init_timeout\fR
/* Timeout in seconds for sending the \fBDATA\fR command, and for
/* receiving the server response.
/* .IP \fBsmtp_data_xfer_timeout\fR
/* Timeout in seconds for sending the message content.
/* .IP \fBsmtp_data_done_timeout\fR
/* Timeout in seconds for sending the "\fB.\fR" command, and for
/* receiving the server response. When no response is received, a
/* warning is logged that the mail may be delivered multiple times.
/* .IP \fBsmtp_quit_timeout\fR
/* Timeout in seconds for sending the \fBQUIT\fR command, and for
/* receiving the server response.
/* SEE ALSO
/* bounce(8) non-delivery status reports
/* master(8) process manager
/* qmgr(8) queue manager
/* syslogd(8) system logging
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/
/* System library. */
#include <sys_defs.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <dict.h>
/* Utility library. */
#include <msg.h>
#include <mymalloc.h>
#include <name_mask.h>
/* Global library. */
#include <deliver_request.h>
#include <mail_params.h>
#include <mail_conf.h>
#include <debug_peer.h>
#include <mail_error.h>
#include <deliver_pass.h>
/* Single server skeleton. */
#include <mail_server.h>
/* Application-specific. */
#include "smtp.h"
/*
* Tunable parameters. These have compiled-in defaults that can be overruled
* by settings in the global Postfix configuration file.
*/
int var_smtp_conn_tmout;
int var_smtp_helo_tmout;
int var_smtp_mail_tmout;
int var_smtp_rcpt_tmout;
int var_smtp_data0_tmout;
int var_smtp_data1_tmout;
int var_smtp_data2_tmout;
int var_smtp_quit_tmout;
char *var_inet_interfaces;
char *var_debug_peer_list;
int var_debug_peer_level;
char *var_notify_classes;
int var_smtp_skip_4xx_greeting;
int var_ign_mx_lookup_err;
int var_skip_quit_resp;
char *var_fallback_relay;
char *var_bestmx_transp;
char *var_error_rcpt;
/*
* Global variables. smtp_errno is set by the address lookup routines and by
* the connection management routines.
*/
int smtp_errno;
/* deliver_message - deliver message with extreme prejudice */
static int deliver_message(DELIVER_REQUEST *request)
{
VSTRING *why;
SMTP_STATE *state;
int result;
if (msg_verbose)
msg_info("deliver_message: from %s", request->sender);
/*
* Sanity checks. The smtp server is unprivileged and chrooted, so we can
* afford to distribute the data censoring code, instead of having it all
* in one place.
*/
if (request->nexthop[0] == 0)
msg_fatal("empty nexthop hostname");
if (request->rcpt_list.len <= 0)
msg_fatal("recipient count: %d", request->rcpt_list.len);
/*
* Initialize. Bundle all information about the delivery request, so that
* we can produce understandable diagnostics when something goes wrong
* many levels below. The alternative would be to make everything global.
*/
why = vstring_alloc(100);
state = smtp_state_alloc();
state->request = request;
state->src = request->fp;
/*
* Establish an SMTP session and deliver this message to all requested
* recipients. At the end, notify the postmaster of any protocol errors.
* Optionally deliver mail locally when this machine is the best mail
* exchanger.
*/
if ((state->session = smtp_connect(request->nexthop, why)) == 0) {
if (smtp_errno == SMTP_OK) {
if (*var_bestmx_transp == 0)
msg_panic("smtp_errno botch");
state->status = deliver_pass_all(MAIL_CLASS_PRIVATE,
var_bestmx_transp,
request);
} else
smtp_site_fail(state, smtp_errno == SMTP_RETRY ? 450 : 550,
"%s", vstring_str(why));
} else {
debug_peer_check(state->session->host, state->session->addr);
if (smtp_helo(state) == 0)
smtp_xfer(state);
if (state->history != 0
&& (state->error_mask & name_mask(mail_error_masks, var_notify_classes)))
smtp_chat_notify(state);
smtp_session_free(state->session);
debug_peer_restore();
}
/*
* Clean up.
*/
vstring_free(why);
smtp_chat_reset(state);
result = state->status;
smtp_state_free(state);
return (result);
}
/* smtp_service - perform service for client */
static void smtp_service(VSTREAM *client_stream, char *unused_service, char **argv)
{
DELIVER_REQUEST *request;
int status;
/*
* Sanity check. This service takes no command-line arguments.
*/
if (argv[0])
msg_fatal("unexpected command-line argument: %s", argv[0]);
/*
* This routine runs whenever a client connects to the UNIX-domain socket
* dedicated to remote SMTP delivery service. What we see below is a
* little protocol to (1) tell the queue manager that we are ready, (2)
* read a request from the queue manager, and (3) report the completion
* status of that request. All connection-management stuff is handled by
* the common code in single_server.c.
*/
if ((request = deliver_request_read(client_stream)) != 0) {
status = deliver_message(request);
deliver_request_done(client_stream, request, status);
}
}
/* pre_init - pre-jail initialization */
static void pre_init(char *unused_name, char **unused_argv)
{
debug_peer_init();
}
/* pre_accept - see if tables have changed */
static void pre_accept(char *unused_name, char **unused_argv)
{
if (dict_changed()) {
msg_info("table has changed -- exiting");
exit(0);
}
}
/* main - pass control to the single-threaded skeleton */
int main(int argc, char **argv)
{
static CONFIG_STR_TABLE str_table[] = {
VAR_DEBUG_PEER_LIST, DEF_DEBUG_PEER_LIST, &var_debug_peer_list, 0, 0,
VAR_NOTIFY_CLASSES, DEF_NOTIFY_CLASSES, &var_notify_classes, 0, 0,
VAR_FALLBACK_RELAY, DEF_FALLBACK_RELAY, &var_fallback_relay, 0, 0,
VAR_BESTMX_TRANSP, DEF_BESTMX_TRANSP, &var_bestmx_transp, 0, 0,
VAR_ERROR_RCPT, DEF_ERROR_RCPT, &var_error_rcpt, 1, 0,
0,
};
static CONFIG_INT_TABLE int_table[] = {
VAR_SMTP_CONN_TMOUT, DEF_SMTP_CONN_TMOUT, &var_smtp_conn_tmout, 0, 0,
VAR_SMTP_HELO_TMOUT, DEF_SMTP_HELO_TMOUT, &var_smtp_helo_tmout, 1, 0,
VAR_SMTP_MAIL_TMOUT, DEF_SMTP_MAIL_TMOUT, &var_smtp_mail_tmout, 1, 0,
VAR_SMTP_RCPT_TMOUT, DEF_SMTP_RCPT_TMOUT, &var_smtp_rcpt_tmout, 1, 0,
VAR_SMTP_DATA0_TMOUT, DEF_SMTP_DATA0_TMOUT, &var_smtp_data0_tmout, 1, 0,
VAR_SMTP_DATA1_TMOUT, DEF_SMTP_DATA1_TMOUT, &var_smtp_data1_tmout, 1, 0,
VAR_SMTP_DATA2_TMOUT, DEF_SMTP_DATA2_TMOUT, &var_smtp_data2_tmout, 1, 0,
VAR_SMTP_QUIT_TMOUT, DEF_SMTP_QUIT_TMOUT, &var_smtp_quit_tmout, 1, 0,
VAR_DEBUG_PEER_LEVEL, DEF_DEBUG_PEER_LEVEL, &var_debug_peer_level, 1, 0,
0,
};
static CONFIG_BOOL_TABLE bool_table[] = {
VAR_SMTP_SKIP_4XX, DEF_SMTP_SKIP_4XX, &var_smtp_skip_4xx_greeting,
VAR_IGN_MX_LOOKUP_ERR, DEF_IGN_MX_LOOKUP_ERR, &var_ign_mx_lookup_err,
VAR_SKIP_QUIT_RESP, DEF_SKIP_QUIT_RESP, &var_skip_quit_resp,
0,
};
single_server_main(argc, argv, smtp_service,
MAIL_SERVER_INT_TABLE, int_table,
MAIL_SERVER_STR_TABLE, str_table,
MAIL_SERVER_BOOL_TABLE, bool_table,
MAIL_SERVER_PRE_INIT, pre_init,
MAIL_SERVER_PRE_ACCEPT, pre_accept,
0);
}