Mercurial > dovecot > core-2.2
changeset 14150:3144001fae84
smtp/lmtp client: Send XCLIENT ADDR+PORT when possible.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Thu, 23 Feb 2012 11:58:35 +0200 |
parents | 1af2a0497f3f |
children | 1bc56eec3f8e |
files | src/lib-lda/lmtp-client.c src/lib-lda/lmtp-client.h |
diffstat | 2 files changed, 91 insertions(+), 7 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib-lda/lmtp-client.c Thu Feb 23 11:12:04 2012 +0200 +++ b/src/lib-lda/lmtp-client.c Thu Feb 23 11:58:35 2012 +0200 @@ -6,6 +6,7 @@ #include "network.h" #include "istream.h" #include "ostream.h" +#include "str.h" #include "dns-lookup.h" #include "lmtp-client.h" @@ -20,7 +21,8 @@ LMTP_INPUT_STATE_MAIL_FROM, LMTP_INPUT_STATE_RCPT_TO, LMTP_INPUT_STATE_DATA_CONTINUE, - LMTP_INPUT_STATE_DATA + LMTP_INPUT_STATE_DATA, + LMTP_INPUT_STATE_XCLIENT }; struct lmtp_rcpt { @@ -44,6 +46,8 @@ enum lmtp_client_protocol protocol; enum lmtp_input_state input_state; const char *global_fail_string; + string_t *input_multiline; + const char **xclient_args; struct istream *input; struct ostream *output; @@ -64,6 +68,7 @@ struct istream *data_input; unsigned char output_last; + unsigned int xclient_sent:1; unsigned int rcpt_to_successes:1; unsigned int output_finished:1; unsigned int finish_called:1; @@ -89,9 +94,12 @@ client->set.my_hostname = p_strdup(pool, set->my_hostname); client->set.dns_client_socket_path = p_strdup(pool, set->dns_client_socket_path); + client->set.source_ip = set->source_ip; + client->set.source_port = set->source_port; client->finish_callback = finish_callback; client->finish_context = context; client->fd = -1; + client->input_multiline = str_new(default_pool, 128); p_array_init(&client->recipients, pool, 16); return client; } @@ -138,6 +146,7 @@ i_stream_unref(&client->input); if (client->output != NULL) o_stream_unref(&client->output); + str_free(&client->input_multiline); pool_unref(&client->pool); } @@ -177,6 +186,8 @@ return t_strdup_printf("DATA (%"PRIuUOFF_T"/?)", client->data_input->v_offset); } + case LMTP_INPUT_STATE_XCLIENT: + return "XCLIENT"; } return "??"; } @@ -356,7 +367,8 @@ } } -static int lmtp_input_get_reply_code(const char *line, int *reply_code_r) +static int lmtp_input_get_reply_code(const char *line, int *reply_code_r, + string_t *multiline) { if (!i_isdigit(line[0]) || !i_isdigit(line[1]) || !i_isdigit(line[2])) return -1; @@ -369,7 +381,9 @@ /* final reply */ return 1; } else if (line[3] == '-') { - /* multiline reply. just ignore it. */ + /* multiline reply. */ + str_append(multiline, line); + str_append_c(multiline, '\n'); return 0; } else { /* invalid input */ @@ -377,11 +391,58 @@ } } +static void +lmtp_client_parse_capabilities(struct lmtp_client *client, const char *lines) +{ + const char *const *linep; + + for (linep = t_strsplit(lines, "\n"); *linep != NULL; linep++) { + const char *line = *linep; + + line += 4; /* already checked this is valid */ + if (strncasecmp(line, "XCLIENT ", 8) == 0) { + client->xclient_args = + (void *)p_strsplit(client->pool, line + 8, " "); + } + } +} + +static bool lmtp_client_send_xclient(struct lmtp_client *client) +{ + string_t *str; + unsigned int empty_len; + + if (client->xclient_args == NULL) { + /* not supported */ + return FALSE; + } + if (client->xclient_sent) + return FALSE; + + str = t_str_new(64); + str_append(str, "XCLIENT"); + empty_len = str_len(str); + if (client->set.source_ip.family != 0 && + str_array_icase_find(client->xclient_args, "ADDR")) + str_printfa(str, " ADDR=%s", net_ip2addr(&client->set.source_ip)); + if (client->set.source_port != 0 && + str_array_icase_find(client->xclient_args, "PORT")) + str_printfa(str, " PORT=%u", client->set.source_port); + + if (str_len(str) == empty_len) + return FALSE; + + str_append(str, "\r\n"); + o_stream_send(client->output, str_data(str), str_len(str)); + return TRUE; +} + static int lmtp_client_input_line(struct lmtp_client *client, const char *line) { int ret, reply_code = 0; - if ((ret = lmtp_input_get_reply_code(line, &reply_code)) <= 0) { + if ((ret = lmtp_input_get_reply_code(line, &reply_code, + client->input_multiline)) <= 0) { if (ret == 0) return 0; lmtp_client_fail(client, line); @@ -390,12 +451,13 @@ switch (client->input_state) { case LMTP_INPUT_STATE_GREET: + case LMTP_INPUT_STATE_XCLIENT: if (reply_code != 220) { lmtp_client_fail(client, line); return -1; } lmtp_client_send_handshake(client); - client->input_state++; + client->input_state = LMTP_INPUT_STATE_LHLO; break; case LMTP_INPUT_STATE_LHLO: case LMTP_INPUT_STATE_MAIL_FROM: @@ -403,6 +465,15 @@ lmtp_client_fail(client, line); return -1; } + str_append(client->input_multiline, line); + lmtp_client_parse_capabilities(client, + str_c(client->input_multiline)); + if (client->input_state == LMTP_INPUT_STATE_LHLO && + lmtp_client_send_xclient(client)) { + client->input_state = LMTP_INPUT_STATE_XCLIENT; + client->xclient_sent = TRUE; + break; + } if (client->input_state == LMTP_INPUT_STATE_LHLO) { o_stream_send_str(client->output, t_strdup_printf("MAIL FROM:%s\r\n", @@ -435,21 +506,27 @@ return -1; break; } - return 0; + return 1; } static void lmtp_client_input(struct lmtp_client *client) { const char *line; + int ret; lmtp_client_ref(client); o_stream_cork(client->output); while ((line = i_stream_read_next_line(client->input)) != NULL) { - if (lmtp_client_input_line(client, line) < 0) { + T_BEGIN { + ret = lmtp_client_input_line(client, line); + } T_END; + if (ret < 0) { o_stream_uncork(client->output); lmtp_client_unref(&client); return; } + if (ret > 0) + str_truncate(client->input_multiline, 0); } if (client->input->stream_errno != 0) {
--- a/src/lib-lda/lmtp-client.h Thu Feb 23 11:12:04 2012 +0200 +++ b/src/lib-lda/lmtp-client.h Thu Feb 23 11:58:35 2012 +0200 @@ -1,6 +1,8 @@ #ifndef LMTP_CLIENT_H #define LMTP_CLIENT_H +#include "network.h" + #define ERRSTR_TEMP_REMOTE_FAILURE "451 4.4.0 Remote server not answering" /* LMTP/SMTP client code. */ @@ -14,6 +16,11 @@ const char *my_hostname; const char *mail_from; const char *dns_client_socket_path; + + /* if remote server supports XCLIENT capability, + send the these as ADDR/PORT */ + struct ip_addr source_ip; + unsigned int source_port; }; /* reply contains the reply coming from remote server, or NULL