Implicitly fixes handling of multi-line replies from proxy backend.
Implicitly adds support for mixing local and proxy recipients.
Implicitly adds support for SMTP CHUNKING.
RCPT failures are reported back to the client immediately, rather than waiting for the DATA command.
- quota: maybe check quota once more at commit time to make sure the whole
transaction fits. avoids multiple parallel slow COPY commands from being
able to go over quota
- - lmtp: Calculate incoming mail's hash, forward it via proxying, have the
- final delivery code verify that it's correct
- METADATA: quota, NOTIFY interaction, METADATA-SERVER capability
- fts: if SEARCH X-MAILBOX is used on virtual/all folder, it doesn't update
any indexes. (and it should skip those physical mailboxes that don't
- after reading whole message text, update has_nul-state to cache
- FIFOs maybe should be counted as connections, but unlisten should
unlink+reopen it in master?
- - lmtp client/proxy: Handle multiline replies better
- - lmtp: support DSN extension (especially ORCPT)
- recreate mailbox -> existing sessions log "indexid changed" error
- add message/mime limits
- imapc:
multiple services can coexist independently on the same HTTP server.
- Implement support for `Range:' requests.
- Review compliance with RFC 7230 and RFC 7231
+
+ - lmtp:
+ - Implement parallel pipelined RCPT TO: verification (requires auth API
+ changes).
+ - Improve efficiency and security by splitting lmtp up into a protocol
+ handler and a one-user local delivery service.
+ - Fully support DSN extension (especially ORCPT)
+ - Calculate incoming mail's hash, forward it via proxying, have the
+ final delivery code verify that it's correct
#include "process-title.h"
#include "var-expand.h"
#include "settings-parser.h"
+#include "smtp-server.h"
#include "master-service.h"
#include "master-service-ssl.h"
#include "master-service-settings.h"
#include <unistd.h>
#define CLIENT_IDLE_TIMEOUT_MSECS (1000*60*5)
-#define CLIENT_MAX_INPUT_SIZE 4096
static struct client *clients = NULL;
-unsigned int clients_count = 0;
+static unsigned int clients_count = 0;
static bool verbose_proctitle = FALSE;
+static const struct smtp_server_callbacks lmtp_callbacks;
+
const char *client_remote_id(struct client *client)
{
const char *addr;
return addr;
}
-void client_state_set(struct client *client, const char *name, const char *args)
+static void refresh_proctitle(void)
{
+ struct client *client;
string_t *title;
- client->state.name = name;
-
if (!verbose_proctitle)
return;
str_append(title, "idling");
break;
case 1:
+ client = clients;
str_append(title, client_remote_id(client));
str_append_c(title, ' ');
- str_append(title, client->state.name);
- if (args[0] != '\0')
- str_printfa(title, " %s", args);
+ str_append(title, client_state_get_name(client));
break;
default:
str_printfa(title, "%u connections", clients_count);
process_title_set(str_c(title));
}
-static void client_idle_timeout(struct client *client)
-{
- client_destroy(client,
- t_strdup_printf("421 4.4.2 %s", client->my_domain),
- "Disconnected client for inactivity");
-}
-
static void client_raw_user_create(struct client *client)
{
void **sets;
client->unexpanded_lda_set = lda_set;
}
-unsigned int client_get_rcpt_count(struct client *client)
-{
- return lmtp_local_rcpt_count(client) + lmtp_proxy_rcpt_count(client);
-}
-
-static void client_generate_session_id(struct client *client)
-{
- guid_128_t guid;
- string_t *id = t_str_new(30);
-
- guid_128_generate(guid);
- base64_encode(guid, sizeof(guid), id);
- i_assert(str_c(id)[str_len(id)-2] == '=');
- str_truncate(id, str_len(id)-2); /* drop trailing "==" */
- client->state.session_id = p_strdup(client->state_pool, str_c(id));
-}
-
-struct client *client_create(int fd_in, int fd_out,
+struct client *client_create(int fd_in, int fd_out, bool ssl_start,
const struct master_service_connection *conn)
{
+ struct smtp_server_settings lmtp_set;
struct client *client;
pool_t pool;
- /* always use nonblocking I/O */
- net_set_nonblock(fd_in, TRUE);
- net_set_nonblock(fd_out, TRUE);
-
pool = pool_alloconly_create("lmtp client", 2048);
client = p_new(pool, struct client, 1);
client->pool = pool;
- client->fd_in = fd_in;
- client->fd_out = fd_out;
client->remote_ip = conn->remote_ip;
client->remote_port = conn->remote_port;
client->local_ip = conn->local_ip;
client->local_port = conn->local_port;
- client->input = i_stream_create_fd(fd_in, CLIENT_MAX_INPUT_SIZE);
- client->output = o_stream_create_fd(fd_out, (size_t)-1);
- o_stream_set_no_error_handling(client->output, TRUE);
-
- client_io_reset(client);
client->state_pool = pool_alloconly_create("client state", 4096);
client->state.mail_data_fd = -1;
+
client_read_settings(client);
client_raw_user_create(client);
- client_generate_session_id(client);
client->my_domain = client->unexpanded_lda_set->hostname;
- client->lhlo = i_strdup("missing");
- client->proxy_ttl = LMTP_PROXY_DEFAULT_TTL;
if (client->service_set->verbose_proctitle)
verbose_proctitle = TRUE;
+ i_zero(&lmtp_set);
+ lmtp_set.capabilities =
+ SMTP_CAPABILITY_PIPELINING |
+ SMTP_CAPABILITY_ENHANCEDSTATUSCODES |
+ SMTP_CAPABILITY_8BITMIME |
+ SMTP_CAPABILITY_CHUNKING;
+ if (!ssl_start && master_service_ssl_is_enabled(master_service))
+ lmtp_set.capabilities |= SMTP_CAPABILITY_STARTTLS;
+ lmtp_set.hostname = client->unexpanded_lda_set->hostname;
+ lmtp_set.rcpt_domain_optional = TRUE;
+ lmtp_set.max_client_idle_time_msecs = CLIENT_IDLE_TIMEOUT_MSECS;
+
+ client->conn = smtp_server_connection_create
+ (lmtp_server, fd_in, fd_out,
+ &conn->remote_ip, conn->remote_port,
+ &lmtp_set, &lmtp_callbacks, client);
+
DLLIST_PREPEND(&clients, client);
clients_count++;
- client_state_set(client, "banner", "");
- client_send_line(client, "220 %s %s", client->my_domain,
- client->lmtp_set->login_greeting);
+ smtp_server_connection_start(client->conn, ssl_start);
i_info("Connect from %s", client_remote_id(client));
+ refresh_proctitle();
return client;
}
-void client_state_reset(struct client *client, const char *state_name)
+void client_state_reset(struct client *client)
{
if (client->local != NULL)
lmtp_local_deinit(&client->local);
i_zero(&client->state);
p_clear(client->state_pool);
client->state.mail_data_fd = -1;
-
- client_generate_session_id(client);
- client_state_set(client, state_name, "");
}
-void client_destroy(struct client *client, const char *prefix,
+void client_destroy(struct client *client, const char *enh_code,
const char *reason)
{
- client_disconnect(client, prefix, reason);
- o_stream_uncork(client->output);
+ if (client->destroyed)
+ return;
+ client->destroyed = TRUE;
+
+ client_disconnect(client, enh_code, reason);
clients_count--;
DLLIST_REMOVE(&clients, client);
- client_state_set(client, "destroyed", "");
-
if (client->raw_mail_user != NULL)
mail_user_unref(&client->raw_mail_user);
- if (client->local != NULL)
- lmtp_local_deinit(&client->local);
- if (client->proxy != NULL)
- lmtp_proxy_deinit(&client->proxy);
- io_remove(&client->io);
- timeout_remove(&client->to_idle);
- if (client->ssl_iostream != NULL)
- ssl_iostream_destroy(&client->ssl_iostream);
- i_stream_destroy(&client->input);
- o_stream_destroy(&client->output);
-
- fd_close_maybe_stdio(&client->fd_in, &client->fd_out);
- client_state_reset(client, "destroyed");
- i_free(client->lhlo);
+
+ client_state_reset(client);
pool_unref(&client->state_pool);
pool_unref(&client->pool);
master_service_client_connection_destroyed(master_service);
}
-static const char *client_get_disconnect_reason(struct client *client)
+const char *client_state_get_name(struct client *client)
{
- const char *err;
-
- if (client->ssl_iostream != NULL &&
- !ssl_iostream_is_handshaked(client->ssl_iostream)) {
- err = ssl_iostream_get_last_error(client->ssl_iostream);
- if (err != NULL) {
- return t_strdup_printf("TLS handshaking failed: %s",
- err);
- }
- }
- return io_stream_get_disconnect_reason(client->input, client->output);
+ enum smtp_server_state state;
+
+ if (client->conn == NULL)
+ state = client->last_state;
+ else
+ state = smtp_server_connection_get_state(client->conn);
+ return smtp_server_state_names[state];
}
-void client_disconnect(struct client *client, const char *prefix,
+void client_disconnect(struct client *client, const char *enh_code,
const char *reason)
{
+ struct smtp_server_connection *conn = client->conn;
+
if (client->disconnected)
return;
+ client->disconnected = TRUE;
- if (reason != NULL)
- client_send_line(client, "%s %s", prefix, reason);
- else
- reason = client_get_disconnect_reason(client);
- i_info("Disconnect from %s: %s (in %s)", client_remote_id(client),
- reason, client->state.name);
+ if (reason == NULL)
+ reason = "Connection closed";
+ i_info("Disconnect from %s: %s (state = %s)", client_remote_id(client),
+ reason, client_state_get_name(client));
- client->disconnected = TRUE;
+ if (conn != NULL) {
+ client->last_state = smtp_server_connection_get_state(conn);
+ smtp_server_connection_terminate(&conn,
+ (enh_code == NULL ? "4.0.0" : enh_code), reason);
+ }
+}
+
+static void
+client_connection_trans_free(void *context,
+ struct smtp_server_transaction *trans ATTR_UNUSED)
+{
+ struct client *client = (struct client *)context;
+
+ client_state_reset(client);
+}
+
+static void
+client_connection_state_changed(void *context ATTR_UNUSED,
+ enum smtp_server_state newstate ATTR_UNUSED)
+{
+ if (clients_count == 1)
+ refresh_proctitle();
+}
+
+static void
+client_connection_proxy_data_updated(void *context,
+ const struct smtp_proxy_data *data)
+{
+ struct client *client = (struct client *)context;
+
+ client->remote_ip = data->source_ip;
+ client->remote_port = data->source_port;
+
+ if (clients_count == 1)
+ refresh_proctitle();
+}
+
+static void client_connection_disconnect(void *context, const char *reason)
+{
+ struct client *client = (struct client *)context;
+ struct smtp_server_connection *conn = client->conn;
+
+ if (conn != NULL)
+ client->last_state = smtp_server_connection_get_state(conn);
+ client_disconnect(client, NULL, reason);
}
-bool client_is_trusted(struct client *client)
+static void client_connection_destroy(void *context)
{
+ struct client *client = (struct client *)context;
+
+ client_destroy(client, NULL, NULL);
+}
+
+static bool client_connection_is_trusted(void *context)
+{
+ struct client *client = (struct client *)context;
const char *const *net;
struct ip_addr net_ip;
unsigned int bits;
void clients_destroy(void)
{
while (clients != NULL) {
- client_destroy(clients,
- t_strdup_printf("421 4.3.2 %s", clients->my_domain),
- "Shutting down");
+ client_destroy(clients, "4.3.2", "Shutting down");
}
}
-/*
- * Input handling
- */
+static const struct smtp_server_callbacks lmtp_callbacks = {
+ .conn_cmd_mail = cmd_mail,
+ .conn_cmd_rcpt = cmd_rcpt,
+ .conn_cmd_data_begin = cmd_data_begin,
+ .conn_cmd_data_continue = cmd_data_continue,
-static int client_input_line(struct client *client, const char *line)
-{
- const char *cmd, *args;
-
- args = strchr(line, ' ');
- if (args == NULL) {
- cmd = line;
- args = "";
- } else {
- cmd = t_strdup_until(line, args);
- args++;
- }
- cmd = t_str_ucase(cmd);
-
- if (strcmp(cmd, "LHLO") == 0)
- return cmd_lhlo(client, args);
- if (strcmp(cmd, "STARTTLS") == 0 &&
- master_service_ssl_is_enabled(master_service))
- return cmd_starttls(client);
- if (strcmp(cmd, "MAIL") == 0)
- return cmd_mail(client, args);
- if (strcmp(cmd, "RCPT") == 0)
- return cmd_rcpt(client, args);
- if (strcmp(cmd, "DATA") == 0)
- return cmd_data(client, args);
- if (strcmp(cmd, "QUIT") == 0)
- return cmd_quit(client, args);
- if (strcmp(cmd, "VRFY") == 0)
- return cmd_vrfy(client, args);
- if (strcmp(cmd, "RSET") == 0)
- return cmd_rset(client, args);
- if (strcmp(cmd, "NOOP") == 0)
- return cmd_noop(client, args);
- if (strcmp(cmd, "XCLIENT") == 0)
- return cmd_xclient(client, args);
-
- client_send_line(client, "502 5.5.2 Unknown command");
- return 0;
-}
+ .conn_trans_free = client_connection_trans_free,
-int client_input_read(struct client *client)
-{
- client->last_input = ioloop_time;
- timeout_reset(client->to_idle);
-
- switch (i_stream_read(client->input)) {
- case -2:
- /* buffer full */
- client_destroy(client, "502 5.5.2",
- "Disconnected: Input buffer full");
- return -1;
- case -1:
- /* disconnected */
- client_destroy(client, NULL, NULL);
- return -1;
- case 0:
- /* nothing new read */
- return 0;
- default:
- /* something was read */
- return 0;
- }
-}
+ .conn_state_changed = client_connection_state_changed,
-void client_input_handle(struct client *client)
-{
- struct ostream *output;
- const char *line;
- int ret;
-
- output = client->output;
- o_stream_ref(output);
- while ((line = i_stream_next_line(client->input)) != NULL) {
- T_BEGIN {
- o_stream_cork(output);
- ret = client_input_line(client, line);
- o_stream_uncork(output);
- } T_END;
- if (ret < 0)
- break;
- }
- o_stream_unref(&output);
-}
+ .conn_proxy_data_updated = client_connection_proxy_data_updated,
-static void client_input(struct client *client)
-{
- if (client_input_read(client) < 0)
- return;
- client_input_handle(client);
-}
+ .conn_disconnect = client_connection_disconnect,
+ .conn_destroy = client_connection_destroy,
-void client_io_reset(struct client *client)
-{
- io_remove(&client->io);
- timeout_remove(&client->to_idle);
- client->io = io_add(client->fd_in, IO_READ, client_input, client);
- client->last_input = ioloop_time;
- client->to_idle = timeout_add(CLIENT_IDLE_TIMEOUT_MSECS,
- client_idle_timeout, client);
-}
+ .conn_is_trusted = client_connection_is_trusted
+};
+
+/*
+ * Input handling
+ */
/*
* Output handling
*/
-void client_send_line(struct client *client, const char *fmt, ...)
-{
- va_list args;
-
- va_start(args, fmt);
- T_BEGIN {
- string_t *str;
-
- str = t_str_new(256);
- str_vprintfa(str, fmt, args);
- str_append(str, "\r\n");
- o_stream_nsend(client->output, str_data(str), str_len(str));
- } T_END;
- va_end(args);
-}
#define CLIENT_H
#include "net.h"
-#include "smtp-params.h"
+#include "smtp-server.h"
#define CLIENT_MAIL_DATA_MAX_INMEMORY_SIZE (1024*128)
struct lmtp_recipient {
struct client *client;
- const char *session_id;
- struct smtp_address *address;
- const char *detail; /* +detail part is also in address */
- struct smtp_params_rcpt params;
+ struct smtp_address *path;
+ struct smtp_server_cmd_ctx *rcpt_cmd;
+ struct smtp_server_recipient *rcpt;
+ unsigned int index;
};
struct client_state {
const char *name;
- const char *session_id;
- struct smtp_address *mail_from;
- struct smtp_params_mail mail_params;
+ unsigned int session_id_seq;
- unsigned int data_end_idx;
+ struct timeval data_end_timeval;
/* Initially we start writing to mail_data. If it grows too large,
start using mail_data_fd. */
buffer_t *mail_data;
int mail_data_fd;
struct ostream *mail_data_output;
- const char *added_headers;
- struct timeval mail_from_timeval, data_end_timeval;
+ const char *added_headers_local;
+ const char *added_headers_proxy;
};
struct client {
const struct lda_settings *unexpanded_lda_set;
const struct lmtp_settings *lmtp_set;
const struct master_service_settings *service_set;
- int fd_in, fd_out;
- struct io *io;
- struct istream *input;
- struct ostream *output;
- struct ssl_iostream *ssl_iostream;
- struct timeout *to_idle;
- time_t last_input;
+ struct smtp_server_connection *conn;
+ enum smtp_server_state last_state;
struct ip_addr remote_ip, local_ip;
in_port_t remote_port, local_port;
struct mail_user *raw_mail_user;
const char *my_domain;
- char *lhlo;
pool_t state_pool;
struct client_state state;
struct istream *dot_input;
struct lmtp_local *local;
struct lmtp_proxy *proxy;
- unsigned int proxy_ttl;
- unsigned int proxy_timeout_secs;
bool disconnected:1;
+ bool destroyed:1;
};
-extern unsigned int clients_count;
-
-struct client *client_create(int fd_in, int fd_out,
+struct client *client_create(int fd_in, int fd_out, bool ssl_start,
const struct master_service_connection *conn);
-void client_destroy(struct client *client, const char *prefix,
+void client_destroy(struct client *client, const char *enh_code,
const char *reason) ATTR_NULL(2, 3);
-void client_disconnect(struct client *client, const char *prefix,
- const char *reason);
-unsigned int client_get_rcpt_count(struct client *client);
-void client_state_reset(struct client *client, const char *state_name);
-void client_state_set(struct client *client, const char *name, const char *args);
+void client_disconnect(struct client *client, const char *enh_code,
+ const char *reason) ATTR_NULL(2, 3);
const char *client_remote_id(struct client *client);
-bool client_is_trusted(struct client *client);
+const char *client_state_get_name(struct client *client);
+void client_state_reset(struct client *client);
void clients_destroy(void);
-void client_input_handle(struct client *client);
-int client_input_read(struct client *client);
-void client_io_reset(struct client *client);
-
-void client_send_line(struct client *client, const char *fmt, ...)
- ATTR_FORMAT(2, 3);
-
#endif
/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */
#include "lib.h"
-#include "ioloop.h"
#include "array.h"
#include "str.h"
#include "istream.h"
#include "istream-concat.h"
#include "ostream.h"
-#include "istream-dot.h"
#include "safe-mkstemp.h"
-#include "anvil-client.h"
-#include "master-service.h"
-#include "master-service-ssl.h"
-#include "iostream-ssl.h"
-#include "rfc822-parser.h"
-#include "message-date.h"
-#include "mail-storage-service.h"
#include "index/raw/raw-storage.h"
+#include "master-service.h"
+#include "settings-parser.h"
#include "lda-settings.h"
#include "lmtp-settings.h"
+#include "smtp-address.h"
+#include "smtp-server.h"
+#include "lmtp-proxy.h"
#include "lmtp-local.h"
#include "mail-deliver.h"
-#include "message-address.h"
+#include "mail-error.h"
#include "main.h"
#include "client.h"
#include "commands.h"
-#include "lmtp-proxy.h"
-
-#define ERRSTR_TEMP_MAILBOX_FAIL "451 4.3.0 <%s> Temporary internal error"
/*
* EHLO command
*/
-int cmd_lhlo(struct client *client, const char *args)
-{
- struct rfc822_parser_context parser;
- string_t *domain = t_str_new(128);
- const char *p;
- int ret = 0;
-
- if (*args == '\0') {
- client_send_line(client, "501 Missing hostname");
- return 0;
- }
-
- /* domain / address-literal */
- rfc822_parser_init(&parser, (const unsigned char *)args, strlen(args),
- NULL);
- if (*args != '[')
- ret = rfc822_parse_dot_atom(&parser, domain);
- else {
- for (p = args+1; *p != ']'; p++) {
- if (*p == '\\' || *p == '[')
- break;
- }
- if (strcmp(p, "]") != 0)
- ret = -1;
- }
- if (ret < 0) {
- str_truncate(domain, 0);
- str_append(domain, "invalid");
- }
-
- client_state_reset(client, "LHLO");
- client_send_line(client, "250-%s", client->my_domain);
- if (master_service_ssl_is_enabled(master_service) &&
- client->ssl_iostream == NULL)
- client_send_line(client, "250-STARTTLS");
- if (client_is_trusted(client))
- client_send_line(client, "250-XCLIENT ADDR PORT TTL TIMEOUT");
- client_send_line(client, "250-8BITMIME");
- client_send_line(client, "250-ENHANCEDSTATUSCODES");
- client_send_line(client, "250 PIPELINING");
-
- i_free(client->lhlo);
- client->lhlo = i_strdup(str_c(domain));
- client_state_set(client, "LHLO", "");
- return 0;
-}
-
/*
* STARTTLS command
*/
-int cmd_starttls(struct client *client)
-{
- struct ostream *plain_output = client->output;
- const char *error;
-
- if (client->ssl_iostream != NULL) {
- o_stream_nsend_str(client->output,
- "443 5.5.1 TLS is already active.\r\n");
- return 0;
- }
-
- if (master_service_ssl_init(master_service,
- &client->input, &client->output,
- &client->ssl_iostream, &error) < 0) {
- i_error("TLS initialization failed: %s", error);
- o_stream_nsend_str(client->output,
- "454 4.7.0 Internal error, TLS not available.\r\n");
- return 0;
- }
- o_stream_nsend_str(plain_output,
- "220 2.0.0 Begin TLS negotiation now.\r\n");
- if (ssl_iostream_handshake(client->ssl_iostream) < 0) {
- client_destroy(client, NULL, NULL);
- return -1;
- }
- return 0;
-}
-
/*
* MAIL command
*/
-int cmd_mail(struct client *client, const char *args)
+int cmd_mail(void *conn_ctx,
+ struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
+ struct smtp_server_cmd_mail *data ATTR_UNUSED)
{
- struct smtp_address *address;
- enum smtp_param_parse_error pperror;
- const char *error;
-
- if (client->state.mail_from != NULL) {
- client_send_line(client, "503 5.5.1 MAIL already given");
- return 0;
- }
-
- if (strncasecmp(args, "FROM:", 5) != 0) {
- client_send_line(client, "501 5.5.4 Invalid parameters");
- return 0;
- }
- if (smtp_address_parse_path_full(pool_datastack_create(), args + 5,
- SMTP_ADDRESS_PARSE_FLAG_ALLOW_EMPTY,
- &address, &error, &args) < 0) {
- client_send_line(client, "501 5.5.4 Invalid FROM: %s", error);
- return 0;
- }
- if (*args == ' ')
- args++;
- else if (*args != '\0') {
- client_send_line(client, "501 5.5.4 Invalid FROM: "
- "Invalid character in path");
- return 0;
- }
-
- /* [SP Mail-parameters] */
- if (smtp_params_mail_parse(client->state_pool, args,
- SMTP_CAPABILITY_8BITMIME, FALSE,
- &client->state.mail_params, &pperror, &error) < 0) {
- switch (pperror) {
- case SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX:
- client_send_line(client, "501 5.5.4 %s", error);
- break;
- case SMTP_PARAM_PARSE_ERROR_NOT_SUPPORTED:
- client_send_line(client, "555 5.5.4 %s", error);
- break;
- default:
- i_unreached();
- }
- return 0;
- }
-
- client->state.mail_from =
- smtp_address_clone(client->state_pool, address);
- client_send_line(client, "250 2.1.0 OK");
- client_state_set(client, "MAIL FROM",
- smtp_address_encode(address));
+ struct client *client = (struct client *)conn_ctx;
if (client->lmtp_set->lmtp_user_concurrency_limit > 0) {
/* connect to anvil before dropping privileges */
lmtp_anvil_init();
}
-
- client->state.mail_from_timeval = ioloop_timeval;
- return 0;
+ return 1;
}
/*
* RCPT command
*/
-int cmd_rcpt(struct client *client, const char *args)
+int cmd_rcpt(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_cmd_rcpt *data)
{
- struct smtp_address *address;
+ struct client *client = (struct client *)conn_ctx;
const char *username, *detail;
- struct smtp_params_rcpt params;
- enum smtp_param_parse_error pperror;
- const char *error = NULL;
char delim = '\0';
-
- if (client->state.mail_from == NULL) {
- client_send_line(client, "503 5.5.1 MAIL needed first");
- return 0;
- }
-
- if (strncasecmp(args, "TO:", 3) != 0) {
- client_send_line(client, "501 5.5.4 Invalid parameters");
- return 0;
- }
- if (smtp_address_parse_path_full(pool_datastack_create(), args + 3,
- SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART,
- &address, &error, &args) < 0) {
- client_send_line(client, "501 5.5.4 Invalid TO: %s", error);
- return 0;
- }
- if (*args == ' ')
- args++;
- else if (*args != '\0') {
- client_send_line(client, "501 5.5.4 Invalid TO: "
- "Invalid character in path");
- return 0;
- }
-
- /* [SP Rcpt-parameters] */
- if (smtp_params_rcpt_parse(client->state_pool, args,
- SMTP_CAPABILITY_DSN, FALSE,
- ¶ms, &pperror, &error) < 0) {
- switch (pperror) {
- case SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX:
- client_send_line(client, "501 5.5.4 %s", error);
- break;
- case SMTP_PARAM_PARSE_ERROR_NOT_SUPPORTED:
- client_send_line(client, "555 5.5.4 %s", error);
- break;
- default:
- i_unreached();
- }
- return 0;
- }
+ int ret;
smtp_address_detail_parse_temp(
client->unexpanded_lda_set->recipient_delimiter,
- address, &username, &delim, &detail);
-
- client_state_set(client, "RCPT TO",
- smtp_address_encode(address));
-
+ data->path, &username, &delim, &detail);
if (client->lmtp_set->lmtp_proxy) {
- if (lmtp_proxy_rcpt(client, address, username, detail, delim,
- ¶ms) != 0)
- return 0;
+ /* proxied? */
+ if ((ret=lmtp_proxy_rcpt(client, cmd, data,
+ username, detail, delim)) != 0)
+ return (ret < 0 ? -1 : 0);
+ /* no */
}
- return lmtp_local_rcpt(client, address, username, detail,
- ¶ms);
+ /* local delivery */
+ return lmtp_local_rcpt(client, cmd, data, username, detail);
}
/*
* QUIT command
*/
-int cmd_quit(struct client *client, const char *args ATTR_UNUSED)
-{
- client_send_line(client, "221 2.0.0 OK");
- /* don't log the (state name) for successful QUITs */
- i_info("Disconnect from %s: Successful quit", client_remote_id(client));
- client->disconnected = TRUE;
- client_destroy(client, NULL, NULL);
- return -1;
-}
-
/*
* VRFY command
*/
-int cmd_vrfy(struct client *client, const char *args ATTR_UNUSED)
-{
- client_send_line(client, "252 2.3.3 Try RCPT instead");
- return 0;
-}
-
/*
* RSET command
*/
-int cmd_rset(struct client *client, const char *args ATTR_UNUSED)
-{
- client_state_reset(client, "RSET");
- client_send_line(client, "250 2.0.0 OK");
- return 0;
-}
-
/*
* NOOP command
*/
-int cmd_noop(struct client *client, const char *args ATTR_UNUSED)
-{
- client_send_line(client, "250 2.0.0 OK");
- return 0;
-}
-
/*
* DATA command
*/
static struct istream *cmd_data_get_input(struct client *client)
{
struct client_state *state = &client->state;
- struct istream *cinput, *inputs[3];
-
- inputs[0] = i_stream_create_from_data(state->added_headers,
- strlen(state->added_headers));
+ struct istream *input;
if (state->mail_data_output != NULL) {
o_stream_unref(&state->mail_data_output);
- inputs[1] = i_stream_create_fd(state->mail_data_fd,
- MAIL_READ_FULL_BLOCK_SIZE);
- i_stream_set_init_buffer_size(inputs[1],
+ input = i_stream_create_fd(state->mail_data_fd,
+ MAIL_READ_FULL_BLOCK_SIZE);
+ i_stream_set_init_buffer_size(input,
MAIL_READ_FULL_BLOCK_SIZE);
} else {
- inputs[1] = i_stream_create_from_data(state->mail_data->data,
- state->mail_data->used);
+ input = i_stream_create_from_data(state->mail_data->data,
+ state->mail_data->used);
}
- inputs[2] = NULL;
-
- cinput = i_stream_create_concat(inputs);
- i_stream_set_name(cinput, "<lmtp DATA>");
- i_stream_unref(&inputs[0]);
- i_stream_unref(&inputs[1]);
- return cinput;
+ return input;
}
-static void client_input_data_finish(struct client *client)
+static void
+cmd_data_create_added_headers(struct client *client,
+ struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
+ struct smtp_server_transaction *trans)
{
- client_io_reset(client);
- client_state_reset(client, "DATA finished");
- if (i_stream_have_bytes_left(client->input))
- client_input_handle(client);
-}
+ size_t proxy_offset = 0;
+ string_t *str;
-static void client_proxy_finish(void *context)
-{
- struct client *client = context;
-
- lmtp_proxy_deinit(&client->proxy);
- client_input_data_finish(client);
-}
-
-static const char *client_get_added_headers(struct client *client)
-{
- string_t *str = t_str_new(200);
- const char *host, *rcpt_to = NULL;
+ str = t_str_new(512);
/* headers for local deliveries only */
if (client->local != NULL)
- lmtp_local_add_headers(client->local, str);
-
- str_printfa(str, "Received: from %s", client->lhlo);
- host = net_ip2addr(&client->remote_ip);
- if (host[0] != '\0')
- str_printfa(str, " ([%s])", host);
- str_append(str, "\r\n");
- if (client->ssl_iostream != NULL) {
- str_printfa(str, "\t(using %s)\r\n",
- ssl_iostream_get_security_string(client->ssl_iostream));
- }
- str_printfa(str, "\tby %s with LMTP id %s",
- client->my_domain, client->state.session_id);
-
- str_append(str, "\r\n\t");
- if (rcpt_to != NULL)
- str_printfa(str, "for <%s>", rcpt_to);
- str_printfa(str, "; %s\r\n", message_date_create(ioloop_time));
- return str_c(str);
-}
+ lmtp_local_add_headers(client->local, trans, str);
-static void client_input_data_write(struct client *client)
-{
- struct istream *input;
+ /* headers for local and proxied messages */
+ proxy_offset = str_len(str);
+ smtp_server_transaction_write_trace_record(str, trans);
- /* stop handling client input until saving/proxying is finished */
- timeout_remove(&client->to_idle);
- io_remove(&client->io);
- i_stream_destroy(&client->dot_input);
-
- client->state.data_end_timeval = ioloop_timeval;
-
- input = cmd_data_get_input(client);
- if (lmtp_local_rcpt_count(client) != 0)
- lmtp_local_data(client, input);
- if (client->proxy != NULL) {
- client_state_set(client, "DATA", "proxying");
- lmtp_proxy_start(client->proxy, input,
- client_proxy_finish, client);
- } else {
- client_input_data_finish(client);
- }
- i_stream_unref(&input);
+ client->state.added_headers_local =
+ p_strdup(client->state_pool, str_c(str));
+ client->state.added_headers_proxy =
+ client->state.added_headers_local + proxy_offset;
}
static int
}
}
-static void client_input_data_handle(struct client *client)
+static int
+cmd_data_finish(struct client *client,
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_transaction *trans)
{
- struct istream *data_input = client->dot_input;
+ struct client_state *state = &client->state;
+ struct istream *input_msg, *input_local, *input_proxy;
+ struct istream *inputs[3];
+
+ client->state.data_end_timeval = ioloop_timeval;
+
+ /* finish the message */
+ input_msg = cmd_data_get_input(client);
+
+ /* formulate prepended headers for both local and proxy delivery */
+ cmd_data_create_added_headers(client, cmd, trans);
+
+ /* construct message streams for local and proxy delivery */
+ input_local = input_proxy = NULL;
+ if (client->local != NULL) {
+ inputs[0] = i_stream_create_from_data(
+ state->added_headers_local,
+ strlen(state->added_headers_local));
+ inputs[1] = input_msg;
+ inputs[2] = NULL;
+
+ input_local = i_stream_create_concat(inputs);
+ i_stream_set_name(input_local, "<lmtp DATA local>");
+ i_stream_unref(&inputs[0]);
+ }
+ if (client->proxy != NULL) {
+ inputs[0] = i_stream_create_from_data(
+ state->added_headers_proxy,
+ strlen(state->added_headers_proxy));
+ inputs[1] = input_msg;
+ inputs[2] = NULL;
+
+ input_proxy = i_stream_create_concat(inputs);
+ i_stream_set_name(input_proxy, "<lmtp DATA proxy>");
+ i_stream_unref(&inputs[0]);
+ }
+
+ i_stream_unref(&input_msg);
+
+ /* local delivery */
+ if (client->local != NULL) {
+ lmtp_local_data(client, cmd, trans, input_local);
+ i_stream_unref(&input_local);
+ }
+ /* proxy delivery */
+ if (client->proxy != NULL) {
+ lmtp_proxy_data(client, cmd, trans, input_proxy);
+ i_stream_unref(&input_proxy);
+ }
+ return 0;
+}
+
+int cmd_data_continue(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_transaction *trans)
+{
+ struct client *client = (struct client *)conn_ctx;
+ struct istream *data_input = (struct istream *)trans->context;
const unsigned char *data;
size_t size;
ssize_t ret;
+ i_assert(client->state.mail_data_output != NULL);
+
while ((ret = i_stream_read(data_input)) > 0 || ret == -2) {
data = i_stream_get_data(data_input, &size);
if (cmd_data_input_add(client, data, size) < 0) {
- client_destroy(client, "451 4.3.0",
- "Temporary internal failure");
- return;
+ smtp_server_reply(cmd, 451, "4.3.0",
+ "Temporary internal failure");
+ return -1;
}
+
i_stream_skip(data_input, size);
}
- if (ret == 0)
- return;
- if (data_input->stream_errno != 0) {
+ if (ret == 0)
+ return 0;
+ if (ret < 0 && data_input->stream_errno != 0) {
/* client probably disconnected */
- client_destroy(client, NULL, NULL);
- return;
+ return -1;
}
- /* the ending "." line was seen. begin saving the mail. */
- client_input_data_write(client);
-}
-
-static void client_input_data(struct client *client)
-{
- if (client_input_read(client) < 0)
- return;
-
- client_input_data_handle(client);
+ /* the ending "." line was seen. finish delivery. */
+ return cmd_data_finish(client, cmd, trans);
}
-int cmd_data(struct client *client, const char *args ATTR_UNUSED)
+int cmd_data_begin(void *conn_ctx,
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_transaction *trans,
+ struct istream *data_input)
{
- if (client->state.mail_from == NULL) {
- client_send_line(client, "503 5.5.1 MAIL needed first");
- return 0;
- }
- if ((lmtp_local_rcpt_count(client) +
- lmtp_proxy_rcpt_count(client)) == 0) {
- client_send_line(client, "554 5.5.1 No valid recipients");
- return 0;
- }
-
- client->state.added_headers =
- p_strdup(client->state_pool, client_get_added_headers(client));
+ struct client *client = (struct client *)conn_ctx;
i_assert(client->state.mail_data == NULL);
client->state.mail_data = buffer_create_dynamic(default_pool, 1024*64);
- i_assert(client->dot_input == NULL);
- client->dot_input = i_stream_create_dot(client->input, TRUE);
- client_send_line(client, "354 OK");
- /* send the DATA reply immediately before we start handling any data */
- o_stream_uncork(client->output);
-
- io_remove(&client->io);
- client_state_set(client, "DATA", "");
- client->io = io_add(client->fd_in, IO_READ, client_input_data, client);
- client_input_data_handle(client);
- return -1;
+ cmd->context = (void*)client;
+
+ trans->context = (void*)data_input;
+ return 0;
}
/*
* XCLIENT command
*/
-
-int cmd_xclient(struct client *client, const char *args)
-{
- const char *const *tmp;
- struct ip_addr remote_ip;
- in_port_t remote_port = 0;
- unsigned int ttl = UINT_MAX, timeout_secs = 0;
- bool args_ok = TRUE;
-
- if (!client_is_trusted(client)) {
- client_send_line(client, "550 You are not from trusted IP");
- return 0;
- }
- remote_ip.family = 0;
- for (tmp = t_strsplit(args, " "); *tmp != NULL; tmp++) {
- if (strncasecmp(*tmp, "ADDR=", 5) == 0) {
- const char *addr = *tmp + 5;
- bool ipv6 = FALSE;
-
- if (strncasecmp(addr, "IPV6:", 5) == 0) {
- addr += 5;
- ipv6 = TRUE;
- }
- if (net_addr2ip(addr, &remote_ip) < 0 ||
- (ipv6 && remote_ip.family != AF_INET6))
- args_ok = FALSE;
- } else if (strncasecmp(*tmp, "PORT=", 5) == 0) {
- if (net_str2port(*tmp + 5, &remote_port) < 0)
- args_ok = FALSE;
- } else if (strncasecmp(*tmp, "TTL=", 4) == 0) {
- if (str_to_uint(*tmp + 4, &ttl) < 0)
- args_ok = FALSE;
- } else if (strncasecmp(*tmp, "TIMEOUT=", 8) == 0) {
- if (str_to_uint(*tmp + 8, &timeout_secs) < 0)
- args_ok = FALSE;
- }
- }
- if (!args_ok) {
- client_send_line(client, "501 Invalid parameters");
- return 0;
- }
-
- /* args ok, set them and reset the state */
- client_state_reset(client, "XCLIENT");
- if (remote_ip.family != 0)
- client->remote_ip = remote_ip;
- if (remote_port != 0)
- client->remote_port = remote_port;
- if (ttl != UINT_MAX)
- client->proxy_ttl = ttl;
- client->proxy_timeout_secs = timeout_secs;
- client_send_line(client, "220 %s %s", client->my_domain,
- client->lmtp_set->login_greeting);
- return 0;
-}
#define COMMANDS_H
struct client;
+struct smtp_server_cmd_ctx;
+struct smtp_server_cmd_helo;
-int cmd_lhlo(struct client *client, const char *args);
-int cmd_starttls(struct client *client);
-int cmd_mail(struct client *client, const char *args);
-int cmd_rcpt(struct client *client, const char *args);
-int cmd_quit(struct client *client, const char *args);
-int cmd_vrfy(struct client *client, const char *args);
-int cmd_rset(struct client *client, const char *args);
-int cmd_noop(struct client *client, const char *args);
-int cmd_data(struct client *client, const char *args);
-int cmd_xclient(struct client *client, const char *args);
+int cmd_mail(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_cmd_mail *data);
+int cmd_rcpt(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_cmd_rcpt *data);
+int cmd_data_continue(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_transaction *trans);
+int cmd_data_begin(void *conn_ctx,
+ struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
+ struct smtp_server_transaction *trans, struct istream *data_input);
#endif
#include "lib.h"
#include "str.h"
+#include "istream.h"
#include "strescape.h"
#include "array.h"
#include "time-util.h"
#include "mail-autoexpunge.h"
#include "index/raw/raw-storage.h"
#include "master-service.h"
+#include "smtp-common.h"
+#include "smtp-params.h"
#include "smtp-address.h"
#include "smtp-submit-settings.h"
+#include "smtp-server.h"
#include "lda-settings.h"
#include "lmtp-settings.h"
#include "client.h"
#include "main.h"
+#include "lmtp-settings.h"
#include "lmtp-local.h"
-#define ERRSTR_TEMP_MAILBOX_FAIL "451 4.3.0 <%s> Temporary internal error"
-#define ERRSTR_TEMP_USERDB_FAIL_PREFIX "451 4.3.0 <%s> "
-
struct lmtp_local_recipient {
struct lmtp_recipient rcpt;
char *session_id;
* Recipient
*/
-unsigned int lmtp_local_rcpt_count(struct client *client)
-{
- if (client->local == NULL)
- return 0;
- return array_count(&client->local->rcpt_to);
-}
-
static void
lmtp_local_rcpt_anvil_disconnect(struct lmtp_local_recipient *rcpt)
{
if (!rcpt->anvil_connect_sent)
return;
+ rcpt->anvil_connect_sent = FALSE;
input = mail_storage_service_user_get_input(rcpt->service_user);
master_service_anvil_send(master_service, t_strconcat(
"/", input->username, "\n", NULL));
}
-void lmtp_local_rcpt_deinit(struct lmtp_local_recipient *rcpt)
+static void
+lmtp_local_rcpt_deinit(struct lmtp_local_recipient *rcpt)
{
if (rcpt->anvil_query != NULL)
anvil_client_query_abort(anvil, &rcpt->anvil_query);
lmtp_local_rcpt_reply_overquota(struct lmtp_local_recipient *rcpt,
const char *error)
{
- struct client *client = rcpt->rcpt.client;
+ struct smtp_address *address = rcpt->rcpt.rcpt->path;
struct lda_settings *lda_set =
mail_storage_service_user_get_set(rcpt->service_user)[2];
- client_send_line(client, "%s <%s> %s",
- lda_set->quota_full_tempfail ? "452 4.2.2" : "552 5.2.2",
- smtp_address_encode(rcpt->rcpt.address), error);
+ if (lda_set->quota_full_tempfail) {
+ smtp_server_reply(rcpt->rcpt.rcpt_cmd,
+ 452, "4.2.2", "<%s> %s",
+ smtp_address_encode(address), error);
+ } else {
+ smtp_server_reply(rcpt->rcpt.rcpt_cmd,
+ 552, "5.2.2", "<%s> %s",
+ smtp_address_encode(address), error);
+ }
}
-static void
-lmtp_local_rcpt_fail_all(struct lmtp_local *local)
+static void ATTR_FORMAT(5,6)
+lmtp_local_rcpt_fail_all(struct lmtp_local *local,
+ struct smtp_server_cmd_ctx *cmd,
+ unsigned int status, const char *enh_code,
+ const char *fmt, ...)
{
- struct lmtp_local_recipient *const *rcptp;
+ struct lmtp_local_recipient *const *rcpts;
+ const char *msg;
+ unsigned int count, i;
+ va_list args;
- array_foreach(&local->rcpt_to, rcptp) {
- client_send_line(local->client, ERRSTR_TEMP_MAILBOX_FAIL,
- smtp_address_encode((*rcptp)->rcpt.address));
+ va_start(args, fmt);
+ msg = t_strdup_vprintf(fmt, args);
+ va_end(args);
+
+ rcpts = array_get(&local->rcpt_to, &count);
+ for (i = 0; i < count; i++) {
+ smtp_server_reply_index(cmd, rcpts[i]->rcpt.index,
+ status, enh_code, "<%s> %s",
+ smtp_address_encode(rcpts[i]->rcpt.rcpt->path), msg);
}
}
lmtp_local_rcpt_check_quota(struct lmtp_local_recipient *rcpt)
{
struct client *client = rcpt->rcpt.client;
- struct smtp_address *address = rcpt->rcpt.address;
+ struct smtp_address *address = rcpt->rcpt.path;
struct mail_user *user;
struct mail_namespace *ns;
struct mailbox *box;
"failed: %s",
mailbox_get_vname(box),
mailbox_get_last_internal_error(box, NULL));
+ ret = -1;
}
}
mailbox_free(&box);
}
if (ret < 0) {
- client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
- smtp_address_encode(address));
+ smtp_server_reply(rcpt->rcpt.rcpt_cmd,
+ 451, "4.3.0", "<%s> Temporary internal error",
+ smtp_address_encode(address));
}
return ret;
}
+static void lmtp_local_rcpt_finished(
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_transaction *trans ATTR_UNUSED,
+ struct smtp_server_recipient *trcpt,
+ unsigned int index)
+{
+ struct lmtp_local_recipient *rcpt =
+ (struct lmtp_local_recipient *)cmd->context;
+ struct client *client = rcpt->rcpt.client;
+
+ if (!smtp_server_command_replied_success(cmd->cmd)) {
+ /* failed in RCPT command; clean up early */
+ lmtp_local_rcpt_deinit(rcpt);
+ return;
+ }
+
+ trcpt->context = (void *)rcpt;
+
+ /* add to local recipients */
+ array_append(&client->local->rcpt_to, &rcpt, 1);
+
+ rcpt->rcpt.rcpt = trcpt;
+ rcpt->rcpt.index = index;
+ rcpt->rcpt.rcpt_cmd = NULL;
+}
+
static bool
lmtp_local_rcpt_anvil_finish(struct lmtp_local_recipient *rcpt)
{
- struct client *client = rcpt->rcpt.client;
+ struct smtp_server_cmd_ctx *cmd = rcpt->rcpt.rcpt_cmd;
int ret;
if ((ret = lmtp_local_rcpt_check_quota(rcpt)) < 0) {
- mail_storage_service_user_unref(&rcpt->service_user);
+ lmtp_local_rcpt_deinit(rcpt);
return FALSE;
}
- array_append(&client->local->rcpt_to, &rcpt, 1);
- client_send_line(client, "250 2.1.5 OK");
+
+ smtp_server_reply(cmd, 250, "2.1.5", "OK");
return TRUE;
}
static void
lmtp_local_rcpt_anvil_cb(const char *reply, void *context)
{
- struct lmtp_local_recipient *rcpt = context;
+ struct lmtp_local_recipient *rcpt =
+ (struct lmtp_local_recipient *)context;
+ struct smtp_server_cmd_ctx *cmd = rcpt->rcpt.rcpt_cmd;
struct client *client = rcpt->rcpt.client;
+ struct smtp_address *address = rcpt->rcpt.path;
const struct mail_storage_service_input *input;
unsigned int parallel_count = 0;
}
if (parallel_count >= client->lmtp_set->lmtp_user_concurrency_limit) {
- client_send_line(client, ERRSTR_TEMP_USERDB_FAIL_PREFIX
- "Too many concurrent deliveries for user",
- smtp_address_encode(rcpt->rcpt.address));
- mail_storage_service_user_unref(&rcpt->service_user);
+ smtp_server_reply(cmd, 451, "4.3.0",
+ "<%s> Too many concurrent deliveries for user",
+ smtp_address_encode(address));
} else if (lmtp_local_rcpt_anvil_finish(rcpt)) {
rcpt->anvil_connect_sent = TRUE;
input = mail_storage_service_user_get_input(rcpt->service_user);
"CONNECT\t", my_pid, "\t", master_service_get_name(master_service),
"/", input->username, "\n", NULL));
}
-
- client_io_reset(client);
- client_input_handle(client);
}
int lmtp_local_rcpt(struct client *client,
- struct smtp_address *address,
- const char *username, const char *detail,
- const struct smtp_params_rcpt *params)
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_cmd_rcpt *data,
+ const char *username, const char *detail)
{
+ struct smtp_server_connection *conn = cmd->conn;
+ const struct smtp_address *address = data->path;
+ struct smtp_server_transaction *trans;
struct lmtp_local_recipient *rcpt;
struct mail_storage_service_input input;
struct mail_storage_service_user *service_user;
const char *session_id, *error = NULL;
- int ret;
+ int ret = 0;
+
+ trans = smtp_server_connection_get_transaction(conn);
+ i_assert(trans != NULL); /* MAIL command is synchronous */
/* Use a unique session_id for each mail delivery. This is especially
important for stats process to not see duplicate sessions. */
- if (client_get_rcpt_count(client) == 0)
- session_id = client->state.session_id;
+ client->state.session_id_seq++;
+ if (client->state.session_id_seq == 1)
+ session_id = trans->id;
else {
- session_id =
- t_strdup_printf("%s:%u", client->state.session_id,
- client_get_rcpt_count(client)+1);
+ session_id = t_strdup_printf("%s:%u",
+ trans->id, client->state.session_id_seq);
}
i_zero(&input);
ret = mail_storage_service_lookup(storage_service, &input,
&service_user, &error);
-
if (ret < 0) {
i_error("Failed to lookup user %s: %s", username, error);
- client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
+ smtp_server_reply(cmd, 451, "4.3.0",
+ "<%s> Temporary internal error",
smtp_address_encode(address));
- return 0;
+ return -1;
}
if (ret == 0) {
- client_send_line(client,
- "550 5.1.1 <%s> User doesn't exist: %s",
- smtp_address_encode(address), username);
- return 0;
- }
- if (client->proxy != NULL) {
- /* NOTE: if this restriction is ever removed, we'll also need
- to send different message bodies to local and proxy
- (with and without Return-Path: header) */
- client_send_line(client, "451 4.3.0 <%s> "
- "Can't handle mixed proxy/non-proxy destinations",
- smtp_address_encode(address));
- mail_storage_service_user_unref(&service_user);
- return 0;
+ smtp_server_reply(cmd, 550, "5.1.1",
+ "<%s> User doesn't exist: %s",
+ smtp_address_encode(address), username);
+ return -1;
}
if (client->local == NULL)
rcpt = i_new(struct lmtp_local_recipient, 1);
rcpt->rcpt.client = client;
- rcpt->rcpt.address = smtp_address_clone(client->state_pool, address);
- smtp_params_rcpt_copy(client->state_pool, &rcpt->rcpt.params, params);
+ rcpt->rcpt.path = data->path;
+ rcpt->rcpt.rcpt_cmd = cmd;
rcpt->detail = i_strdup(detail);
rcpt->service_user = service_user;
rcpt->session_id = i_strdup(session_id);
+ cmd->context = (void*)rcpt;
+ data->hook_finished = lmtp_local_rcpt_finished;
+
if (client->lmtp_set->lmtp_user_concurrency_limit == 0) {
(void)lmtp_local_rcpt_anvil_finish(rcpt);
- return 0;
} else {
/* NOTE: username may change as the result of the userdb
lookup. Look up the new one via service_user. */
const char *query = t_strconcat("LOOKUP\t",
master_service_get_name(master_service),
"/", str_tabescape(input->username), NULL);
- io_remove(&client->io);
rcpt->anvil_query = anvil_client_query(anvil, query,
lmtp_local_rcpt_anvil_cb, rcpt);
- /* stop processing further commands while anvil query is
- pending */
- return rcpt->anvil_query == NULL ? 0 : -1;
+ return 0;
}
+
+ return 1;
}
/*
*/
void lmtp_local_add_headers(struct lmtp_local *local,
+ struct smtp_server_transaction *trans,
string_t *headers)
{
- struct client *client = local->client;
struct lmtp_local_recipient *const *rcpts;
const struct lmtp_settings *lmtp_set;
const struct smtp_address *rcpt_to = NULL;
void **sets;
str_printfa(headers, "Return-Path: <%s>\r\n",
- smtp_address_encode(client->state.mail_from));
+ smtp_address_encode(trans->mail_from));
rcpts = array_get(&local->rcpt_to, &count);
if (count == 1) {
case LMTP_HDR_DELIVERY_ADDRESS_NONE:
break;
case LMTP_HDR_DELIVERY_ADDRESS_FINAL:
- rcpt_to = rcpts[0]->rcpt.address;
+ rcpt_to = rcpts[0]->rcpt.rcpt->path;
break;
case LMTP_HDR_DELIVERY_ADDRESS_ORIGINAL:
- rcpt_to = rcpts[0]->rcpt.params.orcpt.addr;
+ rcpt_to = rcpts[0]->rcpt.rcpt->params.orcpt.addr;
break;
}
}
static int
lmtp_local_deliver(struct lmtp_local *local,
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_transaction *trans,
struct lmtp_local_recipient *rcpt,
struct mail *src_mail,
struct mail_deliver_session *session)
{
struct client *client = local->client;
+ struct smtp_address *rcpt_to = rcpt->rcpt.rcpt->path;
+ unsigned int rcpt_idx = rcpt->rcpt.index;
+ const struct smtp_server_recipient *trcpt =
+ *array_idx(&trans->rcpt_to, rcpt_idx);
struct mail_storage_service_user *service_user = rcpt->service_user;
struct mail_deliver_context dctx;
struct mail_user *rcpt_user;
const struct mail_storage_service_input *input;
const struct mail_storage_settings *mail_set;
struct smtp_submit_settings *smtp_set;
+ struct smtp_proxy_data proxy_data;
struct lda_settings *lda_set;
struct mail_namespace *ns;
struct setting_parser_context *set_parser;
mail_set = mail_storage_service_user_get_mail_set(service_user);
set_parser = mail_storage_service_user_get_settings_parser(service_user);
- if (client->proxy_timeout_secs > 0 &&
+
+ smtp_server_connection_get_proxy_data
+ (client->conn, &proxy_data);
+ if (proxy_data.timeout_secs > 0 &&
(mail_set->mail_max_lock_timeout == 0 ||
- mail_set->mail_max_lock_timeout > client->proxy_timeout_secs)) {
+ mail_set->mail_max_lock_timeout > proxy_data.timeout_secs)) {
/* set lock timeout waits to be less than when proxy has
advertised that it's going to timeout the connection.
this avoids duplicate deliveries in case the delivery
succeeds after the proxy has already disconnected from us. */
line = t_strdup_printf("mail_max_lock_timeout=%us",
- client->proxy_timeout_secs <= 1 ? 1 :
- client->proxy_timeout_secs-1);
+ proxy_data.timeout_secs <= 1 ? 1 :
+ proxy_data.timeout_secs-1);
if (settings_parse_line(set_parser, line) < 0)
i_unreached();
}
io_loop_time_refresh();
delivery_time_started = ioloop_timeval;
- client_state_set(client, "DATA", username);
i_set_failure_prefix("lmtp(%s, %s): ", my_pid, username);
if (mail_storage_service_next(storage_service, service_user,
&rcpt_user, &error) < 0) {
i_error("Failed to initialize user: %s", error);
- client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
- smtp_address_encode(rcpt->rcpt.address));
+ smtp_server_reply_index(cmd, rcpt_idx, 451, "4.3.0",
+ "<%s> Temporary internal error",
+ smtp_address_encode(rcpt_to));
return -1;
}
local->rcpt_user = rcpt_user;
}
if (ret <= 0) {
i_error("Failed to expand settings: %s", error);
- client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
- smtp_address_encode(rcpt->rcpt.address));
+ smtp_server_reply_index(cmd, rcpt_idx, 451, "4.3.0",
+ "<%s> Temporary internal error",
+ smtp_address_encode(rcpt_to));
return -1;
}
rcpt_user, &error) <= 0) {
i_error("Failed to expand mail_log_prefix=%s: %s",
rcpt_user->set->mail_log_prefix, error);
- client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
- smtp_address_encode(rcpt->rcpt.address));
+ smtp_server_reply_index(cmd, rcpt_idx, 451, "4.3.0",
+ "<%s> Temporary internal error",
+ smtp_address_encode(rcpt_to));
return -1;
}
i_set_failure_prefix("%s", str_c(str));
dctx.src_mail = src_mail;
/* MAIL FROM */
- dctx.mail_from = client->state.mail_from;
- dctx.mail_params = client->state.mail_params;
+ dctx.mail_from = trans->mail_from;
+ smtp_params_mail_copy(dctx.pool,
+ &dctx.mail_params, &trans->params);
/* RCPT TO */
dctx.rcpt_user = rcpt_user;
- dctx.rcpt_params = rcpt->rcpt.params;
- if (dctx.rcpt_params.orcpt.addr != NULL) {
- /* used ORCPT */
- } else if (*dctx.set->lda_original_recipient_header != '\0') {
- dctx.rcpt_params.orcpt.addr = mail_deliver_get_address(src_mail,
+ smtp_params_rcpt_copy(dctx.pool,
+ &dctx.rcpt_params, &trcpt->params);
+ if (dctx.rcpt_params.orcpt.addr == NULL &&
+ *dctx.set->lda_original_recipient_header != '\0') {
+ dctx.rcpt_params.orcpt.addr =
+ mail_deliver_get_address(src_mail,
dctx.set->lda_original_recipient_header);
}
if (dctx.rcpt_params.orcpt.addr == NULL)
- dctx.rcpt_params.orcpt.addr = rcpt->rcpt.address;
- dctx.rcpt_to = rcpt->rcpt.address;
+ dctx.rcpt_params.orcpt.addr = rcpt_to;
+ dctx.rcpt_to = rcpt_to;
if (*rcpt->detail == '\0' ||
!client->lmtp_set->lmtp_save_to_detail_mailbox)
dctx.rcpt_default_mailbox = "INBOX";
t_strconcat(ns->prefix, rcpt->detail, NULL);
}
- dctx.save_dest_mail = array_count(&local->rcpt_to) > 1 &&
+ dctx.save_dest_mail = array_count(&trans->rcpt_to) > 1 &&
local->first_saved_mail == NULL;
dctx.session_time_msecs =
timeval_diff_msecs(&client->state.data_end_timeval,
- &client->state.mail_from_timeval);
+ &trans->timestamp);
dctx.delivery_time_started = delivery_time_started;
if (mail_deliver(&dctx, &storage) == 0) {
i_assert(local->first_saved_mail == NULL);
local->first_saved_mail = dctx.dest_mail;
}
- client_send_line(client, "250 2.0.0 <%s> %s Saved",
- smtp_address_encode(rcpt->rcpt.address),
- rcpt->session_id);
+ smtp_server_reply_index(cmd, rcpt_idx,
+ 250, "2.0.0", "<%s> %s Saved",
+ smtp_address_encode(rcpt_to), rcpt->session_id);
ret = 0;
} else if (dctx.tempfail_error != NULL) {
- client_send_line(client, "451 4.2.0 <%s> %s",
- smtp_address_encode(rcpt->rcpt.address),
- dctx.tempfail_error);
+ smtp_server_reply_index(cmd, rcpt_idx,
+ 451, "4.2.0", "<%s> %s",
+ smtp_address_encode(rcpt_to),
+ dctx.tempfail_error);
ret = -1;
} else if (storage != NULL) {
error = mail_storage_get_last_error(storage, &mail_error);
if (mail_error == MAIL_ERROR_NOQUOTA) {
lmtp_local_rcpt_reply_overquota(rcpt, error);
} else {
- client_send_line(client, "451 4.2.0 <%s> %s",
- smtp_address_encode(rcpt->rcpt.address), error);
+ smtp_server_reply_index(cmd, rcpt_idx,
+ 451, "4.2.0", "<%s> %s",
+ smtp_address_encode(rcpt_to), error);
}
ret = -1;
} else {
/* This shouldn't happen */
i_error("BUG: Saving failed to unknown storage");
- client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
- smtp_address_encode(rcpt->rcpt.address));
+ smtp_server_reply_index(cmd, rcpt_idx, 451, "4.3.0",
+ "<%s> Temporary internal error",
+ smtp_address_encode(rcpt_to));
ret = -1;
}
+ lmtp_local_rcpt_anvil_disconnect(rcpt);
return ret;
}
static uid_t
lmtp_local_deliver_to_rcpts(struct lmtp_local *local,
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_transaction *trans,
struct mail_deliver_session *session)
{
- struct client *client = local->client;
uid_t first_uid = (uid_t)-1;
struct mail *src_mail;
struct lmtp_local_recipient *const *rcpts;
int ret;
src_mail = local->raw_mail;
-
rcpts = array_get(&local->rcpt_to, &count);
for (i = 0; i < count; i++) {
struct lmtp_local_recipient *rcpt = rcpts[i];
- ret = lmtp_local_deliver(local, rcpt,
- src_mail, session);
- client_state_set(client, "DATA", "");
+ ret = lmtp_local_deliver(local, cmd,
+ trans, rcpt, src_mail, session);
i_set_failure_prefix("lmtp(%s): ", my_pid);
/* succeeded and mail_user is not saved in first_saved_mail */
static int
lmtp_local_open_raw_mail(struct lmtp_local *local,
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_transaction *trans,
struct istream *input)
{
static const char *wanted_headers[] = {
enum mail_error error;
if (raw_mailbox_alloc_stream(client->raw_mail_user, input,
- (time_t)-1, smtp_address_encode(client->state.mail_from),
+ (time_t)-1, smtp_address_encode(trans->mail_from),
&box) < 0) {
i_error("Can't open delivery mail as raw: %s",
mailbox_get_last_internal_error(box, &error));
mailbox_free(&box);
- lmtp_local_rcpt_fail_all(local);
+ lmtp_local_rcpt_fail_all(local, cmd,
+ 451, "4.3.0", "Temporary internal error");
return -1;
}
return 0;
}
-void lmtp_local_data(struct client *client, struct istream *input)
+void lmtp_local_data(struct client *client,
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_transaction *trans,
+ struct istream *input)
{
struct lmtp_local *local = client->local;
struct mail_deliver_session *session;
uid_t old_uid, first_uid;
- if (lmtp_local_open_raw_mail(local, input) < 0)
+ if (lmtp_local_open_raw_mail(local, cmd, trans, input) < 0)
return;
session = mail_deliver_session_init();
old_uid = geteuid();
- first_uid = lmtp_local_deliver_to_rcpts(local, session);
+ first_uid = lmtp_local_deliver_to_rcpts(local, cmd, trans, session);
mail_deliver_session_deinit(&session);
if (local->first_saved_mail != NULL) {
#ifndef LMTP_LOCAL_H
#define LMTP_LOCAL_H
-struct smtp_address;
-struct smtp_params_rcpt;
-struct lmtp_recipient;
+#include "net.h"
+
+struct mail_deliver_session;
+struct smtp_server_cmd_ctx;
+struct smtp_server_cmd_rcpt;
struct lmtp_local;
struct client;
-unsigned int lmtp_local_rcpt_count(struct client *client);
-
void lmtp_local_deinit(struct lmtp_local **_local);
int lmtp_local_rcpt(struct client *client,
- struct smtp_address *address,
- const char *username, const char *detail,
- const struct smtp_params_rcpt *params);
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_cmd_rcpt *data,
+ const char *username, const char *detail);
void lmtp_local_add_headers(struct lmtp_local *local,
+ struct smtp_server_transaction *trans,
string_t *headers);
-void lmtp_local_data(struct client *client, struct istream *input);
+void lmtp_local_data(struct client *client,
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_transaction *trans,
+ struct istream *input);
#endif
#include "ostream.h"
#include "str.h"
#include "time-util.h"
-#include "smtp-reply.h"
+#include "smtp-common.h"
+#include "smtp-params.h"
+#include "smtp-address.h"
+#include "smtp-server.h"
#include "smtp-client.h"
#include "smtp-client-connection.h"
#include "smtp-client-transaction.h"
#include "auth-master.h"
+#include "settings-parser.h"
#include "master-service.h"
#include "mail-storage-service.h"
+#include "lda-settings.h"
#include "client.h"
#include "main.h"
+#include "lmtp-settings.h"
#include "lmtp-proxy.h"
-#define LMTP_MAX_LINE_LEN 1024
+#define LMTP_MAX_REPLY_SIZE 4096
#define LMTP_PROXY_DEFAULT_TIMEOUT_MSECS (1000*125)
-#define ERRSTR_TEMP_USERDB_FAIL_PREFIX "451 4.3.0 <%s> "
-#define ERRSTR_TEMP_USERDB_FAIL \
- ERRSTR_TEMP_USERDB_FAIL_PREFIX "Temporary user lookup failure"
-
struct lmtp_proxy_rcpt_settings {
enum smtp_protocol protocol;
const char *host;
};
struct lmtp_proxy_recipient {
+ struct lmtp_recipient rcpt;
struct lmtp_proxy_connection *conn;
- struct smtp_address *address;
- char *reply;
- unsigned int idx;
bool rcpt_to_failed:1;
bool data_reply_received:1;
unsigned int max_timeout_msecs;
- lmtp_proxy_finish_callback_t *finish_callback;
- void *finish_context;
+ struct smtp_server_cmd_ctx *pending_data_cmd;
bool finished:1;
};
-static void lmtp_proxy_try_finish(struct lmtp_proxy *proxy);
static void
lmtp_proxy_data_cb(const struct smtp_reply *reply,
struct lmtp_proxy_recipient *rcpt);
*/
static struct lmtp_proxy *
-lmtp_proxy_init(struct client *client)
+lmtp_proxy_init(struct client *client,
+ struct smtp_server_transaction *trans)
{
struct smtp_client_settings lmtp_set;
struct lmtp_proxy *proxy;
- i_assert(client->proxy_ttl > 1);
-
proxy = i_new(struct lmtp_proxy, 1);
proxy->client = client;
+ proxy->trans = trans;
i_array_init(&proxy->rcpt_to, 32);
i_array_init(&proxy->connections, 32);
i_zero(&lmtp_set);
lmtp_set.my_hostname = client->my_domain;
lmtp_set.dns_client_socket_path = dns_client_socket_path;
+ lmtp_set.max_reply_size = LMTP_MAX_REPLY_SIZE;
+ smtp_server_connection_get_proxy_data(client->conn,
+ &lmtp_set.proxy_data);
lmtp_set.proxy_data.source_ip = client->remote_ip;
lmtp_set.proxy_data.source_port = client->remote_port;
- lmtp_set.proxy_data.ttl_plus_1 = client->proxy_ttl + 1;
if (lmtp_set.proxy_data.ttl_plus_1 == 0)
lmtp_set.proxy_data.ttl_plus_1 = LMTP_PROXY_DEFAULT_TTL + 1;
else
lmtp_set.proxy_data.ttl_plus_1--;
+ lmtp_set.peer_trusted = TRUE;
proxy->lmtp_client = smtp_client_init(&lmtp_set);
static void
lmtp_proxy_recipient_deinit(struct lmtp_proxy_recipient *rcpt)
{
- i_free(rcpt->address);
- i_free(rcpt->reply);
i_free(rcpt);
}
static void
lmtp_proxy_connection_finish(struct lmtp_proxy_connection *conn)
{
- struct lmtp_proxy *proxy = conn->proxy;
-
conn->finished = TRUE;
conn->lmtp_trans = NULL;
-
- lmtp_proxy_try_finish(proxy);
}
static struct lmtp_proxy_connection *
lmtp_proxy_get_connection(struct lmtp_proxy *proxy,
const struct lmtp_proxy_rcpt_settings *set)
{
- struct client *client = proxy->client;
struct smtp_client_connection *lmtp_conn;
+ struct smtp_server_transaction *trans = proxy->trans;
struct lmtp_proxy_connection *const *conns, *conn;
const char *host = (set->hostip.family == 0 ?
set->host : net_ip2addr(&set->hostip));
smtp_client_connection_connect(lmtp_conn, NULL, NULL);
conn->lmtp_trans = smtp_client_transaction_create(lmtp_conn,
- client->state.mail_from, &client->state.mail_params,
+ trans->mail_from, &trans->params,
lmtp_proxy_connection_finish, conn);
smtp_client_connection_unref(&lmtp_conn);
return conn;
}
-static bool lmtp_proxy_send_data_replies(struct lmtp_proxy *proxy)
+static bool
+lmtp_proxy_handle_reply(struct smtp_server_cmd_ctx *cmd,
+ const struct smtp_reply *reply,
+ struct smtp_reply *reply_r)
{
- struct client *client = proxy->client;
- struct lmtp_proxy_recipient *const *rcpt;
- unsigned int i, count;
-
- o_stream_cork(client->output);
- rcpt = array_get(&proxy->rcpt_to, &count);
- for (i = proxy->next_data_reply_idx; i < count; i++) {
- if (!(rcpt[i]->rcpt_to_failed || rcpt[i]->data_reply_received))
- break;
- o_stream_nsend_str(client->output,
- t_strconcat(rcpt[i]->reply, "\r\n", NULL));
- }
- o_stream_uncork(client->output);
- proxy->next_data_reply_idx = i;
-
- return i == count;
-}
+ *reply_r = *reply;
-static void lmtp_proxy_finish_timeout(struct lmtp_proxy *proxy)
-{
- i_assert(!proxy->finished);
+ if (!smtp_reply_is_remote(reply)) {
+ const char *detail = "";
- timeout_remove(&proxy->to_finish);
- proxy->finished = TRUE;
- proxy->finish_callback(proxy->finish_context);
-}
-
-static void lmtp_proxy_try_finish(struct lmtp_proxy *proxy)
-{
- if (proxy->finish_callback == NULL) {
- /* DATA command hasn't been sent yet */
- return;
- }
- if (!lmtp_proxy_send_data_replies(proxy)) {
- /* we can't received reply from all clients yet */
- return;
- }
- /* do the actual finishing in a timeout handler, since the finish
- callback causes the proxy to be destroyed and the code leading up
- to this function can be called from many different places. it's
- easier this way rather than having all the callers check if the
- proxy was already destroyed. */
- if (proxy->to_finish == NULL) {
- proxy->to_finish = timeout_add(0, lmtp_proxy_finish_timeout,
- proxy);
- }
-}
-
-static void
-lmtp_proxy_write_reply(string_t *reply, const struct smtp_reply *proxy_reply)
-{
- if (smtp_reply_is_remote(proxy_reply)) {
- smtp_reply_write_one_line(reply, proxy_reply);
- } else {
- str_append(reply, "451 4.4.0 Remote server not answering");
- switch (proxy_reply->status) {
+ switch (reply->status) {
+ case SMTP_CLIENT_COMMAND_ERROR_ABORTED:
+ break;
case SMTP_CLIENT_COMMAND_ERROR_HOST_LOOKUP_FAILED:
- str_append(reply, " (DNS lookup)");
+ detail = " (DNS lookup)";
break;
case SMTP_CLIENT_COMMAND_ERROR_CONNECT_FAILED:
case SMTP_CLIENT_COMMAND_ERROR_AUTH_FAILED:
- str_append(reply, " (connect)");
+ detail = " (connect)";
break;
case SMTP_CLIENT_COMMAND_ERROR_CONNECTION_LOST:
- str_append(reply, " (connection lost)");
+ detail = " (connection lost)";
break;
case SMTP_CLIENT_COMMAND_ERROR_BAD_REPLY:
- str_append(reply, " (bad reply)");
- break;
+ detail = " (bad reply)";
+ break;
case SMTP_CLIENT_COMMAND_ERROR_TIMED_OUT:
- str_append(reply, " (timed out)");
+ detail = " (timed out)";
break;
default:
break;
}
+
+ smtp_server_command_fail(cmd->cmd, 451, "4.4.0",
+ "Remote server not answering%s", detail);
+ return FALSE;
}
+
+ if (!smtp_reply_has_enhanced_code(reply)) {
+ reply_r->enhanced_code =
+ SMTP_REPLY_ENH_CODE(reply->status / 100, 0, 0);
+ }
+ return TRUE;
}
/*
* RCPT command
*/
-unsigned int lmtp_proxy_rcpt_count(struct client *client)
-{
- if (client->proxy == NULL)
- return 0;
- return array_count(&client->proxy->rcpt_to);
-}
-
static bool
lmtp_proxy_rcpt_parse_fields(struct lmtp_proxy_rcpt_settings *set,
const char *const *args, const char **address)
return TRUE;
}
+static void
+lmtp_proxy_rcpt_destroy(struct smtp_server_cmd_ctx *cmd)
+{
+ struct lmtp_proxy_recipient *rcpt =
+ (struct lmtp_proxy_recipient *)cmd->context;
+
+ lmtp_proxy_recipient_deinit(rcpt);
+}
+
+static void
+lmtp_proxy_rcpt_finished(struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_transaction *trans ATTR_UNUSED,
+ struct smtp_server_recipient *trcpt,
+ unsigned int index)
+{
+ struct lmtp_proxy_recipient *rcpt =
+ (struct lmtp_proxy_recipient *)cmd->context;
+ struct client *client = rcpt->rcpt.client;
+
+ if (!smtp_server_command_replied_success(cmd->cmd)) {
+ /* failed in RCPT command; clean up early */
+ lmtp_proxy_recipient_deinit(rcpt);
+ return;
+ }
+
+ cmd->hook_destroy = NULL;
+
+ /* copy to transaction */
+ trcpt->context = (void *)rcpt;
+
+ /* add to local recipients */
+ array_append(&client->proxy->rcpt_to, &rcpt, 1);
+
+ rcpt->rcpt.rcpt = trcpt;
+ rcpt->rcpt.index = index;
+ rcpt->rcpt.rcpt_cmd = NULL;
+}
+
static void
lmtp_proxy_rcpt_cb(const struct smtp_reply *proxy_reply,
struct lmtp_proxy_recipient *rcpt)
{
- string_t *reply;
+ struct smtp_server_cmd_ctx *cmd = rcpt->rcpt.rcpt_cmd;
+ struct smtp_reply reply;
+
+ if (!lmtp_proxy_handle_reply(cmd, proxy_reply, &reply))
+ return;
- i_assert(rcpt->reply == NULL);
+ if (smtp_reply_is_success(proxy_reply)) {
+ /* if backend accepts it, we accept it too */
- reply = t_str_new(128);
- lmtp_proxy_write_reply(reply, proxy_reply);
+ /* the default 2.0.0 code won't do */
+ if (!smtp_reply_has_enhanced_code(proxy_reply))
+ reply.enhanced_code = SMTP_REPLY_ENH_CODE(2, 1, 0);
+ }
- rcpt->reply = i_strdup(str_c(reply));
- rcpt->rcpt_to_failed = !smtp_reply_is_success(proxy_reply);
+ /* forward reply */
+ smtp_server_reply_forward(cmd, &reply);
}
int lmtp_proxy_rcpt(struct client *client,
- struct smtp_address *address,
- const char *username, const char *detail, char delim,
- struct smtp_params_rcpt *params)
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_cmd_rcpt *data,
+ const char *username, const char *detail,
+ char delim)
{
struct auth_master_connection *auth_conn;
struct lmtp_proxy_rcpt_settings set;
struct lmtp_proxy_connection *conn;
struct lmtp_proxy_recipient *rcpt;
+ struct smtp_server_transaction *trans;
+ struct smtp_address *address = data->path;
struct auth_user_info info;
struct mail_storage_service_input input;
const char *const *fields, *errstr, *orig_username = username;
+ struct smtp_proxy_data proxy_data;
struct smtp_address *user;
pool_t auth_pool;
int ret;
+ trans = smtp_server_connection_get_transaction(cmd->conn);
+ i_assert(trans != NULL); /* MAIL command is synchronous */
+
i_zero(&input);
input.module = input.service = "lmtp";
mail_storage_service_init_settings(storage_service, &input);
info.local_port = client->local_port;
info.remote_port = client->remote_port;
+ // FIXME: make this async
auth_pool = pool_alloconly_create("auth lookup", 1024);
auth_conn = mail_storage_service_get_auth_conn(storage_service);
ret = auth_master_pass_lookup(auth_conn, username, &info,
auth_pool, &fields);
if (ret <= 0) {
- errstr = ret < 0 && fields[0] != NULL ? t_strdup(fields[0]) :
- t_strdup_printf(ERRSTR_TEMP_USERDB_FAIL,
- smtp_address_encode(address));
+ errstr = ret < 0 && fields[0] != NULL ?
+ t_strdup(fields[0]) : "Temporary user lookup failure";
pool_unref(&auth_pool);
if (ret < 0) {
- client_send_line(client, "%s", errstr);
+ smtp_server_reply(cmd, 451, "4.3.0", "<%s> %s",
+ smtp_address_encode(address), errstr);
return -1;
} else {
- /* user not found from passdb. try userdb also. */
+ /* user not found from passdb. revert to local delivery */
return 0;
}
}
set.port = client->local_port;
set.protocol = SMTP_PROTOCOL_LMTP;
set.timeout_msecs = LMTP_PROXY_DEFAULT_TIMEOUT_MSECS;
- set.params = *params;
if (!lmtp_proxy_rcpt_parse_fields(&set, fields, &username)) {
/* not proxying this user */
username, &user, &errstr) < 0) {
i_error("%s: Username `%s' returned by passdb lookup is not a valid SMTP address",
orig_username, username);
- client_send_line(client, "550 5.3.5 <%s> "
+ smtp_server_reply(cmd, 550, "5.3.5", "<%s> "
"Internal user lookup failure",
smtp_address_encode(address));
pool_unref(&auth_pool);
}
} else if (lmtp_proxy_is_ourself(client, &set)) {
i_error("Proxying to <%s> loops to itself", username);
- client_send_line(client, "554 5.4.6 <%s> "
- "Proxying loops to itself",
- smtp_address_encode(address));
+ smtp_server_reply(cmd, 554, "5.4.6",
+ "<%s> Proxying loops to itself",
+ smtp_address_encode(address));
pool_unref(&auth_pool);
return -1;
}
- if (client->proxy_ttl <= 1) {
+ smtp_server_connection_get_proxy_data(cmd->conn, &proxy_data);
+ if (proxy_data.ttl_plus_1 == 1) {
i_error("Proxying to <%s> appears to be looping (TTL=0)",
username);
- client_send_line(client, "554 5.4.6 <%s> "
- "Proxying appears to be looping (TTL=0)",
- username);
- pool_unref(&auth_pool);
- return -1;
- }
- if (client_get_rcpt_count(client) >
- lmtp_proxy_rcpt_count(client)) {
- client_send_line(client, "451 4.3.0 <%s> "
- "Can't handle mixed proxy/non-proxy destinations",
+ smtp_server_reply(cmd, 554, "5.4.6",
+ "<%s> Proxying appears to be looping (TTL=0)",
smtp_address_encode(address));
pool_unref(&auth_pool);
return -1;
}
if (client->proxy == NULL)
- client->proxy = lmtp_proxy_init(client);
+ client->proxy = lmtp_proxy_init(client, trans);
+
+ data->path = smtp_address_clone(cmd->pool, address);
conn = lmtp_proxy_get_connection(client->proxy, &set);
- if (conn->failed) {
- client_send_line(client,
- "451 4.4.0 Remote server not answering");
- pool_unref(&auth_pool);
- return -1;
- }
+ pool_unref(&auth_pool);
rcpt = i_new(struct lmtp_proxy_recipient, 1);
- rcpt->idx = array_count(&client->proxy->rcpt_to);
+ rcpt->rcpt.client = client;
+ rcpt->rcpt.rcpt_cmd = cmd;
+ rcpt->rcpt.path = data->path;
rcpt->conn = conn;
- rcpt->address = smtp_address_clone(default_pool, address);
- array_append(&client->proxy->rcpt_to, &rcpt, 1);
+
+ cmd->context = (void*)rcpt;
+ cmd->hook_destroy = lmtp_proxy_rcpt_destroy;
+ data->hook_finished = lmtp_proxy_rcpt_finished;
smtp_client_transaction_add_rcpt(conn->lmtp_trans,
- address, &set.params,
+ address, &data->params,
lmtp_proxy_rcpt_cb, lmtp_proxy_data_cb, rcpt);
-
- client_send_line(client, "250 2.1.5 OK");
- pool_unref(&auth_pool);
return 1;
}
struct lmtp_proxy_recipient *rcpt)
{
struct lmtp_proxy_connection *conn = rcpt->conn;
- struct client *client = conn->proxy->client;
+ struct lmtp_proxy *proxy = conn->proxy;
+ struct smtp_server_cmd_ctx *cmd = proxy->pending_data_cmd;
+ struct smtp_server_transaction *trans = proxy->trans;
+ struct smtp_address *address = rcpt->rcpt.rcpt->path;
const struct smtp_client_transaction_times *times =
smtp_client_transaction_get_times(conn->lmtp_trans);
- string_t *reply;
+ unsigned int rcpt_index = rcpt->rcpt.index;
+ struct smtp_reply reply;
string_t *msg;
- i_assert(!rcpt->rcpt_to_failed);
- i_assert(rcpt->reply != NULL);
-
- /* reset timeout in case there are a lot of RCPT TOs */
- if (conn->to != NULL)
- timeout_reset(conn->to);
-
- reply = t_str_new(128);
- lmtp_proxy_write_reply(reply, proxy_reply);
-
- rcpt->reply = i_strdup(str_c(reply));
- rcpt->data_reply_received = TRUE;
-
+ /* compose log message */
msg = t_str_new(128);
- str_printfa(msg, "%s: ", client->state.session_id);
+ str_printfa(msg, "%s: ", trans->id);
if (smtp_reply_is_success(proxy_reply))
str_append(msg, "Sent message to");
else
str_append(msg, "Failed to send message to");
str_printfa(msg, " <%s> at %s:%u: %s (%u/%u at %u ms)",
- smtp_address_encode(rcpt->address), conn->set.host,
- conn->set.port, str_c(reply),
- rcpt->idx + 1, array_count(&conn->proxy->rcpt_to),
+ smtp_address_encode(address),
+ conn->set.host, conn->set.port,
+ smtp_reply_log(proxy_reply),
+ rcpt_index + 1, array_count(&trans->rcpt_to),
timeval_diff_msecs(&ioloop_timeval, ×->started));
- if (smtp_reply_is_success(proxy_reply) ||
- smtp_reply_is_remote(proxy_reply)) {
- /* the problem isn't with the proxy, it's with the remote side.
- so the remote side will log an error, while for us this is
- just an info event */
+
+ /* handle reply */
+ if (smtp_reply_is_success(proxy_reply)) {
+ /* if backend accepts it, we accept it too */
i_info("%s", str_c(msg));
+
+ /* substitute our own success message */
+ smtp_reply_printf(&reply, 250, "<%s> %s Saved",
+ smtp_address_encode(address), trans->id);
+ /* do let the enhanced code through */
+ if (!smtp_reply_has_enhanced_code(proxy_reply))
+ reply.enhanced_code = SMTP_REPLY_ENH_CODE(2, 0, 0);
+ else
+ reply.enhanced_code = proxy_reply->enhanced_code;
+
} else {
- i_error("%s", str_c(msg));
+ if (smtp_reply_is_remote(proxy_reply)) {
+ /* The problem isn't with the proxy, it's with the
+ remote side. so the remote side will log an error,
+ while for us this is just an info event */
+ i_info("%s", str_c(msg));
+ } else {
+ i_error("%s", str_c(msg));
+ }
+
+ if (!lmtp_proxy_handle_reply(cmd, proxy_reply, &reply))
+ return;
}
- lmtp_proxy_try_finish(conn->proxy);
+
+ /* forward reply */
+ smtp_server_reply_index_forward(cmd, rcpt_index, &reply);
}
static void
/* nothing */
}
-void lmtp_proxy_start(struct lmtp_proxy *proxy, struct istream *data_input,
- lmtp_proxy_finish_callback_t *callback, void *context)
+void lmtp_proxy_data(struct client *client,
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_transaction *trans ATTR_UNUSED,
+ struct istream *data_input)
{
+ struct lmtp_proxy *proxy = client->proxy;
struct lmtp_proxy_connection *const *conns;
uoff_t size;
i_assert(data_input->seekable);
i_assert(proxy->data_input == NULL);
- proxy->finish_callback = callback;
- proxy->finish_context = context;
+ proxy->pending_data_cmd = cmd;
proxy->data_input = data_input;
i_stream_ref(proxy->data_input);
if (i_stream_get_size(proxy->data_input, TRUE, &size) < 0) {
array_foreach(&proxy->connections, conns) {
struct lmtp_proxy_connection *conn = *conns;
+ if (conn->finished) {
+ /* this connection had already failed */
+ continue;
+ }
+
smtp_client_transaction_set_timeout(conn->lmtp_trans,
proxy->max_timeout_msecs);
- if (conn->data_input != NULL) {
- smtp_client_transaction_send(conn->lmtp_trans,
- conn->data_input,
- lmtp_proxy_data_dummy_cb, conn);
- }
+ smtp_client_transaction_send(conn->lmtp_trans,
+ conn->data_input,
+ lmtp_proxy_data_dummy_cb, conn);
}
- /* finish if all of the connections have already failed */
- lmtp_proxy_try_finish(proxy);
}
#define LMTP_PROXY_H
#include "net.h"
-#include "smtp-address.h"
+
+#include "smtp-common.h"
#include "smtp-params.h"
#include "smtp-client.h"
#define LMTP_PROXY_DEFAULT_TTL 5
-struct smtp_address;
+struct smtp_server_cmd_ctx;
+struct smtp_server_cmd_rcpt;
struct lmtp_proxy;
struct client;
-typedef void lmtp_proxy_finish_callback_t(void *context);
-
void lmtp_proxy_deinit(struct lmtp_proxy **proxy);
-unsigned int lmtp_proxy_rcpt_count(struct client *client);
-
int lmtp_proxy_rcpt(struct client *client,
- struct smtp_address *address,
- const char *username, const char *detail, char delim,
- struct smtp_params_rcpt *params);
-
-/* Start proxying */
-void lmtp_proxy_start(struct lmtp_proxy *proxy, struct istream *data_input,
- lmtp_proxy_finish_callback_t *callback, void *context)
- ATTR_NULL(3);
-
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_cmd_rcpt *data,
+ const char *username, const char *detail, char delim);
+
+void lmtp_proxy_data(struct client *client,
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_transaction *trans ATTR_UNUSED,
+ struct istream *data_input);
#endif
struct mail_storage_service_ctx *storage_service;
struct anvil_client *anvil;
+struct smtp_server *lmtp_server;
+
void lmtp_anvil_init(void)
{
if (anvil == NULL) {
static void client_connected(struct master_service_connection *conn)
{
master_service_client_connection_accept(conn);
- (void)client_create(conn->fd, conn->fd, conn);
+ (void)client_create(conn->fd, conn->fd, conn->ssl, conn);
}
static void drop_privileges(void)
static void main_init(void)
{
struct master_service_connection conn;
+ struct smtp_server_settings lmtp_set;
+
+ i_zero(&lmtp_set);
+ lmtp_set.protocol = SMTP_PROTOCOL_LMTP;
+ lmtp_set.auth_optional = TRUE;
+ lmtp_set.rcpt_domain_optional = TRUE;
+
+ lmtp_server = smtp_server_init(&lmtp_set);
if (IS_STANDALONE()) {
i_zero(&conn);
- (void)client_create(STDIN_FILENO, STDOUT_FILENO, &conn);
+ (void)client_create(STDIN_FILENO, STDOUT_FILENO, FALSE, &conn);
}
const char *error, *tmp_socket_path;
anvil_client_deinit(&anvil);
i_free(dns_client_socket_path);
i_free(base_dir);
+ smtp_server_deinit(&lmtp_server);
}
int main(int argc, char *argv[])
extern struct mail_storage_service_ctx *storage_service;
extern struct anvil_client *anvil;
+extern struct smtp_server *lmtp_server;
+
void lmtp_anvil_init(void);
void listener_client_destroyed(void);