1 /* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "str.h"
5 #include "llist.h"
6 #include "array.h"
7 #include "path-util.h"
8 #include "hostpid.h"
9 #include "ioloop.h"
10 #include "istream.h"
11 #include "ostream.h"
12 #include "istream-base64.h"
13 #include "istream-crlf.h"
14 #include "iostream-temp.h"
15 #include "iostream-ssl.h"
16 #include "iostream-ssl-test.h"
17 #ifdef HAVE_OPENSSL
18 #include "iostream-openssl.h"
19 #endif
20 #include "connection.h"
21 #include "test-common.h"
22 #include "test-subprocess.h"
23 #include "smtp-server.h"
24 #include "smtp-client.h"
25 #include "smtp-client-connection.h"
26 #include "smtp-client-transaction.h"
27 
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <dirent.h>
33 
34 #define CLIENT_PROGRESS_TIMEOUT     60
35 #define SERVER_KILL_TIMEOUT_SECS    20
36 #define MAX_PARALLEL_PENDING        200
37 
38 static bool debug = FALSE;
39 static bool small_socket_buffers = FALSE;
40 static const char *failure = NULL;
41 
42 enum test_ssl_mode {
43 	TEST_SSL_MODE_NONE = 0,
44 	TEST_SSL_MODE_IMMEDIATE,
45 	TEST_SSL_MODE_STARTTLS
46 };
47 
48 static unsigned int test_max_pending = 1;
49 static bool test_unknown_size = FALSE;
50 static enum test_ssl_mode test_ssl_mode = TEST_SSL_MODE_NONE;
51 
52 static struct ip_addr bind_ip;
53 static in_port_t bind_port = 0;
54 static int fd_listen = -1;
55 
56 static void main_deinit(void);
57 
58 /*
59  * Test files
60  */
61 
62 static ARRAY_TYPE(const_string) files;
63 static pool_t files_pool;
64 
test_files_read_dir(const char * path)65 static void test_files_read_dir(const char *path)
66 {
67 	DIR *dirp;
68 
69 	/* open the directory */
70 	if ((dirp = opendir(path)) == NULL) {
71 		if (errno == ENOENT || errno == EACCES)
72 			return;
73 		i_fatal("test files: "
74 			"failed to open directory %s: %m", path);
75 	}
76 
77 	/* read entries */
78 	for (;;) {
79 		const char *file;
80 		struct dirent *dp;
81 		struct stat st;
82 #if 0
83 		if (array_count(&files) > 10)
84 			break;
85 #endif
86 		errno = 0;
87 		if ((dp = readdir(dirp)) == NULL)
88 			break;
89 		if (*dp->d_name == '.')
90 			continue;
91 
92 		file = t_abspath_to(dp->d_name, path);
93 		if (stat(file, &st) == 0) {
94 			if (S_ISREG(st.st_mode)) {
95 				file += 2; /* skip "./" */
96 				file = p_strdup(files_pool, file);
97 				array_push_back(&files, &file);
98 			} else if (S_ISDIR(st.st_mode)) {
99 				test_files_read_dir(file);
100 			}
101 		}
102 	}
103 
104 	if (errno != 0)
105 		i_fatal("test files: "
106 			"failed to read directory %s: %m", path);
107 
108 	/* Close the directory */
109 	if (closedir(dirp) < 0)
110 		i_error("test files: "
111 			"failed to close directory %s: %m", path);
112 }
113 
test_files_init(void)114 static void test_files_init(void)
115 {
116 	/* initialize file array */
117 	files_pool = pool_alloconly_create(
118 		MEMPOOL_GROWING"smtp_server_request", 4096);
119 	p_array_init(&files, files_pool, 512);
120 
121 	/* obtain all filenames */
122 	test_files_read_dir(".");
123 }
124 
test_files_deinit(void)125 static void test_files_deinit(void)
126 {
127 	pool_unref(&files_pool);
128 }
129 
test_file_open(const char * path)130 static struct istream *test_file_open(const char *path)
131 {
132 	struct istream *file;
133 	int fd;
134 
135 	fd = open(path, O_RDONLY);
136 	if (fd < 0) {
137 		if (errno != ENOENT && errno != EACCES) {
138 			i_fatal("test files: "
139 				"open(%s) failed: %m", path);
140 		}
141 		if (debug) {
142 			i_debug("test files: "
143 				"open(%s) failed: %m", path);
144 		}
145 		return NULL;
146 	}
147 
148 	file = i_stream_create_fd_autoclose(&fd, 40960);
149 	i_stream_set_name(file, path);
150 	return file;
151 }
152 
153 /*
154  * Test server
155  */
156 
157 struct client {
158 	pool_t pool;
159 	struct client *prev, *next;
160 
161 	struct smtp_server_connection *smtp_conn;
162 };
163 
164 struct client_transaction {
165 	struct client *client;
166 	struct smtp_server_cmd_ctx *data_cmd;
167 	struct smtp_server_transaction *trans;
168 
169 	const char *path;
170 
171 	struct istream *payload, *file;
172 };
173 
174 static struct smtp_server *smtp_server;
175 
176 static struct io *io_listen;
177 static struct client *clients;
178 
179 static int
client_transaction_read_more(struct client_transaction * ctrans)180 client_transaction_read_more(struct client_transaction *ctrans)
181 {
182 	struct istream *payload = ctrans->payload;
183 	const unsigned char *pdata, *fdata;
184 	size_t psize, fsize, pleft;
185 	off_t ret;
186 
187 	if (debug) {
188 		i_debug("test server: read more payload for [%s]",
189 			ctrans->path);
190 	}
191 
192 	/* read payload */
193 	while ((ret = i_stream_read_more(payload, &pdata, &psize)) > 0) {
194 		if (debug) {
195 			i_debug("test server: "
196 				"got data for [%s] (size=%d)",
197 				ctrans->path, (int)psize);
198 		}
199 		/* compare with file on disk */
200 		pleft = psize;
201 		while ((ret = i_stream_read_more(ctrans->file,
202 						 &fdata, &fsize)) > 0 &&
203 		       pleft > 0) {
204 			fsize = (fsize > pleft ? pleft : fsize);
205 			if (memcmp(pdata, fdata, fsize) != 0) {
206 				i_fatal("test server: "
207 					"received data does not match file [%s] "
208 					"(%"PRIuUOFF_T":%"PRIuUOFF_T")",
209 					ctrans->path, payload->v_offset,
210 					ctrans->file->v_offset);
211 			}
212 			i_stream_skip(ctrans->file, fsize);
213 			pleft -= fsize;
214 			pdata += fsize;
215 		}
216 		if (ret < 0 && ctrans->file->stream_errno != 0) {
217 			i_fatal("test server: "
218 				"failed to read file: %s",
219 				i_stream_get_error(ctrans->file));
220 		}
221 		i_stream_skip(payload, psize);
222 	}
223 
224 	if (ret == 0) {
225 		if (debug) {
226 			i_debug("test server: "
227 				"need more data for [%s]",
228 				ctrans->path);
229 		}
230 		/* we will be called again for this request */
231 		return 0;
232 	}
233 
234 	(void)i_stream_read(ctrans->file);
235 	if (payload->stream_errno != 0) {
236 		i_fatal("test server: "
237 			"failed to read transaction payload: %s",
238 			i_stream_get_error(payload));
239 	}
240 	if (i_stream_have_bytes_left(ctrans->file)) {
241 		if (i_stream_read_more(ctrans->file, &fdata, &fsize) <= 0)
242 			fsize = 0;
243 		i_fatal("test server: "
244 			"payload ended prematurely "
245 			"(at least %zu bytes left)", fsize);
246 	}
247 
248 	if (debug) {
249 		i_debug("test server: "
250 			"finished transaction for [%s]",
251 			ctrans->path);
252 	}
253 
254 	/* dereference payload stream; finishes the request */
255 	i_stream_unref(&payload);
256 	ctrans->payload = NULL;
257 	i_stream_unref(&ctrans->file);
258 
259 	/* finished */
260 	smtp_server_reply_all(ctrans->data_cmd, 250, "2.0.0", "OK");
261 	return 1;
262 }
263 
264 static void
client_transaction_handle_payload(struct client_transaction * ctrans,const char * path,struct istream * data_input)265 client_transaction_handle_payload(struct client_transaction *ctrans,
266 				  const char *path, struct istream *data_input)
267 {
268 	struct smtp_server_transaction *trans = ctrans->trans;
269 	struct istream *fstream;
270 
271 	ctrans->path = p_strdup(trans->pool, path);
272 
273 	if (debug) {
274 		i_debug("test server: got transaction for: %s",
275 			path);
276 	}
277 
278 	fstream = test_file_open(path);
279 	if (fstream == NULL)
280 		i_fatal("test server: failed to open: %s", path);
281 
282 	i_stream_ref(data_input);
283 	ctrans->payload = data_input;
284 	i_assert(ctrans->payload != NULL);
285 
286 	ctrans->file = i_stream_create_base64_encoder(fstream, 80, TRUE),
287 	i_stream_unref(&fstream);
288 
289 	(void)client_transaction_read_more(ctrans);
290 }
291 
292 /* transaction */
293 
294 static struct client_transaction *
client_transaction_init(struct client * client,struct smtp_server_cmd_ctx * data_cmd,struct smtp_server_transaction * trans)295 client_transaction_init(struct client *client,
296 			struct smtp_server_cmd_ctx *data_cmd,
297 			struct smtp_server_transaction *trans)
298 {
299 	struct client_transaction *ctrans;
300 	pool_t pool = trans->pool;
301 
302 	ctrans = p_new(pool, struct client_transaction, 1);
303 	ctrans->client = client;
304 	ctrans->trans = trans;
305 	ctrans->data_cmd = data_cmd;
306 
307 	return ctrans;
308 }
309 
client_transaction_deinit(struct client_transaction ** _ctrans)310 static void client_transaction_deinit(struct client_transaction **_ctrans)
311 {
312 	struct client_transaction *ctrans = *_ctrans;
313 
314 	*_ctrans = NULL;
315 
316 	i_stream_unref(&ctrans->payload);
317 	i_stream_unref(&ctrans->file);
318 }
319 
320 static void
test_server_conn_trans_free(void * context ATTR_UNUSED,struct smtp_server_transaction * trans)321 test_server_conn_trans_free(void *context ATTR_UNUSED,
322 			    struct smtp_server_transaction *trans)
323 {
324 	struct client_transaction *ctrans =
325 		(struct client_transaction *)trans->context;
326 	client_transaction_deinit(&ctrans);
327 }
328 
329 static int
test_server_conn_cmd_rcpt(void * conn_ctx ATTR_UNUSED,struct smtp_server_cmd_ctx * cmd ATTR_UNUSED,struct smtp_server_recipient * rcpt)330 test_server_conn_cmd_rcpt(void *conn_ctx ATTR_UNUSED,
331 			  struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
332 			  struct smtp_server_recipient *rcpt)
333 {
334 	if (debug) {
335 		i_debug("test server: RCPT TO:%s",
336 			smtp_address_encode(rcpt->path));
337 	}
338 
339 	return 1;
340 }
341 
342 static int
test_server_conn_cmd_data_begin(void * conn_ctx,struct smtp_server_cmd_ctx * cmd,struct smtp_server_transaction * trans,struct istream * data_input)343 test_server_conn_cmd_data_begin(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
344 				struct smtp_server_transaction *trans,
345 				struct istream *data_input)
346 {
347 	struct client *client = (struct client *)conn_ctx;
348 	const char *fpath = trans->params.envid;
349 	struct client_transaction *ctrans;
350 
351 	i_assert(fpath != NULL);
352 
353 	if (debug)
354 		i_debug("test server: DATA (file path = %s)", fpath);
355 
356 	ctrans = client_transaction_init(client, cmd, trans);
357 	client_transaction_handle_payload(ctrans, fpath, data_input);
358 	trans->context = ctrans;
359 	return 0;
360 }
361 
362 static int
test_server_conn_cmd_data_continue(void * conn_ctx ATTR_UNUSED,struct smtp_server_cmd_ctx * cmd,struct smtp_server_transaction * trans)363 test_server_conn_cmd_data_continue(void *conn_ctx ATTR_UNUSED,
364 				   struct smtp_server_cmd_ctx *cmd,
365 				   struct smtp_server_transaction *trans)
366 {
367 	struct client_transaction *ctrans =
368 		(struct client_transaction *)trans->context;
369 
370 	if (debug)
371 		i_debug("test server: DATA continue");
372 
373 	ctrans->data_cmd = cmd;
374 
375 	return client_transaction_read_more(ctrans);
376 }
377 
378 /* client connection */
379 
380 static void test_server_connection_free(void *context);
381 
382 static const struct smtp_server_callbacks server_callbacks =
383 {
384 	.conn_cmd_rcpt = test_server_conn_cmd_rcpt,
385 	.conn_cmd_data_begin = test_server_conn_cmd_data_begin,
386 	.conn_cmd_data_continue = test_server_conn_cmd_data_continue,
387 
388 	.conn_trans_free = test_server_conn_trans_free,
389 
390 	.conn_free = test_server_connection_free,
391 };
392 
client_init(int fd)393 static void client_init(int fd)
394 {
395 	struct client *client;
396 	pool_t pool;
397 
398 	net_set_nonblock(fd, TRUE);
399 
400 	pool = pool_alloconly_create("client", 256);
401 	client = p_new(pool, struct client, 1);
402 	client->pool = pool;
403 
404 	client->smtp_conn = smtp_server_connection_create(
405 		smtp_server, fd, fd, NULL, 0,
406 		(test_ssl_mode == TEST_SSL_MODE_IMMEDIATE),
407 		NULL, &server_callbacks, client);
408 	smtp_server_connection_start(client->smtp_conn);
409 	DLLIST_PREPEND(&clients, client);
410 }
411 
client_deinit(struct client ** _client)412 static void client_deinit(struct client **_client)
413 {
414 	struct client *client = *_client;
415 
416 	*_client = NULL;
417 
418 	DLLIST_REMOVE(&clients, client);
419 
420 	if (client->smtp_conn != NULL) {
421 		smtp_server_connection_terminate(&client->smtp_conn,
422 						 NULL, "deinit");
423 	}
424 	pool_unref(&client->pool);
425 }
426 
test_server_connection_free(void * context)427 static void test_server_connection_free(void *context)
428 {
429 	struct client *client = context;
430 
431 	client->smtp_conn = NULL;
432 	client_deinit(&client);
433 }
434 
client_accept(void * context ATTR_UNUSED)435 static void client_accept(void *context ATTR_UNUSED)
436 {
437 	int fd;
438 
439 	for (;;) {
440 		/* accept new client */
441 		if ((fd = net_accept(fd_listen, NULL, NULL)) < 0) {
442 			if (errno == EAGAIN)
443 				break;
444 			if (errno == ECONNABORTED)
445 				continue;
446 			i_fatal("test server: accept() failed: %m");
447 		}
448 
449 		client_init(fd);
450 	}
451 }
452 
453 /* */
454 
test_server_init(const struct smtp_server_settings * server_set)455 static void test_server_init(const struct smtp_server_settings *server_set)
456 {
457 	/* open server socket */
458 	io_listen = io_add(fd_listen, IO_READ, client_accept, NULL);
459 
460 	smtp_server = smtp_server_init(server_set);
461 }
462 
test_server_deinit(void)463 static void test_server_deinit(void)
464 {
465 	/* close server socket */
466 	io_remove(&io_listen);
467 
468 	/* deinitialize */
469 	smtp_server_deinit(&smtp_server);
470 }
471 
472 /*
473  * Test client
474  */
475 
476 struct test_client_connection {
477 	struct smtp_client_connection *conn;
478 	struct smtp_client_transaction *trans;
479 };
480 
481 struct test_client_transaction {
482 	struct test_client_transaction *prev, *next;
483 	struct test_client_connection *conn;
484 
485 	struct io *io;
486 	struct istream *file;
487 	unsigned int files_idx;
488 };
489 
490 static struct test_client_connection test_conns[MAX_PARALLEL_PENDING];
491 static struct smtp_client *smtp_client;
492 static enum smtp_protocol client_protocol;
493 static struct test_client_transaction *client_requests;
494 static unsigned int client_files_first, client_files_last;
495 static struct timeout *client_to = NULL;
496 struct timeout *to_client_progress = NULL;
497 
test_client_connection_get(void)498 static struct test_client_connection *test_client_connection_get(void)
499 {
500 	unsigned int i;
501 	enum smtp_client_connection_ssl_mode ssl_mode;
502 
503 	for (i = 0; i < MAX_PARALLEL_PENDING; i++) {
504 		if (test_conns[i].trans == NULL)
505 			break;
506 	}
507 
508 	i_assert(i < MAX_PARALLEL_PENDING);
509 
510 	switch (test_ssl_mode) {
511 	case TEST_SSL_MODE_NONE:
512 	default:
513 		ssl_mode = SMTP_CLIENT_SSL_MODE_NONE;
514 		break;
515 	case TEST_SSL_MODE_IMMEDIATE:
516 		ssl_mode = SMTP_CLIENT_SSL_MODE_IMMEDIATE;
517 		break;
518 	case TEST_SSL_MODE_STARTTLS:
519 		ssl_mode = SMTP_CLIENT_SSL_MODE_STARTTLS;
520 		break;
521 	}
522 
523 	if (test_conns[i].conn == NULL) {
524 		test_conns[i].conn = smtp_client_connection_create(
525 			smtp_client, client_protocol,
526 			net_ip2addr(&bind_ip), bind_port,
527 			ssl_mode, NULL);
528 	}
529 	return &test_conns[i];
530 }
531 
test_client_transaction_new(void)532 static struct test_client_transaction *test_client_transaction_new(void)
533 {
534 	struct test_client_transaction *tctrans;
535 
536 	tctrans = i_new(struct test_client_transaction, 1);
537 	DLLIST_PREPEND(&client_requests, tctrans);
538 
539 	return tctrans;
540 }
541 
542 static void
test_client_transaction_destroy(struct test_client_transaction * tctrans)543 test_client_transaction_destroy(struct test_client_transaction *tctrans)
544 {
545 	smtp_client_transaction_destroy(&tctrans->conn->trans);
546 	io_remove(&tctrans->io);
547 	i_stream_unref(&tctrans->file);
548 
549 	DLLIST_REMOVE(&client_requests, tctrans);
550 	i_free(tctrans);
551 }
552 
553 static void test_client_continue(void *dummy);
554 
test_client_finished(unsigned int files_idx)555 static void test_client_finished(unsigned int files_idx)
556 {
557 	const char **paths;
558 	unsigned int count;
559 
560 	if (debug) {
561 		i_debug("test client: "
562 			"finished [%u]", files_idx);
563 	}
564 
565 	paths = array_get_modifiable(&files, &count);
566 	i_assert(files_idx < count);
567 	i_assert(client_files_first < count);
568 	i_assert(paths[files_idx] != NULL);
569 
570 	paths[files_idx] = NULL;
571 	if (client_to == NULL)
572 		client_to = timeout_add_short(0, test_client_continue, NULL);
573 }
574 
575 static void
test_client_transaction_finish(struct test_client_transaction * tctrans)576 test_client_transaction_finish(struct test_client_transaction *tctrans)
577 {
578 	tctrans->conn->trans = NULL;
579 	if (io_loop_is_running(current_ioloop))
580 		test_client_finished(tctrans->files_idx);
581 	test_client_transaction_destroy(tctrans);
582 }
583 
584 static void
test_client_transaction_rcpt(const struct smtp_reply * reply,struct test_client_transaction * tctrans)585 test_client_transaction_rcpt(const struct smtp_reply *reply,
586 			     struct test_client_transaction *tctrans)
587 {
588 	const char **paths;
589 	const char *path;
590 	unsigned int count;
591 
592 	if (to_client_progress != NULL)
593 		timeout_reset(to_client_progress);
594 
595 	paths = array_get_modifiable(&files, &count);
596 	i_assert(tctrans->files_idx < count);
597 	i_assert(client_files_first < count);
598 	path = paths[tctrans->files_idx];
599 	i_assert(path != NULL);
600 
601 	if (reply->status / 100 != 2) {
602 		i_fatal("test client: "
603 			"SMTP RCPT for %s failed: %s",
604 			path, smtp_reply_log(reply));
605 	}
606 }
607 
608 static void
test_client_transaction_rcpt_data(const struct smtp_reply * reply ATTR_UNUSED,struct test_client_transaction * tctrans)609 test_client_transaction_rcpt_data(const struct smtp_reply *reply ATTR_UNUSED,
610 				  struct test_client_transaction *tctrans)
611 {
612 	const char **paths;
613 	const char *path;
614 	unsigned int count;
615 
616 	if (to_client_progress != NULL)
617 		timeout_reset(to_client_progress);
618 
619 	paths = array_get_modifiable(&files, &count);
620 	i_assert(tctrans->files_idx < count);
621 	i_assert(client_files_first < count);
622 	path = paths[tctrans->files_idx];
623 	i_assert(path != NULL);
624 
625 	if (reply->status / 100 != 2) {
626 		i_fatal("test client: "
627 			"SMTP DATA for %s failed: %s",
628 			path, smtp_reply_log(reply));
629 	}
630 }
631 
632 static void
test_client_transaction_data(const struct smtp_reply * reply,struct test_client_transaction * tctrans)633 test_client_transaction_data(const struct smtp_reply *reply,
634 			     struct test_client_transaction *tctrans)
635 {
636 	const char **paths;
637 	const char *path;
638 	unsigned int count;
639 
640 	if (to_client_progress != NULL)
641 		timeout_reset(to_client_progress);
642 
643 	if (debug) {
644 		i_debug("test client: "
645 			"got response for DATA [%u]",
646 			tctrans->files_idx);
647 	}
648 
649 	paths = array_get_modifiable(&files, &count);
650 	i_assert(tctrans->files_idx < count);
651 	i_assert(client_files_first < count);
652 	path = paths[tctrans->files_idx];
653 	i_assert(path != NULL);
654 
655 	if (debug) {
656 		i_debug("test client: "
657 			"path for [%u]: %s",
658 			tctrans->files_idx, path);
659 	}
660 
661 	if (reply->status / 100 != 2) {
662 		i_fatal("test client: "
663 			"SMTP transaction for %s failed: %s",
664 			path, smtp_reply_log(reply));
665 	}
666 }
667 
test_client_continue(void * dummy ATTR_UNUSED)668 static void test_client_continue(void *dummy ATTR_UNUSED)
669 {
670 	struct test_client_transaction *tctrans;
671 	struct smtp_params_mail mail_params;
672 	const char **paths;
673 	unsigned int count, pending_count, i;
674 
675 	if (debug)
676 		i_debug("test client: continue");
677 
678 	timeout_remove(&client_to);
679 	if (to_client_progress != NULL)
680 		timeout_reset(to_client_progress);
681 
682 	paths = array_get_modifiable(&files, &count);
683 
684 	i_assert(client_files_first <= count);
685 	i_assert(client_files_last <= count);
686 
687 	i_assert(client_files_first <= client_files_last);
688 	for (; (client_files_first < client_files_last &&
689 		paths[client_files_first] == NULL); client_files_first++);
690 
691 	pending_count = 0;
692 	for (i = client_files_first; i < client_files_last; i++) {
693 		if (paths[i] != NULL)
694 			pending_count++;
695 	}
696 
697 	if (debug) {
698 		i_debug("test client: finished until [%u/%u]; "
699 			"sending until [%u/%u] (%u pending)",
700 			client_files_first-1, count,
701 			client_files_last, count, pending_count);
702 	}
703 
704 	if (debug && client_files_first < count) {
705 		const char *path = paths[client_files_first];
706 		i_debug("test client: "
707 			"next blocking: %s [%d]",
708 			(path == NULL ? "none" : path),
709 			client_files_first);
710 	}
711 
712 	if (client_files_first >= count) {
713 		io_loop_stop(current_ioloop);
714 		return;
715 	}
716 
717 	for (; client_files_last < count && pending_count < test_max_pending;
718 	     client_files_last++, pending_count++) {
719 		struct istream *fstream, *payload;
720 		const char *path = paths[client_files_last];
721 		unsigned int r, rcpts;
722 
723 		fstream = test_file_open(path);
724 		if (fstream == NULL) {
725 			paths[client_files_last] = NULL;
726 			if (debug) {
727 				i_debug("test client: "
728 					"skipping %s [%u]",
729 					path, client_files_last);
730 			}
731 			if (client_to == NULL) {
732 				client_to = timeout_add_short(
733 					0, test_client_continue, NULL);
734 			}
735 			continue;
736 		}
737 
738 		if (debug) {
739 			i_debug("test client: "
740 				"retrieving %s [%u]",
741 				path, client_files_last);
742 		}
743 
744 		tctrans = test_client_transaction_new();
745 		tctrans->files_idx = client_files_last;
746 		tctrans->conn = test_client_connection_get();
747 
748 		i_zero(&mail_params);
749 		mail_params.envid = path;
750 
751 		tctrans->conn->trans = smtp_client_transaction_create(
752 			tctrans->conn->conn,
753 			&((struct smtp_address){.localpart = "user",
754 						.domain = "example.com"}),
755 			&mail_params, 0,
756 			test_client_transaction_finish, tctrans);
757 
758 		rcpts = tctrans->files_idx % 10 + 1;
759 		for (r = 1; r <= rcpts; r++) {
760 			smtp_client_transaction_add_rcpt(
761 				tctrans->conn->trans,
762 				smtp_address_create_temp(
763 					t_strdup_printf("rcpt%u", r),
764 					"example.com"), NULL,
765 				test_client_transaction_rcpt,
766 				test_client_transaction_rcpt_data, tctrans);
767 		}
768 
769 		if (!test_unknown_size) {
770 			payload = i_stream_create_base64_encoder(
771 				fstream, 80, TRUE);
772 		} else {
773 			struct istream *b64_stream =
774 				i_stream_create_base64_encoder(
775 					fstream, 80, FALSE);
776 			payload = i_stream_create_crlf(b64_stream);
777 			i_stream_unref(&b64_stream);
778 		}
779 
780 		if (debug) {
781 			uoff_t raw_size = UOFF_T_MAX, b64_size = UOFF_T_MAX;
782 
783 			(void)i_stream_get_size(fstream, TRUE, &raw_size);
784 			(void)i_stream_get_size(payload, TRUE, &b64_size);
785 			i_debug("test client: "
786 				"sending %"PRIuUOFF_T"/%"PRIuUOFF_T" bytes payload %s [%u]",
787 				raw_size, b64_size, path, client_files_last);
788 		}
789 
790 		smtp_client_transaction_send(tctrans->conn->trans, payload,
791 					     test_client_transaction_data,
792 					     tctrans);
793 
794 		i_stream_unref(&payload);
795 		i_stream_unref(&fstream);
796 	}
797 }
798 
test_client_progress_timeout(void * context ATTR_UNUSED)799 static void test_client_progress_timeout(void *context ATTR_UNUSED)
800 {
801 	/* Terminate test due to lack of progress */
802 	failure = "Test is hanging";
803 	timeout_remove(&to_client_progress);
804 	io_loop_stop(current_ioloop);
805 }
806 
807 static void
test_client(enum smtp_protocol protocol,const struct smtp_client_settings * client_set)808 test_client(enum smtp_protocol protocol,
809 	    const struct smtp_client_settings *client_set)
810 {
811 	client_protocol = protocol;
812 
813 	if (!small_socket_buffers) {
814 		to_client_progress = timeout_add(
815 			CLIENT_PROGRESS_TIMEOUT*1000,
816 			test_client_progress_timeout, NULL);
817 	}
818 
819 	/* create client */
820 	smtp_client = smtp_client_init(client_set);
821 
822 	/* start querying server */
823 	client_files_first = client_files_last = 0;
824 	test_client_continue(NULL);
825 }
826 
test_client_init(void)827 static void test_client_init(void)
828 {
829 	i_zero(&test_conns);
830 }
831 
test_client_deinit(void)832 static void test_client_deinit(void)
833 {
834 	timeout_remove(&client_to);
835 	timeout_remove(&to_client_progress);
836 	smtp_client_deinit(&smtp_client);
837 
838 	i_zero(&test_conns);
839 }
840 
841 /*
842  * Tests
843  */
844 
845 struct test_server_data {
846 	const struct smtp_server_settings *set;
847 };
848 
test_open_server_fd(void)849 static void test_open_server_fd(void)
850 {
851 	if (fd_listen != -1)
852 		i_close_fd(&fd_listen);
853 	fd_listen = net_listen(&bind_ip, &bind_port, 128);
854 	if (fd_listen == -1) {
855 		i_fatal("listen(%s:%u) failed: %m",
856 			net_ip2addr(&bind_ip), bind_port);
857 	}
858 	net_set_nonblock(fd_listen, TRUE);
859 }
860 
test_run_server(struct test_server_data * data)861 static int test_run_server(struct test_server_data *data)
862 {
863 	const struct smtp_server_settings *server_set = data->set;
864 	struct ioloop *ioloop;
865 
866 	i_set_failure_prefix("SERVER: ");
867 
868 	if (debug)
869 		i_debug("PID=%s", my_pid);
870 
871 	ioloop = io_loop_create();
872 	test_server_init(server_set);
873 	io_loop_run(ioloop);
874 	test_server_deinit();
875 	io_loop_destroy(&ioloop);
876 
877 	if (debug)
878 		i_debug("Terminated");
879 
880 	i_close_fd(&fd_listen);
881 	test_files_deinit();
882 	main_deinit();
883 	return 0;
884 }
885 
886 static void
test_run_client(enum smtp_protocol protocol,struct smtp_client_settings * client_set,void (* client_init)(enum smtp_protocol protocol,const struct smtp_client_settings * client_set))887 test_run_client(
888 	enum smtp_protocol protocol, struct smtp_client_settings *client_set,
889 	void (*client_init)(enum smtp_protocol protocol,
890 			    const struct smtp_client_settings *client_set))
891 {
892 	struct ioloop *ioloop;
893 
894 	i_set_failure_prefix("CLIENT: ");
895 
896 	if (debug)
897 		i_debug("client: PID=%s", my_pid);
898 
899 	ioloop = io_loop_create();
900 	test_client_init();
901 	client_init(protocol, client_set);
902 	io_loop_run(ioloop);
903 	test_client_deinit();
904 	io_loop_destroy(&ioloop);
905 
906 	if (debug)
907 		i_debug("Terminated");
908 }
909 
910 static void
test_run_client_server(enum smtp_protocol protocol,struct smtp_client_settings * client_set,struct smtp_server_settings * server_set,void (* client_init)(enum smtp_protocol protocol,const struct smtp_client_settings * client_set))911 test_run_client_server(
912 	enum smtp_protocol protocol,
913 	struct smtp_client_settings *client_set,
914 	struct smtp_server_settings *server_set,
915 	void (*client_init)(enum smtp_protocol protocol,
916 			    const struct smtp_client_settings *client_set))
917 {
918 	struct test_server_data data;
919 
920 	if (test_ssl_mode == TEST_SSL_MODE_STARTTLS)
921 		server_set->capabilities |= SMTP_CAPABILITY_STARTTLS;
922 
923 	failure = NULL;
924 
925 	i_zero(&data);
926 	data.set = server_set;
927 
928 	test_files_init();
929 
930 	/* Fork server */
931 	test_open_server_fd();
932 	test_subprocess_fork(test_run_server, &data, FALSE);
933 	i_close_fd(&fd_listen);
934 
935 	/* Run client */
936 	test_run_client(protocol, client_set, client_init);
937 
938 	i_unset_failure_prefix();
939 	bind_port = 0;
940 	test_subprocess_kill_all(SERVER_KILL_TIMEOUT_SECS);
941 	test_files_deinit();
942 }
943 
944 static void
test_run_scenarios(enum smtp_protocol protocol,enum smtp_capability capabilities,void (* client_init)(enum smtp_protocol protocol,const struct smtp_client_settings * client_set))945 test_run_scenarios(
946 	enum smtp_protocol protocol,
947 	enum smtp_capability capabilities,
948 	void (*client_init)(enum smtp_protocol protocol,
949 			    const struct smtp_client_settings *client_set))
950 {
951 	struct smtp_server_settings smtp_server_set;
952 	struct smtp_client_settings smtp_client_set;
953 	struct ssl_iostream_settings ssl_server_set, ssl_client_set;
954 
955 	/* ssl settings */
956 	ssl_iostream_test_settings_server(&ssl_server_set);
957 	ssl_server_set.verbose = debug;
958 	ssl_iostream_test_settings_client(&ssl_client_set);
959 	ssl_client_set.verbose = debug;
960 
961 	/* server settings */
962 	i_zero(&smtp_server_set);
963 	smtp_server_set.protocol = protocol;
964 	smtp_server_set.capabilities = capabilities;
965 	smtp_server_set.hostname = "localhost";
966 	smtp_server_set.max_client_idle_time_msecs =
967 		CLIENT_PROGRESS_TIMEOUT*1000;
968 	smtp_server_set.max_pipelined_commands = 1;
969 	smtp_server_set.auth_optional = TRUE;
970 	smtp_server_set.ssl = &ssl_server_set;
971 	smtp_server_set.debug = debug;
972 
973 	/* client settings */
974 	i_zero(&smtp_client_set);
975 	smtp_client_set.my_hostname = "localhost";
976 	smtp_client_set.temp_path_prefix = "/tmp";
977 	smtp_client_set.command_timeout_msecs = CLIENT_PROGRESS_TIMEOUT*1000;
978 	smtp_client_set.connect_timeout_msecs = CLIENT_PROGRESS_TIMEOUT*1000;
979 	smtp_client_set.ssl = &ssl_client_set;
980 	smtp_client_set.debug = debug;
981 
982 	if (small_socket_buffers) {
983 		smtp_client_set.socket_send_buffer_size = 4096;
984 		smtp_client_set.socket_recv_buffer_size = 4096;
985 		smtp_client_set.command_timeout_msecs = 20*60*1000;
986 		smtp_client_set.connect_timeout_msecs = 20*60*1000;
987 		smtp_server_set.socket_send_buffer_size = 4096;
988 		smtp_server_set.socket_recv_buffer_size = 4096;
989 	}
990 
991 	test_max_pending = 1;
992 	test_unknown_size = FALSE;
993 	test_ssl_mode = TEST_SSL_MODE_NONE;
994 	test_run_client_server(protocol, &smtp_client_set, &smtp_server_set,
995 			       client_init);
996 
997 	test_out_reason("sequential", (failure == NULL), failure);
998 
999 	test_max_pending = MAX_PARALLEL_PENDING;
1000 	test_unknown_size = FALSE;
1001 	test_ssl_mode = TEST_SSL_MODE_NONE;
1002 	test_run_client_server(protocol, &smtp_client_set, &smtp_server_set,
1003 			       client_init);
1004 
1005 	test_out_reason("parallel", (failure == NULL), failure);
1006 
1007 	smtp_server_set.max_pipelined_commands = 5;
1008 	smtp_server_set.capabilities |= SMTP_CAPABILITY_PIPELINING;
1009 	test_max_pending = MAX_PARALLEL_PENDING;
1010 	test_unknown_size = FALSE;
1011 	test_ssl_mode = TEST_SSL_MODE_NONE;
1012 	test_run_client_server(protocol, &smtp_client_set, &smtp_server_set,
1013 			       client_init);
1014 
1015 	test_out_reason("parallel pipelining", (failure == NULL), failure);
1016 
1017 	smtp_server_set.max_pipelined_commands = 5;
1018 	smtp_server_set.capabilities |= SMTP_CAPABILITY_PIPELINING;
1019 	test_max_pending = MAX_PARALLEL_PENDING;
1020 	test_unknown_size = TRUE;
1021 	test_ssl_mode = TEST_SSL_MODE_NONE;
1022 	test_run_client_server(protocol, &smtp_client_set, &smtp_server_set,
1023 			       client_init);
1024 
1025 	test_out_reason("unknown payload size", (failure == NULL), failure);
1026 
1027 #ifdef HAVE_OPENSSL
1028 	smtp_server_set.max_pipelined_commands = 5;
1029 	smtp_server_set.capabilities |= SMTP_CAPABILITY_PIPELINING;
1030 	test_max_pending = MAX_PARALLEL_PENDING;
1031 	test_unknown_size = FALSE;
1032 	test_ssl_mode = TEST_SSL_MODE_IMMEDIATE;
1033 	test_run_client_server(protocol, &smtp_client_set, &smtp_server_set,
1034 			       client_init);
1035 
1036 	test_out_reason("parallel pipelining ssl",
1037 			(failure == NULL), failure);
1038 
1039 	smtp_server_set.max_pipelined_commands = 5;
1040 	smtp_server_set.capabilities |= SMTP_CAPABILITY_PIPELINING;
1041 	test_max_pending = MAX_PARALLEL_PENDING;
1042 	test_unknown_size = FALSE;
1043 	test_ssl_mode = TEST_SSL_MODE_STARTTLS;
1044 	test_run_client_server(protocol, &smtp_client_set, &smtp_server_set,
1045 			       client_init);
1046 
1047 	test_out_reason("parallel pipelining startls",
1048 			(failure == NULL), failure);
1049 #endif
1050 }
1051 
test_smtp_normal(void)1052 static void test_smtp_normal(void)
1053 {
1054 	test_begin("smtp payload - normal");
1055 	test_run_scenarios(SMTP_PROTOCOL_SMTP,
1056 			   SMTP_CAPABILITY_DSN, test_client);
1057 	test_end();
1058 }
1059 
test_smtp_chunking(void)1060 static void test_smtp_chunking(void)
1061 {
1062 	test_begin("smtp payload - chunking");
1063 	test_run_scenarios(SMTP_PROTOCOL_SMTP,
1064 			   SMTP_CAPABILITY_DSN | SMTP_CAPABILITY_CHUNKING,
1065 			   test_client);
1066 	test_end();
1067 }
1068 
test_lmtp_normal(void)1069 static void test_lmtp_normal(void)
1070 {
1071 	test_begin("lmtp payload - normal");
1072 	test_run_scenarios(SMTP_PROTOCOL_LMTP,
1073 			   SMTP_CAPABILITY_DSN, test_client);
1074 	test_end();
1075 }
1076 
test_lmtp_chunking(void)1077 static void test_lmtp_chunking(void)
1078 {
1079 	test_begin("lmtp payload - chunking");
1080 	test_run_scenarios(SMTP_PROTOCOL_LMTP,
1081 			   SMTP_CAPABILITY_DSN | SMTP_CAPABILITY_CHUNKING,
1082 			   test_client);
1083 	test_end();
1084 }
1085 
1086 static void (*const test_functions[])(void) = {
1087 	test_smtp_normal,
1088 	test_smtp_chunking,
1089 	test_lmtp_normal,
1090 	test_lmtp_chunking,
1091 	NULL
1092 };
1093 
1094 /*
1095  * Main
1096  */
1097 
main_init(void)1098 static void main_init(void)
1099 {
1100 #ifdef HAVE_OPENSSL
1101 	ssl_iostream_openssl_init();
1102 #endif
1103 }
1104 
main_deinit(void)1105 static void main_deinit(void)
1106 {
1107 	ssl_iostream_context_cache_free();
1108 #ifdef HAVE_OPENSSL
1109 	ssl_iostream_openssl_deinit();
1110 #endif
1111 }
1112 
main(int argc,char * argv[])1113 int main(int argc, char *argv[])
1114 {
1115 	int c;
1116 	int ret;
1117 
1118 	lib_init();
1119 	main_init();
1120 
1121 	while ((c = getopt(argc, argv, "DS")) > 0) {
1122 		switch (c) {
1123 		case 'D':
1124 			debug = TRUE;
1125 			break;
1126 		case 'S':
1127 			small_socket_buffers = TRUE;
1128 			break;
1129 		default:
1130 			i_fatal("Usage: %s [-D][-S]", argv[0]);
1131 		}
1132 	}
1133 
1134 	test_subprocesses_init(debug);
1135 
1136 	/* listen on localhost */
1137 	i_zero(&bind_ip);
1138 	bind_ip.family = AF_INET;
1139 	bind_ip.u.ip4.s_addr = htonl(INADDR_LOOPBACK);
1140 
1141 	ret = test_run(test_functions);
1142 
1143 	test_subprocesses_deinit();
1144 	main_deinit();
1145 	lib_deinit();
1146 
1147 	return ret;
1148 }
1149