test-smtp-submit.c revision db2fb7b8ba0e699cf807042550db801e0ae88e04
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (c) 2016-2017 Dovecot authors, see the included COPYING file */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib-signals.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "str.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "hostpid.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "ioloop.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "istream.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "istream-chain.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "ostream.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "time-util.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "unlink-directory.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "write-full.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "connection.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "master-service.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "istream-dot.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "test-common.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "smtp-submit.h"
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen#include <sys/types.h>
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include <sys/wait.h>
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include <sys/stat.h>
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include <signal.h>
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include <unistd.h>
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include <fcntl.h>
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic const char *test_message1 =
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "Subject: Test message\r\n"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "To: rcpt@example.com\r\n"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "From: sender@example.com\r\n"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "\r\n"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "Test message\r\n";
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic const char *test_message2 =
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "Subject: Test message\r\n"
7888a9d2008eab9985096c46e1da9ee985c22a2aTimo Sirainen "To: rcpt@example.com\r\n"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "From: sender@example.com\r\n"
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen "\r\n"
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen "Test message Test message Test message Test message Test message\r\n"
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen "Test message Test message Test message Test message Test message\r\n"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "Test message Test message Test message Test message Test message\r\n"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "Test message Test message Test message Test message Test message\r\n"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "Test message Test message Test message Test message Test message\r\n"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "Test message Test message Test message Test message Test message\r\n";
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/*
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen * Types
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen */
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainenstruct server_connection {
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen struct connection conn;
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen void *context;
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen pool_t pool;
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen};
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainentypedef void (*test_server_init_t)(unsigned int index);
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainentypedef bool (*test_client_init_t)
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen (const struct smtp_submit_settings *submit_set);
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen/*
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen * State
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen/* common */
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainenstatic struct ip_addr bind_ip;
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainenstatic in_port_t *bind_ports = NULL;
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainenstatic struct ioloop *ioloop;
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainenstatic bool debug = FALSE;
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainenstatic char *tmp_dir = NULL;
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen/* server */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic struct io *io_listen;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int fd_listen = -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic pid_t *server_pids = NULL;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic in_port_t server_port = 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic unsigned int server_pids_count = 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic struct connection_list *server_conn_list;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic unsigned int server_index;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void (*test_server_input)(struct server_connection *conn);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void (*test_server_init)(struct server_connection *conn);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void (*test_server_deinit)(struct server_connection *conn);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* client */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/*
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen * Forward declarations
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen */
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen/* server */
0d658231054332c3f4c04aab0422af649de89a8cTimo Sirainenstatic void test_server_run(unsigned int index);
0d658231054332c3f4c04aab0422af649de89a8cTimo Sirainenstatic void
0d658231054332c3f4c04aab0422af649de89a8cTimo Sirainenserver_connection_deinit(struct server_connection **_conn);
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen
0d658231054332c3f4c04aab0422af649de89a8cTimo Sirainen/* client */
0d658231054332c3f4c04aab0422af649de89a8cTimo Sirainenstatic void
0d658231054332c3f4c04aab0422af649de89a8cTimo Sirainentest_client_defaults(struct smtp_submit_settings *smtp_set);
0d658231054332c3f4c04aab0422af649de89a8cTimo Sirainenstatic void test_client_deinit(void);
0d658231054332c3f4c04aab0422af649de89a8cTimo Sirainen
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainenstatic int
0d658231054332c3f4c04aab0422af649de89a8cTimo Sirainentest_client_smtp_send_simple(const struct smtp_submit_settings *smtp_set,
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen const char *message, const char *host,
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen unsigned int timeout_secs, const char **error_r);
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainenstatic int
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainentest_client_smtp_send_simple_port(const struct smtp_submit_settings *smtp_set,
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen const char *message, unsigned int port,
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen unsigned int timeout_secs, const char **error_r);
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen/* test*/
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainenstatic const char *test_tmp_dir_get(void);
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen
40ef82c46f6652412b068ebcdac7c3e74840a284Timo Sirainenstatic void
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainentest_message_delivery(const char *message, const char *file);
40ef82c46f6652412b068ebcdac7c3e74840a284Timo Sirainen
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainenstatic void test_run_client_server(
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen const struct smtp_submit_settings *submit_set,
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen test_client_init_t client_test,
40ef82c46f6652412b068ebcdac7c3e74840a284Timo Sirainen test_server_init_t server_test,
40ef82c46f6652412b068ebcdac7c3e74840a284Timo Sirainen unsigned int server_tests_count)
40ef82c46f6652412b068ebcdac7c3e74840a284Timo Sirainen ATTR_NULL(3);
40ef82c46f6652412b068ebcdac7c3e74840a284Timo Sirainen
f75188af11dce30be322cc2eefa3e3884871abf7Timo Sirainen/*
40ef82c46f6652412b068ebcdac7c3e74840a284Timo Sirainen * Host lookup failed
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen */
40ef82c46f6652412b068ebcdac7c3e74840a284Timo Sirainen
ed1f14af0d426b5550521a58fc414d130aa14172Timo Sirainen/* client */
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainenstatic bool
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainentest_client_host_lookup_failed(const struct smtp_submit_settings *submit_set)
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen{
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen const char *error = NULL;
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen int ret;
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen ret = test_client_smtp_send_simple(submit_set,
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen test_message1, "host.invalid", 5, &error);
ed1f14af0d426b5550521a58fc414d130aa14172Timo Sirainen test_out_reason("run (ret < 0)", ret < 0, error);
ed1f14af0d426b5550521a58fc414d130aa14172Timo Sirainen
ed1f14af0d426b5550521a58fc414d130aa14172Timo Sirainen return FALSE;
ed1f14af0d426b5550521a58fc414d130aa14172Timo Sirainen}
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen
8eeafcb306872435f3171e6acf5a9937aec3a175Timo Sirainen/* test */
8eeafcb306872435f3171e6acf5a9937aec3a175Timo Sirainen
8eeafcb306872435f3171e6acf5a9937aec3a175Timo Sirainenstatic void test_host_lookup_failed(void)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct smtp_submit_settings smtp_submit_set;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
1b56f5fdd415270c743a38719d41b4d9497bcacdTimo Sirainen test_client_defaults(&smtp_submit_set);
1b56f5fdd415270c743a38719d41b4d9497bcacdTimo Sirainen
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen test_begin("host lookup failed");
1b56f5fdd415270c743a38719d41b4d9497bcacdTimo Sirainen test_expect_errors(1);
1b56f5fdd415270c743a38719d41b4d9497bcacdTimo Sirainen test_run_client_server(&smtp_submit_set,
1b56f5fdd415270c743a38719d41b4d9497bcacdTimo Sirainen test_client_host_lookup_failed,
f75188af11dce30be322cc2eefa3e3884871abf7Timo Sirainen NULL, 0);
1b56f5fdd415270c743a38719d41b4d9497bcacdTimo Sirainen test_end();
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
1b56f5fdd415270c743a38719d41b4d9497bcacdTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/*
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen * Connection refused
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen/* server */
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainenstatic void
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainentest_server_connection_refused(unsigned int index ATTR_UNUSED)
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen{
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen i_close_fd(&fd_listen);
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen}
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen/* client */
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainenstatic bool
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainentest_client_connection_refused(const struct smtp_submit_settings *submit_set)
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen{
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen const char *error = NULL;
e86d0d34fe365da4c7ca4312d575bfcbf3a01c0eTimo Sirainen int ret;
e86d0d34fe365da4c7ca4312d575bfcbf3a01c0eTimo Sirainen
e86d0d34fe365da4c7ca4312d575bfcbf3a01c0eTimo Sirainen ret = test_client_smtp_send_simple_port(submit_set,
e86d0d34fe365da4c7ca4312d575bfcbf3a01c0eTimo Sirainen test_message1, bind_ports[0], 5, &error);
e86d0d34fe365da4c7ca4312d575bfcbf3a01c0eTimo Sirainen test_out_reason("run (ret < 0)", ret < 0, error);
e86d0d34fe365da4c7ca4312d575bfcbf3a01c0eTimo Sirainen
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen return FALSE;
1b56f5fdd415270c743a38719d41b4d9497bcacdTimo Sirainen}
1b56f5fdd415270c743a38719d41b4d9497bcacdTimo Sirainen
8830fab191cab8440281eb641dfdd93974b2933bTimo Sirainen/* test */
f75188af11dce30be322cc2eefa3e3884871abf7Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void test_connection_refused(void)
1b56f5fdd415270c743a38719d41b4d9497bcacdTimo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct smtp_submit_settings smtp_submit_set;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen test_client_defaults(&smtp_submit_set);
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen test_begin("connection refused");
1b56f5fdd415270c743a38719d41b4d9497bcacdTimo Sirainen test_expect_errors(1);
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen test_run_client_server(&smtp_submit_set,
1b56f5fdd415270c743a38719d41b4d9497bcacdTimo Sirainen test_client_connection_refused,
1b56f5fdd415270c743a38719d41b4d9497bcacdTimo Sirainen test_server_connection_refused, 1);
1b56f5fdd415270c743a38719d41b4d9497bcacdTimo Sirainen test_end();
f75188af11dce30be322cc2eefa3e3884871abf7Timo Sirainen}
1b56f5fdd415270c743a38719d41b4d9497bcacdTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/*
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen * Connection timed out
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
527ed64bc924b4a13b570a8450f8be3efdf71879Timo Sirainen/* server */
527ed64bc924b4a13b570a8450f8be3efdf71879Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainentest_connection_timed_out_input(struct server_connection *conn)
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen{
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen sleep(10);
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen server_connection_deinit(&conn);
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen}
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainenstatic void test_server_connection_timed_out(unsigned int index)
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen{
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen test_server_input = test_connection_timed_out_input;
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen test_server_run(index);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* client */
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainenstatic bool
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainentest_client_connection_timed_out(const struct smtp_submit_settings *submit_set)
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen{
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen time_t time;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *error = NULL;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen int ret;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen io_loop_time_refresh();
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen time = ioloop_time;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ret = test_client_smtp_send_simple_port(submit_set,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen test_message1, bind_ports[0], 1, &error);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen test_out_reason("run (ret < 0)", ret < 0, error);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen io_loop_time_refresh();
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen test_out("timeout", (ioloop_time - time) < 5);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen return FALSE;
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen}
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* test */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void test_connection_timed_out(void)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct smtp_submit_settings smtp_submit_set;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen test_client_defaults(&smtp_submit_set);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen test_begin("connection timed out");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen test_expect_errors(0);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen test_run_client_server(&smtp_submit_set,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen test_client_connection_timed_out,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen test_server_connection_timed_out, 1);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen test_end();
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/*
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen * Bad greeting
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* server */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
c1d45cada20777e1973579d40d0ebe43f89bb053Timo Sirainenstatic void
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainentest_bad_greeting_input(struct server_connection *conn)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen server_connection_deinit(&conn);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainentest_bad_greeting_init(struct server_connection *conn)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen o_stream_send_str(conn->conn.output,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "554 No SMTP service here.\r\n");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
a8012fea2a7315033bc467acbf46be8e7323318cTimo Sirainen
a8012fea2a7315033bc467acbf46be8e7323318cTimo Sirainenstatic void test_server_bad_greeting(unsigned int index)
a8012fea2a7315033bc467acbf46be8e7323318cTimo Sirainen{
834b90e1f426d1e3308670e09c050bcdea546eb8Timo Sirainen test_server_init = test_bad_greeting_init;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen test_server_input = test_bad_greeting_input;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen test_server_run(index);
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* client */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainenstatic bool
f1e1d821d93e4a1dc6ed8f23febde868b5d64cd5Timo Sirainentest_client_bad_greeting(const struct smtp_submit_settings *submit_set)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen const char *error = NULL;
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen int ret;
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen ret = test_client_smtp_send_simple_port(submit_set,
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen test_message1, bind_ports[0], 5, &error);
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen // FIXME: lmtp client handles this wrong, the greeting is not "bad"
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen //test_out_reason("run", ret == 0, error);
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen test_out_reason("run (ret < 0)", ret < 0, error);
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen return FALSE;
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen}
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen/* test */
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainenstatic void test_bad_greeting(void)
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen{
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen struct smtp_submit_settings smtp_submit_set;
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen test_client_defaults(&smtp_submit_set);
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen test_begin("bad greeting");
d6f50f100ce17fa4b3a89e9567a5ff993b38b872Timo Sirainen test_expect_errors(0);
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen test_run_client_server(&smtp_submit_set,
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen test_client_bad_greeting,
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen test_server_bad_greeting, 1);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen test_end();
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen}
b7cf555b699d73f2d71de0dabc088af6a7be3627Timo Sirainen
f1e1d821d93e4a1dc6ed8f23febde868b5d64cd5Timo Sirainen/*
f1e1d821d93e4a1dc6ed8f23febde868b5d64cd5Timo Sirainen * Denied HELO
f1e1d821d93e4a1dc6ed8f23febde868b5d64cd5Timo Sirainen */
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* server */
f1e1d821d93e4a1dc6ed8f23febde868b5d64cd5Timo Sirainen
f1e1d821d93e4a1dc6ed8f23febde868b5d64cd5Timo Sirainenstatic void
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainentest_denied_helo_input(struct server_connection *conn)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *line;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
6eb30032b4a50c383dea4c9c74342d906de6ad36Timo Sirainen line = i_stream_read_next_line(conn->conn.input);
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen if (line == NULL) {
1175f27441385a7011629f295f42708f9a3a4ffcTimo Sirainen if (i_stream_is_eof(conn->conn.input) ||
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen conn->conn.input->stream_errno != 0)
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen server_connection_deinit(&conn);
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen return;
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen }
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen o_stream_send_str(conn->conn.output,
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen "502 Command not implemented\r\n");
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen server_connection_deinit(&conn);
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen}
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainenstatic void
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainentest_denied_helo_init(struct server_connection *conn)
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen{
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen o_stream_send_str(conn->conn.output,
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen "220 testserver ESMTP Testfix (Debian/GNU)\r\n");
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen}
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainenstatic void test_server_denied_helo(unsigned int index)
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen{
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen test_server_init = test_denied_helo_init;
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen test_server_input = test_denied_helo_input;
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen test_server_run(index);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* client */
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic bool
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainentest_client_denied_helo(const struct smtp_submit_settings *submit_set)
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen{
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen const char *error = NULL;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen int ret;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ret = test_client_smtp_send_simple_port(submit_set,
6eb30032b4a50c383dea4c9c74342d906de6ad36Timo Sirainen test_message1, bind_ports[0], 5, &error);
6eb30032b4a50c383dea4c9c74342d906de6ad36Timo Sirainen // FIXME: lmtp client handles this wrong, the greeting is not "bad"
6eb30032b4a50c383dea4c9c74342d906de6ad36Timo Sirainen //test_out_reason("run", ret == 0, error);
6eb30032b4a50c383dea4c9c74342d906de6ad36Timo Sirainen test_out_reason("run (ret < 0)", ret < 0, error);
6eb30032b4a50c383dea4c9c74342d906de6ad36Timo Sirainen
6eb30032b4a50c383dea4c9c74342d906de6ad36Timo Sirainen return FALSE;
6eb30032b4a50c383dea4c9c74342d906de6ad36Timo Sirainen}
6eb30032b4a50c383dea4c9c74342d906de6ad36Timo Sirainen
6eb30032b4a50c383dea4c9c74342d906de6ad36Timo Sirainen/* test */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void test_denied_helo(void)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct smtp_submit_settings smtp_submit_set;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen test_client_defaults(&smtp_submit_set);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen test_begin("denied helo");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen test_expect_errors(0);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen test_run_client_server(&smtp_submit_set,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen test_client_denied_helo,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen test_server_denied_helo, 1);
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen test_end();
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen}
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen/*
a9bbb619908c206d9cb319398d4aaad37a22cd67Timo Sirainen * Disconnect HELO
a9bbb619908c206d9cb319398d4aaad37a22cd67Timo Sirainen */
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen/* server */
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainenstatic void
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainentest_disconnect_helo_input(struct server_connection *conn)
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen{
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen const char *line;
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen line = i_stream_read_next_line(conn->conn.input);
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen if (line == NULL) {
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen if (i_stream_is_eof(conn->conn.input) ||
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen conn->conn.input->stream_errno != 0)
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen server_connection_deinit(&conn);
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen return;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen server_connection_deinit(&conn);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
1175f27441385a7011629f295f42708f9a3a4ffcTimo Sirainen
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainenstatic void
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainentest_disconnect_helo_init(struct server_connection *conn)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen o_stream_send_str(conn->conn.output,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "220 testserver ESMTP Testfix (Debian/GNU)\r\n");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void test_server_disconnect_helo(unsigned int index)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen test_server_init = test_disconnect_helo_init;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen test_server_input = test_disconnect_helo_input;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen test_server_run(index);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* client */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic bool
834b90e1f426d1e3308670e09c050bcdea546eb8Timo Sirainentest_client_disconnect_helo(const struct smtp_submit_settings *submit_set)
6e354c4070b611471727692919d29440d73a73f7Timo Sirainen{
6e354c4070b611471727692919d29440d73a73f7Timo Sirainen const char *error = NULL;
6e354c4070b611471727692919d29440d73a73f7Timo Sirainen int ret;
6e354c4070b611471727692919d29440d73a73f7Timo Sirainen
6e354c4070b611471727692919d29440d73a73f7Timo Sirainen ret = test_client_smtp_send_simple_port(submit_set,
6e354c4070b611471727692919d29440d73a73f7Timo Sirainen test_message1, bind_ports[0], 5, &error);
6e354c4070b611471727692919d29440d73a73f7Timo Sirainen // FIXME: lmtp client handles this wrong, the greeting is not "bad"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen //test_out_reason("run", ret == 0, error);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen test_out_reason("run (ret < 0)", ret < 0, error);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return FALSE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* test */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void test_disconnect_helo(void)
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen{
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen struct smtp_submit_settings smtp_submit_set;
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen test_client_defaults(&smtp_submit_set);
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen test_begin("disconnect helo");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen test_expect_errors(0);
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen test_run_client_server(&smtp_submit_set,
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen test_client_disconnect_helo,
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen test_server_disconnect_helo, 1);
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen test_end();
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/*
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen * Denied MAIL
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen/* server */
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenenum _denied_mail_state {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen DENIED_MAIL_STATE_EHLO = 0,
DENIED_MAIL_STATE_MAIL_FROM
};
struct _denied_mail_server {
enum _denied_mail_state state;
};
static void
test_denied_mail_input(struct server_connection *conn)
{
struct _denied_mail_server *ctx;
const char *line;
if (conn->context == NULL) {
ctx = p_new(conn->pool, struct _denied_mail_server, 1);
conn->context = (void*)ctx;
} else {
ctx = (struct _denied_mail_server *)conn->context;
}
for (;;) {
line = i_stream_read_next_line(conn->conn.input);
if (line == NULL) {
if (i_stream_is_eof(conn->conn.input) ||
conn->conn.input->stream_errno != 0)
server_connection_deinit(&conn);
return;
}
switch (ctx->state) {
case DENIED_MAIL_STATE_EHLO:
o_stream_send_str(conn->conn.output,
"250-testserver\r\n"
"250-PIPELINING\r\n"
"250-ENHANCEDSTATUSCODES\r\n"
"250-8BITMIME\r\n"
"250 DSN\r\n");
ctx->state = DENIED_MAIL_STATE_MAIL_FROM;
return;
case DENIED_MAIL_STATE_MAIL_FROM:
o_stream_send_str(conn->conn.output,
"453 4.3.2 Incapable of accepting messages at this time.\r\n");
server_connection_deinit(&conn);
return;
}
i_unreached();
}
}
static void
test_denied_mail_init(struct server_connection *conn)
{
o_stream_send_str(conn->conn.output,
"220 testserver ESMTP Testfix (Debian/GNU)\r\n");
}
static void test_server_denied_mail(unsigned int index)
{
test_server_init = test_denied_mail_init;
test_server_input = test_denied_mail_input;
test_server_run(index);
}
/* client */
static bool
test_client_denied_mail(const struct smtp_submit_settings *submit_set)
{
const char *error = NULL;
int ret;
ret = test_client_smtp_send_simple_port(submit_set,
test_message1, bind_ports[0], 5, &error);
// FIXME: lmtp client handles this wrong, the greeting is not "bad"
//test_out_reason("run", ret == 0, error);
test_out_reason("run (ret < 0)", ret < 0, error);
return FALSE;
}
/* test */
static void test_denied_mail(void)
{
struct smtp_submit_settings smtp_submit_set;
test_client_defaults(&smtp_submit_set);
test_begin("denied mail");
test_expect_errors(0);
test_run_client_server(&smtp_submit_set,
test_client_denied_mail,
test_server_denied_mail, 1);
test_end();
}
/*
* Denied RCPT
*/
/* server */
enum _denied_rcpt_state {
DENIED_RCPT_STATE_EHLO = 0,
DENIED_RCPT_STATE_MAIL_FROM,
DENIED_RCPT_STATE_RCPT_TO
};
struct _denied_rcpt_server {
enum _denied_rcpt_state state;
};
static void
test_denied_rcpt_input(struct server_connection *conn)
{
struct _denied_rcpt_server *ctx;
const char *line;
if (conn->context == NULL) {
ctx = p_new(conn->pool, struct _denied_rcpt_server, 1);
conn->context = (void*)ctx;
} else {
ctx = (struct _denied_rcpt_server *)conn->context;
}
for (;;) {
line = i_stream_read_next_line(conn->conn.input);
if (line == NULL) {
if (i_stream_is_eof(conn->conn.input) ||
conn->conn.input->stream_errno != 0)
server_connection_deinit(&conn);
return;
}
switch (ctx->state) {
case DENIED_RCPT_STATE_EHLO:
o_stream_send_str(conn->conn.output,
"250-testserver\r\n"
"250-PIPELINING\r\n"
"250-ENHANCEDSTATUSCODES\r\n"
"250-8BITMIME\r\n"
"250 DSN\r\n");
ctx->state = DENIED_RCPT_STATE_MAIL_FROM;
return;
case DENIED_RCPT_STATE_MAIL_FROM:
o_stream_send_str(conn->conn.output,
"250 2.1.0 Ok\r\n");
ctx->state = DENIED_RCPT_STATE_RCPT_TO;
continue;
case DENIED_RCPT_STATE_RCPT_TO:
o_stream_send_str(conn->conn.output,
"550 5.4.3 Directory server failure\r\n");
server_connection_deinit(&conn);
return;
}
i_unreached();
}
}
static void
test_denied_rcpt_init(struct server_connection *conn)
{
o_stream_send_str(conn->conn.output,
"220 testserver ESMTP Testfix (Debian/GNU)\r\n");
}
static void test_server_denied_rcpt(unsigned int index)
{
test_server_init = test_denied_rcpt_init;
test_server_input = test_denied_rcpt_input;
test_server_run(index);
}
/* client */
static bool
test_client_denied_rcpt(const struct smtp_submit_settings *submit_set)
{
const char *error = NULL;
int ret;
ret = test_client_smtp_send_simple_port(submit_set,
test_message1, bind_ports[0], 5, &error);
test_out_reason("run (ret == 0)", ret == 0, error);
return FALSE;
}
/* test */
static void test_denied_rcpt(void)
{
struct smtp_submit_settings smtp_submit_set;
test_client_defaults(&smtp_submit_set);
test_begin("denied rcpt");
test_expect_errors(0);
test_run_client_server(&smtp_submit_set,
test_client_denied_rcpt,
test_server_denied_rcpt, 1);
test_end();
}
/*
* Denied second RCPT
*/
/* server */
enum _denied_second_rcpt_state {
DENIED_SECOND_RCPT_STATE_EHLO = 0,
DENIED_SECOND_RCPT_STATE_MAIL_FROM,
DENIED_SECOND_RCPT_STATE_RCPT_TO,
DENIED_SECOND_RCPT_STATE_RCPT_TO2
};
struct _denied_second_rcpt_server {
enum _denied_second_rcpt_state state;
};
static void
test_denied_second_rcpt_input(struct server_connection *conn)
{
struct _denied_second_rcpt_server *ctx;
const char *line;
if (conn->context == NULL) {
ctx = p_new(conn->pool, struct _denied_second_rcpt_server, 1);
conn->context = (void*)ctx;
} else {
ctx = (struct _denied_second_rcpt_server *)conn->context;
}
for (;;) {
line = i_stream_read_next_line(conn->conn.input);
if (line == NULL) {
if (i_stream_is_eof(conn->conn.input) ||
conn->conn.input->stream_errno != 0)
server_connection_deinit(&conn);
return;
}
switch (ctx->state) {
case DENIED_SECOND_RCPT_STATE_EHLO:
o_stream_send_str(conn->conn.output,
"250-testserver\r\n"
"250-PIPELINING\r\n"
"250-ENHANCEDSTATUSCODES\r\n"
"250-8BITMIME\r\n"
"250 DSN\r\n");
ctx->state = DENIED_SECOND_RCPT_STATE_MAIL_FROM;
return;
case DENIED_SECOND_RCPT_STATE_MAIL_FROM:
o_stream_send_str(conn->conn.output,
"250 2.1.0 Ok\r\n");
ctx->state = DENIED_SECOND_RCPT_STATE_RCPT_TO;
continue;
case DENIED_SECOND_RCPT_STATE_RCPT_TO:
o_stream_send_str(conn->conn.output,
"250 2.1.5 Ok\r\n");
ctx->state = DENIED_SECOND_RCPT_STATE_RCPT_TO2;
continue;
case DENIED_SECOND_RCPT_STATE_RCPT_TO2:
o_stream_send_str(conn->conn.output,
"550 5.4.3 Directory server failure\r\n");
server_connection_deinit(&conn);
return;
}
i_unreached();
}
}
static void
test_denied_second_rcpt_init(struct server_connection *conn)
{
o_stream_send_str(conn->conn.output,
"220 testserver ESMTP Testfix (Debian/GNU)\r\n");
}
static void test_server_denied_second_rcpt(unsigned int index)
{
test_server_init = test_denied_second_rcpt_init;
test_server_input = test_denied_second_rcpt_input;
test_server_run(index);
}
/* client */
static bool
test_client_denied_second_rcpt(const struct smtp_submit_settings *submit_set)
{
struct smtp_submit *smtp_submit;
struct smtp_submit_settings smtp_submit_set;
struct ostream *output;
const char *error = NULL;
int ret;
smtp_submit_set = *submit_set;
smtp_submit_set.submission_host =
t_strdup_printf("127.0.0.1:%u", bind_ports[0]);
smtp_submit = smtp_submit_init_simple(&smtp_submit_set, "sender@example.com");
smtp_submit_add_rcpt(smtp_submit, "rcpt@example.com");
smtp_submit_add_rcpt(smtp_submit, "rcpt2@example.com");
output = smtp_submit_send(smtp_submit);
o_stream_send_str(output, test_message1);
ret = smtp_submit_run_timeout(smtp_submit, 1000, &error);
test_out_reason("run (ret == 0)", ret == 0, error);
smtp_submit_deinit(&smtp_submit);
return FALSE;
}
/* test */
static void test_denied_second_rcpt(void)
{
struct smtp_submit_settings smtp_submit_set;
test_client_defaults(&smtp_submit_set);
test_begin("denied second rcpt");
test_expect_errors(0);
test_run_client_server(&smtp_submit_set,
test_client_denied_second_rcpt,
test_server_denied_second_rcpt, 1);
test_end();
}
/*
* Denied DATA
*/
/* server */
enum _denied_data_state {
DENIED_DATA_STATE_EHLO = 0,
DENIED_DATA_STATE_MAIL_FROM,
DENIED_DATA_STATE_RCPT_TO,
DENIED_DATA_STATE_DATA
};
struct _denied_data_server {
enum _denied_data_state state;
};
static void
test_denied_data_input(struct server_connection *conn)
{
struct _denied_data_server *ctx;
const char *line;
if (conn->context == NULL) {
ctx = p_new(conn->pool, struct _denied_data_server, 1);
conn->context = (void*)ctx;
} else {
ctx = (struct _denied_data_server *)conn->context;
}
for (;;) {
line = i_stream_read_next_line(conn->conn.input);
if (line == NULL) {
if (i_stream_is_eof(conn->conn.input) ||
conn->conn.input->stream_errno != 0)
server_connection_deinit(&conn);
return;
}
switch (ctx->state) {
case DENIED_DATA_STATE_EHLO:
o_stream_send_str(conn->conn.output,
"250-testserver\r\n"
"250-PIPELINING\r\n"
"250-ENHANCEDSTATUSCODES\r\n"
"250-8BITMIME\r\n"
"250 DSN\r\n");
ctx->state = DENIED_DATA_STATE_MAIL_FROM;
return;
case DENIED_DATA_STATE_MAIL_FROM:
o_stream_send_str(conn->conn.output,
"250 2.1.0 Ok\r\n");
ctx->state = DENIED_DATA_STATE_RCPT_TO;
continue;
case DENIED_DATA_STATE_RCPT_TO:
o_stream_send_str(conn->conn.output,
"250 2.1.5 Ok\r\n");
ctx->state = DENIED_DATA_STATE_DATA;
continue;
case DENIED_DATA_STATE_DATA:
o_stream_send_str(conn->conn.output,
"500 5.0.0 Unacceptabe recipients\r\n");
server_connection_deinit(&conn);
return;
}
i_unreached();
}
}
static void
test_denied_data_init(struct server_connection *conn)
{
o_stream_send_str(conn->conn.output,
"220 testserver ESMTP Testfix (Debian/GNU)\r\n");
}
static void test_server_denied_data(unsigned int index)
{
test_server_init = test_denied_data_init;
test_server_input = test_denied_data_input;
test_server_run(index);
}
/* client */
static bool
test_client_denied_data(const struct smtp_submit_settings *submit_set)
{
const char *error = NULL;
int ret;
ret = test_client_smtp_send_simple_port(submit_set,
test_message1, bind_ports[0], 5, &error);
test_out_reason("run (ret == 0)", ret == 0, error);
return FALSE;
}
/* test */
static void test_denied_data(void)
{
struct smtp_submit_settings smtp_submit_set;
test_client_defaults(&smtp_submit_set);
test_begin("denied data");
test_expect_errors(0);
test_run_client_server(&smtp_submit_set,
test_client_denied_data,
test_server_denied_data, 1);
test_end();
}
/*
* Data failure
*/
/* server */
enum _data_failure_state {
DATA_FAILURE_STATE_EHLO = 0,
DATA_FAILURE_STATE_MAIL_FROM,
DATA_FAILURE_STATE_RCPT_TO,
DATA_FAILURE_STATE_DATA,
DATA_FAILURE_STATE_FINISH
};
struct _data_failure_server {
enum _data_failure_state state;
};
static void
test_data_failure_input(struct server_connection *conn)
{
struct _data_failure_server *ctx;
const char *line;
if (conn->context == NULL) {
ctx = p_new(conn->pool, struct _data_failure_server, 1);
conn->context = (void*)ctx;
} else {
ctx = (struct _data_failure_server *)conn->context;
}
for (;;) {
line = i_stream_read_next_line(conn->conn.input);
if (line == NULL) {
if (i_stream_is_eof(conn->conn.input) ||
conn->conn.input->stream_errno != 0)
server_connection_deinit(&conn);
return;
}
switch (ctx->state) {
case DATA_FAILURE_STATE_EHLO:
o_stream_send_str(conn->conn.output,
"250-testserver\r\n"
"250-PIPELINING\r\n"
"250-ENHANCEDSTATUSCODES\r\n"
"250-8BITMIME\r\n"
"250 DSN\r\n");
ctx->state = DATA_FAILURE_STATE_MAIL_FROM;
return;
case DATA_FAILURE_STATE_MAIL_FROM:
o_stream_send_str(conn->conn.output,
"250 2.1.0 Ok\r\n");
ctx->state = DATA_FAILURE_STATE_RCPT_TO;
continue;
case DATA_FAILURE_STATE_RCPT_TO:
o_stream_send_str(conn->conn.output,
"250 2.1.5 Ok\r\n");
ctx->state = DATA_FAILURE_STATE_DATA;
continue;
case DATA_FAILURE_STATE_DATA:
o_stream_send_str(conn->conn.output,
"354 End data with <CR><LF>.<CR><LF>\r\n");
ctx->state = DATA_FAILURE_STATE_FINISH;
continue;
case DATA_FAILURE_STATE_FINISH:
if (strcmp(line, ".") == 0) {
o_stream_send_str(conn->conn.output,
"552 5.2.3 Message length exceeds administrative limit\r\n");
server_connection_deinit(&conn);
return;
}
continue;
}
i_unreached();
}
}
static void
test_data_failure_init(struct server_connection *conn)
{
o_stream_send_str(conn->conn.output,
"220 testserver ESMTP Testfix (Debian/GNU)\r\n");
}
static void test_server_data_failure(unsigned int index)
{
test_server_init = test_data_failure_init;
test_server_input = test_data_failure_input;
test_server_run(index);
}
/* client */
static bool
test_client_data_failure(const struct smtp_submit_settings *submit_set)
{
const char *error = NULL;
int ret;
ret = test_client_smtp_send_simple_port(submit_set,
test_message1, bind_ports[0], 5, &error);
test_out_reason("run (ret == 0)", ret == 0, error);
return FALSE;
}
/* test */
static void test_data_failure(void)
{
struct smtp_submit_settings smtp_submit_set;
test_client_defaults(&smtp_submit_set);
test_begin("data failure");
test_expect_errors(0);
test_run_client_server(&smtp_submit_set,
test_client_data_failure,
test_server_data_failure, 1);
test_end();
}
/*
* Data disconnect
*/
/* server */
enum _data_disconnect_state {
DATA_DISCONNECT_STATE_EHLO = 0,
DATA_DISCONNECT_STATE_MAIL_FROM,
DATA_DISCONNECT_STATE_RCPT_TO,
DATA_DISCONNECT_STATE_DATA,
DATA_DISCONNECT_STATE_FINISH
};
struct _data_disconnect_server {
enum _data_disconnect_state state;
};
static void
test_data_disconnect_input(struct server_connection *conn)
{
struct _data_disconnect_server *ctx;
const char *line;
if (conn->context == NULL) {
ctx = p_new(conn->pool, struct _data_disconnect_server, 1);
conn->context = (void*)ctx;
} else {
ctx = (struct _data_disconnect_server *)conn->context;
}
for (;;) {
line = i_stream_read_next_line(conn->conn.input);
if (line == NULL) {
if (i_stream_is_eof(conn->conn.input) ||
conn->conn.input->stream_errno != 0)
server_connection_deinit(&conn);
return;
}
switch (ctx->state) {
case DATA_DISCONNECT_STATE_EHLO:
o_stream_send_str(conn->conn.output,
"250-testserver\r\n"
"250-PIPELINING\r\n"
"250-ENHANCEDSTATUSCODES\r\n"
"250-8BITMIME\r\n"
"250 DSN\r\n");
ctx->state = DATA_DISCONNECT_STATE_MAIL_FROM;
return;
case DATA_DISCONNECT_STATE_MAIL_FROM:
o_stream_send_str(conn->conn.output,
"250 2.1.0 Ok\r\n");
ctx->state = DATA_DISCONNECT_STATE_RCPT_TO;
continue;
case DATA_DISCONNECT_STATE_RCPT_TO:
o_stream_send_str(conn->conn.output,
"250 2.1.5 Ok\r\n");
ctx->state = DATA_DISCONNECT_STATE_DATA;
continue;
case DATA_DISCONNECT_STATE_DATA:
o_stream_send_str(conn->conn.output,
"354 End data with <CR><LF>.<CR><LF>\r\n");
ctx->state = DATA_DISCONNECT_STATE_FINISH;
continue;
case DATA_DISCONNECT_STATE_FINISH:
server_connection_deinit(&conn);
return;
}
i_unreached();
}
}
static void
test_data_disconnect_init(struct server_connection *conn)
{
o_stream_send_str(conn->conn.output,
"220 testserver ESMTP Testfix (Debian/GNU)\r\n");
}
static void test_server_data_disconnect(unsigned int index)
{
test_server_init = test_data_disconnect_init;
test_server_input = test_data_disconnect_input;
test_server_run(index);
}
/* client */
static bool
test_client_data_disconnect(const struct smtp_submit_settings *submit_set)
{
const char *error = NULL;
int ret;
ret = test_client_smtp_send_simple_port(submit_set,
test_message1, bind_ports[0], 5, &error);
test_out_reason("run (ret < 0)", ret < 0, error);
return FALSE;
}
/* test */
static void test_data_disconnect(void)
{
struct smtp_submit_settings smtp_submit_set;
test_client_defaults(&smtp_submit_set);
test_begin("data disconnect");
test_expect_errors(0);
test_run_client_server(&smtp_submit_set,
test_client_data_disconnect,
test_server_data_disconnect, 1);
test_end();
}
/*
* Data timout
*/
/* server */
enum _data_timout_state {
DATA_TIMEOUT_STATE_EHLO = 0,
DATA_TIMEOUT_STATE_MAIL_FROM,
DATA_TIMEOUT_STATE_RCPT_TO,
DATA_TIMEOUT_STATE_DATA,
DATA_TIMEOUT_STATE_FINISH
};
struct _data_timout_server {
enum _data_timout_state state;
};
static void
test_data_timout_input(struct server_connection *conn)
{
struct _data_timout_server *ctx;
const char *line;
if (conn->context == NULL) {
ctx = p_new(conn->pool, struct _data_timout_server, 1);
conn->context = (void*)ctx;
} else {
ctx = (struct _data_timout_server *)conn->context;
}
for (;;) {
line = i_stream_read_next_line(conn->conn.input);
if (line == NULL) {
if (i_stream_is_eof(conn->conn.input) ||
conn->conn.input->stream_errno != 0)
server_connection_deinit(&conn);
return;
}
switch (ctx->state) {
case DATA_TIMEOUT_STATE_EHLO:
o_stream_send_str(conn->conn.output,
"250-testserver\r\n"
"250-PIPELINING\r\n"
"250-ENHANCEDSTATUSCODES\r\n"
"250-8BITMIME\r\n"
"250 DSN\r\n");
ctx->state = DATA_TIMEOUT_STATE_MAIL_FROM;
return;
case DATA_TIMEOUT_STATE_MAIL_FROM:
o_stream_send_str(conn->conn.output,
"250 2.1.0 Ok\r\n");
ctx->state = DATA_TIMEOUT_STATE_RCPT_TO;
continue;
case DATA_TIMEOUT_STATE_RCPT_TO:
o_stream_send_str(conn->conn.output,
"250 2.1.5 Ok\r\n");
ctx->state = DATA_TIMEOUT_STATE_DATA;
continue;
case DATA_TIMEOUT_STATE_DATA:
o_stream_send_str(conn->conn.output,
"354 End data with <CR><LF>.<CR><LF>\r\n");
ctx->state = DATA_TIMEOUT_STATE_FINISH;
continue;
case DATA_TIMEOUT_STATE_FINISH:
sleep(10);
server_connection_deinit(&conn);
return;
}
i_unreached();
}
}
static void
test_data_timout_init(struct server_connection *conn)
{
o_stream_send_str(conn->conn.output,
"220 testserver ESMTP Testfix (Debian/GNU)\r\n");
}
static void test_server_data_timout(unsigned int index)
{
test_server_init = test_data_timout_init;
test_server_input = test_data_timout_input;
test_server_run(index);
}
/* client */
static bool
test_client_data_timout(const struct smtp_submit_settings *submit_set)
{
time_t time;
const char *error = NULL;
int ret;
io_loop_time_refresh();
time = ioloop_time;
ret = test_client_smtp_send_simple_port(submit_set,
test_message1, bind_ports[0], 2, &error);
test_out_reason("run (ret < 0)", ret < 0, error);
io_loop_time_refresh();
test_out("timeout", (ioloop_time - time) < 5);
return FALSE;
}
/* test */
static void test_data_timeout(void)
{
struct smtp_submit_settings smtp_submit_set;
test_client_defaults(&smtp_submit_set);
test_begin("data timeout");
test_expect_errors(0);
test_run_client_server(&smtp_submit_set,
test_client_data_timout,
test_server_data_timout, 1);
test_end();
}
/*
* Successful delivery
*/
/* server */
enum _successful_delivery_state {
SUCCESSFUL_DELIVERY_STATE_EHLO = 0,
SUCCESSFUL_DELIVERY_STATE_MAIL_FROM,
SUCCESSFUL_DELIVERY_STATE_RCPT_TO,
SUCCESSFUL_DELIVERY_STATE_DATA,
SUCCESSFUL_DELIVERY_STATE_FINISH
};
struct _successful_delivery_server {
enum _successful_delivery_state state;
char *file_path;
struct istream *dot_input;
struct ostream *file;
};
static void
test_successful_delivery_input(struct server_connection *conn)
{
struct _successful_delivery_server *ctx;
const char *line;
if (conn->context == NULL) {
ctx = p_new(conn->pool, struct _successful_delivery_server, 1);
conn->context = (void*)ctx;
} else {
ctx = (struct _successful_delivery_server *)conn->context;
}
for (;;) {
if (ctx->state == SUCCESSFUL_DELIVERY_STATE_FINISH) {
// FIXME: put somewhere common
enum ostream_send_istream_result res;
if (ctx->dot_input == NULL) {
int fd;
ctx->dot_input = i_stream_create_dot(conn->conn.input, TRUE);
ctx->file_path = p_strdup_printf(conn->pool,
"%s/message-%u.eml", test_tmp_dir_get(), server_port);
if ( (fd=open(ctx->file_path, O_WRONLY | O_CREAT, 0600)) < 0 ) {
i_fatal("failed create tmp file for message: "
"open(%s) failed: %m", ctx->file_path);
}
ctx->file = o_stream_create_fd_autoclose(&fd, IO_BLOCK_SIZE);
}
res = o_stream_send_istream(ctx->file, ctx->dot_input);
switch (res) {
case OSTREAM_SEND_ISTREAM_RESULT_FINISHED:
break;
case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT:
case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT:
return;
case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT:
i_error("test server: "
"Failed to read all message payload [%s]", ctx->file_path);
server_connection_deinit(&conn);
return;
case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT:
i_error("test server: "
"Failed to write all message payload [%s]", ctx->file_path);
server_connection_deinit(&conn);
return;
}
o_stream_send_str(conn->conn.output,
"250 2.0.0 Ok: queued as 73BDE342129\r\n");
ctx->state = SUCCESSFUL_DELIVERY_STATE_MAIL_FROM;
continue;
}
line = i_stream_read_next_line(conn->conn.input);
if (line == NULL) {
if (i_stream_is_eof(conn->conn.input) ||
conn->conn.input->stream_errno != 0)
server_connection_deinit(&conn);
return;
}
switch (ctx->state) {
case SUCCESSFUL_DELIVERY_STATE_EHLO:
o_stream_send_str(conn->conn.output,
"250-testserver\r\n"
"250-PIPELINING\r\n"
"250-ENHANCEDSTATUSCODES\r\n"
"250-8BITMIME\r\n"
"250 DSN\r\n");
ctx->state = SUCCESSFUL_DELIVERY_STATE_MAIL_FROM;
return;
case SUCCESSFUL_DELIVERY_STATE_MAIL_FROM:
o_stream_send_str(conn->conn.output,
"250 2.1.0 Ok\r\n");
ctx->state = SUCCESSFUL_DELIVERY_STATE_RCPT_TO;
continue;
case SUCCESSFUL_DELIVERY_STATE_RCPT_TO:
o_stream_send_str(conn->conn.output,
"250 2.1.5 Ok\r\n");
ctx->state = SUCCESSFUL_DELIVERY_STATE_DATA;
continue;
case SUCCESSFUL_DELIVERY_STATE_DATA:
o_stream_send_str(conn->conn.output,
"354 End data with <CR><LF>.<CR><LF>\r\n");
ctx->state = SUCCESSFUL_DELIVERY_STATE_FINISH;
continue;
case SUCCESSFUL_DELIVERY_STATE_FINISH:
break;
}
i_unreached();
}
}
static void
test_successful_delivery_init(struct server_connection *conn)
{
o_stream_send_str(conn->conn.output,
"220 testserver ESMTP Testfix (Debian/GNU)\r\n");
}
static void
test_successful_delivery_deinit(struct server_connection *conn)
{
struct _successful_delivery_server *ctx =
(struct _successful_delivery_server *)conn->context;
if (ctx->dot_input != NULL)
i_stream_unref(&ctx->dot_input);
if (ctx->file != NULL)
o_stream_unref(&ctx->file);
}
static void test_server_successful_delivery(unsigned int index)
{
test_server_init = test_successful_delivery_init;
test_server_input = test_successful_delivery_input;
test_server_deinit = test_successful_delivery_deinit;
test_server_run(index);
}
/* client */
static bool
test_client_successful_delivery(const struct smtp_submit_settings *submit_set)
{
const char *error = NULL;
int ret;
/* send the message */
ret = test_client_smtp_send_simple_port(submit_set,
test_message1, bind_ports[0], 5, &error);
test_out_reason("run (ret > 0)", ret > 0, error);
/* verify delivery */
test_message_delivery(test_message1,
t_strdup_printf("%s/message-%u.eml",
test_tmp_dir_get(), bind_ports[0]));
return FALSE;
}
struct _parallel_delivery_client {
unsigned int count;
};
static void
test_client_parallel_delivery_callback(const struct smtp_submit_result *result,
struct _parallel_delivery_client *ctx)
{
if (result->status <= 0)
i_error("Submit failed: %s", result->error);
if (--ctx->count == 0)
io_loop_stop(current_ioloop);
}
static bool
test_client_parallel_delivery(const struct smtp_submit_settings *submit_set)
{
struct smtp_submit_settings smtp_submit_set;
struct _parallel_delivery_client *ctx;
struct smtp_submit *smtp_submit1, *smtp_submit2;
struct ostream *output;
struct ioloop *ioloop;
ioloop = io_loop_create();
ctx = i_new(struct _parallel_delivery_client, 1);
ctx->count = 2;
smtp_submit_set = *submit_set;
/* submit 1 */
smtp_submit_set.submission_host =
t_strdup_printf("127.0.0.1:%u", bind_ports[0]);
smtp_submit1 = smtp_submit_init_simple(&smtp_submit_set, "sender@example.com");
smtp_submit_add_rcpt(smtp_submit1, "rcpt@example.com");
output = smtp_submit_send(smtp_submit1);
o_stream_send_str(output, test_message1);
smtp_submit_run_async(smtp_submit1, 5,
test_client_parallel_delivery_callback, ctx);
/* submit 2 */
smtp_submit_set.submission_host =
t_strdup_printf("127.0.0.1:%u", bind_ports[1]);
smtp_submit2 = smtp_submit_init_simple(&smtp_submit_set, "sender@example.com");
smtp_submit_add_rcpt(smtp_submit2, "rcpt@example.com");
output = smtp_submit_send(smtp_submit2);
o_stream_send_str(output, test_message2);
smtp_submit_run_async(smtp_submit2, 5,
test_client_parallel_delivery_callback, ctx);
io_loop_run(ioloop);
smtp_submit_deinit(&smtp_submit1);
smtp_submit_deinit(&smtp_submit2);
io_loop_destroy(&ioloop);
/* verify delivery */
test_message_delivery(test_message1,
t_strdup_printf("%s/message-%u.eml",
test_tmp_dir_get(), bind_ports[0]));
test_message_delivery(test_message2,
t_strdup_printf("%s/message-%u.eml",
test_tmp_dir_get(), bind_ports[1]));
i_free(ctx);
return FALSE;
}
/* test */
static void test_successful_delivery(void)
{
struct smtp_submit_settings smtp_submit_set;
test_client_defaults(&smtp_submit_set);
test_begin("successful delivery");
test_expect_errors(0);
test_run_client_server(&smtp_submit_set,
test_client_successful_delivery,
test_server_successful_delivery, 1);
test_end();
test_begin("parallel delivery");
test_expect_errors(0);
test_run_client_server(&smtp_submit_set,
test_client_parallel_delivery,
test_server_successful_delivery, 2);
test_end();
}
/*
* Failed sendmail
*/
/* client */
static bool
test_client_failed_sendmail(const struct smtp_submit_settings *submit_set)
{
struct smtp_submit_settings smtp_submit_set;
struct smtp_submit *smtp_submit;
struct ostream *output;
const char *sendmail_path, *error = NULL;
int ret;
sendmail_path = TEST_BIN_DIR"/sendmail-exit-1.sh";
smtp_submit_set = *submit_set;
smtp_submit_set.sendmail_path = sendmail_path;
smtp_submit = smtp_submit_init_simple(&smtp_submit_set, "sender@example.com");
smtp_submit_add_rcpt(smtp_submit, "rcpt@example.com");
output = smtp_submit_send(smtp_submit);
o_stream_send_str(output, test_message1);
ret = smtp_submit_run_timeout(smtp_submit, 5, &error);
test_out_reason("run (ret < 0)", ret < 0, error);
smtp_submit_deinit(&smtp_submit);
return FALSE;
}
/* test */
static void test_failed_sendmail(void)
{
struct smtp_submit_settings smtp_submit_set;
test_client_defaults(&smtp_submit_set);
test_begin("failed sendmail");
test_expect_errors(0);
test_run_client_server(&smtp_submit_set,
test_client_failed_sendmail, NULL, 0);
test_end();
}
/*
* Successful sendmail
*/
/* client */
static bool
test_client_successful_sendmail(const struct smtp_submit_settings *submit_set)
{
struct smtp_submit_settings smtp_submit_set;
struct smtp_submit *smtp_submit;
struct ostream *output;
const char *sendmail_path, *msg_path, *error = NULL;
int ret;
msg_path = t_strdup_printf("%s/message.eml", test_tmp_dir_get());
sendmail_path = t_strdup_printf(
TEST_BIN_DIR"/sendmail-success.sh %s", msg_path);
smtp_submit_set = *submit_set;
smtp_submit_set.sendmail_path = sendmail_path;
smtp_submit = smtp_submit_init_simple(&smtp_submit_set, "sender@example.com");
smtp_submit_add_rcpt(smtp_submit, "rcpt@example.com");
output = smtp_submit_send(smtp_submit);
o_stream_send_str(output, test_message1);
ret = smtp_submit_run_timeout(smtp_submit, 5, &error);
test_out_reason("run (ret > 0)", ret > 0, error);
smtp_submit_deinit(&smtp_submit);
/* verify delivery */
test_message_delivery(test_message1, msg_path);
return FALSE;
}
/* test */
static void test_successful_sendmail(void)
{
struct smtp_submit_settings smtp_submit_set;
test_client_defaults(&smtp_submit_set);
test_begin("successful sendmail");
test_expect_errors(0);
test_run_client_server(&smtp_submit_set,
test_client_successful_sendmail, NULL, 0);
test_end();
}
/*
* Parallel sendmail
*/
/* client */
struct _parallel_sendmail_client {
unsigned int count;
};
static void
test_client_parallel_sendmail_callback(const struct smtp_submit_result *result,
struct _parallel_sendmail_client *ctx)
{
if (result->status <= 0)
i_error("Submit failed: %s", result->error);
if (--ctx->count == 0)
io_loop_stop(current_ioloop);
}
static bool
test_client_parallel_sendmail(const struct smtp_submit_settings *submit_set)
{
struct smtp_submit_settings smtp_submit_set;
struct _parallel_sendmail_client *ctx;
struct smtp_submit *smtp_submit1, *smtp_submit2;
struct ostream *output;
const char *sendmail_path1, *sendmail_path2;
const char *msg_path1, *msg_path2;
struct ioloop *ioloop;
ctx = i_new(struct _parallel_sendmail_client, 1);
ctx->count = 2;
ioloop = io_loop_create();
msg_path1 = t_strdup_printf("%s/message1.eml", test_tmp_dir_get());
msg_path2 = t_strdup_printf("%s/message2.eml", test_tmp_dir_get());
sendmail_path1 = t_strdup_printf(
TEST_BIN_DIR"/sendmail-success.sh %s", msg_path1);
sendmail_path2 = t_strdup_printf(
TEST_BIN_DIR"/sendmail-success.sh %s", msg_path2);
smtp_submit_set = *submit_set;
/* submit 1 */
smtp_submit_set.sendmail_path = sendmail_path1;
smtp_submit1 = smtp_submit_init_simple(&smtp_submit_set, "sender@example.com");
smtp_submit_add_rcpt(smtp_submit1, "rcpt@example.com");
output = smtp_submit_send(smtp_submit1);
o_stream_send_str(output, test_message1);
smtp_submit_run_async(smtp_submit1, 5,
test_client_parallel_sendmail_callback, ctx);
/* submit 2 */
smtp_submit_set.sendmail_path = sendmail_path2;
smtp_submit2 = smtp_submit_init_simple(&smtp_submit_set, "sender@example.com");
smtp_submit_add_rcpt(smtp_submit2, "rcpt@example.com");
output = smtp_submit_send(smtp_submit2);
o_stream_send_str(output, test_message2);
smtp_submit_run_async(smtp_submit2, 5,
test_client_parallel_sendmail_callback, ctx);
io_loop_run(ioloop);
smtp_submit_deinit(&smtp_submit1);
smtp_submit_deinit(&smtp_submit2);
io_loop_destroy(&ioloop);
/* verify delivery */
test_message_delivery(test_message1, msg_path1);
test_message_delivery(test_message2, msg_path2);
return FALSE;
}
/* test */
static void test_parallel_sendmail(void)
{
struct smtp_submit_settings smtp_submit_set;
test_client_defaults(&smtp_submit_set);
test_begin("parallel sendmail");
test_expect_errors(0);
test_run_client_server(&smtp_submit_set,
test_client_parallel_sendmail, NULL, 0);
test_end();
}
/*
* All tests
*/
static void (*const test_functions[])(void) = {
test_host_lookup_failed,
test_connection_refused,
test_connection_timed_out,
test_bad_greeting,
test_denied_helo,
test_disconnect_helo,
test_denied_mail,
test_denied_rcpt,
test_denied_second_rcpt,
test_denied_data,
test_data_failure,
test_data_disconnect,
test_data_timeout,
test_successful_delivery,
test_failed_sendmail,
test_successful_sendmail,
test_parallel_sendmail,
NULL
};
/*
* Test client
*/
static void
test_client_defaults(struct smtp_submit_settings *smtp_set)
{
i_zero(smtp_set);
smtp_set->hostname = "test";
smtp_set->submission_host = "";
smtp_set->sendmail_path = "/bin/false";
}
static void test_client_deinit(void)
{
}
static int
test_client_smtp_send_simple(const struct smtp_submit_settings *smtp_set,
const char *message, const char *host,
unsigned int timeout_secs, const char **error_r)
{
struct smtp_submit_settings smtp_submit_set;
struct smtp_submit *smtp_submit;
struct ostream *output;
int ret;
/* send the message */
smtp_submit_set = *smtp_set;
smtp_submit_set.submission_host = host,
smtp_submit = smtp_submit_init_simple(&smtp_submit_set, "sender@example.com");
smtp_submit_add_rcpt(smtp_submit, "rcpt@example.com");
output = smtp_submit_send(smtp_submit);
o_stream_send_str(output, message);
ret = smtp_submit_run_timeout(smtp_submit, timeout_secs, error_r);
smtp_submit_deinit(&smtp_submit);
return ret;
}
static int
test_client_smtp_send_simple_port(const struct smtp_submit_settings *smtp_set,
const char *message, unsigned int port,
unsigned int timeout_secs, const char **error_r)
{
const char *host = t_strdup_printf("127.0.0.1:%u", port);
return test_client_smtp_send_simple(smtp_set,
message, host, timeout_secs, error_r);
}
/*
* Test server
*/
/* client connection */
static void
server_connection_input(struct connection *_conn)
{
struct server_connection *conn = (struct server_connection *)_conn;
test_server_input(conn);
}
static void
server_connection_init(int fd)
{
struct server_connection *conn;
pool_t pool;
net_set_nonblock(fd, TRUE);
pool = pool_alloconly_create("server connection", 256);
conn = p_new(pool, struct server_connection, 1);
conn->pool = pool;
connection_init_server
(server_conn_list, &conn->conn, "server connection", fd, fd);
if (test_server_init != NULL)
test_server_init(conn);
}
static void
server_connection_deinit(struct server_connection **_conn)
{
struct server_connection *conn = *_conn;
*_conn = NULL;
if (test_server_deinit != NULL)
test_server_deinit(conn);
connection_deinit(&conn->conn);
pool_unref(&conn->pool);
}
static void
server_connection_destroy(struct connection *_conn)
{
struct server_connection *conn =
(struct server_connection *)_conn;
server_connection_deinit(&conn);
}
static void
server_connection_accept(void *context ATTR_UNUSED)
{
int fd;
/* accept new client */
fd = net_accept(fd_listen, NULL, NULL);
if (fd == -1)
return;
if (fd == -2) {
i_fatal("test server: accept() failed: %m");
}
server_connection_init(fd);
}
/* */
static struct connection_settings server_connection_set = {
.input_max_size = (size_t)-1,
.output_max_size = (size_t)-1,
.client = FALSE
};
static const struct connection_vfuncs server_connection_vfuncs = {
.destroy = server_connection_destroy,
.input = server_connection_input
};
static void test_server_run(unsigned int index)
{
server_index = index;
/* open server socket */
io_listen = io_add(fd_listen,
IO_READ, server_connection_accept, (void *)NULL);
server_conn_list = connection_list_init
(&server_connection_set, &server_connection_vfuncs);
io_loop_run(ioloop);
/* close server socket */
io_remove(&io_listen);
connection_list_deinit(&server_conn_list);
}
/*
* Tests
*/
static int test_open_server_fd(in_port_t *bind_port)
{
int fd = net_listen(&bind_ip, bind_port, 128);
if (debug)
i_debug("server listening on %u", *bind_port);
if (fd == -1) {
i_fatal("listen(%s:%u) failed: %m",
net_ip2addr(&bind_ip), *bind_port);
}
return fd;
}
static void test_servers_kill_all(void)
{
unsigned int i;
if (server_pids_count > 0) {
for (i = 0; i < server_pids_count; i++) {
if (server_pids[i] != (pid_t)-1) {
(void)kill(server_pids[i], SIGKILL);
(void)waitpid(server_pids[i], NULL, 0);
server_pids[i] = -1;
}
}
}
server_pids_count = 0;
}
static void test_tmp_dir_init(void)
{
tmp_dir = i_strdup_printf
("/tmp/dovecot-test-smtp-client.%s.%s",
dec2str(time(NULL)), dec2str(getpid()));
}
static const char *test_tmp_dir_get(void)
{
if (mkdir(tmp_dir, 0700) < 0 && errno != EEXIST) {
i_fatal("failed to create temporary directory `%s': %m",
tmp_dir);
}
return tmp_dir;
}
static void test_tmp_dir_deinit(void)
{
const char *error;
if (unlink_directory(tmp_dir,
UNLINK_DIRECTORY_FLAG_RMDIR, &error) < 0 && errno != ENOENT) {
i_warning("failed to remove temporary directory `%s': %s.",
tmp_dir, error);
}
i_free(tmp_dir);
}
static void
test_message_delivery(const char *message, const char *file)
{
struct istream *input;
const unsigned char *data;
size_t size, msize;
int ret;
msize = strlen(message);
input = i_stream_create_file(file, (size_t)-1);
while ((ret=i_stream_read_more(input, &data, &size)) > 0) {
const unsigned char *mdata;
test_assert(input->v_offset < (uoff_t)msize &&
(input->v_offset + (uoff_t)size) <= (uoff_t)msize);
if (test_has_failed())
break;
mdata = (const unsigned char *)message + input->v_offset;
test_assert(memcmp(data, mdata, size) == 0);
if (test_has_failed())
break;
i_stream_skip(input, size);
}
test_out_reason("delivery", ret < 0 &&
input->stream_errno == 0 &&
i_stream_is_eof(input) &&
input->v_offset == (uoff_t)msize,
(input->stream_errno == 0 ? NULL : i_stream_get_error(input)));
i_stream_unref(&input);
}
static void test_run_client_server(
const struct smtp_submit_settings *submit_set,
test_client_init_t client_test,
test_server_init_t server_test,
unsigned int server_tests_count)
{
unsigned int i;
server_pids = NULL;
server_pids_count = 0;
test_tmp_dir_init();
if (server_tests_count > 0) {
int fds[server_tests_count];
bind_ports = i_new(in_port_t, server_tests_count);
lib_signals_ioloop_detach();
server_pids = i_new(pid_t, server_tests_count);
for (i = 0; i < server_tests_count; i++)
server_pids[i] = (pid_t)-1;
server_pids_count = server_tests_count;
for (i = 0; i < server_tests_count; i++)
fds[i] = test_open_server_fd(&bind_ports[i]);
for (i = 0; i < server_tests_count; i++) {
fd_listen = fds[i];
server_port = bind_ports[i];
if ((server_pids[i] = fork()) == (pid_t)-1)
i_fatal("fork() failed: %m");
if (server_pids[i] == 0) {
server_pids[i] = (pid_t)-1;
server_pids_count = 0;
hostpid_init();
while (current_ioloop != NULL) {
ioloop = current_ioloop;
io_loop_destroy(&ioloop);
}
lib_signals_deinit();
if (debug)
i_debug("server[%d]: PID=%s", i+1, my_pid);
/* child: server */
ioloop = io_loop_create();
server_test(i);
io_loop_destroy(&ioloop);
if (fd_listen != -1)
i_close_fd(&fd_listen);
i_free(bind_ports);
i_free(server_pids);
test_tmp_dir_deinit();
/* wait for it to be killed; this way, valgrind will not
object to this process going away inelegantly. */
sleep(60);
exit(1);
}
if (fd_listen != -1)
i_close_fd(&fd_listen);
}
if (debug)
i_debug("client: PID=%s", my_pid);
lib_signals_ioloop_attach();
}
/* parent: client */
usleep(100000); /* wait a little for server setup */
server_port = 0;
ioloop = io_loop_create();
if (client_test(submit_set))
io_loop_run(ioloop);
test_client_deinit();
io_loop_destroy(&ioloop);
test_servers_kill_all();
i_free(server_pids);
i_free(bind_ports);
test_tmp_dir_deinit();
}
/*
* Main
*/
volatile sig_atomic_t terminating = 0;
static void
test_signal_handler(int signo)
{
if (terminating != 0)
raise(signo);
terminating = 1;
/* make sure we don't leave any pesky children alive */
test_servers_kill_all();
(void)signal(signo, SIG_DFL);
raise(signo);
}
static void test_atexit(void)
{
test_servers_kill_all();
}
int main(int argc, char *argv[])
{
int c;
atexit(test_atexit);
(void)signal(SIGCHLD, SIG_IGN);
(void)signal(SIGTERM, test_signal_handler);
(void)signal(SIGQUIT, test_signal_handler);
(void)signal(SIGINT, test_signal_handler);
(void)signal(SIGSEGV, test_signal_handler);
(void)signal(SIGABRT, test_signal_handler);
master_service = master_service_init("test-smtp-submit",
MASTER_SERVICE_FLAG_STANDALONE, &argc, &argv, "D");
while ((c = master_getopt(master_service)) > 0) {
switch (c) {
case 'D':
debug = TRUE;
break;
default:
i_fatal("Usage: %s [-D]", argv[0]);
}
}
master_service_init_finish(master_service);
/* listen on localhost */
i_zero(&bind_ip);
bind_ip.family = AF_INET;
bind_ip.u.ip4.s_addr = htonl(INADDR_LOOPBACK);
test_run(test_functions);
master_service_deinit(&master_service);
}