/* * Copyright (c) 2003-2006 Sendmail, Inc. and its suppliers. * All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. */ #include "sm/generic.h" SM_RCSID("@(#)$Id: bounce.c,v 1.108 2007/06/02 04:11:40 ca Exp $") #include "sm/error.h" #include "sm/assert.h" #include "sm/memops.h" #include "sm/qmgr.h" #include "sm/qmgr-int.h" #include "sm/da.h" #include "qmgr.h" #include "log.h" /* ** QM_BOUNCE_NEW -- Create new "bounce" recipient ** ** Parameters: ** qmgr_ctx -- QMGR context ** aq_ta -- AQ transaction ** bounce_type -- type of "bounce" (see below) ** owner_idx -- reference to owner- address (0: none) ** paq_rcpt_bounce -- (pointer to) bounce recipient (output) ** ** Returns: ** usual sm_error code; ENOMEM, SM_E_FULL, et.al. ** ** Side Effects: none on error ** ** Locking: aq_ctx must be locked by caller. ** ** Operation: ** Create new aq_rcpt which will be used to send out a DSN. ** rcpt_idx will be aqt_nxt_idx (which will also be stored in ** aqt_dbl_bounce_idx/aqt_bounce_idx/aqt_delay_idx). ** aqr_dsns is an array of rcpt_idx's which will be sent with this DSN; ** this array is probably oversized. ** ** Invokes qmgr_rcpt2ar() unless it's a dDSN (should be a flag instead ** of local "knowledge" that a dDSN isn't scheduled directly). ** ** Last code review: 2005-03-29 00:52:28; see comments below ** Last code change: 2005-05-02 23:51:40 */ #define SM_STR_BOUNCE_LEN 1024 #define SM_STR_DBL_BOUNCE_LEN (SM_STR_BOUNCE_LEN + 256) /* bounce_type */ #define SM_DELAY 1 #define SM_BOUNCE 2 #define SM_DBL_BOUNCE 4 static sm_ret_T qm_bounce_new(qmgr_ctx_P qmgr_ctx, aq_ta_P aq_ta, uint bounce_type, rcpt_idx_T owner_idx, aq_rcpt_P *paq_rcpt_bounce) { sm_ret_T ret; uint flags; rcpt_idx_T bounce_idx; sm_str_P pa; aq_ctx_P aq_ctx; aq_rcpt_P aq_rcpt; time_T time_now; size_t size_dsns; #define SM_BN_FL_IDX_INCR 0x0001 /* aq_ta->aqt_nxt_idx has been incr */ SM_IS_QMGR_CTX(qmgr_ctx); SM_IS_AQ_TA(aq_ta); SM_REQUIRE(aq_ta->aqt_nxt_idx > 0); aq_ctx = qmgr_ctx->qmgr_aq; flags = 0; if (aq_ta->aqt_nxt_idx >= SMTP_RCPTIDX_MAX - 1) { /* ** Oops, we reached the limit. There's no way to recover from ** this except by generating a completely new transaction. ** This limit should never be reached... */ return sm_error_perm(SM_EM_Q_DSN, SM_E_FULL); } if (bounce_type == SM_DBL_BOUNCE) bounce_idx = aq_ta->aqt_dbl_bounce_idx = aq_ta->aqt_nxt_idx++; else if (bounce_type == SM_DELAY) bounce_idx = aq_ta->aqt_delay_idx = aq_ta->aqt_nxt_idx++; else if (bounce_type == SM_BOUNCE) bounce_idx = aq_ta->aqt_bounce_idx = aq_ta->aqt_nxt_idx++; else { SM_ASSERT((bounce_type == SM_DBL_BOUNCE) || (bounce_type == SM_DELAY) || (bounce_type == SM_BOUNCE)); return sm_error_perm(SM_EM_Q_DSN, SM_E_UNEXPECTED); /* NOTREACHED */ } SM_SET_FLAG(flags, SM_BN_FL_IDX_INCR); #if QMGR_TEST if (SM_IS_FLAG(qmgr_ctx->qmgr_cnf.q_cnf_tests, QMGR_TEST_BNC_RCPT_ADD)) ret = sm_error_temp(SM_EM_AQ, ENOMEM); else /* WARNING: be careful about changing the next statement */ #endif ret = aq_rcpt_add_new(aq_ctx, aq_ta, paq_rcpt_bounce, 0, THR_NO_LOCK); if (sm_is_err(ret)) { SM_ASSERT(aq_ta->aqt_nxt_idx > 0); --aq_ta->aqt_nxt_idx; SM_CLR_FLAG(flags, SM_BN_FL_IDX_INCR); return ret; } /* Just to simplify code below */ aq_rcpt = *paq_rcpt_bounce; /* ** Can we use aqt_rcpts_perm instead? Is that value correct when ** the function is invoked or is it still incremented while going ** through the recipient lists? Note: there might be also timeouts ** for temporary errors so it would be at least ** aqt_rcpts_perm + aqt_rcpts_temp. ** ** If it is a double bounce: only 1 entry? Not necessarily: ** there could be multiple bounces. */ aq_rcpt->aqr_dsn_rcpts_max = (qmgr_ctx->qmgr_cnf.q_cnf_dsn_rcpts_max > 0 && qmgr_ctx->qmgr_cnf.q_cnf_dsn_rcpts_max < aq_ta->aqt_rcpts_tot) ? qmgr_ctx->qmgr_cnf.q_cnf_dsn_rcpts_max : aq_ta->aqt_rcpts_tot; size_dsns = sizeof(*(aq_rcpt->aqr_dsns)) * aq_rcpt->aqr_dsn_rcpts_max; if (size_dsns < aq_rcpt->aqr_dsn_rcpts_max) /* overflow? */ { /* this is way too big... */ aq_rcpt->aqr_dsn_rcpts_max = UINT_MAX / sizeof(*(aq_rcpt->aqr_dsns)); aq_rcpt->aqr_dsn_rcpts_max /= 2; size_dsns = sizeof(*(aq_rcpt->aqr_dsns)) * aq_rcpt->aqr_dsn_rcpts_max; SM_ASSERT(size_dsns >= aq_rcpt->aqr_dsn_rcpts_max); } #if QMGR_TEST if (SM_IS_FLAG(qmgr_ctx->qmgr_cnf.q_cnf_tests, QMGR_TEST_ALLOC_DSNS)) aq_rcpt->aqr_dsns = NULL; else /* WARNING: be careful about changing the next statement */ #endif aq_rcpt->aqr_dsns = (rcpt_idx_T *) sm_malloc(size_dsns); if (aq_rcpt->aqr_dsns == NULL) { aq_rcpt->aqr_dsn_rcpts_max = 0; goto error; } SM_ASSERT(owner_idx <= aq_ta->aqt_owners_n); /* Fill in data... (should there be a "double bounce address"??) */ pa = (bounce_type == SM_DBL_BOUNCE) ? qmgr_ctx->qmgr_pm_addr : ((owner_idx > 0) ? aq_ta->aqt_owners_pa[owner_idx - 1] : aq_ta->aqt_mail->aqm_pa); aq_rcpt->aqr_pa = sm_str_dup(NULL, pa); if (aq_rcpt->aqr_pa == NULL) goto error; ret = aq_rcpt_set_domain(aq_rcpt, qmgr_ctx->qmgr_hostname); if (sm_is_err(ret)) goto error; AQR_DA_INIT(aq_rcpt); AQR_SS_INIT(aq_rcpt); aq_rcpt->aqr_idx = bounce_idx; aq_rcpt->aqr_owner_idx = owner_idx; time_now = evthr_time(qmgr_ctx->qmgr_ev_ctx); aq_rcpt->aqr_entered = time_now; aq_rcpt->aqr_st_time = time_now; /* Use aqr_dsn_msg to hold error message report */ #if SM_RCB_MAX_LEN <= 2048 ERROR _SM_RCB_MAX_LEN <= 2048 SM_ASSERT(SM_RCB_MAX_LEN > 2048); #endif aq_rcpt->aqr_dsn_msg = sm_str_new(NULL, (bounce_type == SM_DBL_BOUNCE) ? SM_STR_DBL_BOUNCE_LEN : SM_STR_BOUNCE_LEN, SM_RCB_MAX_LEN - 1024); if (aq_rcpt->aqr_dsn_msg == NULL) goto error; /* ** fixme: From which queue? It's newly created... What to do? ** Usually a rcpt address is either in IBDB or DEFEDB, however, ** this is "artificial". Should it be recreated each time or ** stored in DEFEDB? If the latter, when? */ if (bounce_type == SM_DBL_BOUNCE) AQR_SET_FLAG(aq_rcpt, AQR_FL_IS_DBNC); else if (bounce_type == SM_DELAY) AQR_SET_FLAG(aq_rcpt, AQR_FL_IS_DLY); else AQR_SET_FLAG(aq_rcpt, AQR_FL_IS_BNC); SESSTA_COPY(aq_rcpt->aqr_ss_ta_id, aq_ta->aqt_ss_ta_id); aq_rcpt->aqr_status = AQR_ST_NEW; /* Send to SMAR */ if (bounce_type != SM_DELAY) { ret = qmgr_rcpt2ar(qmgr_ctx, aq_rcpt, THR_NO_LOCK); #if 0 /* ignore error, rely on cleanup */ if (sm_is_err(ret)) goto error; #endif } ++aq_ta->aqt_rcpts_tot; ++aq_ta->aqt_rcpts_inaq; QM_LEV_DPRINTFC(QDC_BOUNCE, 6, (QM_DEBFP, "sev=DBG, func=qm_bounce_new, bounce_type=%u, aq_rcpt=%p, aqr_dsn_rcpts_max=%d, aqt_rcpts_tot=%u, aqt_rcpts_inaq=%u\n", bounce_type, aq_rcpt, aq_rcpt->aqr_dsn_rcpts_max, aq_ta->aqt_rcpts_tot, aq_ta->aqt_rcpts_inaq)); return SM_SUCCESS; error: /* More clean up?? */ if (SM_IS_FLAG(flags, SM_BN_FL_IDX_INCR)) { SM_ASSERT(aq_ta->aqt_nxt_idx > 0); --aq_ta->aqt_nxt_idx; SM_CLR_FLAG(flags, SM_BN_FL_IDX_INCR); } (void) aq_rcpt_rm(aq_ctx, aq_rcpt, 0); ret = sm_is_err(ret) ? ret : sm_error_temp(SM_EM_Q_DSN, ENOMEM); QM_LEV_DPRINTFC(QDC_BOUNCE, 0, (QM_DEBFP, "sev=ERROR, qm_bounce_new=%r\n", ret)); return ret; } /* ** QM_DSN_WHERE -- Return "where" something went wrong ** (see also aq_rcpt_err_state()) ** ** Parameters: ** aq_rcpt -- AQ recipient ** err_txt -- buffer to write error text (for "variable" text) ** err_len -- length of buffer to write error text ** perr_msg -- error text (output) ** ** Returns: ** SM_SUCCESS ** ** Last code review: ** Last code change: */ static sm_ret_T qm_dsn_where(aq_rcpt_P aq_rcpt, char *err_txt, uint err_len, char **perr_msg) { char *err_msg; SM_REQUIRE(aq_rcpt != NULL); SM_REQUIRE(perr_msg != NULL); err_msg = NULL; switch (da_err_cmd(aq_rcpt->aqr_err_st)) { case DA_TA_ERR_MAIL: err_msg = "during MAIL\r\n"; break; case DA_TA_ERR_RCPT: err_msg = "during RCPT\r\n"; break; case DA_TA_ERR_DATA: err_msg = "during DATA\r\n"; break; case DA_TA_ERR_CDB: err_msg = "during opening of body\r\n"; break; case DA_TA_ERR_BODY: err_msg = "during transmission of body\r\n"; break; case DA_TA_ERR_DOT: err_msg = "during final dot\r\n"; break; case DA_TA_ERR_L_RCPT: err_msg = "during LMTP RCPT\r\n"; break; case DA_TA_ERR_RSET: err_msg = "during RSET\r\n"; break; case DA_TA_ERR_SIZE: err_msg = "during SIZE check\r\n"; break; case DA_SE_ERR_OPEN: err_msg = "during session open\r\n"; break; case DA_SE_ERR_GRET: err_msg = "during session greeting\r\n"; break; case DA_SE_ERR_EHLO: err_msg = "during EHLO\r\n"; break; case DA_SE_ERR_HELO: err_msg = "during HELO\r\n"; break; case DA_SE_ERR_STLS: err_msg = "during STARTTLS\r\n"; break; case DA_SE_ERR_TTMYSLEF: #if 0 err_msg = "server greeting contains my hostname\r\n"; #else /* 0 */ err_msg = "config error: mail loops back to me\r\n"; #endif /* 0 */ break; case DA_AR_ERR: switch (aq_rcpt->aqr_status) { case SMTP_AR_NOTF: err_msg = "error from address resolver\r\n" "record not found\r\n"; break; case SMTP_AR_TEMP: err_msg = "error from address resolver\r\n" "temporary error\r\n"; break; case SMTP_AR_PERM: err_msg = "error from address resolver\r\n" "permanent error\r\n"; break; case SMTP_AR_TMO: err_msg = "error from address resolver\r\n" "timeout during query\r\n"; break; case SMTP_AR_MXEMPTY: err_msg = "error from address resolver\r\n" "MX points back to me\r\n"; break; case SMTP_AR_ALIAS: err_msg = "error from address resolver\r\n" "alias expansion failed\r\n"; break; case SMTP_AR_AL_REC: err_msg = "error from address resolver\r\n" "alias nesting too deep\r\n"; break; case SMTP_AR_LOOP: err_msg = "error from address resolver\r\n" "CNAME recursion\r\n"; break; case SMTP_MAP_TEMP: err_msg = "error from address resolver\r\n" "temporary map lookup error\r\n"; break; default: sm_snprintf(err_txt, err_len, IS_SMTP_REPLY(aq_rcpt->aqr_status) ? "%d from address resolver\r\n" : "unknown error %d from address resolver\r\n" , aq_rcpt->aqr_status); err_msg = err_txt; break; } break; case DA_AQ_TMO_ERR: err_msg = "item too long in active queue\r\n"; break; case DA_DQ_TMO_ERR: err_msg = "maximum time in queue exceeded\r\n"; break; case DA_TERMINATED: err_msg = "delivery agent module did not respond before timeout\r\n"; break; } *perr_msg = err_msg; return SM_SUCCESS; } /* ** QM_DSN_COMPOSE -- Compose DSN (bounce) message ** (see also qm_dsn_log()) ** ** Parameters: ** aq_rcpt -- AQ recipient ** aq_rcpt_bounce -- AQ recipient containing the bounce ** errmsg -- error message (might be NULL) ** (must be "sanitized" by caller) ** ** Returns: ** error: usual sm_error code; SM_E_UNEXPECTED, ** ** Side Effects: ** fills in aq_rcpt_bounce->aqr_dsn_msg; ** caller needs to clean this up in case of an error. ** ** Locking: aq_ctx must be locked by caller. ** ** Last code review: 2005-03-24 19:25:34 ** Last code change: 2005-06-09 02:05:38 */ static sm_ret_T qm_dsn_compose(aq_rcpt_P aq_rcpt, aq_rcpt_P aq_rcpt_bounce, sm_str_P errmsg) { sm_ret_T ret; char *err_msg, err_txt[80]; struct in_addr addr; addr.s_addr = aq_rcpt->aqr_addr_fail; /* Compose error message for bounce. */ if (sm_is_err(ret = sm_str_scat(aq_rcpt_bounce->aqr_dsn_msg, "Recipient:\r\n")) || sm_is_err(ret = sm_str_cat(aq_rcpt_bounce->aqr_dsn_msg, aq_rcpt->aqr_pa))) goto error; /* Alias? [information leak? don't show expanded alias??] */ if (aq_rcpt->aqr_orig_pa != NULL) { if (sm_is_err(ret = sm_str_scat(aq_rcpt_bounce->aqr_dsn_msg, "\r\nExpanded from:\r\n")) || sm_is_err(ret = sm_str_cat(aq_rcpt_bounce->aqr_dsn_msg, aq_rcpt->aqr_orig_pa))) goto error; } if (aq_rcpt->aqr_addr_fail != 0 && (sm_is_err(ret = sm_str_scat(aq_rcpt_bounce->aqr_dsn_msg, "\r\nRemote-MTA: ")) || sm_is_err(ret = sm_inet_inaddr2str(addr, aq_rcpt_bounce->aqr_dsn_msg)) || sm_is_err(ret = sm_str_scat(aq_rcpt_bounce->aqr_dsn_msg, "\r\n")))) goto error; if (errmsg != NULL && sm_str_getlen(errmsg) > 0) { if (sm_is_err(ret = sm_str_scat(aq_rcpt_bounce->aqr_dsn_msg, "Reason:\r\n")) || sm_is_err(ret = sm_str_cat(aq_rcpt_bounce->aqr_dsn_msg, errmsg)) || sm_is_err(ret = sm_str_scat(aq_rcpt_bounce->aqr_dsn_msg, "\r\n"))) goto error; } else if (aq_rcpt->aqr_msg != NULL && sm_str_getlen(aq_rcpt->aqr_msg) > 0) { if (sm_is_err(ret = sm_str_scat(aq_rcpt_bounce->aqr_dsn_msg, "Reason:\r\n")) || sm_is_err(ret = sm_str_cat(aq_rcpt_bounce->aqr_dsn_msg, aq_rcpt->aqr_msg)) || sm_is_err(ret = sm_str_scat(aq_rcpt_bounce->aqr_dsn_msg, "\r\n"))) goto error; } /* HACKs ahead (3): session open error status */ else if (aq_rcpt->aqr_status == SMTPC_SE_OP_TMO) { if (sm_is_err(ret = sm_str_scat(aq_rcpt_bounce->aqr_dsn_msg, "timeout\r\n"))) goto error; } else if (aq_rcpt->aqr_status == SMTPC_SE_OP_REFUSED) { if (sm_is_err(ret = sm_str_scat(aq_rcpt_bounce->aqr_dsn_msg, "connection refused\r\n"))) goto error; } else if (aq_rcpt->aqr_status == SMTPC_SE_OP_UNREACH) { if (sm_is_err(ret = sm_str_scat(aq_rcpt_bounce->aqr_dsn_msg, "host/net unreachable\r\n"))) goto error; } else if (sm_is_err(ret = sm_str_scat(aq_rcpt_bounce->aqr_dsn_msg, "\r\n"))) goto error; err_msg = NULL; (void) qm_dsn_where(aq_rcpt, err_txt, sizeof(err_txt), &err_msg); if (err_msg != NULL && sm_is_err(ret = sm_str_scat(aq_rcpt_bounce->aqr_dsn_msg, err_msg))) goto error; /* print also error state itself? */ if (AQR_IS_FLAG(aq_rcpt, AQR_FL_DSN_TMT) && sm_is_err(ret = sm_str_scat(aq_rcpt_bounce->aqr_dsn_msg, "maximum time in queue exceeded\r\n"))) goto error; else if (sm_is_err(ret = sm_str_scat(aq_rcpt_bounce->aqr_dsn_msg, "\r\n"))) goto error; return SM_SUCCESS; error: return ret; } /* ** QM_DSN_LOG -- Log fact that DSN was created ** (see also qm_dsn_compose()) ** ** Parameters: ** qmgr_ctx -- QMGR context ** aq_rcpt -- AQ recipient ** errmsg -- error message (might be NULL) ** (must be "sanitized" by caller) ** ** Returns: ** error: usual sm_error code ** ** Last code review: ** Last code change: */ static sm_ret_T qm_dsn_log(qmgr_ctx_P qmgr_ctx, aq_rcpt_P aq_rcpt, sm_str_P errmsg) { sm_ret_T ret; sm_str_P log; struct in_addr addr; ret = SM_SUCCESS; if (!sm_log_wouldlog(qmgr_ctx->qmgr_lctx, QM_LCAT_SCHED, QM_LMOD_BOUNCE, 9)) return ret; /* use macros for size... */ log = sm_str_new(NULL, 256, 512); if (NULL == log) return sm_error_temp(SM_EM_Q_DSN, ENOMEM); /* Compose log message for bounce. */ ret = sm_strprintf(log, "ss_ta=%s, rcpt=%@S, rcpt_idx=%u" , aq_rcpt->aqr_ss_ta_id, aq_rcpt->aqr_pa, aq_rcpt->aqr_idx); addr.s_addr = aq_rcpt->aqr_addr_fail; if (aq_rcpt->aqr_addr_fail != 0 && (sm_is_err(ret = sm_str_scat(log, ", remote-MTA=")) || sm_is_err(ret = sm_inet_inaddr2str(addr, log)))) goto error; if (errmsg != NULL && sm_str_getlen(errmsg) > 0) { if (sm_is_err(ret = sm_str_scat(log, ", reason=")) || sm_is_err(ret = sm_str_cat(log, errmsg))) goto error; } else if (aq_rcpt->aqr_msg != NULL && sm_str_getlen(aq_rcpt->aqr_msg) > 0) { if (sm_is_err(ret = sm_str_scat(log, ", reason=")) || sm_is_err(ret = sm_str_cat(log, aq_rcpt->aqr_msg))) goto error; } /* HACKs ahead (3): session open error status */ else if (aq_rcpt->aqr_status == SMTPC_SE_OP_TMO) { if (sm_is_err(ret = sm_str_scat(log, ", reason=timeout"))) goto error; } else if (aq_rcpt->aqr_status == SMTPC_SE_OP_REFUSED) { if (sm_is_err(ret = sm_str_scat(log, ", reason=connection refused"))) goto error; } else if (aq_rcpt->aqr_status == SMTPC_SE_OP_UNREACH) { if (sm_is_err(ret = sm_str_scat(log, ", reason=host/net unreachable"))) goto error; } if (sm_is_err(ret = sm_str_scat(log, ", when="))) goto error; (void) aq_rcpt_err_state(aq_rcpt, true, log); if (AQR_IS_FLAG(aq_rcpt, AQR_FL_DSN_TMT) && sm_is_err(ret = sm_str_scat(log, ", status=maximum time in queue exceeded"))) goto error; sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_SCHED, QM_LMOD_BOUNCE, SM_LOG_INFO, 9, "sev=INFO, func=qm_dsn_log, %S", log); SM_STR_FREE(log); return SM_SUCCESS; error: SM_STR_FREE(log); return ret; } /* ** QM_BOUNCE_ADD -- Add a new recipient to a DSN (might create new aq_rcpt) ** ** Parameters: ** qmgr_ctx -- QMGR context ** aq_ta -- AQ transaction ** aq_rcpt -- AQ recipient (which causes the DSN) ** errmsg -- error message (might be NULL) ** (must be "sanitized" by caller) ** paq_rcpt_dsn -- (pointer to) AQ DSN recipient (output) ** ** Returns: ** success: flags as shown in sm/qmgr-int.h ** to activate scheduler or SMAR. ** Note: caller must act on this! ** error: usual sm_error code; ENOMEM, SM_E_UNEXPECTED, ** ** Side Effects: ** creates new "bounce" rcpt if non exists or if the bounce ** message is too long for an existing rcpt. ** ** Called by: qm_get_edb_entries(), q_upd_rcpt_fail() ** ** Locking: aq_ctx must be locked by caller. ** ** Note: if creating a double bounce fails, should this just be ** logged (with all necessary information?) and then ** be ignored (by the caller?)? ** ** Last code review: 2005-03-24 22:06:32; see comments below ** Last code change: 2005-05-02 23:51:28 */ sm_ret_T qm_bounce_add(qmgr_ctx_P qmgr_ctx, aq_ta_P aq_ta, aq_rcpt_P aq_rcpt, sm_str_P errmsg, aq_rcpt_P *paq_rcpt_dsn) { sm_ret_T ret, rflags; uint flags; rcpt_idx_T bounce_idx; sm_str_T dsn_msg_save; aq_ctx_P aq_ctx; aq_rcpt_P aq_rcpt_dsn; /* dsn msg has been saved in dsn_msg_save */ #define SM_B_FL_SAVED_DSN_MSG 0x0001 /* a new bounce recipient has been created */ #define SM_B_FL_NEW_BOUNCE 0x0002 /* sm_str_scat() failed */ #define SM_B_FL_SCAT_FAIL 0x0004 /* been in error code section before */ #define SM_B_FL_ERROR 0x0008 /* try again */ #define SM_B_FL_AGAIN 0x0010 SM_IS_QMGR_CTX(qmgr_ctx); SM_IS_AQ_TA(aq_ta); aq_ctx = qmgr_ctx->qmgr_aq; aq_rcpt_dsn = NULL; sm_memzero(&dsn_msg_save, sizeof(dsn_msg_save)); rflags = 0; flags = 0; /* Is this a double bounce? Shouldn't happen ... */ if (AQR_IS_FLAG(aq_rcpt, AQR_FL_IS_DBNC)) { sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_SCHED, QM_LMOD_BOUNCE, SM_LOG_INCONS, 2, "sev=FATAL, func=qm_bounce_add, ss_ta=%s, rcpt_idx=%u, aqr_flags=%#x, status=unexpected_doublebounce", aq_ta->aqt_ss_ta_id, aq_rcpt->aqr_idx, aq_rcpt->aqr_flags); return sm_error_perm(SM_EM_Q_DSN, SM_E_UNEXPECTED); } /* Check this earlier?? (explicitly check for "<>"?) w*/ if (!AQ_TA_IS_FLAG(aq_ta, AQ_TA_FL_EMPTYSENDER) && sm_str_getlen(aq_ta->aqt_mail->aqm_pa) == 2) AQ_TA_SET_FLAG(aq_ta, AQ_TA_FL_EMPTYSENDER); if (AQ_TA_IS_FLAG(aq_ta, AQ_TA_FL_EMPTYSENDER)) AQR_SET_FLAG(aq_rcpt, AQR_FL_IS_BNC); /* Does a bounce recipient exist? */ bounce_idx = 0; /* == has no bounce */ if (AQR_IS_DSNFL(aq_rcpt, AQR_DSNFL_D_NTG) && !AQR_IS_DSNFL(aq_rcpt, AQR_DSNFL_D_HBG) && aq_ta_has_delay(aq_ta)) bounce_idx = aq_ta->aqt_delay_idx; else if (AQR_IS_FLAG(aq_rcpt, AQR_FL_IS_BNC) && aq_ta_has_dbl_bounce(aq_ta)) bounce_idx = aq_ta->aqt_dbl_bounce_idx; else if (!AQR_IS_FLAG(aq_rcpt, AQR_FL_IS_BNC) && aq_ta_has_bounce(aq_ta)) bounce_idx = aq_ta->aqt_bounce_idx; QM_LEV_DPRINTFC(QDC_BOUNCE, 3, (QM_DEBFP, "sev=DBG, func=qm_bounce_add, aq_ta=%p, aq_rcpt=%p, aqr_ss_ta=%s, aqr_flags=%#x, has_bounce=%d, dbl_bounce_idx=%d, bounce_idx=%d, err_st=%#x\n", aq_ta, aq_rcpt, aq_rcpt->aqr_ss_ta_id, aq_rcpt->aqr_flags, aq_ta_has_bounce(aq_ta), aq_ta->aqt_dbl_bounce_idx, aq_ta->aqt_bounce_idx, aq_rcpt->aqr_err_st)); if (bounce_idx != 0) { /* let's find the bounce rcpt ... */ ret = aq_rcpt_find_ss(aq_ctx, aq_ta->aqt_ss_ta_id, bounce_idx, THR_NO_LOCK, &aq_rcpt_dsn); if (sm_is_err(ret)) { /* Not fatal, will simply create a new bounce */ aq_rcpt_dsn = NULL; QM_LEV_DPRINTFC(QDC_BOUNCE, 0, (QM_DEBFP, "sev=INFO, func=qm_bounce_add, aq_ta=%p, aq_rcpt=%p, ss_ta=%s, idx=%d, aq_rcpt_find_ss=%#x, flags=%#x\n", aq_ta, aq_rcpt, aq_ta->aqt_ss_ta_id, bounce_idx, ret, aq_rcpt->aqr_flags)); sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_SMTPS, QM_LMOD_BOUNCE, SM_LOG_INFO, 9, "sev=INFO, func=qm_bounce_add, ss_ta=%s, rcpt_idx=%d, bounce_idx=%d, aq_rcpt_find_ss=%m, flags=%#x", aq_ta->aqt_ss_ta_id, aq_rcpt->aqr_idx, bounce_idx, ret, aq_rcpt->aqr_flags); } else if ( /* Recipient is already scheduled */ AQR_IS_FLAG(aq_rcpt_dsn, AQR_FL_SCHED) || /* all slots exhausted */ (aq_rcpt_dsn->aqr_dsn_rcpts >= aq_rcpt_dsn->aqr_dsn_rcpts_max) || /* different bounce address */ (aq_rcpt_has_owner(aq_rcpt) && aq_rcpt->aqr_owner_idx != aq_rcpt_dsn->aqr_owner_idx) ) { aq_rcpt_dsn = NULL; } } sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_SCHED, QM_LMOD_BOUNCE, SM_LOG_DEBUG, 15, "func=qm_bounce_add, ss_ta=%s, rcpt_idx=%d, aqr_flags=%#x, has_bounce=%d, bounce_idx=%d, rcpt_bounce=%s", aq_ta->aqt_ss_ta_id, aq_rcpt->aqr_idx, aq_rcpt->aqr_flags, aq_ta_has_bounce(aq_ta), bounce_idx, aq_rcpt_dsn == NULL ? "new" : "old"); do { SM_CLR_FLAG(flags, SM_B_FL_AGAIN); if (aq_rcpt_dsn == NULL) { /* Create new bounce recipient */ ret = qm_bounce_new(qmgr_ctx, aq_ta, AQR_IS_FLAG(aq_rcpt, AQR_FL_IS_BNC) ? SM_DBL_BOUNCE /* XXX NOT CORRECT! How to recognize where a DELAY DSN should be generated? */ /* the sequence of operations is important, i.e., when is which flag set? */ : (AQR_IS_DSNFL(aq_rcpt, AQR_DSNFL_D_NTG) && !AQR_IS_DSNFL(aq_rcpt, AQR_DSNFL_D_HBG)) ? SM_DELAY : SM_BOUNCE, aq_rcpt->aqr_owner_idx, &aq_rcpt_dsn); if (sm_is_err(ret)) { /* caller has to deal with this */ QM_LEV_DPRINTFC(QDC_BOUNCE, 0, (QM_DEBFP, "sev=ERROR, func=qm_bounce_add, aq_ta=%p, aq_rcpt=%p, qm_bounce_new=%r\n", aq_ta, aq_rcpt, ret)); sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_SCHED, QM_LMOD_BOUNCE, SM_LOG_ERR, 4, "sev=ERROR, func=qm_bounce_add, ss_ta=%s, rcpt_idx=%u, qm_bounce_new=%m", aq_ta->aqt_ss_ta_id, aq_rcpt->aqr_idx, ret); goto error; } SM_SET_FLAG(flags, SM_B_FL_NEW_BOUNCE); if (aq_rcpt->aqr_dsn_msg != NULL) { SM_STR_SAVE(*aq_rcpt_dsn->aqr_dsn_msg, dsn_msg_save); SM_SET_FLAG(flags, SM_B_FL_SAVED_DSN_MSG); } /* if double bounce: copy old bounce message */ if (AQR_IS_FLAG(aq_rcpt_dsn, AQR_FL_IS_DBNC) && aq_rcpt->aqr_dsn_msg != NULL && (sm_is_err(ret = sm_str_cat( aq_rcpt_dsn->aqr_dsn_msg, aq_rcpt->aqr_dsn_msg)) || sm_is_err(ret = sm_str_scat( aq_rcpt_dsn->aqr_dsn_msg, "\r\n\r\nOriginal bounce message follows:\r\n")))) { /* ** Shouldn't happen as double bounce msg is ** larger than a "normal" bounce msg. ** What to do in this case?? */ sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_SCHED, QM_LMOD_BOUNCE, SM_LOG_ERR, 3, "sev=ERROR, func=qm_bounce_add, ss_ta=%s, rcpt_idx=%d, where=create_double_bounce_dsn_msg, stat=%m", aq_ta->aqt_ss_ta_id, aq_rcpt->aqr_idx, ret); goto error; } rflags |= QDA_FL_ACT_SMAR; #if QMGR_DEBUG /* Only required for debug output below */ if (AQR_IS_FLAG(aq_rcpt, AQR_FL_IS_BNC)) bounce_idx = aq_ta->aqt_dbl_bounce_idx; else bounce_idx = aq_ta->aqt_bounce_idx; #endif /* QMGR_DEBUG */ } else rflags |= QDA_FL_ACT_SCHED; if (!SM_IS_FLAG(flags, SM_B_FL_SAVED_DSN_MSG)) { SM_STR_SAVE(*aq_rcpt_dsn->aqr_dsn_msg, dsn_msg_save); SM_SET_FLAG(flags, SM_B_FL_SAVED_DSN_MSG); } #if QMGR_TEST /* ** Trigger an error if flag is set and this is not the ** first bounce. */ if (SM_IS_FLAG(qmgr_ctx->qmgr_cnf.q_cnf_tests, QMGR_TEST_BNC_FAIL) && !SM_IS_FLAG(flags, SM_B_FL_NEW_BOUNCE)) ret = sm_error_perm(SM_EM_STR, SM_E_OVFLW_NS); else /* WARNING: be careful about changing the next statement */ #endif /* QMGR_TEST */ ret = qm_dsn_compose(aq_rcpt, aq_rcpt_dsn, errmsg); if (sm_is_err(ret)) { /* other errors? */ if (ret == sm_error_perm(SM_EM_STR, SM_E_OVFLW_SC) || ret == sm_error_perm(SM_EM_STR, SM_E_OVFLW_NS)) SM_SET_FLAG(flags, SM_B_FL_SCAT_FAIL); if (SM_IS_FLAG(flags, SM_B_FL_SAVED_DSN_MSG) && aq_rcpt_dsn != NULL && aq_rcpt_dsn->aqr_dsn_msg != NULL) { SM_STR_RESTORE(*aq_rcpt_dsn->aqr_dsn_msg, dsn_msg_save); SM_CLR_FLAG(flags, SM_B_FL_SAVED_DSN_MSG); } if (!SM_IS_FLAG(flags, SM_B_FL_NEW_BOUNCE) && !SM_IS_FLAG(flags, SM_B_FL_ERROR) && SM_IS_FLAG(flags, SM_B_FL_SCAT_FAIL)) { aq_rcpt_dsn = NULL; SM_SET_FLAG(flags, SM_B_FL_AGAIN); SM_SET_FLAG(flags, SM_B_FL_ERROR); } else goto error; } } while (SM_IS_FLAG(flags, SM_B_FL_AGAIN)); /* ** If a smaller array is initially allocated then we may have to ** reallocate a bigger one here. For now we just make sure it's ** large enough. */ SM_ASSERT(aq_rcpt_dsn->aqr_dsn_rcpts < aq_rcpt_dsn->aqr_dsn_rcpts_max); aq_rcpt_dsn->aqr_dsns[aq_rcpt_dsn->aqr_dsn_rcpts++] = aq_rcpt->aqr_idx; /* no error hereafter, otherwise it's necessary to undo this too */ QM_LEV_DPRINTFC(QDC_BOUNCE, 3, (QM_DEBFP, "sev=DBG, func=qm_bounce_add, aq_ta=%p, aq_rcpt=%p, aq_rcpt_dsn=%p, ss_ta=%s, da_ta=%s, bounce_idx=%d, status=done\n", aq_ta, aq_rcpt, aq_rcpt_dsn, aq_rcpt->aqr_ss_ta_id, aq_rcpt->aqr_da_ta_id, bounce_idx)); if (AQR_IS_FLAG(aq_rcpt, AQR_FL_DSN_TMT)) (void) qm_dsn_log(qmgr_ctx, aq_rcpt, errmsg); sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_SCHED, QM_LMOD_BOUNCE, SM_LOG_INFO, 10, "func=qm_bounce_add, ss_ta=%s, rcpt=%@S, rcpt_idx=%d, rcpt_bounce_idx=%d, da_ta=%s, bounce_idx=%d", aq_ta->aqt_ss_ta_id, aq_rcpt->aqr_pa, aq_rcpt->aqr_idx, aq_rcpt_dsn->aqr_idx, aq_rcpt->aqr_da_ta_id, bounce_idx); if (AQR_IS_DSNFL(aq_rcpt, AQR_DSNFL_D_NTG) && !AQR_IS_DSNFL(aq_rcpt, AQR_DSNFL_D_HBG)) { AQR_SET_DSNFL(aq_rcpt, AQR_DSNFL_D_HBG); aq_rcpt->aqr_dly_idx = aq_rcpt_dsn->aqr_idx; /* there's now one more recipient to deliver; see TODO */ ++aq_ta->aqt_rcpts_left; } else { aq_rcpt->aqr_dsn_idx = aq_rcpt_dsn->aqr_idx; AQR_SET_DSNFL(aq_rcpt, AQR_DSNFL_F_HBG); } if (paq_rcpt_dsn != NULL) *paq_rcpt_dsn = aq_rcpt_dsn; return rflags; error: QM_LEV_DPRINTFC(QDC_BOUNCE, 0, (QM_DEBFP, "sev=WARN, func=qm_bounce_add, aq_ta=%p, aq_rcpt=%p, flags=%#x, ret=%r\n", aq_ta, aq_rcpt, flags, ret)); if (SM_IS_FLAG(flags, SM_B_FL_SAVED_DSN_MSG) && aq_rcpt_dsn != NULL && aq_rcpt_dsn->aqr_dsn_msg != NULL) { SM_STR_RESTORE(*aq_rcpt_dsn->aqr_dsn_msg, dsn_msg_save); SM_CLR_FLAG(flags, SM_B_FL_SAVED_DSN_MSG); } QM_LEV_DPRINTFC(QDC_BOUNCE, 0, (QM_DEBFP, "sev=ERROR, func=qm_bounce_add, aq_ta=%p, aq_rcpt=%p, aq_rcpt_dsn=%p, has_bounce=%d, failed=%r\n", aq_ta, aq_rcpt, aq_rcpt_dsn, aq_ta_has_bounce(aq_ta), ret)); sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_SCHED, QM_LMOD_BOUNCE, SM_LOG_ERR, 5, "sev=ERROR, func=qm_bounce_add, ss_ta=%s, rcpt_idx=%u, rcpt_bounce_idx=%u, has_bounce=%d, failed=%m", aq_ta->aqt_ss_ta_id, aq_rcpt->aqr_idx, aq_rcpt_dsn == NULL ? SMTP_RCPTIDX_MAX : aq_rcpt_dsn->aqr_idx, aq_ta_has_bounce(aq_ta), ret); if (SM_IS_FLAG(flags, SM_B_FL_NEW_BOUNCE) && aq_rcpt_dsn != NULL) { aq_rcpt_rm(aq_ctx, aq_rcpt_dsn, 0); SM_CLR_FLAG(flags, SM_B_FL_NEW_BOUNCE); aq_rcpt_dsn = NULL; } /* XXX More cleanup? */ return ret; }