/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "lib-signals.h"
#include "str.h"
#include "hostpid.h"
#include "ioloop.h"
#include "istream.h"
#include "istream-dot.h"
#include "istream-chain.h"
#include "istream-failure-at.h"
#include "ostream.h"
#include "time-util.h"
#include "connection.h"
#include "test-common.h"
#include "smtp-client.h"
#include "smtp-client-connection.h"
#include "smtp-client-transaction.h"
#include <signal.h>
#include <unistd.h>
/*
* Types
*/
enum server_connection_state {
};
struct server_connection {
void *context;
char *file_path;
};
typedef bool (*test_client_init_t)
(const struct smtp_client_settings *client_set);
typedef void (*test_dns_init_t)(void);
/*
* State
*/
/* common */
/* dns */
/* server */
static unsigned int server_pids_count = 0;
static unsigned int server_index;
const char *line);
/* client */
/*
* Forward declarations
*/
/* server */
static void test_server_run(unsigned int index);
static void
/* client */
static void
static void test_client_deinit(void);
/* test*/
static void test_run_client_server(
const struct smtp_client_settings *client_set,
unsigned int server_tests_count,
ATTR_NULL(3);
/*
* Unconfigured SSL
*/
/* server */
static void
{
/* nothing */
}
static void
{
sleep(100);
}
/* client */
struct _unconfigured_ssl {
unsigned int count;
};
static void
void *context)
{
if (debug)
}
}
static bool
{
return TRUE;
}
/* test */
static void test_unconfigured_ssl(void)
{
test_begin("unconfigured ssl");
test_end();
}
/*
* Unconfigured SSL abort
*/
/* server */
static void
{
/* nothing */
}
static void
{
sleep(100);
}
/* client */
struct _unconfigured_ssl_abort {
unsigned int count;
};
static void
{
if (debug)
}
static void
struct _unconfigured_ssl_abort *ctx)
{
if (debug)
}
static bool
const struct smtp_client_settings *client_set)
{
return TRUE;
}
/* test */
static void test_unconfigured_ssl_abort(void)
{
test_begin("unconfigured ssl abort");
test_end();
}
/*
* Host lookup failed
*/
/* client */
struct _host_lookup_failed {
unsigned int count;
};
static void
struct _host_lookup_failed *ctx)
{
if (debug)
}
}
static bool
const struct smtp_client_settings *client_set)
{
return TRUE;
}
/* test */
static void test_host_lookup_failed(void)
{
test_begin("host lookup failed");
test_end();
}
/*
* Connection refused
*/
/* server */
static void
{
}
/* client */
struct _connection_refused {
unsigned int count;
};
static void
struct _connection_refused *ctx)
{
if (debug)
}
}
static bool
const struct smtp_client_settings *client_set)
{
return TRUE;
}
/* test */
static void test_connection_refused(void)
{
test_begin("connection refused");
test_end();
}
/*
* Connection lost prematurely
*/
/* server */
static void
{
const char *line;
}
return;
}
}
static int
{
return 1;
}
{
}
/* client */
struct _connection_lost_prematurely {
unsigned int count;
};
static void
struct _connection_lost_prematurely *ctx)
{
if (debug)
}
}
static bool
const struct smtp_client_settings *client_set)
{
return TRUE;
}
/* test */
static void test_connection_lost_prematurely(void)
{
test_begin("connection lost prematurely");
test_end();
}
/*
* Connection timed out
*/
/* server */
{
sleep(10);
}
/* client */
struct _connection_timed_out {
unsigned int count;
};
static void
struct _connection_timed_out *ctx)
{
if (debug)
}
}
static bool
const struct smtp_client_settings *client_set)
{
return TRUE;
}
/* test */
static void test_connection_timed_out(void)
{
test_begin("connection timed out");
test_end();
}
/*
* Broken payload
*/
/* server */
static int
const char *line ATTR_UNUSED)
{
return 0;
}
{
}
static int
{
"250-testserver\r\n"
"250-PIPELINING\r\n"
"250-CHUNKING\r\n"
"250-ENHANCEDSTATUSCODES\r\n"
"250 DSN\r\n");
return 1;
}
return 0;
}
{
}
/* client */
static void
void *context ATTR_UNUSED)
{
}
static void
void *context ATTR_UNUSED)
{
if (debug)
}
static void
void *context ATTR_UNUSED)
{
if (debug)
}
static void
{
}
static bool
const struct smtp_client_settings *client_set)
{
return TRUE;
}
static bool
const struct smtp_client_settings *client_set)
{
static const char *message =
"From: lucifer@example.com\r\n"
"To: lostsoul@example.com\r\n"
"Subject: Moehahaha!\r\n"
"\r\n"
"Moehahahahahahahahahahahahahahahahahahahahahaha!!\r\n"
"Moehahahahahahahahahahahahahahahahahahahahahaha!!\r\n"
"Moehahahahahahahahahahahahahahahahahahahahahaha!!\r\n"
"Moehahahahahahahahahahahahahahahahahahahahahaha!!\r\n"
"Moehahahahahahahahahahahahahahahahahahahahahaha!!\r\n"
"Moehahahahahahahahahahahahahahahahahahahahahaha!!\r\n"
"Moehahahahahahahahahahahahahahahahahahahahahaha!!\r\n"
"Moehahahahahahahahahahahahahahahahahahahahahaha!!\r\n"
"Moehahahahahahahahahahahahahahahahahahahahahaha!!\r\n"
"Moehahahahahahahahahahahahahahahahahahahahahaha!!\r\n"
"Moehahahahahahahahahahahahahahahahahahahahahaha!!\r\n"
"Moehahahahahahahahahahahahahahahahahahahahahaha!!\r\n"
"Moehahahahahahahahahahahahahahahahahahahahahaha!!\r\n"
"Moehahahahahahahahahahahahahahahahahahahahahaha!!\r\n"
"Moehahahahahahahahahahahahahahahahahahahahahaha!!\r\n"
"Moehahahahahahahahahahahahahahahahahahahahahaha!!\r\n"
"Moehahahahahahahahahahahahahahahahahahahahahaha!!\r\n";
EIO, "Moehahahaha!!");
return TRUE;
}
/* test */
static void test_broken_payload(void)
{
test_begin("broken payload");
test_end();
test_begin("broken payload (later)");
test_end();
test_begin("broken payload (later, chunking)");
test_end();
}
/*
* Connection lost
*/
/* server */
static int
const char *line ATTR_UNUSED)
{
if (server_index == 0) {
sleep(1);
return -1;
}
break;
if (server_index == 1) {
sleep(1);
return -1;
}
break;
if (server_index == 2) {
sleep(1);
return -1;
}
break;
if (server_index == 3) {
sleep(1);
return -1;
}
break;
break;
}
return 0;
}
static int
{
sleep(1);
return -1;
}
{
}
/* client */
struct _connection_lost {
unsigned int count;
};
struct _connection_lost_peer {
unsigned int index;
};
static void
struct _connection_lost_peer *pctx)
{
if (debug) {
i_debug("RCPT TO REPLY[%u]: %s",
}
case 0:
break;
case 1:
break;
case 2:
break;
case 3:
break;
}
}
static void
struct _connection_lost_peer *pctx)
{
if (debug) {
i_debug("RCPT DATA REPLY[%u]: %s",
}
case 0:
break;
case 1:
break;
case 2:
break;
case 3:
break;
}
}
static void
struct _connection_lost_peer *pctx)
{
if (debug) {
i_debug("DATA REPLY[%u]: %s",
}
}
static void
{
if (debug)
}
}
static void
unsigned int index)
{
static const char *message =
"From: stephan@example.com\r\n"
"To: timo@example.com\r\n"
"Subject: Frop!\r\n"
"\r\n"
"Frop!\r\n";
}
static bool
const struct smtp_client_settings *client_set)
{
unsigned int i;
return TRUE;
}
/* test */
static void test_connection_lost(void)
{
test_begin("connection lost");
test_end();
}
/*
* Unexpected reply
*/
/* server */
static int
{
if (server_index == 5) {
"421 testserver Server shutting down for maintenance\r\n");
sleep(4);
return 1;
}
return 0;
}
static int
const char *line ATTR_UNUSED)
{
if (server_index == 4) {
"250-testserver\r\n"
"250-PIPELINING\r\n"
"250-ENHANCEDSTATUSCODES\r\n"
"250 DSN\r\n");
"421 testserver Server shutting down for maintenance\r\n");
sleep(4);
return -1;
}
break;
if (server_index == 3) {
"250 2.1.0 Ok\r\n");
"421 testserver Server shutting down for maintenance\r\n");
sleep(4);
return -1;
}
break;
if (server_index == 2) {
"250 2.1.5 Ok\r\n");
"421 testserver Server shutting down for maintenance\r\n");
sleep(4);
return -1;
}
break;
if (server_index == 1) {
"354 End data with <CR><LF>.<CR><LF>\r\n");
"421 testserver Server shutting down for maintenance\r\n");
sleep(4);
return -1;
}
break;
break;
}
return 0;
}
{
}
/* client */
struct _unexpected_reply {
unsigned int count;
};
struct _unexpected_reply_peer {
unsigned int index;
};
static void
void *context)
{
(struct _unexpected_reply_peer *)context;
if (debug)
case 0: case 1: case 2: case 3: case 4:
break;
case 5:
break;
}
}
static void
struct _unexpected_reply_peer *pctx)
{
if (debug)
case 0: case 1: case 2: case 3:
break;
case 4: case 5:
break;
}
}
static void
struct _unexpected_reply_peer *pctx)
{
if (debug)
case 0: case 1: case 2:
break;
case 3: case 4: case 5:
break;
}
}
static void
struct _unexpected_reply_peer *pctx)
{
if (debug)
case 0:
break;
case 1: case 2:
break;
case 3: case 4: case 5:
i_unreached();
}
}
static void
struct _unexpected_reply_peer *pctx)
{
if (debug)
case 0:
break;
case 1: case 2: case 3: case 4: case 5:
break;
}
}
static void
{
if (debug)
}
case 0: case 1: case 2:
break;
case 3: case 4: case 5:
break;
}
}
static void
{
static const char *message =
"From: stephan@example.com\r\n"
"To: timo@example.com\r\n"
"Subject: Frop!\r\n"
"\r\n"
"Frop!\r\n";
}
static void
{
}
static void
unsigned int index)
{
test_client_unexpected_reply_login_cb, (void *)pctx);
}
static bool
const struct smtp_client_settings *client_set)
{
unsigned int i;
return TRUE;
}
/* test */
static void test_unexpected_reply(void)
{
test_begin("unexpected reply");
test_end();
}
/*
* Partial reply
*/
/* server */
static int
const char *line ATTR_UNUSED)
{
return 0;
"500 Command not");
return -1;
}
{
}
/* client */
struct _partial_reply {
unsigned int count;
};
static void
struct _partial_reply *ctx)
{
if (debug)
}
}
static bool
const struct smtp_client_settings *client_set)
{
return TRUE;
}
/* test */
static void test_partial_reply(void)
{
test_begin("partial reply");
test_end();
}
/*
* Bad reply
*/
/* server */
static int
const char *line ATTR_UNUSED)
{
return 0;
"666 Really bad reply\r\n");
return -1;
}
{
}
/* client */
struct _bad_reply {
unsigned int count;
};
static void
struct _bad_reply *ctx)
{
if (debug)
}
}
static bool
const struct smtp_client_settings *client_set)
{
return TRUE;
}
/* test */
static void test_bad_reply(void)
{
test_begin("bad reply");
test_end();
}
/*
* Bad greeting
*/
/* server */
static int
{
switch (server_index) {
case 0:
"666 Mouhahahaha!!\r\n");
break;
case 1:
"446 Not right now, sorry.\r\n");
break;
case 2:
"233 Gimme all your mail, NOW!!\r\n");
break;
}
return -1;
}
{
}
/* client */
struct _bad_greeting {
unsigned int count;
};
struct _bad_greeting_peer {
unsigned int index;
};
static void
struct _bad_greeting_peer *pctx)
{
if (debug)
case 0:
break;
case 1:
break;
case 2:
break;
}
}
}
static void
unsigned int index)
{
}
static bool
const struct smtp_client_settings *client_set)
{
return TRUE;
}
/* test */
static void test_bad_greeting(void)
{
test_begin("bad greeting");
test_end();
}
/*
* Command timeout
*/
/* server */
static int
const char *line ATTR_UNUSED)
{
return 0;
sleep(10);
return -1;
}
{
}
/* client */
struct _command_timed_out {
unsigned int count;
};
static void
struct _command_timed_out *ctx)
{
if (debug)
}
}
static bool
const struct smtp_client_settings *client_set)
{
return TRUE;
}
/* test */
static void test_command_timed_out(void)
{
test_begin("command timed out");
test_end();
}
/*
* Command aborted early
*/
/* server */
static int
const char *line ATTR_UNUSED)
{
return 0;
sleep(1);
return -1;
}
{
}
/* client */
struct _command_aborted_early {
};
static void
{
if (debug)
/* abort does not trigger callback */
}
static void
struct _command_aborted_early *ctx)
{
if (debug)
i_debug("ABORT");
/* abort early */
/* wait a little for server to actually respond to an
already aborted request */
} else {
if (debug)
i_debug("FINISHED");
/* all done */
}
}
static bool
const struct smtp_client_settings *client_set)
{
return TRUE;
}
/* test */
static void test_command_aborted_early(void)
{
test_begin("command aborted early");
test_end();
}
/*
* Client deinit early
*/
/* server */
static int
const char *line ATTR_UNUSED)
{
return 0;
sleep(1);
return -1;
}
{
}
/* client */
struct _client_deinit_early {
};
static void
{
if (debug)
/* abort does not trigger callback */
}
static void
struct _client_deinit_early *ctx)
{
/* deinit early */
/* all done */
}
static bool
const struct smtp_client_settings *client_set)
{
return TRUE;
}
/* test */
static void test_client_deinit_early(void)
{
test_begin("client deinit early");
test_end();
}
/*
* DNS service failure
*/
/* client */
struct _dns_service_failure {
unsigned int count;
};
static void
struct _dns_service_failure *ctx)
{
if (debug)
}
}
static bool
const struct smtp_client_settings *client_set)
{
return TRUE;
}
/* test */
static void test_dns_service_failure(void)
{
test_begin("dns service failure");
test_end();
}
/*
* DNS timeout
*/
/* dns */
static void
{
/* hang */
sleep(100);
}
static void test_dns_dns_timeout(void)
{
test_server_run(0);
}
/* client */
struct _dns_timeout {
unsigned int count;
};
static void
struct _dns_timeout *ctx)
{
if (debug)
}
}
static bool
const struct smtp_client_settings *client_set)
{
return TRUE;
}
/* test */
static void test_dns_timeout(void)
{
test_begin("dns timeout");
test_end();
}
/*
* DNS lookup failure
*/
/* dns */
static void
{
}
static void test_dns_dns_lookup_failure(void)
{
test_server_run(0);
}
/* client */
struct _dns_lookup_failure {
unsigned int count;
};
static void
struct _dns_lookup_failure *ctx)
{
if (debug)
}
}
static bool
const struct smtp_client_settings *client_set)
{
return TRUE;
}
/* test */
static void test_dns_lookup_failure(void)
{
test_begin("dns lookup failure");
test_end();
}
/*
* Authentication failed
*/
/* server */
static int
const char *line ATTR_UNUSED)
{
if (server_index == 1) {
"250-testserver\r\n"
"250-PIPELINING\r\n"
"250-ENHANCEDSTATUSCODES\r\n"
"250-AUTH PLAIN\r\n"
"250 DSN\r\n");
return 1;
}
break;
if (server_index == 1) {
"535 5.7.8 "
"Authentication credentials invalid\r\n");
sleep(10);
return -1;
}
break;
default:
break;
}
return 0;
}
{
}
/* client */
struct _authentication_failed {
unsigned int count;
};
struct _authentication_failed_peer {
unsigned int index;
};
static void
void *context)
{
(struct _authentication_failed_peer *)context;
if (debug) {
i_debug("LOGIN REPLY[%u]: %s",
}
case 0:
break;
case 1:
break;
}
}
static void
struct _authentication_failed_peer *pctx)
{
if (debug) {
i_debug("MAIL FROM REPLY[%u]: %s",
}
case 0:
break;
case 1:
break;
}
}
static void
struct _authentication_failed_peer *pctx)
{
if (debug) {
i_debug("RCPT TO REPLY[%u]: %s",
}
case 0:
break;
case 1:
break;
}
}
static void
struct _authentication_failed_peer *pctx)
{
if (debug) {
i_debug("RCPT DATA REPLY[%u]: %s",
}
}
static void
struct _authentication_failed_peer *pctx)
{
if (debug) {
i_debug("DATA REPLY[%u]: %s",
}
case 0:
break;
case 1:
break;
}
}
static void
{
if (debug)
}
}
static void
unsigned int index)
{
static const char *message =
"From: stephan@example.com\r\n"
"To: timo@example.com\r\n"
"Subject: Frop!\r\n"
"\r\n"
"Frop!\r\n";
test_client_authentication_failed_login_cb, (void *)pctx);
}
static bool
const struct smtp_client_settings *client_set)
{
unsigned int i;
return TRUE;
}
/* test */
static void test_authentication_failed(void)
{
test_begin("authentication failed");
test_end();
}
/*
* All tests
*/
static void (*const test_functions[])(void) = {
};
/*
* Test client
*/
static void
{
/* client settings */
}
static void test_client_deinit(void)
{
if (smtp_client != NULL)
smtp_client = NULL;
}
/*
* Test server
*/
/* client connection */
static void
{
const char *line;
if (test_server_input != NULL) {
return;
}
for (;;) {
const unsigned char *data;
int ret;
if (test_server_input_data != NULL) {
return;
}
}
if (ret == 0)
return;
i_error("Failed to read message payload: %s",
return;
}
"250 2.0.0 Ok: queued as 73BDE342129\r\n");
continue;
}
return;
}
if (test_server_input_line != NULL) {
return;
}
"250-testserver\r\n"
"250-PIPELINING\r\n"
"250-ENHANCEDSTATUSCODES\r\n"
"250 DSN\r\n");
return;
"235 2.7.0 "
"Authentication successful\r\n");
continue;
}
"250 2.1.0 Ok\r\n");
continue;
"250 2.1.5 Ok\r\n");
continue;
"354 End data with <CR><LF>.<CR><LF>\r\n");
continue;
break;
}
i_unreached();
}
}
static void
{
if (test_server_init != NULL) {
if (test_server_init(conn) != 0)
return;
}
if (test_server_input == NULL) {
}
}
static void
{
if (test_server_deinit != NULL)
}
static void
{
(struct server_connection *)_conn;
}
static void
{
int fd;
/* accept new client */
if (fd == -1)
return;
if (fd == -2) {
i_fatal("test server: accept() failed: %m");
}
}
/* */
};
};
{
/* open server socket */
/* close server socket */
}
/*
* Tests
*/
{
if (debug)
if (fd == -1) {
i_fatal("listen(%s:%u) failed: %m",
}
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++) {
server_pids[i] = -1;
}
}
}
server_pids_count = 0;
}
}
static void test_run_client_server(
const struct smtp_client_settings *client_set,
unsigned int server_tests_count,
{
unsigned int i;
server_pids = NULL;
server_pids_count = 0;
if (server_tests_count > 0) {
for (i = 0; i < server_tests_count; i++)
for (i = 0; i < server_tests_count; i++)
for (i = 0; i < server_tests_count; i++) {
i_fatal("fork() failed: %m");
if (server_pids[i] == 0) {
server_pids_count = 0;
hostpid_init();
if (debug)
/* child: server */
ioloop = io_loop_create();
server_test(i);
/* wait for it to be killed; this way, valgrind will not
object to this process going away inelegantly. */
sleep(60);
exit(1);
}
}
if (debug)
}
int fd;
i_unlink_if_exists("./dns-test");
if (fd == -1) {
i_fatal("listen(./dns-test) failed: %m");
}
i_fatal("fork() failed: %m");
if (dns_pid == 0) {
hostpid_init();
if (debug)
/* child: server */
ioloop = io_loop_create();
dns_test();
/* wait for it to be killed; this way, valgrind will not
object to this process going away inelegantly. */
sleep(60);
exit(1);
}
}
/* parent: client */
ioloop = io_loop_create();
if (client_test(client_set))
i_unlink_if_exists("./dns-test");
}
/*
* Main
*/
static void
{
if (terminating != 0)
terminating = 1;
/* make sure we don't leave any pesky children alive */
}
static void test_atexit(void)
{
}
{
int c;
switch (c) {
case 'D':
break;
default:
}
}
/* listen on localhost */
}