/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "strfuncs.h" #include "message-date.h" #include "smtp-address.h" #include "smtp-params.h" #include "smtp-server-private.h" static void smtp_server_transaction_update_event(struct smtp_server_transaction *trans) { struct event *event = trans->event; event_add_str(event, "transaction_id", trans->id); event_add_str(event, "session", trans->id); event_add_str(event, "mail_from", smtp_address_encode(trans->mail_from)); event_add_str(event, "mail_from_raw", smtp_address_encode_raw(trans->mail_from)); smtp_params_mail_add_to_event(&trans->params, event); event_set_append_log_prefix(event, t_strdup_printf("trans <%s>: ", trans->id)); } struct smtp_server_transaction * smtp_server_transaction_create(struct smtp_server_connection *conn, const struct smtp_server_cmd_mail *mail_data) { struct smtp_server_transaction *trans; pool_t pool; /* create new transaction */ pool = pool_alloconly_create("smtp server transaction", 4096); trans = p_new(pool, struct smtp_server_transaction, 1); trans->pool = pool; trans->conn = conn; /* generate transaction ID */ if (conn->transaction_seq++ == 0) trans->id = conn->session_id; else { trans->id = p_strdup_printf(pool, "%s:T%u", conn->session_id, conn->transaction_seq); } trans->flags = mail_data->flags; trans->mail_from = smtp_address_clone(trans->pool, mail_data->path); smtp_params_mail_copy(pool, &trans->params, &mail_data->params); trans->timestamp = mail_data->timestamp; if (conn->next_trans_event == NULL) trans->event = event_create(conn->event); else { trans->event = conn->next_trans_event; conn->next_trans_event = NULL; } smtp_server_transaction_update_event(trans); struct event_passthrough *e = event_create_passthrough(trans->event)-> set_name("smtp_server_transaction_started"); e_debug(e->event(), "Start"); if (conn->callbacks != NULL && conn->callbacks->conn_trans_start != NULL) conn->callbacks->conn_trans_start(conn->context, trans); return trans; } void smtp_server_transaction_free(struct smtp_server_transaction **_trans) { struct smtp_server_transaction *trans = *_trans; struct smtp_server_connection *conn = trans->conn; struct smtp_server_recipient **rcpts; unsigned int rcpts_total, rcpts_aborted, rcpts_failed; unsigned int rcpts_count, i; *_trans = NULL; if (conn->callbacks != NULL && conn->callbacks->conn_trans_free != NULL) conn->callbacks->conn_trans_free(conn->context, trans); rcpts_count = 0; if (array_is_created(&trans->rcpt_to)) rcpts = array_get_modifiable(&trans->rcpt_to, &rcpts_count); rcpts_aborted = rcpts_count + conn->state.pending_rcpt_cmds; rcpts_failed = conn->state.denied_rcpt_cmds; rcpts_total = rcpts_aborted + rcpts_failed; for (i = 0; i < rcpts_count; i++) smtp_server_recipient_destroy(&rcpts[i]); if (!trans->finished) { struct event_passthrough *e = event_create_passthrough(trans->event)-> set_name("smtp_server_transaction_finished")-> add_int("recipients", rcpts_total)-> add_int("recipients_denied", rcpts_failed)-> add_int("recipients_aborted", rcpts_aborted)-> add_int("recipients_failed", rcpts_failed)-> add_int("recipients_succeeded", 0); e->add_int("status_code", 9000); e->add_str("enhanced_code", "9.0.0"); e->add_str("error", "Aborted"); e_debug(e->event(), "Aborted"); } event_unref(&trans->event); pool_unref(&trans->pool); } struct smtp_server_recipient * smtp_server_transaction_find_rcpt_duplicate( struct smtp_server_transaction *trans, struct smtp_server_recipient *rcpt) { struct smtp_server_recipient *drcpt; i_assert(array_is_created(&trans->rcpt_to)); array_foreach_elem(&trans->rcpt_to, drcpt) { if (drcpt == rcpt) continue; if (smtp_address_equals(drcpt->path, rcpt->path) && smtp_params_rcpt_equal(&drcpt->params, &rcpt->params)) return drcpt; } return NULL; } void smtp_server_transaction_add_rcpt(struct smtp_server_transaction *trans, struct smtp_server_recipient *rcpt) { if (!array_is_created(&trans->rcpt_to)) p_array_init(&trans->rcpt_to, trans->pool, 8); rcpt->trans = trans; rcpt->index = array_count(&trans->rcpt_to); array_push_back(&trans->rcpt_to, &rcpt); } bool smtp_server_transaction_has_rcpt(struct smtp_server_transaction *trans) { return (array_is_created(&trans->rcpt_to) && array_count(&trans->rcpt_to) > 0); } unsigned int smtp_server_transaction_rcpt_count(struct smtp_server_transaction *trans) { if (!array_is_created(&trans->rcpt_to)) return 0; return array_count(&trans->rcpt_to); } void smtp_server_transaction_data_command(struct smtp_server_transaction *trans, struct smtp_server_cmd_ctx *cmd) { struct smtp_server_recipient *rcpt; trans->cmd = cmd; if (!array_is_created(&trans->rcpt_to)) return; array_foreach_elem(&trans->rcpt_to, rcpt) smtp_server_recipient_data_command(rcpt, cmd); } void smtp_server_transaction_received(struct smtp_server_transaction *trans, uoff_t data_size) { event_add_int(trans->event, "data_size", data_size); } void smtp_server_transaction_reset(struct smtp_server_transaction *trans) { struct smtp_server_connection *conn = trans->conn; struct smtp_server_recipient *const *rcpts = NULL; unsigned int rcpts_total, rcpts_failed, rcpts_aborted; unsigned int rcpts_count, i; i_assert(!trans->finished); trans->finished = TRUE; rcpts_count = 0; if (array_is_created(&trans->rcpt_to)) rcpts = array_get(&trans->rcpt_to, &rcpts_count); rcpts_aborted = rcpts_count + conn->state.pending_rcpt_cmds; rcpts_failed = conn->state.denied_rcpt_cmds; rcpts_total = rcpts_aborted + rcpts_failed; for (i = 0; i < rcpts_count; i++) smtp_server_recipient_reset(rcpts[i]); struct event_passthrough *e = event_create_passthrough(trans->event)-> set_name("smtp_server_transaction_finished")-> add_int("recipients", rcpts_total)-> add_int("recipients_denied", rcpts_failed)-> add_int("recipients_aborted", rcpts_aborted)-> add_int("recipients_failed", rcpts_failed)-> add_int("recipients_succeeded", 0)-> add_str("is_reset", "yes"); e_debug(e->event(), "Finished"); } void smtp_server_transaction_finished(struct smtp_server_transaction *trans, struct smtp_server_cmd_ctx *cmd) { struct smtp_server_connection *conn = trans->conn; struct smtp_server_recipient *const *rcpts = NULL; const struct smtp_server_reply *trans_reply = NULL; unsigned int rcpts_total, rcpts_denied, rcpts_failed, rcpts_succeeded; unsigned int rcpts_count, i; i_assert(conn->state.pending_rcpt_cmds == 0); i_assert(!trans->finished); trans->finished = TRUE; rcpts_count = 0; if (array_is_created(&trans->rcpt_to)) rcpts = array_get(&trans->rcpt_to, &rcpts_count); rcpts_succeeded = 0; rcpts_denied = conn->state.denied_rcpt_cmds; rcpts_failed = conn->state.denied_rcpt_cmds; rcpts_total = rcpts_count + conn->state.denied_rcpt_cmds; for (i = 0; i < rcpts_count; i++) { struct smtp_server_reply *reply; if ((trans->flags & SMTP_SERVER_TRANSACTION_FLAG_REPLY_PER_RCPT) != 0) reply = smtp_server_command_get_reply(cmd->cmd, i); else reply = smtp_server_command_get_reply(cmd->cmd, 0); smtp_server_recipient_finished(rcpts[i], reply); if (smtp_server_reply_is_success(reply)) rcpts_succeeded++; else { rcpts_failed++; if (trans_reply == NULL) trans_reply = reply; } } if (trans_reply == NULL) { /* record first success reply in transaction */ trans_reply = smtp_server_command_get_reply(cmd->cmd, 0); } struct event_passthrough *e = event_create_passthrough(trans->event)-> set_name("smtp_server_transaction_finished")-> add_int("recipients", rcpts_total)-> add_int("recipients_denied", rcpts_denied)-> add_int("recipients_aborted", 0)-> add_int("recipients_failed", rcpts_failed)-> add_int("recipients_succeeded", rcpts_succeeded); smtp_server_reply_add_to_event(trans_reply, e); e_debug(e->event(), "Finished"); } void smtp_server_transaction_fail_data(struct smtp_server_transaction *trans, struct smtp_server_cmd_ctx *data_cmd, unsigned int status, const char *enh_code, const char *fmt, va_list args) { struct smtp_server_recipient *const *rcpts; const char *msg; unsigned int count, i; msg = t_strdup_vprintf(fmt, args); rcpts = array_get(&trans->rcpt_to, &count); for (i = 0; i < count; i++) { smtp_server_reply_index(data_cmd, i, status, enh_code, "<%s> %s", smtp_address_encode(rcpts[i]->path), msg); } } void smtp_server_transaction_write_trace_record( string_t *str, struct smtp_server_transaction *trans, enum smtp_server_trace_rcpt_to_address rcpt_to_address) { struct smtp_server_connection *conn = trans->conn; const struct smtp_server_helo_data *helo_data = &conn->helo; const char *host, *secstr, *rcpt_to = NULL; if (array_count(&trans->rcpt_to) == 1) { struct smtp_server_recipient *const *rcpts = array_front(&trans->rcpt_to); switch (rcpt_to_address) { case SMTP_SERVER_TRACE_RCPT_TO_ADDRESS_NONE: break; case SMTP_SERVER_TRACE_RCPT_TO_ADDRESS_FINAL: rcpt_to = smtp_address_encode(rcpts[0]->path); break; case SMTP_SERVER_TRACE_RCPT_TO_ADDRESS_ORIGINAL: rcpt_to = smtp_address_encode( smtp_server_recipient_get_original(rcpts[0])); break; } } /* from */ str_append(str, "Received: from "); if (helo_data->domain_valid) str_append(str, helo_data->domain); else str_append(str, "unknown"); host = ""; if (conn->conn.remote_ip.family != 0) host = net_ip2addr(&conn->conn.remote_ip); if (host[0] != '\0') { str_append(str, " (["); str_append(str, host); str_append(str, "])"); } /* (using) */ secstr = smtp_server_connection_get_security_string(conn); if (secstr != NULL) { str_append(str, "\r\n\t(using "); str_append(str, secstr); str_append(str, ")"); } /* by, with */ str_append(str, "\r\n\tby "); str_append(str, conn->set.hostname); str_append(str, " with "); str_append(str, smtp_server_connection_get_protocol_name(conn)); /* id */ str_append(str, "\r\n\tid "); str_append(str, trans->id); /* (envelope-from) */ str_append(str, "\r\n\t(envelope-from <"); smtp_address_write(str, trans->mail_from); str_append(str, ">)"); /* for */ if (rcpt_to != NULL) { str_append(str, "\r\n\tfor <"); str_append(str, rcpt_to); str_append(str, ">"); } str_append(str, "; "); /* date */ str_append(str, message_date_create(trans->timestamp.tv_sec)); str_printfa(str, "\r\n"); }