/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
+#include "buffer.h"
#include "unichar.h"
#include "istream.h"
#include "istream-failure-at.h"
struct smtp_command_limits limits;
const unsigned char *cur, *end;
+ buffer_t *line_buffer;
struct istream *data;
struct smtp_command_parser_state_data state;
struct smtp_command_parser *parser = *_parser;
i_stream_unref(&parser->data);
+ buffer_free(&parser->line_buffer);
i_free(parser->state.cmd_name);
i_free(parser->state.cmd_params);
i_free(parser->error);
static void smtp_command_parser_restart(struct smtp_command_parser *parser)
{
+ buffer_free(&parser->line_buffer);
i_free(parser->state.cmd_name);
i_free(parser->state.cmd_params);
size_t max_size = (parser->auth_response ?
parser->limits.max_auth_size :
parser->limits.max_parameters_size);
+ size_t buf_size = (parser->line_buffer == NULL ?
+ 0 : parser->line_buffer->used);
int nch = 1;
+ i_assert(max_size == 0 || buf_size <= max_size);
+ if (max_size > 0 && buf_size == max_size) {
+ smtp_command_parser_error(
+ parser, SMTP_COMMAND_PARSE_ERROR_LINE_TOO_LONG,
+ "%s line is too long",
+ (parser->auth_response ? "AUTH response" : "Command"));
+ return -1;
+ }
+
/* We assume parameters to match textstr (HT, SP, Printable US-ASCII).
For command parameters, we also accept valid UTF-8 characters.
*/
break;
p += nch;
}
- if (max_size > 0 && (size_t)(p - parser->cur) > max_size) {
+ if (max_size > 0 && (size_t)(p - parser->cur) > (max_size - buf_size)) {
smtp_command_parser_error(
parser, SMTP_COMMAND_PARSE_ERROR_LINE_TOO_LONG,
"%s line is too long",
return -1;
}
parser->state.poff = p - parser->cur;
- if (p == parser->end || nch == 0)
+ if (p == parser->end || nch == 0) {
+ /* Parsed up to end of what is currently buffered in the input
+ stream. */
+ unsigned int ch_size = (p == parser->end ?
+ 0 : uni_utf8_char_bytes(*p));
+ size_t max_input = i_stream_get_max_buffer_size(parser->input);
+
+ /* Move parsed data to parser's line buffer if the input stream
+ buffer is full. This can happen when the parser's limits
+ exceed the input stream max buffer size. */
+ if ((parser->state.poff + ch_size) >= max_input) {
+ if (parser->line_buffer == NULL) {
+ buf_size = (max_input < SIZE_MAX / 2 ?
+ max_input * 2 : SIZE_MAX);
+ buf_size = I_MAX(buf_size, 2048);
+ buf_size = I_MIN(buf_size, max_size);
+
+ parser->line_buffer = buffer_create_dynamic(
+ default_pool, buf_size);
+ }
+ buffer_append(parser->line_buffer, parser->cur,
+ (p - parser->cur));
+
+ parser->cur = p;
+ parser->state.poff = 0;
+ }
return 0;
+ }
/* In the interest of improved interoperability, SMTP receivers SHOULD
tolerate trailing white space before the terminating <CRLF>.
return -1;
}
- parser->state.cmd_params = i_strdup_until(parser->cur, mp);
+ if (parser->line_buffer == NULL) {
+ /* Buffered only in input stream */
+ parser->state.cmd_params = i_strdup_until(parser->cur, mp);
+ } else {
+ /* Buffered also in the parser */
+ buffer_append(parser->line_buffer, parser->cur,
+ (mp - parser->cur));
+ parser->state.cmd_params =
+ buffer_free_without_data(&parser->line_buffer);
+ }
parser->cur = p;
parser->state.poff = 0;
return 1;
return ret;
old_bytes = i_stream_get_data_size(parser->input);
}
+ i_assert(ret != -2);
- if (ret == -2) {
- /* Should not really happen */
- smtp_command_parser_error(
- parser, SMTP_COMMAND_PARSE_ERROR_LINE_TOO_LONG,
- "%s line is too long",
- (parser->auth_response ? "AUTH response" : "Command"));
- return -1;
- }
if (ret < 0) {
i_assert(parser->input->eof);
if (parser->input->stream_errno == 0) {
static struct smtp_server *smtp_server = NULL;
static struct io *io_listen;
static int fd_listen = -1;
+static size_t server_io_buffer_size = 0;
static struct smtp_server_callbacks server_callbacks;
static unsigned int server_pending;
test_server_run(server_set);
}
+static void
+test_server_long_auth_line_small_buf(
+ const struct smtp_server_settings *server_set)
+{
+ server_io_buffer_size = 1024;
+
+ server_callbacks.conn_disconnect =
+ test_server_long_auth_line_disconnect;
+
+ server_callbacks.conn_cmd_helo =
+ test_server_long_auth_line_helo;
+ server_callbacks.conn_cmd_auth =
+ test_server_long_auth_line_auth;
+ server_callbacks.conn_cmd_auth_continue =
+ test_server_long_auth_line_auth_continue;
+ server_callbacks.conn_cmd_rcpt =
+ test_server_long_auth_line_rcpt;
+ server_callbacks.conn_cmd_data_begin =
+ test_server_long_auth_line_data_begin;
+ server_callbacks.conn_cmd_data_continue =
+ test_server_long_auth_line_data_continue;
+ test_server_run(server_set);
+}
+
/* test */
static void test_long_auth_line(void)
test_end();
}
+static void test_long_auth_line_small_buf(void)
+{
+ struct smtp_server_settings smtp_server_set;
+
+ test_server_defaults(&smtp_server_set);
+ smtp_server_set.capabilities = SMTP_CAPABILITY_AUTH;
+ smtp_server_set.max_client_idle_time_msecs = 1000;
+
+ test_begin("long auth line (small i/o buffers)");
+ test_run_client_server(&smtp_server_set,
+ test_server_long_auth_line_small_buf,
+ test_client_long_auth_line, 3);
+ test_end();
+}
+
+
/*
* Big data
*/
test_many_bad_commands,
test_long_command,
test_long_auth_line,
+ test_long_auth_line_small_buf,
test_big_data,
test_bad_helo,
test_bad_mail,
if (debug)
i_debug("Accepted connection");
+ net_set_nonblock(fd, TRUE);
+
sconn = i_new(struct server_connection, 1);
server_callbacks.conn_free = server_connection_free;
- conn = smtp_server_connection_create(smtp_server, fd, fd,
- NULL, 0, FALSE, NULL,
- &server_callbacks, sconn);
+ if (server_io_buffer_size == 0) {
+ conn = smtp_server_connection_create(smtp_server, fd, fd,
+ NULL, 0, FALSE, NULL,
+ &server_callbacks, sconn);
+ } else {
+ struct istream *input;
+ struct ostream *output;
+
+ input = i_stream_create_fd(fd, server_io_buffer_size);
+ output = o_stream_create_fd(fd, server_io_buffer_size);
+ o_stream_set_no_error_handling(output, TRUE);
+
+ conn = smtp_server_connection_create_from_streams(
+ smtp_server, input, output, NULL, 0, NULL,
+ &server_callbacks, sconn);
+
+ i_stream_unref(&input);
+ o_stream_unref(&output);
+ }
smtp_server_connection_start(conn);
}
{
unsigned int i;
+ server_io_buffer_size = 0;
+
fd_listen = test_open_server_fd();
for (i = 0; i < client_tests_count; i++) {