/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "llist.h"
#include "array.h"
#include "str.h"
#include "ioloop.h"
#include "istream.h"
#include "ostream.h"
#include "iostream.h"
#include "connection.h"
#include "iostream-rawlog.h"
#include "iostream-ssl.h"
#include "master-service.h"
#include "master-service-ssl.h"
#include "smtp-command-parser.h"
#include "smtp-server-private.h"
const char *const smtp_server_state_names[] = {
"GREETING",
"XCLIENT",
"HELO",
"STARTTLS",
"AUTH",
"READY",
"MAIL FROM",
"RCPT TO",
"DATA"
};
/*
* Logging
*/
const char *format, ...)
{
i_debug("%s-server: conn %s: %s",
}
}
const char *format, ...)
{
i_error("%s-server: conn %s: %s",
}
/*
* Connection
*/
static void
static int
static void
static void
{
}
const struct smtp_server_stats *
{
}
{
}
{
/* only resume when we actually can */
return;
if (conn->command_queue_count >
return;
/* is queued command still blocking input? */
if (cmd->input_locked) {
cmd_locked = TRUE;
break;
}
}
if (cmd_locked)
return;
/* restore input handler */
}
}
}
{
}
{
}
{
}
static void
{
return;
if (!conn->rawlog_checked) {
}
if (conn->rawlog_enabled) {
}
}
static void
{
}
{
}
{
}
static void
{
"4.4.2", "Disconnected for inactivity");
}
{
}
}
{
if (conn->disconnected)
return;
}
}
{
}
static void
{
return;
}
break;
if (cmd->input_captured) {
/* command updates timeout internally */
return;
}
break;
break;
i_unreached();
}
}
static void
{
if (conn->authenticated) {
/* RFC 4954, Section 4:
Should the client successfully complete the exchange, the
SMTP server issues a 235 reply. */
"235 2.7.0 Logged in.");
} else {
}
}
{
(struct smtp_server_connection *)_conn;
}
static bool
const char *cmd_name, const char *cmd_params)
{
if (!smtp_server_connection_unref(&tmp_conn)) {
/* the command start callback managed to get this connection
destroyed */
return FALSE;
}
}
{
const char *error;
/* recreate rawlog after STARTTLS */
}
"Couldn't initialize SSL server for %s: %s",
return -1;
}
"SSL handshake failed: %s",
return -1;
}
else
return 0;
}
static void
{
int ret;
/* check whether we are continuing a command */
}
/* parse commands */
ret = 1;
if (pending_command != NULL) {
/* previous command is now fully read and ready
to reply */
}
"Received new command: %s %s",
/* handle command
cmd may be destroyed after this */
return;
if (conn->disconnected)
return;
/* client indicated it will close after this command;
stop trying to read more. */
break;
if (conn->command_queue_count >=
return;
}
}
}
stream_errno != ECONNRESET) {
"Connection lost: read(%s) failed: %s",
"Read failure");
} else {
"Connection lost: Remote disconnected");
/* no pending commands; close */
"Remote closed connection");
/* unfinished command; close */
"Remote closed connection unexpectedly");
} else {
/* a command is still processing;
only drop input io for now */
}
}
return;
}
if (ret < 0) {
"Client sent invalid command: %s", error);
switch (error_code) {
/* fall through */
500, "5.5.2", "Invalid command syntax");
break;
500, "5.5.2", "Line too long");
break;
/* Command data size exceeds the absolute limit;
i.e. beyond which we don't even want to skip
data anymore. The command error is usually
already submitted by the application and sent
to the client. */
"Command data size exceeds absolute limit");
return;
"Command data ended prematurely");
return;
default:
i_unreached();
}
}
if (conn->disconnected)
return;
return;
}
/* previous command is now fully read and ready to
reply */
}
}
}
{
(struct smtp_server_connection *)_conn;
if (conn->handling_input)
return;
if (smtp_server_connection_ssl_init(conn) < 0) {
"SSL Initialization failed");
return;
}
return;
}
}
if (conn->command_queue_count >
return;
}
}
}
}
struct smtp_server_connection *conn)
{
return FALSE;
}
/*
* Command reply handling
*/
struct smtp_server_connection *conn)
{
"Connection lost: write(%s) failed: %s",
"Write failure");
} else {
"Connection lost: Remote disconnected");
"Remote closed connection unexpectedly");
}
}
static bool
{
unsigned int i;
/* no commands pending */
"No more commands pending");
return FALSE;
}
return FALSE;
}
/* send command replies */
// FIXME: handle LMTP DATA command with enormous number of recipients;
// i.e. don't keep filling output stream with replies indefinitely.
for (i = 0; i < cmd->replies_expected; i++) {
break;
}
if (smtp_server_reply_send(reply) < 0)
return FALSE;
}
return FALSE;
return TRUE;
}
{
}
{
return;
}
}
}
static void
{
/* send more replies until no more replies remain, the output
blocks again, or the connection is closed */
/* accept more commands if possible */
}
{
int ret;
if (ret < 0)
return ret;
}
return 1;
}
static int
{
int ret;
"Sending replies");
}
}
return ret;
}
struct smtp_server_connection *conn)
{
"Trigger output");
}
}
/*
*
*/
};
};
struct connection_list *
{
}
{
static unsigned int id = 0;
/* merge settings with global server settings */
}
if (set->capabilities != 0)
if (set->max_client_idle_time_msecs > 0) {
}
if (set->max_pipelined_commands > 0) {
}
if (set->max_bad_commands > 0) {
}
if (set->max_recipients > 0)
&set->command_limits);
}
if (set->socket_send_buffer_size > 0) {
}
if (set->socket_recv_buffer_size > 0) {
}
}
/* tty connection probably */
} else {
/* unix */
}
} else {
/* ip / ip6 */
}
}
if (set->socket_send_buffer_size > 0) {
set->socket_send_buffer_size) < 0)
}
if (set->socket_recv_buffer_size > 0) {
set->socket_recv_buffer_size) < 0)
}
return conn;
}
static const char *
{
const char *name;
/* get a name for this connection */
switch (conn->socket_family) {
case 0:
break;
case AF_UNIX:
} else {
}
break;
case AF_INET6:
break;
case AF_INET:
break;
default:
i_unreached();
}
return name;
}
struct smtp_server_connection *
{
const char *name;
if (ssl_start)
/* halt input until started */
return conn;
}
struct smtp_server_connection *
const struct smtp_server_settings *set,
{
const char *name;
/* halt input until started */
return conn;
}
{
}
static const char *
struct smtp_server_connection *conn)
{
const char *err;
return t_strdup_printf(
"TLS handshaking failed: %s", err);
}
}
}
static void
const char *reason)
{
if (conn->disconnected)
return;
/* preserve statistics */
/* the callback may close the fd, so remove IO before that */
reason);
}
if (!conn->created_from_streams)
else {
}
}
{
return TRUE;
/* drop transaction */
/* clear command queue */
}
return FALSE;
}
const char *fmt, ...)
{
T_BEGIN {
} T_END;
}
struct smtp_server_connection *conn,
{
T_BEGIN {
} T_END;
/* send immediately */
}
}
bool ssl_secured)
{
if (pdata_len > 0) {
i_panic("Couldn't add client input to stream");
}
}
{
}
{
}
{
}
{
}
const char *reason)
{
return;
}
{
return;
}
struct smtp_server_helo_data *
{
}
enum smtp_server_state
{
}
enum smtp_server_state state)
{
}
}
}
const char *
{
return NULL;
}
{
}
}
/* RFC 3030, Section 2:
The RSET command, when issued after the first BDAT and before the
BDAT LAST, clears all segments sent during that transaction and resets
the session.
*/
/* reset state */
}
{
}
{
}
{
}
{
return conn->ssl_secured;
}
{
return FALSE;
}
enum smtp_protocol
{
}
const char *
{
case SMTP_PROTOCOL_SMTP:
else
break;
case SMTP_PROTOCOL_LMTP:
break;
default:
i_unreached();
}
if (conn->ssl_secured)
if (conn->authenticated)
}
struct smtp_server_transaction *
{
}
const char *
{
return NULL;
}
struct smtp_proxy_data *proxy_data)
{
else
}
const struct smtp_proxy_data *proxy_data)
{
if (proxy_data->source_port != 0)
}
}
if (proxy_data->ttl_plus_1 > 0)
if (conn->proxy_timeout_secs > 0)
}
}
{
}