diff options
Diffstat (limited to 'src/lib-smtp/smtp-server-cmd-xclient.c')
-rw-r--r-- | src/lib-smtp/smtp-server-cmd-xclient.c | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/src/lib-smtp/smtp-server-cmd-xclient.c b/src/lib-smtp/smtp-server-cmd-xclient.c new file mode 100644 index 0000000..d926b45 --- /dev/null +++ b/src/lib-smtp/smtp-server-cmd-xclient.c @@ -0,0 +1,234 @@ +/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "istream.h" +#include "smtp-syntax.h" +#include "smtp-reply.h" + +#include "smtp-server-private.h" + +/* XCLIENT command (http://www.postfix.org/XCLIENT_README.html) */ + +static bool +cmd_xclient_check_state(struct smtp_server_cmd_ctx *cmd) +{ + struct smtp_server_connection *conn = cmd->conn; + + /* http://www.postfix.org/XCLIENT_README.html: + + The XCLIENT command may be sent at any time, except in the middle + of a mail delivery transaction (i.e. between MAIL and DOT, or MAIL + and RSET). */ + if (conn->state.trans != NULL) { + smtp_server_reply(cmd, 503, "5.5.0", + "XCLIENT not permitted during a mail transaction"); + return FALSE; + } + return TRUE; +} + +static void +cmd_xclient_completed(struct smtp_server_cmd_ctx *cmd, + struct smtp_proxy_data *proxy_data) +{ + struct smtp_server_connection *conn = cmd->conn; + struct smtp_server_command *command = cmd->cmd; + + i_assert(smtp_server_command_is_replied(command)); + if (!smtp_server_command_replied_success(command)) { + /* failure */ + return; + } + + /* success */ + smtp_server_connection_reset_state(conn); + smtp_server_connection_set_proxy_data(conn, proxy_data); +} + +static void +cmd_xclient_recheck(struct smtp_server_cmd_ctx *cmd, + struct smtp_proxy_data *proxy_data ATTR_UNUSED) +{ + struct smtp_server_connection *conn = cmd->conn; + + /* all preceeding commands have finished and now the transaction state is + clear. This provides the opportunity to re-check the protocol state */ + if (!cmd_xclient_check_state(cmd)) + return; + smtp_server_connection_set_state(conn, SMTP_SERVER_STATE_XCLIENT, NULL); + + /* succes; send greeting */ + smtp_server_reply(cmd, 220, NULL, "%s %s", + conn->set.hostname, conn->set.login_greeting); + return; +} + +static void +smtp_server_cmd_xclient_extra_field(struct smtp_server_connection *conn, + pool_t pool, const struct smtp_param *param, + ARRAY_TYPE(smtp_proxy_data_field) *fields) +{ + struct smtp_proxy_data_field *field; + + if (conn->set.xclient_extensions == NULL || + !str_array_icase_find(conn->set.xclient_extensions, param->keyword)) + return; + + if (!array_is_created(fields)) + p_array_init(fields, pool, 8); + field = array_append_space(fields); + field->name = p_strdup(pool, param->keyword); + field->value = p_strdup(pool, param->value); +} + +void smtp_server_cmd_xclient(struct smtp_server_cmd_ctx *cmd, + const char *params) +{ + struct smtp_server_connection *conn = cmd->conn; + struct smtp_server_command *command = cmd->cmd; + const struct smtp_server_callbacks *callbacks = conn->callbacks; + struct smtp_proxy_data *proxy_data; + ARRAY_TYPE(smtp_proxy_data_field) extra_fields = ARRAY_INIT; + const char *const *argv; + + /* xclient-command = XCLIENT 1*( SP attribute-name"="attribute-value ) + attribute-name = ( NAME | ADDR | PORT | PROTO | HELO | LOGIN ) + attribute-value = xtext + */ + + if ((conn->set.capabilities & SMTP_CAPABILITY_XCLIENT) == 0) { + smtp_server_reply(cmd, + 502, "5.5.1", "Unsupported command"); + return; + } + + /* check transaction state as far as possible */ + if (!cmd_xclient_check_state(cmd)) + return; + + /* check whether client is trusted */ + if (!smtp_server_connection_is_trusted(conn)) { + smtp_server_reply(cmd, 550, "5.7.14", + "You are not from trusted IP"); + return; + } + + proxy_data = p_new(cmd->pool, struct smtp_proxy_data, 1); + + argv = t_strsplit(params, " "); + for (; *argv != NULL; argv++) { + struct smtp_param param; + const char *error; + + if (smtp_param_parse(pool_datastack_create(), *argv, + ¶m, &error) < 0) { + smtp_server_reply(cmd, 501, "5.5.4", + "Invalid parameter: %s", error); + return; + } + + param.keyword = t_str_ucase(param.keyword); + + if (smtp_xtext_parse(param.value, ¶m.value, &error) < 0) { + smtp_server_reply(cmd, 501, "5.5.4", + "Invalid %s parameter: %s", + param.keyword, error); + return; + } + + if (strcmp(param.keyword, "ADDR") == 0) { + bool ipv6 = FALSE; + if (strcasecmp(param.value, "[UNAVAILABLE]") == 0) + continue; + if (strncasecmp(param.value, "IPV6:", 5) == 0) { + ipv6 = TRUE; + param.value += 5; + } + if (net_addr2ip(param.value, &proxy_data->source_ip) < 0 || + (ipv6 && proxy_data->source_ip.family != AF_INET6)) { + smtp_server_reply(cmd, 501, "5.5.4", + "Invalid ADDR parameter"); + return; + } + } else if (strcmp(param.keyword, "HELO") == 0) { + if (strcasecmp(param.value, "[UNAVAILABLE]") == 0) + continue; + if (smtp_helo_domain_parse + (param.value, TRUE, &proxy_data->helo) >= 0) + proxy_data->helo = + p_strdup(cmd->pool, proxy_data->helo); + } else if (strcmp(param.keyword, "LOGIN") == 0) { + if (strcasecmp(param.value, "[UNAVAILABLE]") == 0) + continue; + proxy_data->login = p_strdup(cmd->pool, param.value); + } else if (strcmp(param.keyword, "PORT") == 0) { + if (strcasecmp(param.value, "[UNAVAILABLE]") == 0) + continue; + if (net_str2port(param.value, &proxy_data->source_port) < 0) { + smtp_server_reply(cmd, 501, "5.5.4", + "Invalid PORT parameter"); + return; + } + } else if (strcmp(param.keyword, "PROTO") == 0) { + param.value = t_str_ucase(param.value); + if (strcmp(param.value, "SMTP") == 0) + proxy_data->proto = SMTP_PROXY_PROTOCOL_SMTP; + else if (strcmp(param.value, "ESMTP") == 0) + proxy_data->proto = SMTP_PROXY_PROTOCOL_ESMTP; + else if (strcmp(param.value, "LMTP") == 0) + proxy_data->proto = SMTP_PROXY_PROTOCOL_LMTP; + else { + smtp_server_reply(cmd, 501, "5.5.4", + "Invalid PROTO parameter"); + return; + } + } else if (strcmp(param.keyword, "SESSION") == 0) { + if (strcasecmp(param.value, "[UNAVAILABLE]") == 0) + continue; + proxy_data->session = p_strdup(cmd->pool, param.value); + } else if (strcmp(param.keyword, "TIMEOUT") == 0) { + if (str_to_uint(param.value, + &proxy_data->timeout_secs) < 0) { + smtp_server_reply(cmd, 501, "5.5.4", + "Invalid TIMEOUT parameter"); + return; + } + } else if (strcmp(param.keyword, "TTL") == 0) { + if (str_to_uint(param.value, + &proxy_data->ttl_plus_1) < 0) { + smtp_server_reply(cmd, 501, "5.5.4", + "Invalid TTL parameter"); + return; + } + proxy_data->ttl_plus_1++; + } else { + smtp_server_cmd_xclient_extra_field(conn, + cmd->pool, ¶m, &extra_fields); + } + } + + if (array_is_created(&extra_fields)) { + proxy_data->extra_fields = array_get(&extra_fields, + &proxy_data->extra_fields_count); + } + + smtp_server_command_input_lock(cmd); + + smtp_server_command_add_hook(command, SMTP_SERVER_COMMAND_HOOK_NEXT, + cmd_xclient_recheck, proxy_data); + smtp_server_command_add_hook(command, SMTP_SERVER_COMMAND_HOOK_COMPLETED, + cmd_xclient_completed, proxy_data); + + if (conn->state.state == SMTP_SERVER_STATE_GREETING) { + smtp_server_connection_set_state( + conn, SMTP_SERVER_STATE_XCLIENT, NULL); + } + + smtp_server_command_ref(command); + if (callbacks != NULL && callbacks->conn_cmd_xclient != NULL) { + /* specific implementation of XCLIENT command */ + callbacks->conn_cmd_xclient(conn->context, cmd, proxy_data); + } + smtp_server_command_unref(&command); +} |