struct smtp_server_cmd_mail *mail_data;
enum smtp_address_parse_flags path_parse_flags;
const char *const *param_extensions = NULL;
- struct smtp_address *path;
+ struct smtp_address *path = NULL;
enum smtp_param_parse_error pperror;
const char *error;
int ret;
"Unexpected whitespace before path");
return;
}
- path_parse_flags = SMTP_ADDRESS_PARSE_FLAG_ALLOW_EMPTY;
+ path_parse_flags =
+ SMTP_ADDRESS_PARSE_FLAG_ALLOW_EMPTY |
+ SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW;
if (*params != '\0' &&
- (set->workarounds & SMTP_SERVER_WORKAROUND_MAILBOX_FOR_PATH) != 0)
+ (set->mail_path_allow_broken ||
+ (set->workarounds & SMTP_SERVER_WORKAROUND_MAILBOX_FOR_PATH) != 0))
path_parse_flags |= SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL;
- if (smtp_address_parse_path_full(pool_datastack_create(), params,
- path_parse_flags, &path, &error,
- ¶ms) < 0) {
- smtp_server_reply(cmd, 501, "5.5.4", "Invalid FROM: %s", error);
+ if (set->mail_path_allow_broken) {
+ path_parse_flags |=
+ SMTP_ADDRESS_PARSE_FLAG_ALLOW_BAD_LOCALPART |
+ SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN;
+ }
+ ret = smtp_address_parse_path_full(pool_datastack_create(), params,
+ path_parse_flags, &path, &error,
+ ¶ms);
+ if (ret < 0 && !smtp_address_is_broken(path)) {
+ smtp_server_reply(cmd, 501, "5.5.4",
+ "Invalid FROM: %s", error);
return;
}
if (*params == ' ')
params++;
else if (*params != '\0') {
- smtp_server_reply(cmd, 501, "5.5.4",
+ smtp_server_reply(
+ cmd, 501, "5.5.4",
"Invalid FROM: Invalid character in path");
return;
}
+ if (ret < 0) {
+ e_debug(conn->event, "Invalid FROM: %s "
+ "(proceeding with <> as sender)", error);
+ }
mail_data = p_new(cmd->pool, struct smtp_server_cmd_mail, 1);
test_end();
}
+/*
+ * MAIL broken path
+ */
+
+/* client */
+
+struct _mail_broken_path_client {
+ struct smtp_reply_parser *parser;
+ unsigned int reply;
+
+ bool replied:1;
+};
+
+static void
+test_mail_broken_path_client_input(struct client_connection *conn)
+{
+ struct _mail_broken_path_client *ctx = conn->context;
+ struct smtp_reply *reply;
+ const char *error;
+ int ret;
+
+ while ((ret=smtp_reply_parse_next(ctx->parser, FALSE,
+ &reply, &error)) > 0) {
+ if (debug)
+ i_debug("REPLY: %s", smtp_reply_log(reply));
+
+ switch (ctx->reply++) {
+ case 0: /* greeting */
+ i_assert(reply->status == 220);
+ break;
+ case 1: /* bad command reply */
+ switch (client_index) {
+ case 0: case 1: case 2: case 4: case 5:
+ case 6: case 7: case 8:
+ i_assert(reply->status == 501);
+ break;
+ case 3: case 9: case 10: case 11: case 12: case 13:
+ case 14: case 15: case 16: case 17:
+ i_assert(reply->status == 250);
+ break;
+ default:
+ i_info("STATUS: %u", reply->status);
+ i_unreached();
+ }
+ ctx->replied = TRUE;
+ io_loop_stop(ioloop);
+ connection_disconnect(&conn->conn);
+ return;
+ default:
+ i_unreached();
+ }
+ }
+
+ i_assert(ret >= 0);
+}
+
+static void
+test_mail_broken_path_client_connected(struct client_connection *conn)
+{
+ struct _mail_broken_path_client *ctx;
+
+ ctx = p_new(conn->pool, struct _mail_broken_path_client, 1);
+ ctx->parser = smtp_reply_parser_init(conn->conn.input, (size_t)-1);
+ conn->context = ctx;
+
+ switch (client_index) {
+ case 0:
+ o_stream_nsend_str(conn->conn.output,
+ "MAIL FROM: <hendrik@example.com>\r\n");
+ break;
+ case 1:
+ o_stream_nsend_str(conn->conn.output,
+ "MAIL FROM:\t<hendrik@example.com>\r\n");
+ break;
+ case 2:
+ o_stream_nsend_str(conn->conn.output,
+ "MAIL FROM:\t <hendrik@example.com>\r\n");
+ break;
+ case 3:
+ o_stream_nsend_str(conn->conn.output,
+ "MAIL FROM:hendrik@example.com\r\n");
+ break;
+ case 4:
+ o_stream_nsend_str(conn->conn.output,
+ "MAIL FROM: hendrik@example.com\r\n");
+ break;
+ case 5:
+ o_stream_nsend_str(conn->conn.output,
+ "MAIL FROM:\r\n");
+ break;
+ case 6:
+ o_stream_nsend_str(conn->conn.output,
+ "MAIL FROM: \r\n");
+ break;
+ case 7:
+ o_stream_nsend_str(conn->conn.output,
+ "MAIL FROM: BODY=7BIT\r\n");
+ break;
+ case 8:
+ o_stream_nsend_str(conn->conn.output,
+ "MAIL FROM: <>\r\n");
+ break;
+ case 9:
+ o_stream_nsend_str(conn->conn.output,
+ "MAIL FROM:<hendrik@example.com>\r\n");
+ break;
+ case 10:
+ o_stream_nsend_str(conn->conn.output,
+ "MAIL FROM:<>\r\n");
+ break;
+ case 11:
+ o_stream_nsend_str(conn->conn.output,
+ "MAIL FROM:bla$die%bla@die&bla\r\n");
+ break;
+ case 12:
+ o_stream_nsend_str(conn->conn.output,
+ "MAIL FROM:<u\"ser>\r\n");
+ break;
+ case 13:
+ o_stream_nsend_str(conn->conn.output,
+ "MAIL FROM:<u\"ser@domain.tld>\r\n");
+ break;
+ case 14:
+ o_stream_nsend_str(conn->conn.output,
+ "MAIL FROM:/@)$@)BLAARGH!@#$$\r\n");
+ break;
+ case 15:
+ o_stream_nsend_str(conn->conn.output,
+ "MAIL FROM:</@)$@)BLAARGH!@#$$>\r\n");
+ break;
+ case 16:
+ o_stream_nsend_str(conn->conn.output,
+ "MAIL FROM:f\xc3\xb6\xc3\xa4@\xc3\xb6\xc3\xa4\r\n");
+ break;
+ case 17:
+ o_stream_nsend_str(conn->conn.output,
+ "MAIL FROM:<f\xc3\xb6\xc3\xa4@\xc3\xb6\xc3\xa4>\r\n");
+ break;
+ default:
+ i_unreached();
+ }
+}
+
+static void
+test_mail_broken_path_client_deinit(struct client_connection *conn)
+{
+ struct _mail_broken_path_client *ctx = conn->context;
+
+ i_assert(ctx->replied);
+ smtp_reply_parser_deinit(&ctx->parser);
+}
+
+static void test_client_mail_broken_path(unsigned int index)
+{
+ test_client_input = test_mail_broken_path_client_input;
+ test_client_connected = test_mail_broken_path_client_connected;
+ test_client_deinit = test_mail_broken_path_client_deinit;
+ test_client_run(index);
+}
+
+/* server */
+
+static void
+test_server_mail_broken_path_disconnect(void *context ATTR_UNUSED,
+ const char *reason)
+{
+ if (debug)
+ i_debug("Disconnect: %s", reason);
+}
+
+static int
+test_server_mail_broken_path_rcpt(void *conn_ctx ATTR_UNUSED,
+ struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
+ struct smtp_server_recipient *rcpt ATTR_UNUSED)
+{
+ test_assert(FALSE);
+ return 1;
+}
+
+static int
+test_server_mail_broken_path_data_begin(void *conn_ctx ATTR_UNUSED,
+ struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
+ struct smtp_server_transaction *trans ATTR_UNUSED,
+ struct istream *data_input ATTR_UNUSED)
+{
+ test_assert(FALSE);
+ return 1;
+}
+
+static void test_server_mail_broken_path
+(const struct smtp_server_settings *server_set)
+{
+ server_callbacks.conn_disconnect =
+ test_server_mail_broken_path_disconnect;
+
+ server_callbacks.conn_cmd_rcpt =
+ test_server_mail_broken_path_rcpt;
+ server_callbacks.conn_cmd_data_begin =
+ test_server_mail_broken_path_data_begin;
+ test_server_run(server_set);
+}
+
+/* test */
+
+static void test_mail_broken_path(void)
+{
+ struct smtp_server_settings smtp_server_set;
+
+ test_server_defaults(&smtp_server_set);
+ smtp_server_set.mail_path_allow_broken = TRUE;
+ smtp_server_set.max_client_idle_time_msecs = 1000;
+
+ test_begin("MAIL workarounds");
+ test_run_client_server(&smtp_server_set,
+ test_server_mail_broken_path,
+ test_client_mail_broken_path, 16);
+ test_end();
+}
+
/*
* All tests
*/
test_data_no_mail,
test_data_no_rcpt,
test_data_binarymime,
+ test_mail_broken_path,
NULL
};