/* * Copyright (c) 2002-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. * * $Id: actdb-int.h,v 1.123 2007/06/03 02:19:14 ca Exp $ */ #ifndef SM_AQ_INT_H #define SM_AQ_INT_H 1 #include "sm/generic.h" #include "sm/types.h" #include "sm/magic.h" #include "sm/str.h" #include "sm/time.h" #include "sm/mta.h" #include "sm/cdb.h" #include "sm/qmgr.h" #include "sm/edb.h" #include "sm/ibdb.h" #include "sm/queue.h" #include "sm/ring.h" #include "sm/bhtable.h" #include "sm/pthread.h" #include "sm/actdb.h" #include "sm/aqrdqstr.h" #include "sm/net.h" #include "sm/hdrmod.h" /* abstraction layer for active envelope database AQ (memory only) */ #if 0 /* from db.func.tex: aq_open(IN name, IN size, IN flags, OUT status, OUT actdb-handle): open active envelope database, specify size. aq_close(IN actdb-handle, OUT status): close an envelope database. aq_env_add(IN actdb-handle, IN sender-env-info, OUT status): add a new envelope (sender information contains also trans-id, maybe make it a separate parameter). aq_env_rm(IN actdb-handle, IN trans-id, OUT status): remove an envelope from the AQ (after all recipients have been taken care of) [aq_ta_rm()] aq_rcpt_add_new(IN actdb-handle, IN trans-id, IN int-rcpt, OUT status): add a new recipient. aq_rcpt_status(IN actdb-handle, IN trans-id, IN rcpt, IN d-stat, OUT status) update recipient status and remove it from active queue. May also update the appropriate queue, i.e., incoming or deferred depending on where the entry came from and what the new status is. Another way to deal with that is to defer the update and perform a ``group commit'' just like for the incoming queue. aq_commit(IN actdb-handle, IN trans-id, OUT status) commit envelope information to stable storage. Question: Shouldn't this be done separately for rcpt and mail? That is: aq_mail_commit(IN actdb-handle, IN trans-id, OUT status) and aq_rcpt_commit(IN actdb-handle, IN trans-id, OUT status). That may make it more complicated to have the data in sync. It should really update all necessary data in one step (as much as this is possible, we probably won't implement real transaction based commits). */ #endif /* 0 */ #ifndef AQ_CHECK # define AQ_CHECK 1 #endif #ifndef AQ_TA_CHECK # define AQ_TA_CHECK 1 #endif #ifndef AQ_RCPT_CHECK # define AQ_RCPT_CHECK 1 #endif #ifndef AQ_STATS # define AQ_STATS 1 #endif TAILQ_HEAD(aq_tas_S, aq_ta_S); /* AQ context */ struct aq_ctx_S { #if AQ_CHECK sm_magic_T sm_magic; #endif pthread_mutex_t aq_mutex; /* only one mutex for now */ qmgr_ctx_P aq_qmgr_ctx; /* pointer to qmgr_ctx */ timeval_T aq_nextrun; /* if set: don't run before this time */ uint aq_max_entries; /* maximum number of entries */ uint aq_limit; /* current limit on number of entries */ uint aq_entries; /* current number of entries */ uint aq_d_entries; /* entries from DEFEDB */ uint aq_t_da; /* entries being delivered */ #if 0 uint aq_arfails; /* #of entries with SMAR failure */ aq_rcpt_P aq_rcpt_arfail; /* first aq_rcpt with SMAR failure */ #endif /* more counters? total number of recipients number of recipients being delivered number of recipients waiting for AR number of recipients ready to be scheduled, total number of transactions */ /* ** XXX For now we just use lists of aq_ta/aq_rcpt structures. ** Of course we need better access methods (rdq, waitq; see below). */ aq_tas_T aq_tas; aq_rcpts_T aq_rcpts; /* ** Note: it might be useful to introduce mutexes to protect ** only "sublists", e.g., rdqs, rcpts_wait. ** However, careful programming is required to avoid deadlocks ** in that case (moving recipients from one list to another ** if it is possible to move from R to W and from W to R ** then a function must get both mutexes first). */ /* ** Active Queue Recipient Destination Queues, see sm/aqrdq.h. ** Hash table to access a rdq (key: IPv4 address) ** ** Note: it might be useful to put this data into a separate ** structure or otherwise integrate aqrdq.h into this file. ** Currently there is a very strong interdepency which does ** not allow for a clean separation in two .h files. */ bht_P aq_rdq_ht; /* List of AQ RDQs (which are in use) */ aqrdq_ctx_hd_T aq_rdqs; uint aq_rdq_used; uint aq_rdq_used_max; /* "freelist" of AQ RDQs */ aqrdq_ctx_hd_T aq_rdqs_free; uint aq_rdq_free; /* ** Wait Queues: sorted (aqr_expire) list of recipients ** that are waiting for some event, i.e., returning result from AR ** or a status from DA. This list is used by the cleanup task ** qmgr_aq_cleanup(). */ #define AQWQ_ANY 0 #define AQWQ_AR 1 #define AQWQ_DA 2 CIRCLEQ_HEAD(, aq_rcpt_S) aq_waitq; /* CIRCLEQ_HEAD(, aq_rcpt_S) aq_waitq_da; */ #if AQ_STATS uint aq_waitq_entries; uint aq_waitq_max; #endif }; #define aq_is_empty(aq_ctx) ((aq_ctx)->aq_entries == 0) /* Only the external representation of addresses is needed here */ struct aq_mail_S { sm_str_P aqm_pa; /* printable addr */ /* XXX parameters? */ }; struct aq_raddr_S { ipv4_T aqra_ipv4; /* XXX HACK */ /* TTL from DNS; converted to expiration time */ time_T aqra_expt; unsigned short aqra_pref; /* preference from DNS */ }; typedef struct aq_raddr_S aq_raddr_T, *aq_raddr_P; /* ** AQ recipient ** ** XXX need: ** host(list) for connection (pre-MX?) ** ** access keys are: ** ss_ta_id (match rcpt - mail) ** da_ta_id (find rcpt after delivery attempt) ** da/delivery address (find more recipients for session reuse) */ struct aq_rcpt_S { #if AQ_RCPT_CHECK sm_magic_T sm_magic; #endif sessta_id_T aqr_ss_ta_id; /* ta id in SMTPS */ sessta_id_T aqr_da_ta_id; /* ta id in DA */ sm_str_P aqr_pa; /* printable addr */ sm_str_P aqr_orig_pa; /* original pa (alias exp.) */ sm_str_P aqr_domain; /* domain part of addr */ /* port for all addresses, 0: use default */ short aqr_port; smtp_status_T aqr_status; /* status */ smtp_status_T aqr_status_new; /* new status */ uint aqr_err_st; /* state which caused error */ uint32_t aqr_flags; /* flags */ uint32_t aqr_dsn_flags; /* DSN specific flags */ rcpt_idx_T aqr_idx; /* rcpt idx */ /* ** Reference to owner: only valid if > 0 ** Note: the indices are running from 1 to aq_ta->aqt_owners_n ** such that 0 can be used a "no owner". To actually access ** the owner address 1 must be subtracted from the index: ** invariant: ** (!aq_rcpt_has_owner(aq_rcpt) || ** (aq_rcpt->aqr_owner_idx > 0 && ** aq_rcpt->aqr_owner_idx <= aq_ta->aqt_owners_n)); ** access: if (aq_rcpt_has_owner(aq_rcpt)) ** aq_ta->aqt_owners_pa[aq_rcpt->aqr_owner_idx - 1] */ rcpt_idx_T aqr_owner_idx; uint aqr_tries; /* # of delivery attempts */ uint aqr_da_idx; /* DA idx (kind of DA) */ /* ** HACK! Need list of addresses. Do we only need IP addresses ** or do we need more (MX records, TTLs)? We need at least some ** kind of ordering, i.e., the priority. This is needed for the ** scheduler (if a domain has several MX records with the same ** priority, we can deliver to any of those, there's no order ** between them). Moreover, if we store this data in DEFEDB, ** we also need TTLs. */ /* ** Number of entries in address array. ** Should this be "int" instead and denote the maximum index, ** where -1 means: no entries? ** Currently the check for "is there another entry" is ** (aqr_addr_cur < aqr_addr_max - 1) ** i.e., valid entries are 0 to aqr_addr_max - 1. ** invariant: aqr_addr_cur < aqr_addr_max unless aqr_addr_max==0 */ uint aqr_addr_max; uint aqr_addr_cur; /* cur idx in address array */ aq_raddr_T *aqr_addrs; /* array of addresses */ #define AQR_MORE_ADDR(aq_rcpt) ((aq_rcpt)->aqr_addr_cur < (aq_rcpt)->aqr_addr_max - 1) /* XXX Hack */ ipv4_T aqr_addr_fail; /* failed address */ /* address storage to use if memory allocaction failed */ aq_raddr_T aqr_addr_mf; time_T aqr_entered; /* entered into AQ */ time_T aqr_expire; /* when to remove */ time_T aqr_st_time; /* start time (rcvd) */ time_T aqr_last_try; /* last time scheduled */ /* next time to try (after it has been stored in DEFEDB) */ time_T aqr_next_try; /* Error message if delivery failed */ sm_str_P aqr_msg; /* ** DSN recipient: stores list of recipient indices for which ** this is a DSN message. ** Note: if this is stored in DEFEDB, then the array doesn't need ** to be saved provided that the recipients are removed in the ** same (DB) transaction because the bounce recipient contains ** all necessary data for the DSN. If, however, the recipients ** are not removed simultaneously, then it is a bit harder to ** get consistency because it isn't obvious for which recipients ** this bounce has been created. That data is only indirectly ** available through aqr_dsn_idx (see below). */ sm_str_P aqr_dsn_msg; uint aqr_dsn_rcpts; /* current number of entries */ uint aqr_dsn_rcpts_max; /* max number of entries */ rcpt_idx_T *aqr_dsns; /* array of rcpt indices */ /* ** rcpt idx for bounce: stores the rcpt_idx (> 0) if a bounce ** for this recipient is generated and being delivered. ** This is used as "semaphore" to avoid multiple bounces for ** the same recipient (needs to be stored in DEFEDB). */ rcpt_idx_T aqr_dsn_idx; /* ** rcpt idx for alias: stores the rcpt_idx of the address ** from which this has been expanded. This is only valid if ** AQR_FL_ALIAS is set, and it must point to a rcpt which has ** AQR_FL_REPLACED set. */ rcpt_idx_T aqr_alias_idx; /* ** XXX can we merge aqr_dsn_idx and aqr_alias_idx into one aqr_refer_idx ** and have flags that indicate which type it is? ** Maybe not: an expanded address can bounce, right? */ /* ** rcpt idx for "delayed" DSN: stores the rcpt_idx (> 0) if a "delayed" ** DSN for this recipient is generated and being delivered. ** This is used as "semaphore" to avoid multiple bounces for ** the same recipient (needs to be stored in DEFEDB). */ rcpt_idx_T aqr_dly_idx; /* linked list for AQ, currently this is the way to access all rcpts */ TAILQ_ENTRY(aq_rcpt_S) aqr_db_link; /* links */ /* ** Linked lists for: ** - SMTPS transaction: ** to find all recipients for the original transaction ** (to find out whether they can be delivered in the same ** transaction, i.e., same DA, + MX piggybacking) ** - DA transaction: ** to find the recipients that belong to one delivery attempt ** and update their status ** - Recipients with same destination host ** to find other recipients that can be sent over ** an open connection (todo queue) ** - Wait queue: recipients that are waiting for some update, ** e.g., from AR or DA. ** ** Link to ta: ** to update the recipient counter(s). */ sm_ring_T aqr_ss_link; sm_ring_T aqr_da_link; TAILQ_ENTRY(aq_rcpt_S) aqr_dest_link; /* waiting for some result: from AR or DA */ CIRCLEQ_ENTRY(aq_rcpt_S) aqr_wait_link; aq_ta_P aqr_ss_ta; /* transaction */ /* XXX parameters? */ #if MTA_USE_TLS sm_ret_T aqr_maprescnf; sm_str_P aqr_conf; /* extra configuration data */ #endif }; /* Only a recipient index > 0 is a valid bounce index. */ #define aq_rcpt_has_bounce(aq_rcpt) ((aq_rcpt)->aqr_dsn_idx > 0) #define aq_rcpt_has_delay(aq_rcpt) ((aq_rcpt)->aqr_dly_idx > 0) /* Only an owner index > 0 is a valid owner reference. */ #define aq_rcpt_has_owner(aq_rcpt) ((aq_rcpt)->aqr_owner_idx > 0) #define aq_rcpt_is_alias(aq_rcpt) AQR_IS_FLAG(aq_rcpt, AQR_FL_ALIAS) /* Operations on aq_rcpt lists (in AQ) */ #define AQR_INIT(aq_ctx) TAILQ_INIT(&((aq_ctx)->aq_rcpts)) #define AQR_FIRST(aq_ctx) TAILQ_FIRST(&((aq_ctx)->aq_rcpts)) #define AQR_END(aq_ctx) TAILQ_END(&((aq_ctx)->aq_rcpts)) #define AQR_NEXT(aq_rcpt) TAILQ_NEXT(aq_rcpt, aqr_db_link) #define AQR_INSERT_TAIL(aq_ctx, aq_rcpt) TAILQ_INSERT_TAIL(&((aq_ctx)->aq_rcpts), aq_rcpt, aqr_db_link) #define AQR_INSERT_HEAD(aq_ctx, aq_rcpt) TAILQ_INSERT_HEAD(&((aq_ctx)->aq_rcpts), aq_rcpt, aqr_db_link) #define AQR_REMOVE(aq_ctx, aq_rcpt) TAILQ_REMOVE(&((aq_ctx)->aq_rcpts), aq_rcpt, aqr_db_link) /* Operations on aq_rcpt lists (from original SMTPS transaction) */ #define AQR_SS2R(aq_rcpt) (&((aq_rcpt)->aqr_ss_link)) #define AQR_R2SS(ring) SM_RING_EMBED((ring), aq_rcpt_T, aqr_ss_link) #define AQR_SS_INIT(aq_rcpt) SM_RING_INIT(AQR_SS2R(aq_rcpt)) #define AQR_SS_APP(aq_rcpt, aq_rcpt_nxt) SM_RING_APPEND(AQR_SS2R(aq_rcpt), AQR_SS2R(aq_rcpt_nxt)) #define AQR_SS_PRE(aq_rcpt, aq_rcpt_nxt) SM_RING_PREPEND(AQR_SS2R(aq_rcpt), AQR_SS2R(aq_rcpt_nxt)) #define AQR_SS_SUCC(aq_rcpt) AQR_R2SS(sm_ring_succ(AQR_SS2R(aq_rcpt))) #define AQR_SS_PRED(aq_rcpt) AQR_R2SS(sm_ring_pred(AQR_SS2R(aq_rcpt))) #define AQR_SS_DELENTRY(aq_rcpt) sm_ring_delentry(AQR_SS2R(aq_rcpt)) /* Operations on aq_rcpt lists (for DA transaction) */ #define AQR_DA2R(aq_rcpt) (&((aq_rcpt)->aqr_da_link)) #define AQR_R2DA(ring) SM_RING_EMBED((ring), aq_rcpt_T, aqr_da_link) #define AQR_DA_INIT(aq_rcpt) SM_RING_INIT(AQR_DA2R(aq_rcpt)) #define AQR_DA_APP(aq_rcpt, aq_rcpt_nxt) SM_RING_APPEND(AQR_DA2R(aq_rcpt), AQR_DA2R(aq_rcpt_nxt)) #define AQR_DA_PRE(aq_rcpt, aq_rcpt_nxt) SM_RING_PREPEND(AQR_DA2R(aq_rcpt), AQR_DA2R(aq_rcpt_nxt)) #define AQR_DA_SUCC(aq_rcpt) AQR_R2DA(sm_ring_succ(AQR_DA2R(aq_rcpt))) #define AQR_DA_PRED(aq_rcpt) AQR_R2DA(sm_ring_pred(AQR_DA2R(aq_rcpt))) #define AQR_DA_DELENTRY(aq_rcpt) sm_ring_delentry(AQR_DA2R(aq_rcpt)) /* Operations on aq_rcpt wait lists (in AQ) */ #define AQR_WAITQ_INIT(aq_ctx) CIRCLEQ_INIT(&((aq_ctx)->aq_waitq)) #define AQR_WAITQ_EMPTY(aq_ctx) CIRCLEQ_EMPTY(&((aq_ctx)->aq_waitq)) #define AQR_WAITQ_FIRST(aq_ctx) CIRCLEQ_FIRST(&((aq_ctx)->aq_waitq)) #define AQR_WAITQ_LAST(aq_ctx) CIRCLEQ_LAST(&((aq_ctx)->aq_waitq)) #define AQR_WAITQ_END(aq_ctx) CIRCLEQ_END(&((aq_ctx)->aq_waitq)) #define AQR_WAITQ_NEXT(aq_rcpt) CIRCLEQ_NEXT(aq_rcpt, aqr_wait_link) #define AQR_WAITQ_PREV(aq_rcpt) CIRCLEQ_PREV(aq_rcpt, aqr_wait_link) #define AQR_WAITQ_INSERT_TAIL(aq_ctx, aq_rcpt) CIRCLEQ_INSERT_TAIL(&((aq_ctx)->aq_waitq), aq_rcpt, aqr_wait_link) #define AQR_WAITQ_INSERT_HEAD(aq_ctx, aq_rcpt) CIRCLEQ_INSERT_HEAD(&((aq_ctx)->aq_waitq), aq_rcpt, aqr_wait_link) #define AQR_WAITQ_INSERT_AFTER(aq_ctx, aq_rcpt_cur, aq_rcpt_new) CIRCLEQ_INSERT_AFTER(&((aq_ctx)->aq_waitq), aq_rcpt_cur, aq_rcpt_new, aqr_wait_link) #define AQR_WAITQ_INSERT_BEFORE(aq_ctx, aq_rcpt_cur, aq_rcpt_new) CIRCLEQ_INSERT_BEFORE(&((aq_ctx)->aq_waitq), aq_rcpt_cur, aq_rcpt_new, aqr_wait_link) #define AQR_WAITQ_REMOVE(aq_ctx, aq_rcpt) CIRCLEQ_REMOVE(&((aq_ctx)->aq_waitq), aq_rcpt, aqr_wait_link) #define AQR_WAITQ_FOREACH(aq_ctx, aq_rcpt) CIRCLEQ_FOREACH(aq_rcpt, &((aq_ctx)->aq_waitq), aqr_wait_link) #define AQR_WAITQ_FOREACH_REVERSE(aq_ctx, aq_rcpt) CIRCLEQ_FOREACH_REVERSE(aq_rcpt, &((aq_ctx)->aq_waitq), aqr_wait_link) #define AQR_CUR_DEST(aq_rcpt) ((aq_rcpt)->aqr_addrs[(aq_rcpt)->aqr_addr_cur].aqra_ipv4) /* In which queue is the entry? Flags for ta and aq_rcpt */ #define AQ_FL_IQDB 0x00000001 #define AQ_FL_DEFEDB 0x00000002 /* Recipient flags */ /* In which DB? */ #define AQR_FL_IQDB AQ_FL_IQDB #define AQR_FL_DEFEDB AQ_FL_DEFEDB /* Progress */ #define AQR_FL_SENT2AR 0x00000004 /* Sent to AR */ #define AQR_FL_RCVD4AR 0x00000008 /* Received from AR */ #define AQR_FL_RDY4DLVRY 0x00000010 /* Ready for delivery */ /* Scheduled for delivery, is going to be sent to DA */ #define AQR_FL_SCHED 0x00000020 /* Waiting for status update from DA, must not be touched by scheduler */ #define AQR_FL_WAIT4UPD 0x00000040 /* Nevertheless, some timeout must be imposed on entries in AQ */ #define AQR_FL_TMOUT 0x00000080 /* too long in AQ */ /* Error status */ #define AQR_FL_TEMP 0x00000100 /* temporary failure */ #define AQR_FL_PERM 0x00000200 /* permanent failure */ #define AQR_FL_ARF 0x00000400 /* failure from SMAR */ /* free: 0x00000800 was: failure from DA */ /* memory allocation for aqr_addrs failed, use fallback */ #define AQR_FL_MEMAR 0x00001000 #define AQR_FL_ARINCOMPL 0x00002000 /* addr resolution incomplete */ /* send bounce (incomplete, not a DSN according to the RFC yet) */ /* ** This is a bounce. Note: this flag is only set by ** sm_q_bounce_new() and sm_q_bounce_add(). ** It must not be set "outside" of these functions because they determine ** whether a recipient is a double bounce by checking this flag. That is, ** the order of operations matter (if some caller sets this flag then ** the recipient will be turned into a double bounce). */ #define AQR_FL_IS_DLY 0x00008000 /* "delayed" DSN */ #define AQR_FL_IS_BNC 0x00010000 /* (simple) bounce ("failure") DSN */ #define AQR_FL_IS_DBNC 0x00020000 /* double bounce */ #define AQR_FL_IS_DSN (AQR_FL_IS_DLY|AQR_FL_IS_BNC|AQR_FL_IS_DBNC) /* reason for bounce */ #define AQR_FL_DSN_PERM 0x00040000 /* perm error */ #define AQR_FL_DSN_TMT 0x00080000 /* timeout, i.e. too long in queue */ /* free: 0x00[124]00000 */ /* in which destination queue is this recipient? */ #define AQR_FL_RDQ 0x00800000 /* recipient in (AR) wait queue; AQR_FL_SENT2AR&~AQR_FL_RCVD4AR */ #define AQR_FL_WAITQ_AR 0x01000000 #if 0 #define AQR_FL_WAITQ(which) ((AQWQ_AR == (which)) ? AQR_FL_WAITQ_AR : (AQWQ_DA == (which)) ? AQR_FL_WAIT4UPD : (SM_ASSERT(AQWQ_AR == (which) || AQWQ_DA == (which)), 0)) #else #define AQR_FL_WAITQ(which) ((AQWQ_AR == (which)) ? AQR_FL_WAITQ_AR : (AQWQ_DA == (which)) ? AQR_FL_WAIT4UPD : 0) #endif /* rcpt status (aqr_err_st) has been updated individually */ #define AQR_FL_ERRST_UPD 0x02000000 /* new rcpt status (aqr_status_new) is valid */ #define AQR_FL_STAT_NEW 0x04000000 #define AQR_FL_HAS_VERP 0x08000000 /* superseded by an alias (two-step process? 1. expanded, 2. replaced) */ #define AQR_FL_REPLACED 0x10000000 /* from alias expansion */ #define AQR_FL_ALIAS 0x20000000 /* flag for "change sender to list-owner"? */ #define AQR_FL_SCHEDF 0x40000000 /* failure from sched */ /* Mask for storing flags in DEFEDB: only persistent state */ #define AQR_FL_MASK (AQR_FL_DEFEDB|AQR_FL_PERM|\ AQR_FL_IS_DLY|AQR_FL_IS_BNC|AQR_FL_IS_DBNC|\ AQR_FL_DSN_PERM|AQR_FL_DSN_TMT|AQR_FL_HAS_VERP|\ AQR_FL_REPLACED|AQR_FL_ALIAS) #define AQR_SET_FLAG(aq_rcpt, fl) (aq_rcpt)->aqr_flags |= (fl) #define AQR_CLR_FLAG(aq_rcpt, fl) (aq_rcpt)->aqr_flags &= ~(fl) #define AQR_IS_FLAG(aq_rcpt, fl) (((aq_rcpt)->aqr_flags & (fl)) != 0) #define AQR_IS_FLAGS(aq_rcpt, fl) (((aq_rcpt)->aqr_flags & (fl)) == (fl)) /* rcpt can be scheduled iff ready for delivery but not yet scheduled */ #define AQR_SCHEDULE(aq_rcpt) (((aq_rcpt)->aqr_flags & (AQR_FL_RDY4DLVRY|AQR_FL_SCHED|AQR_FL_WAIT4UPD|AQR_FL_REPLACED)) == AQR_FL_RDY4DLVRY) /* DSN flags (NOT yet implemented! [only failure DSNs]) */ #define AQR_DSNFL_NONE 0x00000000 #define AQR_DSNFL_F_REQ 0x00000001 /* Failure DSN requested */ #define AQR_DSNFL_S_REQ 0x00000002 /* Success DSN requested */ #define AQR_DSNFL_D_REQ 0x00000004 /* Delayed DSN requested */ #define AQR_DSNFL_F_NTG 0x00000010 /* need to generate Failure DSN */ #define AQR_DSNFL_S_NTG 0x00000020 /* need to generate Success DSN */ #define AQR_DSNFL_D_NTG 0x00000040 /* need to generate Delayed DSN */ #define AQR_DSNFL_F_HBG 0x00000100 /* Failure DSN has been generated */ #define AQR_DSNFL_S_HBG 0x00000200 /* Success DSN has been generated */ #define AQR_DSNFL_D_HBG 0x00000400 /* Delayed DSN has been generated */ #define AQR_DSNFL_F_SNT 0x00001000 /* Failure DSN sent */ #define AQR_DSNFL_S_SNT 0x00002000 /* Success DSN sent */ #define AQR_DSNFL_D_SNT 0x00004000 /* Delayed DSN sent */ #define AQR_SET_DSNFL(aq_rcpt, fl) (aq_rcpt)->aqr_dsn_flags |= (fl) #define AQR_CLR_DSNFL(aq_rcpt, fl) (aq_rcpt)->aqr_dsn_flags &= ~(fl) #define AQR_IS_DSNFL(aq_rcpt, fl) (((aq_rcpt)->aqr_dsn_flags & (fl)) != 0) /* Recipient status; should some of these be flags instead? */ #define AQR_ST_NEW 1 /* just added to AQ (tried) */ #define AQR_ST_NONE 2 /* added to AQ, not tried */ #define AQR_ST_DONE 200 /* delivery successful */ #define AQR_ST_TEMP 400 /* temporary delivery error */ #define AQR_ST_PERM 500 /* permanent delivery error */ /* XXX more ... maybe use SMTP reply codes? */ #define aqr_is_smtp_reply(st) ((st) >= AQR_ST_DONE) /* Are there more destination addresses? */ #define AQR_MORE_DESTS(aq_rcpt) ((aq_rcpt)->aqr_addr_cur + 1 < (aq_rcpt)->aqr_addr_max) /* Don't try again now, save in DEFEDB and maybe try again later */ #define AQR_DEFER(aq_rcpt) (((aq_rcpt)->aqr_flags & (AQR_FL_ARF|AQR_FL_TMOUT)) != 0) /* ** AQ Transaction context ** Notice: this stores a superset of the DEFEDB content to make it ** simpler to write back the data to DEFEDB. ** ** XXX we need two of these: ** 1. SMTPS TA (superset of data stored in DEFEDB) ** 2. DA TA (only stored in ACTEDB) ** ** according to the document the following invariances should hold: ** rcpts_tot = rcpts_temp + rcpts_perm + rcpts_succ ** ** rcpts_left = rcpts_temp + rcpts_perm ** Check whether these assumptions are true... */ struct aq_ta_S { #if AQ_TA_CHECK sm_magic_T sm_magic; #endif /* other times??? */ time_T aqt_st_time; /* start time (received) [constant] */ aq_mail_P aqt_mail; /* mail from [constant]*/ off_t aqt_msg_sz_b; /* message size in bytes [constant] */ uint aqt_rcpts_inaq;/* number of recipients in AQ */ uint aqt_rcpts_ar; /* rcpts to receive from AR */ uint aqt_rcpts_arf; /* #of entries with SMAR failure */ /* ** Total number of recipients. ** Maximum number of recipients that were ever "linked" to this ta. ** This also counts the number of bounces. It is never decremented. */ uint aqt_rcpts_tot; /* ** Recipients still to deliver. ** If this reaches 0 all recipients have been delivered, and hence ** the TA can be removed. In this case aqt_rcpts_inaq and ** aqt_rcpts_ar must be 0. */ uint aqt_rcpts_left; /* ** Note: this counter does not reflect the number of temporary ** failed entries in DEFEDB! AQR_FL_TEMP is not saved in EDB, ** the "error condition" will be "reconstructed" on the next try. ** 2004-06-07 however, aqr_status is saved, so it should reflect ** the total number??? */ uint aqt_rcpts_temp; /* rcpts temp failed */ uint aqt_rcpts_perm; /* rcpts perm failed */ uint aqt_rcpts_tried; /* rcpts already tried */ rcpt_idx_T aqt_nxt_idx; /* next recipient index */ uint aqt_state; uint aqt_flags; /* ** rcpt idx for (double) bounce; when a bounce is needed a recipient ** struct is created, its rcpt_idx is this bounce_idx. ** It should be aqt_rcpts_tot (+1) when it is created; afterwards ** aqt_rcpts_tot is increased of course. */ rcpt_idx_T aqt_bounce_idx; rcpt_idx_T aqt_dbl_bounce_idx; rcpt_idx_T aqt_delay_idx; sessta_id_T aqt_ss_ta_id; /* ta id in SMTPS [constant] */ cdb_id_P aqt_cdb_id; /* [constant] */ TAILQ_ENTRY(aq_ta_S) aqt_ta_l; /* links */ rcpt_idx_T aqt_owners_n; /* size of array aqt_owners_pa */ sm_str_P *aqt_owners_pa; /* list of owner addresses */ sm_hdrmodhd_P aqt_hdrmodhd; /* add list of recipients??? that makes lookups easier... see above */ }; /* Only a recipient index > 0 is a valid bounce index. */ #define aq_ta_has_bounce(aq_ta) ((aq_ta)->aqt_bounce_idx > 0) #define aq_ta_has_dbl_bounce(aq_ta) ((aq_ta)->aqt_dbl_bounce_idx > 0) #define aq_ta_has_delay(aq_ta) ((aq_ta)->aqt_delay_idx > 0) /* Transaction flags; more? */ #define AQ_TA_FL_IQDB AQ_FL_IQDB #define AQ_TA_FL_DEFEDB AQ_FL_DEFEDB /* update DEFEDB due to counter change */ #define AQ_TA_FL_EDB_UPD_C 0x0004 /* update DEFEDB because rcpt has changed */ #define AQ_TA_FL_EDB_UPD_R 0x0008 #define AQ_TA_FL_EDB_RM 0x0010 /* remove from DEFEDB (unused) */ #define AQ_TA_FL_NO_BODY 0x0100 /* do not send body (bounce) */ #define AQ_TA_FL_EMPTYSENDER 0x1000 /* From:<> */ #define AQ_TA_FL_VERP 0x2000 /* use VERP for this transaction */ #define AQ_TA_SET_FLAG(aq_ta, fl) (aq_ta)->aqt_flags |= (fl) #define AQ_TA_CLR_FLAG(aq_ta, fl) (aq_ta)->aqt_flags &= ~(fl) #define AQ_TA_IS_FLAG(aq_ta, fl) (((aq_ta)->aqt_flags & (fl)) != 0) /* Mask for storing flags in DEFEDB: only persistent state */ #define AQ_TA_FL_MASK (AQ_TA_FL_NO_BODY|AQ_TA_FL_EMPTYSENDER|AQ_TA_FL_VERP) /* operations on transactions lists */ #define AQ_TAS_INIT(aq_ctx) TAILQ_INIT(&((aq_ctx)->aq_tas)) #define AQ_TAS_FIRST(aq_ctx) TAILQ_FIRST(&((aq_ctx)->aq_tas)) #define AQ_TAS_END(aq_ctx) TAILQ_END(&((aq_ctx)->aq_tas)) #define AQ_TAS_NEXT(aq_ta) TAILQ_NEXT(aq_ta, aqt_ta_l) #define AQ_TAS_INSERT_TAIL(aq_ctx, aq_ta) TAILQ_INSERT_TAIL(&((aq_ctx)->aq_tas), aq_ta, aqt_ta_l) #define AQ_TAS_INSERT_HEAD(aq_ctx, aq_ta) TAILQ_INSERT_HEAD(&((aq_ctx)->aq_tas), aq_ta, aqt_ta_l) #define AQ_TAS_REMOVE(aq_ctx, aq_ta) TAILQ_REMOVE(&((aq_ctx)->aq_tas), aq_ta, aqt_ta_l) #define AQ_TAS_REMOVE_FREE(aq_ctx, aq_ta) do { \ AQ_TAS_REMOVE((aq_ctx), (aq_ta)); \ aq_ta_free((aq_ctx), (aq_ta)); \ } while (0) /* context for rcb send callback */ struct aq_rsnd_ctx_S { aq_ctx_P aqrsc_aq_ctx; /* pointer to aq_ctx */ aq_rcpt_P aqrsc_rcpt; /* one recipient */ }; /* Type checks */ #if AQ_CHECK # define SM_IS_AQ(aq_ctx) SM_REQUIRE_ISA((aq_ctx), SM_AQ_MAGIC) #else # define SM_IS_AQ(aq_ctx) SM_REQUIRE((aq_ctx) != NULL) #endif #if AQ_TA_CHECK # define SM_IS_AQ_TA(aq_ta) SM_REQUIRE_ISA((aq_ta), SM_AQ_TA_MAGIC) #else # define SM_IS_AQ_TA(aq_ta) SM_REQUIRE((aq_ta) != NULL) #endif #if AQ_RCPT_CHECK # define SM_IS_AQ_RCPT(aq_rcpt) SM_REQUIRE_ISA((aq_rcpt), SM_AQ_RCPT_MAGIC) #else # define SM_IS_AQ_RCPT(aq_rcpt) SM_REQUIRE((aq_rcpt) != NULL) #endif /* for aq_usage() parameter "which" */ #define AQ_USAGE_ALL 0 #define AQ_USAGE_DEFEDB 1 typedef sm_ret_T (*aq_rcpt_F)(aq_rcpt_P _adb_rcpt, void *_ctx); typedef sm_ret_T (*aq_ta_F)(aq_ta_P _adb_ta, void *_ctx); /* for aq_open() parameter "flags" */ #define AQ_OPEN_FL_NONE 0x00 #define AQ_OPEN_FL_NOHT 0x01 /* don't create hash tables */ sm_ret_T aq_close(aq_ctx_P _adb); sm_ret_T aq_open(qmgr_ctx_P qmgr_ctx, aq_ctx_P *_adb, uint _size, uint _flags); int aq_usage(aq_ctx_P _adb, int _which); sm_ret_T aq_env_add_iqdb(aq_ctx_P _adb, qss_ta_P _qss_ta, qmgr_ctx_P _qmgr_ctx); sm_ret_T aq_rcpt_status(aq_ctx_P _aq_ctx, sessta_id_T _da_ta_id, rcpt_idx_T _rcpt_idx, smtp_status_T _rcpt_status, uint _err_st, sm_str_P _errmsg); #if 0 sm_ret_T aq_rcpt_walk(aq_ctx_P _adb, aq_rcpt_F _f, void *_ctx); sm_ret_T aq_ta_walk(aq_ctx_P _adb, aq_ta_F _f, void *_ctx); #endif sm_ret_T aq_ta_find(aq_ctx_P _adb_ctx, sessta_id_T _ss_ta_id, bool _lockit, aq_ta_P *_padb_ta); sm_ret_T aq_rcpt_find_ss(aq_ctx_P _aq_ctx, sessta_id_T _ss_ta_id, rcpt_idx_T _rcpt_idx, thr_lock_T _lockit, aq_rcpt_P *_paq_rcpt); sm_ret_T aq_rcpt_find_da(aq_ctx_P _aq_ctx, sessta_id_T _da_ta_id, rcpt_idx_T _rcpt_idx, thr_lock_T _lockit, aq_rcpt_P *_paq_rcpt); sm_ret_T aq_rcpt_find_one_ss(aq_ctx_P _aq_ctx, sessta_id_T _ss_ta_id, thr_lock_T _locktype, aq_rcpt_P *_padb_rcpt); sm_ret_T aq_rcpt_find_one_da(aq_ctx_P _aq_ctx, sessta_id_T _da_ta_id, thr_lock_T _locktype, aq_rcpt_P *_padb_rcpt); sm_ret_T aq_rcpt_lockop(aq_ctx_P _aq_ctx, aq_rcpt_P _paq_rcpt, thr_lock_T _lockit); sm_ret_T aq_rcpt_add_new(aq_ctx_P _aq, aq_ta_P _aq_ta, aq_rcpt_P *_prcpt, uint32_t _flags, thr_lock_T _locktype); sm_ret_T aq_ta_add_new(aq_ctx_P _aq, aq_ta_P *_aq_ta, uint32_t _flags, uint _nrcpts, thr_lock_T _locktype); sm_ret_T aq_waitq_add(aq_ctx_P _aq_ctx, aq_rcpt_P _aq_rcpt, time_T _startt, uint _which, bool _lockit); sm_ret_T aq_waitq_rm(aq_ctx_P _aq_ctx, aq_rcpt_P _aq_rcpt, uint _which, bool _lockit); time_T aq_waitq_first_tmo(aq_ctx_P _aq_ctx, uint _which, bool _lockit); sm_ret_T aq_rsnd_ctx_free(aq_rsnd_ctx_P _aq_rsnd_ctx); sm_ret_T aq_rsnd_ctx_new(aq_ctx_P _aq_ctx, aq_rcpt_P _aq_rcpt, aq_rsnd_ctx_P *_paq_rsnd_ctx); sm_ret_T aq_rcpt_err_state(aq_rcpt_P _aq_rcpt, bool _unknown, sm_str_P _errmsg); #endif /* SM_AQ_INT_H */