/* * Copyright (c) 2003-2005 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: rcpt.c,v 1.155 2007/11/14 06:03:08 ca Exp $") #include "sm/error.h" #include "sm/assert.h" #include "sm/ctype.h" #include "sm/limits.h" #include "sm/io.h" #include "sm/net.h" #include "sm/rcb.h" #include "sm/common.h" #include "sm/mta.h" #include "sm/das.h" #include "sm/rfc2821.h" #include "smar.h" #include "log.h" #include "sm/qmgrcomm.h" #include "sm/reccom.h" /* ** XXX Try to merge reply functions into one (or at least use fewer) ** to avoid code duplications. ** Pass res to reply function, if success send routing info ** else res (error status). */ /* Protocol: 2004-06-27 RT_PROT_VER, PROT_VER_RT, RT_R2Q_RCPT_ID, smar_rcpt->arr_id, SMTP_RCPTID_SIZE, if "global" error: RT_R2Q_RCPT_ST, rcpt_status stop. if owners exist: RT_R2Q_OWN_N, arrs_owners_n for each owner address: RT_R2Q_OWN_IDX, smar_rcpt->arr_idx, RT_R2Q_OWN_PA, smar_rcpt->arr_pa, if expanded to multiple addresses (smar_rcpts->arrs_lst_n > 1) RT_R2Q_RCPT_AE, smar_rcpts->arrs_lst_n, for each address: if error: RT_R2Q_RCPT_ST, rcpt_status RT_R2Q_OWN_REF, smar_rcpt->arr_owner_idx, [could be optional] maybe: RT_R2Q_RCPT_PA, smar_rcpt->arr_pa, else RT_R2Q_RCPT_DA, smar_rcpt->arr_da RT_R2Q_RCPT_PORT, smar_rcpt->arr_port, [optional] RT_R2Q_OWN_REF, smar_rcpt->arr_owner_idx, [could be optional] RT_R2Q_RCPT_FL, smar_rcpt flags [optional] maybe: RT_R2Q_RCPT_PA, smar_rcpt->arr_pa, RT_R2Q_RCPT_NAR, smar_rcpt->arr_n_A for each MX record (smar_rcpt->arr_A_qsent) for each A record (smar_dns->ardns_n_A) RT_R2Q_RCPT_IPV4, smar_dns->ardns_A_rrs[j], RT_R2Q_RCPT_PRE, smar_dns->ardns_pref, RT_R2Q_RCPT_TTL, smar_dns->ardns_ttl, 2004-06-30 can this be simplified? too many tests on receiver side. */ /* ** SMAR_RCPT_RE_FIRST -- Send begin of reply for a RCPT address, i.e., ** put begin of data into RCB (common to most smar_rcpt_re_*() fcts) ** ** Parameters: ** smar_rcpt -- rcpt routing information ** ** Returns: ** usual sm_error code ** ** Called by: smar_rcpt_re_all(), smar_rcpt_re_ipv4(), smar_rcpt_re_err() ** ** Last code review: 2004-03-15 16:31:34 ** Last code change: */ static sm_ret_T smar_rcpt_re_first(smar_rcpt_P smar_rcpt) { sm_rcb_P rcb; sm_ret_T ret; int owners; smar_rcpts_P smar_rcpts; SM_IS_SMAR_RCPT(smar_rcpt); smar_rcpts = smar_rcpt->arr_rcpts; SM_IS_SMAR_RCPTS(smar_rcpts); rcb = &smar_rcpts->arrs_rcbe->rcbe_rcb; ret = sm_rcb_putv(rcb, RCB_PUTV_FIRST, SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT, SM_RCBV_BUF, RT_R2Q_RCPT_ID, smar_rcpt->arr_id, SMTP_RCPTID_SIZE, SM_RCBV_END); if (SM_SUCCESS == ret && (owners = smar_rcpts->arrs_owners_n) > 0) { ret = sm_rcb_put3uint32(rcb, 4, RT_R2Q_OWN_N, owners); if (SM_SUCCESS == ret) { int n; smar_rcpt_P smar_owner; for (smar_owner = OWNER_FIRST(smar_rcpts), n = 0; smar_owner != OWNER_END(smar_rcpts) && n < owners; smar_owner = OWNER_NEXT(smar_owner), ++n) { ret = sm_rcb_putv(rcb, RCB_PUTV_NONE, SM_RCBV_INT, RT_R2Q_OWN_IDX, smar_owner->arr_idx, SM_RCBV_STR, RT_R2Q_OWN_PA, smar_owner->arr_owner_pa, SM_RCBV_END); } SM_ASSERT(n == owners); } } if (SM_SUCCESS == ret && smar_rcpts->arrs_lst_n > 1) ret = sm_rcb_putv(rcb, RCB_PUTV_NONE, SM_RCBV_INT, RT_R2Q_RCPT_AE, smar_rcpts->arrs_lst_n, SM_RCBV_END); SMARRS_SET_FLAG(smar_rcpts, SMARRS_FL_INITRE); return ret; } /* ** SMAR_RCPT_RE_ALL -- Complete reply for a RCPT routing request ** ** Parameters: ** smar_rcpt -- SMAR RCPT context ** ** Returns: ** usual sm_error code ** ** Called by: smar_rcpt_cb() ** ** Last code review: 2004-03-15 16:52:39; see comments ** Last code change: */ static sm_ret_T smar_rcpt_re_all(smar_rcpt_P smar_rcpt) { sm_rcb_P rcb; int i, j; uint n_A, cnt_A; sm_ret_T ret; smar_dns_P smar_dns; smar_rcpts_P smar_rcpts; SM_IS_SMAR_RCPT(smar_rcpt); smar_rcpts = smar_rcpt->arr_rcpts; SM_IS_SMAR_RCPTS(smar_rcpts); if (SMARRS_IS_FLAG(smar_rcpts, SMARRS_FL_NOSEND)) return SM_SUCCESS; rcb = &smar_rcpts->arrs_rcbe->rcbe_rcb; /* First: send basic data and number of entries */ SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_re_all, entries=%d, sm_rcb_len=%d, sm_rcb_rw=%d\n", smar_rcpt->arr_n_A, rcb->sm_rcb_len, rcb->sm_rcb_rw)); if (!SMARRS_IS_FLAG(smar_rcpts, SMARRS_FL_INITRE)) { ret = smar_rcpt_re_first(smar_rcpt); if (sm_is_err(ret)) goto error; } ret = sm_rcb_putv(rcb, RCB_PUTV_NONE, SM_RCBV_INT, RT_R2Q_RCPT_DA, smar_rcpt->arr_da, SM_RCBV_INT, (0 == smar_rcpt->arr_port) ? RT_NOSEND : RT_R2Q_RCPT_PORT, (uint32_t) smar_rcpt->arr_port, SM_RCBV_INT, RT_R2Q_OWN_REF, smar_rcpt->arr_owner_idx, SM_RCBV_INT, SMARR_IS_FLAG(smar_rcpt, SMARR_FL_HASVERP) ? RT_R2Q_RCPT_FL: RT_NOSEND, (uint32_t) SMARRT_FL_VERP, SM_RCBV_END); if (SM_SUCCESS == ret && SMARRQ_IS_FLAG(smar_rcpt, SMARRQ_FL_CONF)) { ret = sm_rcb_putv(rcb, RCB_PUTV_NONE, SM_RCBV_INT, RT_R2Q_MAP_RES_CNF_RCPT, smar_rcpt->arr_maprescnf, SM_RCBV_STR, RT_R2Q_RHS_CNF_RCPT, smar_rcpt->arr_rhs_conf, SM_RCBV_END); } if (SM_SUCCESS == ret && (smar_rcpts->arrs_lst_n > 1 || (smar_rcpts->arrs_lst_n == 1 && SMARR_IS_FLAG(smar_rcpt, SMARR_FL_ALIAS)))) ret = sm_rcb_putv(rcb, RCB_PUTV_NONE, SM_RCBV_STR, RT_R2Q_RCPT_PA, smar_rcpt->arr_pa, SM_RCBV_END); n_A = smar_rcpt->arr_n_A; if (n_A > SM_DNS_A_MAX) n_A = SM_DNS_A_MAX; if (SM_SUCCESS == ret) ret = sm_rcb_put3uint32(rcb, 4, RT_R2Q_RCPT_NAR, n_A); SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "da=%d, n_A=%d, n_MX=%d, putv=%r\n", smar_rcpt->arr_da, n_A, smar_rcpt->arr_A_qsent, ret)); if (sm_is_err(ret)) goto error; /* Now go through all MX entries */ /* Note: smar_rcpt->arr_A_qsent > 0 iff smar_rcpt->arr_res != NULL */ cnt_A = 0; for (i = 0; i < smar_rcpt->arr_A_qsent; i++) { smar_dns = &(smar_rcpt->arr_res[i]); SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "i=%d, smar_dns=%p\n", i, smar_dns)); /* and send all A records with the MX data */ for (j = 0; j < smar_dns->ardns_n_A; j++) { SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "i=%d, j=%d, a=%A\n", i, j, smar_dns->ardns_A_rrs[j])); #if 0 /* XXX HACK: ignore addresses with value 0 */ if (smar_dns->ardns_A_rrs[j] == 0) continue; #endif if (cnt_A >= n_A) break; ++cnt_A; ret = sm_rcb_putv(rcb, RCB_PUTV_NONE, SM_RCBV_INT, RT_R2Q_RCPT_IPV4, smar_dns->ardns_A_rrs[j], SM_RCBV_INT, RT_R2Q_RCPT_PRE, (uint32_t) smar_dns->ardns_pref, /* XXX TTL is from MX record! change it? */ SM_RCBV_INT, RT_R2Q_RCPT_TTL, smar_dns->ardns_ttl, SM_RCBV_END); SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "i=%d, j=%d, putv=%r\n", i, j, ret)); if (sm_is_err(ret)) goto error; } if (cnt_A >= n_A) break; } return ret; error: /* cleanup? */ return ret; } /* ** SMAR_RCPT_RE_IPV4 -- Send reply for a RCPT routing request ** ** Parameters: ** smar_rcpt -- rcpt routing information ** n_addr -- number of addresses ** first -- is this the first invocation for this RCB? ** ** Returns: ** usual sm_error code ** ** Called by: smar_rcpt_rslv() for mailertable entries with RHS [I.P.V.4]* ** ** Last code review: 2004-03-15 17:17:27 ** Last code change: */ static sm_ret_T smar_rcpt_re_ipv4(smar_rcpt_P smar_rcpt, uint32_t n_addr, bool first) { sm_rcb_P rcb; sm_ret_T ret; smar_rcpts_P smar_rcpts; SM_IS_SMAR_RCPT(smar_rcpt); smar_rcpts = smar_rcpt->arr_rcpts; SM_IS_SMAR_RCPTS(smar_rcpts); if (SMARRS_IS_FLAG(smar_rcpts, SMARRS_FL_NOSEND)) return SM_SUCCESS; rcb = &smar_rcpts->arrs_rcbe->rcbe_rcb; if (!SMARRS_IS_FLAG(smar_rcpts, SMARRS_FL_INITRE)) { ret = smar_rcpt_re_first(smar_rcpt); if (sm_is_err(ret)) return ret; } if (SMARR_IS_FLAG(smar_rcpt, SMARR_FL_ALIAS) || !first || smar_rcpts->arrs_lst_n > 1) ret = sm_rcb_putv(rcb, RCB_PUTV_NONE, SM_RCBV_INT, RT_R2Q_RCPT_DA, smar_rcpt->arr_da, SM_RCBV_INT, (0 == smar_rcpt->arr_port) ? RT_NOSEND : RT_R2Q_RCPT_PORT, (uint32_t) smar_rcpt->arr_port, SM_RCBV_INT, RT_R2Q_OWN_REF, smar_rcpt->arr_owner_idx, SM_RCBV_INT, SMARR_IS_FLAG(smar_rcpt, SMARR_FL_HASVERP) ? RT_R2Q_RCPT_FL : RT_NOSEND, (uint32_t) SMARRT_FL_VERP, SM_RCBV_INT, SMARRQ_IS_FLAG(smar_rcpt, SMARRQ_FL_CONF) ? RT_R2Q_MAP_RES_CNF_RCPT : RT_NOSEND, smar_rcpt->arr_maprescnf, SM_RCBV_STR, SMARRQ_IS_FLAG(smar_rcpt, SMARRQ_FL_CONF) ? RT_R2Q_RHS_CNF_RCPT : RT_NOSEND, smar_rcpt->arr_rhs_conf, SM_RCBV_STR, RT_R2Q_RCPT_PA, smar_rcpt->arr_pa, SM_RCBV_INT, RT_R2Q_RCPT_NAR, n_addr, SM_RCBV_INT, RT_R2Q_RCPT_IPV4, smar_rcpt->arr_ipv4, SM_RCBV_INT, RT_R2Q_RCPT_PRE, (uint32_t) 0, SM_RCBV_INT, RT_R2Q_RCPT_TTL, SMAR_DEFAULT_TTL, SM_RCBV_END); else ret = sm_rcb_putv(rcb, RCB_PUTV_NONE, SM_RCBV_INT, RT_R2Q_RCPT_DA, smar_rcpt->arr_da, SM_RCBV_INT, (0 == smar_rcpt->arr_port) ? RT_NOSEND : RT_R2Q_RCPT_PORT, (uint32_t) smar_rcpt->arr_port, SM_RCBV_INT, RT_R2Q_OWN_REF, smar_rcpt->arr_owner_idx, SM_RCBV_INT, SMARR_IS_FLAG(smar_rcpt, SMARR_FL_HASVERP) ? RT_R2Q_RCPT_FL: RT_NOSEND, (uint32_t) SMARRT_FL_VERP, SM_RCBV_INT, SMARRQ_IS_FLAG(smar_rcpt, SMARRQ_FL_CONF) ? RT_R2Q_MAP_RES_CNF_RCPT : RT_NOSEND, smar_rcpt->arr_maprescnf, SM_RCBV_STR, SMARRQ_IS_FLAG(smar_rcpt, SMARRQ_FL_CONF) ? RT_R2Q_RHS_CNF_RCPT : RT_NOSEND, smar_rcpt->arr_rhs_conf, SM_RCBV_INT, RT_R2Q_RCPT_NAR, n_addr, SM_RCBV_INT, RT_R2Q_RCPT_IPV4, smar_rcpt->arr_ipv4, SM_RCBV_INT, RT_R2Q_RCPT_PRE, (uint32_t) 0, SM_RCBV_INT, RT_R2Q_RCPT_TTL, SMAR_DEFAULT_TTL, SM_RCBV_END); return ret; } /* ** SMAR_RCPT_RE_IPV4_MORE -- Add another reply for a RCPT routing request ** (used to send another IPv4 address) ** ** Parameters: ** rcb -- RCB to use for sending back data ** arr_ipv4 -- resolved IPv4 address ** ** Returns: ** usual sm_error code ** ** Called by: smar_rcpt_rslv() for mt entries with RHS [I.P.V.4]+ ** ** Last code review: 2004-03-15 17:18:05 ** Last code change: */ static sm_ret_T smar_rcpt_re_ipv4_more(sm_rcb_P rcb, ipv4_T arr_ipv4) { return sm_rcb_putv(rcb, RCB_PUTV_NONE, SM_RCBV_INT, RT_R2Q_RCPT_IPV4, arr_ipv4, SM_RCBV_INT, RT_R2Q_RCPT_PRE, (uint32_t) 0, SM_RCBV_INT, RT_R2Q_RCPT_TTL, SMAR_DEFAULT_TTL, SM_RCBV_END); } /* ** SMAR_RCPT_RE_ERR -- Send an error reply for a RCPT address ** ** Parameters: ** smar_rcpt -- rcpt routing information ** res -- error code ** XXX this should also send an error text, otherwise QMGR has ** problem to "reconstruct" the correct error (e.g., during which ** lookup did the error occur (MX, A, ...)) ** ** Returns: ** usual sm_error code ** ** Last code review: 2004-03-15 17:22:36 ** Last code change: 2004-05-20 21:06:35 */ static sm_ret_T smar_rcpt_re_err(smar_rcpt_P smar_rcpt, sm_ret_T res) { sm_rcb_P rcb; sm_ret_T ret; smar_rcpts_P smar_rcpts; SM_IS_SMAR_RCPT(smar_rcpt); smar_rcpts = smar_rcpt->arr_rcpts; SM_IS_SMAR_RCPTS(smar_rcpts); if (SMARRS_IS_FLAG(smar_rcpts, SMARRS_FL_NOSEND)) { smar_rcpts->arrs_ret = res; return SM_SUCCESS; } rcb = &smar_rcpts->arrs_rcbe->rcbe_rcb; if (!SMARRS_IS_FLAG(smar_rcpts, SMARRS_FL_INITRE)) { ret = smar_rcpt_re_first(smar_rcpt); if (sm_is_err(ret)) return ret; } ret = sm_rcb_putv(rcb, RCB_PUTV_NONE, SM_RCBV_INT, RT_R2Q_RCPT_ST, res, SM_RCBV_INT, RT_R2Q_OWN_REF, smar_rcpt->arr_owner_idx, SM_RCBV_END); /* need to send address? */ if (SM_SUCCESS == ret && (smar_rcpts->arrs_lst_n > 1 || (smar_rcpts->arrs_lst_n == 1 && SMARR_IS_FLAG(smar_rcpt, SMARR_FL_ALIAS)))) ret = sm_rcb_putv(rcb, RCB_PUTV_NONE, SM_RCBV_STR, RT_R2Q_RCPT_PA, smar_rcpt->arr_pa, SM_RCBV_END); return ret; } /* ** SMAR_RCPTS_RE_ERR -- Send an error reply for a RCPT list ** ** Parameters: ** smar_rcpts -- SMAR RCPT LIST context ** res -- error code ** XXX this should also send an error text, otherwise QMGR has ** problem to "reconstruct" the correct error (e.g., during which ** lookup did the error occur (MX, A, ...)) ** ** Returns: ** usual sm_error code ** ** Last code review: 2004-03-15 17:22:55 ** Last code change: */ static sm_ret_T smar_rcpts_re_err(smar_rcpts_P smar_rcpts, sm_ret_T res) { sm_rcb_P rcb; sm_ret_T ret; SM_IS_SMAR_RCPTS(smar_rcpts); rcb = &smar_rcpts->arrs_rcbe->rcbe_rcb; ret = sm_rcb_putv(rcb, RCB_PUTV_FIRST, SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT, SM_RCBV_BUF, RT_R2Q_RCPT_ID, smar_rcpts->arrs_rcpt_id, SMTP_RCPTID_SIZE, SM_RCBV_INT, RT_R2Q_RCPT_ST, res, SM_RCBV_END); return ret; } /* ** SMAR_RES_GOOD -- Check whether result is "good enough", i.e., ** one of the best MX records has been resolved. ** ** Parameters: ** smar_rcpt -- rcpt routing information ** ** Returns: ** good enough? ** ** Last code review: 2004-03-15 17:27:39 ** Last code change: */ static bool smar_res_good(smar_rcpt_P smar_rcpt) { int i; ushort pref; smar_dns_P smar_dns; SM_IS_SMAR_RCPT(smar_rcpt); if (NULL == smar_rcpt->arr_res || smar_rcpt->arr_A_qsent <= 0) return false; pref = smar_rcpt->arr_res[0].ardns_pref; /* Now go through all MX entries (note: the entries are sorted!) */ for (i = 0; i < smar_rcpt->arr_A_qsent; i++) { smar_dns = &(smar_rcpt->arr_res[i]); if (pref < smar_dns->ardns_pref) break; if (smar_dns->ardns_n_A > 0 && smar_dns->ardns_A_rrs != NULL) return true; } return false; } /* ** ISLOCALNAME -- Is name a "local" name? ** ** Parameters: ** smar_ctx -- SMAR context ** name -- name of MX host ** ** Returns: ** true iff name is the name of the host ** ** Notes: ** - this needs to allow for a set of names... ** should we check mt: if name is in there and RHS is local ** then return true? [done] ** ** - this is ugly: it has to check for trailing dot. ** can we make certain that only one form of names is ** used as input? ** ** - this function can fail if there isn't enough memory... ** that's bad because bool can't be used to return a ** temporary error. Pass in a str (maybe part of some context?) ** ** Last code review: 2004-03-15 17:29:27; see comments! ** Last code change: */ static bool islocalname(smar_ctx_P smar_ctx, sm_cstr_P name) { sm_str_T str; sm_str_P rhs; size_t len; bool islocal; SM_IS_SMAR_CTX(smar_ctx); SM_REQUIRE(name != NULL); islocal = false; len = sm_cstr_getlen(name); if (len > 1 && sm_cstr_rd_elem(name, len - 1) == '.') --len; if (sm_str_getlen(smar_ctx->smar_hostname) == len && strncasecmp((const char *) sm_str_data(smar_ctx->smar_hostname), (const char *) sm_cstr_data(name), len) == 0) return true; /* XXX Check other maps/...? */ sm_str_assign(str, NULL, sm_cstr_data(name), len, len); rhs = sm_str_new(NULL, MAXADDRLEN, MAXADDRLEN + 2); if (NULL == rhs) return false; /* XXX ??? */ if (sm_map_lookup(smar_ctx->smar_mt_map, 0, &str, rhs) == SM_SUCCESS) islocal = strcmp((const char *) sm_str_getdata(rhs), LMTP_IPV4_S2) == 0 || strcmp((const char *) sm_str_getdata(rhs), LMTP_MT) == 0; sm_str_free(rhs); return islocal; } /* ** GOTA -- Is A record already in list? ** ** Parameters: ** smar_rcpt -- SMAR recipient ** ipv4 -- IPv4 address to check ** ** Returns: ** true iff ipv4 address is already in list ** ** Last code review: ** Last code change: */ static bool gotA(smar_rcpt_P smar_rcpt, ipv4_T ipv4) { int i, j; for (i = 0; i < smar_rcpt->arr_A_qsent; i++) { ipv4_T *A_rrs; A_rrs = smar_rcpt->arr_res[i].ardns_A_rrs; if (NULL == A_rrs) continue; for (j = 0; j < smar_rcpt->arr_res[i].ardns_n_A; j++) { if (A_rrs[j] == ipv4) return true; } } return false; } /* ** SMAR_RCPT_CB -- Callback function for DNS resolver ** ** Parameters: ** dns_res -- DNS resolver result ** ctx -- context: smar_rcpt ** ** Returns: ** usual sm_error code ** ** Locking: ** locks smar_ctx ** ** Used as callback for dns_req_add() by smar_rcpt_rslv() and by itself. ** ** To return results to the client this function calls: ** - smar_rcpt_re_all() ** - smar_rcpt_re_err() ** - smar_rcpts_re_err() ** ** ToDo: check error handling ** ** This function needs to keep track of the number of open requests. ** Only when it received all answers it must send them back (in an RCB) ** to the client. Unfortunately there are a lot of error cases to handle. ** Nevertheless, the function requires that all outstanding requests ** are answered (even if the DNS resolver encounters an error), there ** is no timeout in here (can't be: it's a callback function). ** The number of recipients is stored in smar_rcpts->arrs_lst_n, ** which is also the initial number of (MX) requests. ** For each successful MX lookup this function starts DNS requests for ** the correspoding A records (number of submitted queries: arr_A_qsent, ** number of received results: arr_A_rrcvd). ** Hence there is one counter per recipient (smar_rcpt->arr_A_rrcvd) ** and one counter per recipient list (smar_rcpts->arrs_lst_n). ** ** There's a rather complex handling of "acceptable" errors, e.g., ** if some A records don't resolve. ** If all requests for a single recipient have been fulfilled, then ** arrs_resolved is incremented. When that value reaches arrs_lst_n ** then the created RCB is sent. ** ** Problems: ** - RCB is full: what to do? It is still necessary to collect all ** responses, but there should be an error returned to the caller: ** add a state flag in rcpts? ** - others? ** ** Last code review: ** Last code change: */ static sm_ret_T smar_rcpt_cb(dns_res_P dns_res, void *ctx) { dns_type_T dnstype; sm_ret_T ret, dnsres; uint n_entries, i; size_t n; int r; uint8_t flags; sm_rcb_P rcb; smar_rcpts_P smar_rcpts; smar_rcpt_P smar_rcpt; dns_rese_P dns_rese; smar_ctx_P smar_ctx; dns_mgr_ctx_P dns_mgr_ctx; smar_clt_ctx_P smar_clt_ctx; sm_cstr_P q; #define SM_INCR_C_MX 0x01 #define SM_INCR_RESOLVED 0x02 #define SM_NOSEND 0x10 #define SM_NOFREE 0x20 if (NULL == dns_res || ctx == NULL) { SMAR_LEV_DPRINTF(0, (SMAR_DEBFP, "sev=ERROR, func=smar_rcpt_cb, dns_res=%p, ctx=%p\n", dns_res, ctx)); /* XXX Oops... how to do logging if there is no context? */ sm_log_write(NULL, AR_LCAT_RESOLVER, AR_LMOD_RCPT_CB, SM_LOG_ERROR, 0, "sev=ERROR, func=smar_rcpt_cb, dns_res=%p, ctx=%p", dns_res, ctx); return SM_FAILURE; } smar_rcpt = (smar_rcpt_P) ctx; SM_IS_SMAR_RCPT(smar_rcpt); smar_rcpts = smar_rcpt->arr_rcpts; SM_IS_SMAR_RCPTS(smar_rcpts); smar_clt_ctx = smar_rcpts->arrs_smar_clt_ctx; SM_IS_SMAR_CLT_CTX(smar_clt_ctx); smar_ctx = smar_rcpts->arrs_smar_ctx; SM_IS_SMAR_CTX(smar_ctx); flags = 0; /* ** Check whether SMAR is shutting down: if yes: don't ** do anything with the replies since the tasks are invalid. */ r = pthread_mutex_lock(&smar_ctx->smar_mutex); SM_LOCK_OK(r); if (r != 0) { /* LOG; XXX What to do in this error case? */ sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RCPT_CB, SM_LOG_CRIT, 1, "sev=CRIT, func=smar_rcpt_cb, lock=%d", r); return SM_FAILURE; } if (smar_is_stop(smar_ctx)) { sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RCPT_CB, SM_LOG_NOTICE, 10, "sev=NOTICE, func=smar_rcpt_cb, shutting down"); /* XXX free smar_rcpts? */ r = pthread_mutex_unlock(&smar_ctx->smar_mutex); return SM_SUCCESS; } dns_mgr_ctx = smar_ctx->smar_dns_mgr_ctx; SM_REQUIRE(dns_mgr_ctx != NULL); rcb = &smar_rcpts->arrs_rcbe->rcbe_rcb; ret = SM_SUCCESS; dnsres = dns_res->dnsres_ret; dnstype = dns_res->dnsres_qtype; n_entries = dns_res->dnsres_entries; if (SMARRS_IS_FLAG(smar_rcpts, SMARRS_FL_NOSEND)) SM_SET_FLAG(flags, SM_NOSEND); if (SMARRS_IS_FLAG(smar_rcpts, SMARRS_FL_NOFREE)) SM_SET_FLAG(flags, SM_NOFREE); SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_cb, pa=%S, dnsres=%r, dnstype=%d, flags=%#x, entries=%d\n", smar_rcpt->arr_pa, dnsres, dnstype, smar_rcpt->arr_flags, n_entries)); if (DNSR_NOTFOUND == dnsres && SMARR_IS_FLAG(smar_rcpt, SMARR_FL_A4MX) && !SMARR_IS_FLAG(smar_rcpt, SMARR_FL_A4A)) { /* MX record not found -> try A (see RFC 2821(?)) */ dnstype = T_MX; dnsres = SM_SUCCESS; /* pretend it's ok */ n_entries = 0; /* got no entries, MX code has a case for this */ } else if (sm_is_err(dnsres)) { /* Some other DNS error */ if (DNSR_REFUSED == dnsres) sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RCPT_CB, SM_LOG_CRIT, 6, "sev=CRIT, func=smar_rcpt_cb, pa=%#S, query=%.256C, dnstype=%s, error=%m", smar_rcpt->arr_pa, dns_res->dnsres_query, dnstype2txt(dnstype), dnsres); else sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RCPT_CB, SM_LOG_NOTICE, 7, "sev=NOTICE, func=smar_rcpt_cb, pa=%S, query=%.256C, dnstype=%s, error=%m", smar_rcpt->arr_pa, dns_res->dnsres_query, dnstype2txt(dnstype), dnsres); /* ** This needs to deal with errors for A records of MX records, ** i.e., partial results! */ if (T_A == dnstype || SMARR_IS_FLAG(smar_rcpt, SMARR_FL_A4A)) { /* count it as a reply */ smar_rcpt->arr_A_rrcvd++; SM_SET_FLAG(flags, SM_INCR_C_MX); } switch (dnsres) { case DNSR_TEMP: case DNSR_TIMEOUT: /* change error code? */ SMARR_SET_FLAG(smar_rcpt, SMARR_FL_TEMP); break; case DNSR_REFUSED: case DNSR_NOTFOUND: case DNSR_PERM: case DNSR_NO_DATA: case DNSR_MXINVALID: case DNSR_PTRINVALID: case DNSR_CNINVALID: case sm_error_perm(SM_EM_DNS, EINVAL): /* change error code? */ break; default: sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RCPT_CB, SM_LOG_ERROR, 0, "sev=ERROR, func=smar_rcpt_cb, unknown_error=%#x", dns_res->dnsres_ret); /* Which error to return to caller? */ ret = dnsres; /* sm_error_perm(SM_EM_AR, EINVAL); */ goto error; } /* ** Error out iff ** this query was for an MX record or ** this query was for an A record ** and all records have been received ** and (there was a temporary error or ** there was only one (failed) query or ** there are no results (A records)) ** and !(the first result is valid) ** ** What about errors only for "less important" data, e.g., ** A MX 1 A1 ** A MX 2 A2 ** Lookup for A1 is ok, for A2 (temp) fails. ** It might be good enough to send A1 back, BUT ** if delivery to A1 fails, A2 should be tried, which ** won't happen until TTL expires... ** We could add a flag indicating a temporary failure ** or simply reduce TTL (cheating...) to force another lookup ** later on. ** ** libdns sends the MX records sorted according to preference, ** hence we only need to check whether the first entry in ** arr_res is valid, e.g., arr_res[0]->ardns_n_A > 0 && ** arr_res[0]->ardns_A_rrs != NULL ** not really: the first entries could have the same ** preference... need to check them (while not valid but ** preference is the same as first entry...) */ #if 0 sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RCPT_CB, SM_LOG_WARN, 1, "sev=WARN, func=smar_rcpt_cb, pa=%S, dnstype=%d, flags=%#x, error=%#x, arr_A_rrcvd=%d, arr_A_qsent=%d, smar_res_good=%d", smar_rcpt->arr_pa, dnstype, smar_rcpt->arr_flags, dnsres, smar_rcpt->arr_A_qsent, smar_rcpt->arr_A_rrcvd, smar_res_good(smar_rcpt)); #endif /* 0 */ if ((SMARR_IS_FLAG(smar_rcpt, SMARR_FL_A4MX) && !SMARR_IS_FLAG(smar_rcpt, SMARR_FL_A4A)) || (smar_rcpt->arr_A_qsent == smar_rcpt->arr_A_rrcvd && SMARR_IS_FLAG(smar_rcpt, SMARR_FL_A4A) && (SMARR_IS_FLAG(smar_rcpt, SMARR_FL_TEMP) || 1 == smar_rcpt->arr_A_qsent || 0 == smar_rcpt->arr_n_A) && !smar_res_good(smar_rcpt) ) ) { ret = smar_rcpt_re_err(smar_rcpt, dnsres); if (sm_is_err(ret)) { SMARRS_SET_FLAG(smar_rcpts, SMARRS_FL_ERCB); goto error; /* XXX need more cleanup here? */ } } else if (smar_rcpt->arr_A_qsent == smar_rcpt->arr_A_rrcvd) { ret = smar_rcpt_re_all(smar_rcpt); if (sm_is_err(ret)) { SMARRS_SET_FLAG(smar_rcpts, SMARRS_FL_ERCB); goto error; /* XXX need more cleanup here? */ } } /* got all results for this rcpt? */ if (smar_rcpt->arr_A_qsent == smar_rcpt->arr_A_rrcvd) { SMARR_SET_FLAG(smar_rcpt, SMARR_FL_FREEIT); ++smar_rcpts->arrs_resolved; SM_SET_FLAG(flags, SM_INCR_RESOLVED); } /* fall through: dnsres != SM_SUCCESS -> reply part below */ } if (SM_SUCCESS == dnsres && n_entries == 1 && (dns_rese = DRESL_FIRST(dns_res)) != NULL && T_CNAME == dns_rese->dnsrese_type) { SMAR_LEV_DPRINTF(1, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_cb, pa=%S, got=CNAME, entries=%d\n", smar_rcpt->arr_pa, n_entries)); if (T_MX == dns_res->dnsres_qtype) { if (SMARR_IS_FLAG(smar_rcpt, SMARR_FL_C_MX)) { /* don't want two CNAMEs */ SMARR_SET_FLAG(smar_rcpt, SMARR_FL_C_MX_L); /* XXX set error code? */ } else SMARR_SET_FLAG(smar_rcpt, SMARR_FL_C_MX); } else if (T_A == dns_res->dnsres_qtype) { if (SMARR_IS_FLAG(smar_rcpt, SMARR_FL_C_A)) { /* don't want two CNAMEs */ SMARR_SET_FLAG(smar_rcpt, SMARR_FL_C_A_L); /* XXX set error code? */ } else SMARR_SET_FLAG(smar_rcpt, SMARR_FL_C_A); } if (SMARR_IS_FLAG(smar_rcpt, SMARR_FL_C_MX_L|SMARR_FL_C_A_L)) { /* got CNAME before */ ret = smar_rcpt_re_err(smar_rcpt, sm_error_perm(SM_EM_AR, ELOOP)); if (sm_is_err(ret)) { SMARRS_SET_FLAG(smar_rcpts, SMARRS_FL_ERCB); goto error; /* XXX need more cleanup? */ } ++smar_rcpts->arrs_resolved; SM_SET_FLAG(flags, SM_INCR_RESOLVED); SMARR_SET_FLAG(smar_rcpt, SMARR_FL_FREEIT); goto done; } else if (T_MX == dns_res->dnsres_qtype || dns_res->dnsres_qtype == T_A) { q = dns_rese->dnsrese_val.dnsresu_name; ret = dns_req_add(dns_mgr_ctx, q, dns_res->dnsres_qtype, smar_rcpt->arr_timeout, smar_rcpt_cb, ctx); if (sm_is_err(ret)) { /* XXX more cleanup? */ SMARRS_SET_FLAG(smar_rcpts, SMARRS_FL_TEMP); } } #if 0 if (SMARR_IS_FLAG(smar_rcpt, SMARR_FL_C_MX)) { /* got CNAME before */ ret = smar_rcpt_re_err(smar_rcpt, sm_error_perm(SM_EM_AR, ELOOP)); if (sm_is_err(ret)) { SMARRS_SET_FLAG(smar_rcpts, SMARRS_FL_ERCB); goto error; /* XXX need more cleanup? */ } ++smar_rcpts->arrs_resolved; SM_SET_FLAG(flags, SM_INCR_RESOLVED); SMARR_SET_FLAG(smar_rcpt, SMARR_FL_FREEIT); goto done; } SMARR_SET_FLAG(smar_rcpt, SMARR_FL_C_MX); q = dns_rese->dnsrese_val.dnsresu_name; SMAR_LEV_DPRINTF(1, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_cb, pa=%S, MX=%.256s\n", smar_rcpt->arr_pa, sm_cstr_data(q))); ret = dns_req_add(dns_mgr_ctx, q, T_MX, smar_rcpt->arr_timeout, smar_rcpt_cb, ctx); if (sm_is_err(ret)) { SMARRS_SET_FLAG(smar_rcpts, SMARRS_FL_TEMP); } #endif /* 0 */ } else if (SM_SUCCESS == dnsres && dnstype == T_MX) { uint n_MX; int cutoff_pref; SMARR_SET_FLAG(smar_rcpt, SMARR_FL_GOTMX); n_MX = (0 == n_entries) ? 1 : n_entries; /* Check whether local name is in the list */ cutoff_pref = -1; for (dns_rese = DRESL_FIRST(dns_res), i = 0; !SMARRQ_IS_FLAG(smar_rcpt, SMARRQ_FL_NOMXCUT) && dns_rese != DRESL_END(dns_res) && i < n_entries; dns_rese = DRESL_NEXT(dns_rese), i++) { /* XXX ignore other RR types... CNAME handling!?!? */ if (dns_rese->dnsrese_type != T_MX) continue; q = dns_rese->dnsrese_val.dnsresu_name; if (islocalname(smar_ctx, q)) { cutoff_pref = dns_rese->dnsrese_pref; break; } } /* if it is: count number of entries with lower preference */ if (cutoff_pref != -1) { n_MX = 0; for (dns_rese = DRESL_FIRST(dns_res), i = 0; dns_rese != DRESL_END(dns_res) && i < n_entries; dns_rese = DRESL_NEXT(dns_rese), i++) { /* XXX ignore other RR types... CNAME!?!? */ if (dns_rese->dnsrese_type != T_MX) continue; if (cutoff_pref > dns_rese->dnsrese_pref) ++n_MX; } if (0 == n_MX) { SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_cb, pa=%S, cutoff_pref=%d, n_MX=%d\n", smar_rcpt->arr_pa, cutoff_pref, n_MX)); /* Oops... "Mail loops back to me"? */ ret = smar_rcpt_re_err(smar_rcpt, sm_error_perm(SM_EM_AR, SM_E_MXEMPTY)); if (sm_is_err(ret)) { SMARRS_SET_FLAG(smar_rcpts, SMARRS_FL_ERCB); goto error; /* XXX need more cleanup? */ } ++smar_rcpts->arrs_resolved; SM_SET_FLAG(flags, SM_INCR_RESOLVED); SMARR_SET_FLAG(smar_rcpt, SMARR_FL_FREEIT); goto done; } } SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_cb, nMX=%d\n", n_entries)); /* restrict the number of entries to some sane value */ if (n_MX > SM_DNS_MX_MAX) n_MX = SM_DNS_MX_MAX; n = n_MX * sizeof(*(smar_rcpt->arr_res)); if (n < n_MX || n < sizeof(*(smar_rcpt->arr_res))) { SMARRS_SET_FLAG(smar_rcpts, SMARRS_FL_TEMP); ret = sm_error_perm(SM_EM_AR, SM_E_OVFLW_SC); goto error; /* XXX need more cleanup here? */ } smar_rcpt->arr_res = (smar_dns_T *) sm_zalloc(n); if (NULL == smar_rcpt->arr_res) { SMARRS_SET_FLAG(smar_rcpts, SMARRS_FL_TEMP); ret = sm_error_perm(SM_EM_AR, ENOMEM); goto error; /* XXX need more cleanup here? */ } for (dns_rese = DRESL_FIRST(dns_res), i = 0; dns_rese != DRESL_END(dns_res) && i < n_entries; dns_rese = DRESL_NEXT(dns_rese)) { /* XXX ignore other RR types... CNAME handling!?!? */ if (dns_rese->dnsrese_type != T_MX) { SMAR_LEV_DPRINTF(1, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_cb, i=%d, pa=%S, CNAME=%.256C, skipped\n", i, smar_rcpt->arr_pa, (T_CNAME == dns_rese->dnsrese_type) ? dns_rese->dnsrese_val.dnsresu_name : NULL)); continue; } if (cutoff_pref != -1 && cutoff_pref <= dns_rese->dnsrese_pref) continue; if (i >= n_MX) continue; q = dns_rese->dnsrese_val.dnsresu_name; smar_rcpt->arr_res[i].ardns_ttl = dns_rese->dnsrese_ttl; smar_rcpt->arr_res[i].ardns_pref = dns_rese->dnsrese_pref; smar_rcpt->arr_res[i].ardns_name = SM_CSTR_DUP(q); smar_rcpt->arr_res[i].ardns_n_A = -1; smar_rcpt->arr_res[i].ardns_A_rrs = NULL; SMAR_LEV_DPRINTF(1, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_cb, i=%d, pa=%S, MX=%.256C\n", i, smar_rcpt->arr_pa, q)); ret = dns_req_add(dns_mgr_ctx, q, T_A, smar_rcpt->arr_timeout, smar_rcpt_cb, ctx); SMAR_LEV_DPRINTF(8, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_cb, pa=%S, MX=%.256C, dns_req_add=%#x\n", smar_rcpt->arr_pa, q, ret)); if (sm_is_err(ret)) { SMARRS_SET_FLAG(smar_rcpts, SMARRS_FL_TEMP); smar_rcpt->arr_A_qsent = i; break; /* XXX need more cleanup here? */ } ++smar_rcpt->arr_A_qsent; ++i; } SM_ASSERT(smar_rcpt->arr_A_qsent <= n_MX); /* got no MX record but look for A record? */ if (n_entries == 0 || i == 0) { q = dns_res->dnsres_query; SM_ASSERT(n_MX > 0); smar_rcpt->arr_res[0].ardns_ttl = SMAR_DEFAULT_TTL; smar_rcpt->arr_res[0].ardns_pref = 0; smar_rcpt->arr_res[0].ardns_name = SM_CSTR_DUP(q); smar_rcpt->arr_res[0].ardns_n_A = -1; smar_rcpt->arr_res[0].ardns_A_rrs = NULL; ret = dns_req_add(dns_mgr_ctx, q, T_A, smar_rcpt->arr_timeout, smar_rcpt_cb, ctx); if (sm_is_err(ret)) { SMARRS_SET_FLAG(smar_rcpts, SMARRS_FL_TEMP); SMARR_SET_FLAG(smar_rcpt, SMARR_FL_FREEIT); goto error; /* XXX need more cleanup here? */ } ++smar_rcpt->arr_A_qsent; SM_ASSERT(smar_rcpt->arr_A_qsent <= n_MX); } SMARR_SET_FLAG(smar_rcpt, SMARR_FL_A4A); } /* XXX code review: continue here 2004-03-16 17:32:37 */ else if (SM_SUCCESS == dnsres && dnstype == T_A) { SMARR_SET_FLAG(smar_rcpt, SMARR_FL_GOTA); if (n_entries > 0) { uint n_A; smar_dns_P smar_dns; SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_cb, nA_entries=%d, nMX=%d\n", n_entries, smar_rcpt->arr_A_qsent)); /* XXX Count this always? */ smar_rcpt->arr_A_rrcvd++; if (smar_rcpt->arr_A_qsent == smar_rcpt->arr_A_rrcvd) SMARR_SET_FLAG(smar_rcpt, SMARR_FL_FREEIT); /* Find matching entry in list of MX records */ for (i = 0; i < smar_rcpt->arr_A_qsent; i++) { q = smar_rcpt->arr_res[i].ardns_name; SMAR_LEV_DPRINTF(5, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_cb, i=%d, MX=%.256C, compare=%.256C\n", i, dns_res->dnsres_query, q)); if (q != NULL && SM_CSTR_CASEQ(q, dns_res->dnsres_query)) break; } if (i >= smar_rcpt->arr_A_qsent) { sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RCPT_CB, SM_LOG_ERROR, 0, "sev=ERROR, func=smar_rcpt_cb, MX=%.256C, status=cannot_find_MX, entries=%d", dns_res->dnsres_query, smar_rcpt->arr_A_qsent); /* ** XXX What now? Abort query somehow? */ SMARRS_SET_FLAG(smar_rcpts, SMARRS_FL_TEMP); ret = sm_error_perm(SM_EM_AR, SM_E_NOTFOUND); goto error; /* XXX more cleanup! better handling */ } smar_dns = &(smar_rcpt->arr_res[i]); SM_SET_FLAG(flags, SM_INCR_C_MX); n_A = (n_entries < SM_DNS_A_PER_MX_MAX) ? n_entries : SM_DNS_A_PER_MX_MAX; n = n_A * sizeof(*(smar_dns->ardns_A_rrs)); if (n < n_A || n < sizeof(*(smar_dns->ardns_A_rrs))) { SMARRS_SET_FLAG(smar_rcpts, SMARRS_FL_TEMP); ret = sm_error_perm(SM_EM_AR, SM_E_OVFLW_SC); goto error; /* XXX need more cleanup here? */ } smar_dns->ardns_A_rrs = (ipv4_T *) sm_zalloc(n); if (NULL == smar_dns->ardns_A_rrs) { SMARRS_SET_FLAG(smar_rcpts, SMARRS_FL_TEMP); ret = sm_error_perm(SM_EM_AR, ENOMEM); goto error; /* XXX need more cleanup here? */ } smar_dns->ardns_n_A = 0; for (dns_rese = DRESL_FIRST(dns_res), i = 0; dns_rese != DRESL_END(dns_res) && i < n_entries; dns_rese = DRESL_NEXT(dns_rese)) { /* XXX ignore other RR types... CNAME!?!? */ if (dns_rese->dnsrese_type != T_A) continue; if (i >= n_A) continue; /* got the IP address already? */ if (gotA(smar_rcpt, dns_rese->dnsrese_val.dnsresu_a)) continue; sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RCPT_CB, SM_LOG_INFO, 14, "sev=INFO, func=smar_rcpt_cb, i=%d, pa=%S, ip=%A", i, smar_rcpt->arr_pa, dns_rese->dnsrese_val.dnsresu_a); smar_dns->ardns_A_rrs[i] = dns_rese->dnsrese_val.dnsresu_a; ++i; ++smar_rcpt->arr_n_A; ++smar_dns->ardns_n_A; } /* Got all entries? */ if (smar_rcpt->arr_A_qsent == smar_rcpt->arr_A_rrcvd) { ret = smar_rcpt_re_all(smar_rcpt); if (sm_is_err(ret)) { SMARRS_SET_FLAG(smar_rcpts, SMARRS_FL_ERCB); goto error; /* XXX need more cleanup? */ } ++smar_rcpts->arrs_resolved; SM_SET_FLAG(flags, SM_INCR_RESOLVED); } } else { sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RCPT_CB, SM_LOG_ERROR, 0, "sev=ERROR, func=smar_rcpt_cb, dnstype=T_A, query=%.256C, entries=0", dns_res->dnsres_query); } #if 0 sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RCPT_CB, SM_LOG_INFO, 12, "sev=INFO, func=smar_rcpt_cb, pa=%S, ip=%A", smar_rcpt->arr_pa, smar_rcpt->arr_ipv4); #endif /* 0 */ #if 0 sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RCPT_CB, SM_LOG_WARN, 1, "sev=WARN, func=smar_rcpt_cb, pa=%S, dnstype=%d, arr_A_rrcvd=%d, arr_A_qsent=%d", smar_rcpt->arr_pa, dnstype, smar_rcpt->arr_A_qsent, smar_rcpt->arr_A_rrcvd); #endif /* 0 */ } else if (SM_SUCCESS == dnsres && dnstype == T_CNAME) { SMAR_LEV_DPRINTF(1, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_cb, pa=%S, got=CNAME, entries=%d\n", smar_rcpt->arr_pa, n_entries)); if (SMARR_IS_FLAG(smar_rcpt, SMARR_FL_C_MX) || n_entries != 1) { /* got CNAME before or got not exactly one result */ ret = smar_rcpt_re_err(smar_rcpt, sm_error_perm(SM_EM_AR, ELOOP)); if (sm_is_err(ret)) { SMARRS_SET_FLAG(smar_rcpts, SMARRS_FL_ERCB); goto error; /* XXX need more cleanup? */ } ++smar_rcpts->arrs_resolved; SM_SET_FLAG(flags, SM_INCR_RESOLVED); SMARR_SET_FLAG(smar_rcpt, SMARR_FL_FREEIT); goto done; } SMARR_SET_FLAG(smar_rcpt, SMARR_FL_C_MX); dns_rese = DRESL_FIRST(dns_res); q = dns_rese->dnsrese_val.dnsresu_name; SMAR_LEV_DPRINTF(1, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_cb, pa=%S, MX=%.256s\n", smar_rcpt->arr_pa, sm_cstr_data(q))); ret = dns_req_add(dns_mgr_ctx, q, T_MX, smar_rcpt->arr_timeout, smar_rcpt_cb, ctx); if (sm_is_err(ret)) SMARRS_SET_FLAG(smar_rcpts, SMARRS_FL_TEMP); } else if (SM_SUCCESS == dnsres) { /* XXX other T_*: ignore for now, deal with them later! */ } done: if (smar_rcpt != NULL && SMARR_IS_FLAG(smar_rcpt, SMARR_FL_FREEIT) && !SM_IS_FLAG(flags, SM_NOFREE)) { ret = smar_rcpt_free(smar_rcpt, smar_rcpts); smar_rcpt = NULL; } if (smar_rcpts->arrs_resolved == smar_rcpts->arrs_lst_n) { if (SMARRS_IS_FLAG(smar_rcpts, SMARRS_FL_ERCB|SMARRS_FL_TEMP)) { /* ** Some error occurred earlier on which causes ** a bogus result. Replace the data in RCB with ** an appropriate error message. */ SMARRS_CLR_FLAG(smar_rcpts, SMARRS_FL_INITRE); ret = smar_rcpts_re_err(smar_rcpts, SM_E_ALIASEXP); /* XXX what to do if this fails? */ if (sm_is_err(ret)) { SMARRS_SET_FLAG(smar_rcpts, SMARRS_FL_FAIL); goto error; } } if (SMARRS_IS_FLAG(smar_rcpts, SMARRS_FL_NOSEND)) { SM_REQUIRE(smar_rcpts->arrs_cbf != NULL); ret = smar_rcpts->arrs_cbf(smar_rcpts, smar_rcpts->arrs_cb_ctx); if (sm_is_err(ret)) goto error; } else { ret = sm_rcbcom_endrep(&smar_clt_ctx->smac_com_ctx, smar_clt_ctx->smac_com_ctx.rcbcom_tsk, false, &smar_rcpts->arrs_rcbe); if (sm_is_err(ret)) goto error; } if (!SM_IS_FLAG(flags, SM_NOFREE)) { ret = smar_rcpts_free(smar_rcpts); /* XXX */ smar_rcpts = NULL; } } r = pthread_mutex_unlock(&smar_ctx->smar_mutex); SM_ASSERT(0 == r); if (r != 0 && sm_is_success(ret)) ret = sm_error_perm(SM_EM_AR, r); return ret; error: /* XXX Cleanup? */ sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RCPT_CB, SM_LOG_ERROR, 0, "sev=ERROR, func=smar_rcpt_cb, ret=%m", ret); if (T_A == dnstype && !SM_IS_FLAG(flags, SM_INCR_C_MX) && smar_rcpt != NULL) { smar_rcpt->arr_A_rrcvd++; SM_SET_FLAG(flags, SM_INCR_C_MX); } if (T_MX == dnstype || (smar_rcpt != NULL && (smar_rcpt->arr_A_qsent == smar_rcpt->arr_A_rrcvd || SMARR_IS_FLAG(smar_rcpt, SMARR_FL_FREEIT)))) { sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RCPT_CB, SM_LOG_WARN, 1, "sev=ERROR, func=smar_rcpt_cb, pa=%S, dnstype=%s, error=%#x, arr_A_rrcvd=%d, arr_A_qsent=%d, flags=%#x" , smar_rcpt->arr_pa, dnstype2txt(dnstype), ret , smar_rcpt->arr_A_qsent, smar_rcpt->arr_A_rrcvd , flags); if (!SM_IS_FLAG(flags, SM_NOFREE)) { ret = smar_rcpt_free(smar_rcpt, smar_rcpts); smar_rcpt = NULL; } if (!SM_IS_FLAG(flags, SM_INCR_RESOLVED) && smar_rcpts != NULL) { /* Really?? */ ++smar_rcpts->arrs_resolved; SM_SET_FLAG(flags, SM_INCR_RESOLVED); } if (smar_rcpts != NULL && smar_rcpts->arrs_resolved == smar_rcpts->arrs_lst_n) { if (SMARRS_IS_FLAG(smar_rcpts, SMARRS_FL_ERCB|SMARRS_FL_TEMP) && !SMARRS_IS_FLAG(smar_rcpts, SMARRS_FL_FAIL)) { ret = smar_rcpts_re_err(smar_rcpts, SM_E_ALIASEXP); if (SM_SUCCESS == ret) { if (SMARRS_IS_FLAG(smar_rcpts, SMARRS_FL_NOSEND)) { SM_REQUIRE(smar_rcpts->arrs_cbf != NULL); ret = smar_rcpts->arrs_cbf(smar_rcpts, smar_rcpts->arrs_cb_ctx); } else { ret = sm_rcbcom_endrep(&smar_clt_ctx->smac_com_ctx, smar_clt_ctx->smac_com_ctx.rcbcom_tsk, false, &smar_rcpts->arrs_rcbe); } } } if (!SM_IS_FLAG(flags, SM_NOFREE)) { ret = smar_rcpts_free(smar_rcpts); /* XXX */ smar_rcpts = NULL; } } } r = pthread_mutex_unlock(&smar_ctx->smar_mutex); SM_ASSERT(0 == r); if (r != 0 && sm_is_success(ret)) ret = sm_error_perm(SM_EM_AR, r); return ret; } /* ** SMAR_RCPT_RSLV -- Resolve RCPT address ** ** Parameters: ** smar_ctx -- SMTPC context ** smar_rcpts -- SMAR RCPT LIST context ** ** Returns: ** usual sm_error code ** ** Called by: smar_react() ** smar_access_chk() ** ** Side Effects: ** smar_rcpts is freed before returning unless SMARRS_FL_NOFREE is set. ** ** To return results to the client this function calls: ** - smar_rcpt_re_ipv4() ** - smar_rcpt_re_ipv4_more() ** - smar_rcpt_re_err() ** and it invokes the dns resolver library with smar_rcpt_cb() as callback ** ** ToDo: check error handling ** allow this function to be used as subroutine by others, i.e., do not ** create a reply (RCB), but return some result(s) instead. ** compare smar_rvrs_cb() ** Note: as soon as the DNS resolver comes into play, this ** operates asynchronously. */ sm_ret_T smar_rcpt_rslv(smar_ctx_P smar_ctx, smar_rcpts_P smar_rcpts) { sm_ret_T ret; char *ipv4s, *eos; #if 0 int r; bool locked; #endif bool incremented, gotport; uint8_t flags; dns_mgr_ctx_P dns_mgr_ctx; sm_cstr_P q; smar_clt_ctx_P smar_clt_ctx; sm_str_P mtstr; smar_rcpt_P smar_rcpt, smar_rcpt_nxt; #if SMAR_TEST long randval; #endif SM_IS_SMAR_CTX(smar_ctx); SM_IS_SMAR_RCPTS(smar_rcpts); /* either nosend must be set or RCB must exist */ SM_REQUIRE(SMARRS_IS_FLAG(smar_rcpts, SMARRS_FL_NOSEND) || smar_rcpts->arrs_rcbe != NULL); smar_rcpt = smar_rcpts->arrs_rcpt; SM_IS_SMAR_RCPT(smar_rcpt); smar_clt_ctx = smar_rcpts->arrs_smar_clt_ctx; SM_IS_SMAR_CLT_CTX(smar_clt_ctx); ret = SM_SUCCESS; SMARR_SET_FLAG(smar_rcpt, SMARR_FL_A4MT); smar_rcpt->arr_da = 0; /* XXX HACK DA */ mtstr = NULL; q = NULL; #if 0 locked = false; #endif incremented = false; smar_rcpts->arrs_ret = SM_SUCCESS; /* just in case something goes wrong early on */ smar_rcpt->arr_rcpts = smar_rcpts; flags = 0; /* expand rcpt according to aliases (also parses address) */ ret = smar_rcpt_expand(smar_rcpts, smar_rcpt, 0, 0, 0); if (sm_is_err(ret)) goto error; ret = smar_rcpts_t2l(smar_rcpts); if (sm_is_err(ret)) goto error; #if 0 r = pthread_mutex_lock(&smar_rcpts->arrs_mutex); SM_LOCK_OK(r); if (r != 0) { ret = sm_error_perm(SM_EM_AR, r); goto error; } locked = true; #endif /* 0 */ if (RCPTS_EMPTY(smar_rcpts)) { ret = sm_error_perm(SM_EM_AR, EINVAL); goto error; } if (SMARRS_IS_FLAG(smar_rcpts, SMARRS_FL_NOSEND)) SM_SET_FLAG(flags, SM_NOSEND); if (SMARRS_IS_FLAG(smar_rcpts, SMARRS_FL_NOFREE)) SM_SET_FLAG(flags, SM_NOFREE); /* XXX use different length? */ mtstr = sm_str_new(NULL, MAXADDRLEN, MAXADDRLEN + 2); if (NULL == mtstr) goto enomem; /* loop through all recipients in list */ for (smar_rcpt = RCPTS_FIRST(smar_rcpts); smar_rcpt != RCPTS_END(smar_rcpts); smar_rcpt = smar_rcpt_nxt) { smar_rcpt_nxt = RCPTS_NEXT(smar_rcpt); /* XXX HACK XXX */ if (smar_rcpts->arrs_rcpt != smar_rcpt && smar_rcpts->arrs_lst_n == 1) { SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_rslv, 1-1-replace: pa=%S\n", smar_rcpt->arr_pa)); /* ** free() original recipient unless it is referenced ** from the list or the hash table. */ if (smar_rcpts->arrs_rcpt != NULL && !SMARR_IS_FLAG(smar_rcpts->arrs_rcpt, SMARR_FL_INRLST|SMARR_FL_INOWNLST|SMARR_FL_INHT)) { SM_ASSERT(SMARR_IS_FLAG(smar_rcpts->arrs_rcpt, SMARR_FL_ORCPT)); SMARR_CLR_FLAG(smar_rcpts->arrs_rcpt, SMARR_FL_ORCPT); smar_rcpt_free(smar_rcpts->arrs_rcpt, NULL); } smar_rcpts->arrs_rcpt = smar_rcpt; SMARR_SET_FLAG(smar_rcpt, SMARR_FL_ALIAS|SMARR_FL_ORCPT); } /* is there an error already? */ if (smar_rcpt->arr_ret != SMTP_R_OK) { SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_rslv, pa=%S, arr_ret=%d\n", smar_rcpt->arr_pa, smar_rcpt->arr_ret)); ret = smar_rcpt_re_err(smar_rcpt, smar_rcpt->arr_ret); if (sm_is_err(ret)) { SMARRS_SET_FLAG(smar_rcpts, SMARRS_FL_ERCB); goto error; /* XXX need more cleanup here? */ } goto endcom; } #if SMAR_TEST if (smar_ctx->smar_rand_err > 0 && smar_ctx->smar_rand_err < (randval = random())) { ret = smar_rcpt_re_err(smar_rcpt, ((randval & 3) == 0) ? DNSR_PERM : DNSR_TEMP); /* ** XXX WRONG... but not important: ** TEST only ** should be set error-flag; continue; */ if (sm_is_err(ret)) goto error; goto endcom; } #endif /* SMAR_TEST */ /* scanning/parsing has been done by smar_rcpt_expand() */ SM_ASSERT(smar_rcpt->arr_domain_pa != NULL); sm_str2lower(smar_rcpt->arr_domain_pa); /* Lookup domain in mailertable */ sm_str_clr(mtstr); SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_rslv, pa=%S, mt_lfl=%#x, user_pa=%S, detail=%S, delim=%c\n", smar_rcpt->arr_pa, smar_ctx->smar_mt_lfl, smar_rcpt->arr_user_pa, smar_rcpt->arr_detail_pa, smar_rcpt->arr_delim)); if (SMAR_MT_FULL_LOOKUP(smar_ctx) && smar_rcpt->arr_user_pa != NULL && smar_rcpt->arr_detail_pa != NULL) { uint32_t lfl; lfl = smar_ctx->smar_mt_lfl; if (!SMARR_IS_FLAG(smar_rcpt, SMARR_FL_HASDET)) lfl &= ~SMMAP_LFL_DET; ret = sm_map_lookup_addr(smar_ctx->smar_mt_map, smar_rcpt->arr_user_pa, SMARR_IS_FLAG(smar_rcpt, SMARR_FL_HASDET) ? smar_rcpt->arr_detail_pa : NULL, smar_rcpt->arr_domain_pa, /* tag */ NULL, smar_rcpt->arr_delim, lfl, mtstr); } else ret = sm_map_lookup_domain(smar_ctx->smar_mt_map, (sm_rdstr_P) smar_rcpt->arr_domain_pa, NULL, SMMAP_LFL_DOT, mtstr); SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_rslv, pa=%S, domain=%S, lookup=%#x, mtstr=%S\n", smar_rcpt->arr_pa, smar_rcpt->arr_domain_pa, ret, mtstr)); if (SM_SUCCESS == ret) { /* just the delimiter? -> use DNS lookups */ if (sm_str_getlen(mtstr) == 1 && sm_str_rd_elem(mtstr, 0) == SMAR_RHS_PROT_C) ipv4s = NULL; else ipv4s = (char *) sm_str_getdata(mtstr); } else if (sm_str_rd_elem(smar_rcpt->arr_domain_pa, 0) == '[') ipv4s = (char *) sm_str_getdata(smar_rcpt->arr_domain_pa); else { ret = SM_SUCCESS; ipv4s = NULL; } gotport = false; /* port ? */ if (ipv4s != NULL && ISDIGIT(*ipv4s) #if SMAR_TEST /* change this to a "mailer" name? temp: perm: noreply: */ && !((*ipv4s == '4' || *ipv4s == '5' || *ipv4s == '6') && *(ipv4s + 1) == '\0') #endif /* SMAR_TEST */ ) { char *endptr; ulong v; v = strtoul(ipv4s, &endptr, 10); if (v > SHRT_MAX || *endptr != SMAR_RHS_PORT_C) { /* log and return some error */ sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RESOLVER, SM_LOG_ERROR, 1, "sev=ERROR, func=smar_rcpt_rslv, mt_rhs=%.256s, status=invalid_syntax", ipv4s); /* XXX better error code! */ ret = smar_rcpt_re_err(smar_rcpt, sm_error_perm(SM_EM_AR, EINVAL)); if (sm_is_err(ret)) { SMARRS_SET_FLAG(smar_rcpts, SMARRS_FL_ERCB); goto error; /* XXX need more cleanup here? */ } /* XXX what's the correct next step? */ continue; } ipv4s = endptr + 1; SM_ASSERT(ipv4s <= (char *) sm_str_data(mtstr) + sm_str_getlen(mtstr)); smar_rcpt->arr_port = (short) v; gotport = true; } #define LMTP_MT_LEN (sizeof(LMTP_MT) - 1) /* lmtp: ? */ if (ipv4s != NULL && strncmp(ipv4s, LMTP_MT, LMTP_MT_LEN) == 0) { smar_rcpt->arr_da = gotport ? DA_IDX_LMTP_INET : DA_IDX_LMTP_UNIX; SMARR_SET_FLAG(smar_rcpt, SMARR_FL_GOTMT); smar_rcpt->arr_ipv4 = LMTP_IPV4_ADDR; sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RESOLVER, SM_LOG_INFO, 10, "sev=INFO, func=smar_rcpt_rslv, status=resolved, pa=%S, mailer=%s", smar_rcpt->arr_pa, (DA_IDX_LMTP_INET == smar_rcpt->arr_da) ? "lmtp_inet" : "lmtp_unix"); if (DA_IDX_LMTP_UNIX == smar_rcpt->arr_da) { ret = smar_rcpt_re_ipv4(smar_rcpt, 1, smar_rcpts->arrs_resolved == 0); if (sm_is_err(ret)) { sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RESOLVER, SM_LOG_ERROR, 0, "sev=ERROR, func=smar_rcpt_rslv, called=resolved, pa=%S, mailer=lmtp, ret=%m", smar_rcpt->arr_pa, ret); SMARRS_SET_FLAG(smar_rcpts, SMARRS_FL_ERCB); /* XXX OK? need more cleanup? */ goto error; } goto endcom; } ipv4s += LMTP_MT_LEN; SM_ASSERT(ipv4s <= (char *) sm_str_data(mtstr) + sm_str_getlen(mtstr)); } #define ESMTP_MT "esmtp:" #define ESMTP_MT_LEN 6 /* strlen(ESMTP_MT) */ if (ipv4s != NULL && strncmp(ipv4s, ESMTP_MT, ESMTP_MT_LEN) == 0) { ipv4s += ESMTP_MT_LEN; SM_ASSERT(ipv4s <= (char *) sm_str_data(mtstr) + sm_str_getlen(mtstr)); if (DA_IDX_LMTP_INET == smar_rcpt->arr_da) { sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RESOLVER, SM_LOG_ERROR, 4, "sev=ERROR, func=smar_rcpt_rslv, status=bogus_RHS_in_mt, RHS=%@S" , mtstr); } smar_rcpt->arr_da = DA_IDX_ESMTP; /* default anyway */ } /* ** If RHS starts with '[': it should be an IP address (IPv4) ** otherwise it should be a host/domainname which is given ** to the DNS resolver (see below). ** Note: NO recursive mailertable lookups! ** Problem: ** the DNS resolver currently deals only with one domain, ** it's not possible to have multiple... */ if (ipv4s != NULL && (*ipv4s == '[' #if SMAR_TEST || ((*ipv4s == '4' || *ipv4s == '5' || *ipv4s == '6') && *(ipv4s + 1) == '\0') #endif )) { uint32_t n_addr, i; char *addr, *endaddr; n_addr = 1; SMARR_SET_FLAG(smar_rcpt, SMARR_FL_GOTMT); /* ** Allow multiple entries? ** First: count number of entries. */ for (addr = ipv4s; *addr != '\0'; ++addr) { if (*addr == SMAR_RHS_SEP_C && *(addr + 1) != '\0' && *(addr + 1) != SMAR_RHS_SEP_C) ++n_addr; } eos = addr; /* end of string */ for (addr = ipv4s, i = 0; *addr != '\0' && addr < eos; ++addr, ++i) { while (*addr != '\0' && *addr == SMAR_RHS_SEP_C && addr < eos) ++addr; if (*addr == '\0' || addr >= eos) break; #if SMAR_TEST if ((*addr == '4' || *addr == '5') && *(addr + 1) == '\0') { smar_rcpt->arr_ipv4 = atoi(addr); ret = smar_rcpt_re_err(smar_rcpt, (*addr == '4') ? DNSR_TEMP : DNSR_PERM); /* ** XXX WRONG... but not important: ** TEST only ** should be set error-flag; continue; */ if (sm_is_err(ret)) goto error; goto endcom; } if (*addr == '6' && *(addr + 1) == '\0') { /* don't reply (timeout for caller) */ sm_rcb_close_n(&smar_rcpts->arrs_rcbe->rcbe_rcb); /* XXX is this a leak? free rcbe? */ /* XXX only works for one entry! */ smar_rcpt_free(smar_rcpt, smar_rcpts); if (!SM_IS_FLAG(flags, SM_NOFREE)) { /* XXX */ smar_rcpts_free(smar_rcpts); } return SM_SUCCESS; } #endif /* SMAR_TEST */ ret = sm_inet_a2ipv4(addr, &endaddr, &smar_rcpt->arr_ipv4); if (sm_is_err(ret)) { sm_log_write(smar_ctx->smar_lctx, AR_LCAT_INIT, AR_LMOD_CONFIG, SM_LOG_ERROR, 0, "sev=ERROR, func=smar_rcpt_rslv, not_an_IPv4_address=\"%.256s\"", addr); smar_rcpt->arr_ipv4 = INADDR_NONE; } #if 0 SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_rslv, resolved: pa=%S, ipv4=%A, addr=%.256s, i=%d/%d\n", smar_rcpt->arr_pa, (ipv4_T) smar_rcpt->arr_ipv4, addr, i, n_addr)); #endif /* 0 */ /* ** note: addr may point to a list of addresses ** should that really be logged? for example: ** addr=[1.0.0.65] [1.0.0.66] , */ sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RESOLVER, SM_LOG_INFO, 13, "sev=INFO, func=smar_rcpt_rslv, status=resolved, pa=%S, ipv4=%A, addr=%.256s, port=%hd, i=%d/%d", smar_rcpt->arr_pa, (ipv4_T) smar_rcpt->arr_ipv4, addr, smar_rcpt->arr_port, i, n_addr); if (SM_SUCCESS == ret && endaddr != NULL && endaddr <= eos) addr = endaddr; if (0 == i) { if (n_addr == 0 || INADDR_NONE == smar_rcpt->arr_ipv4) { smar_rcpt->arr_ipv4 = inet_addr("127.0.0.1"); n_addr = 1; } ret = smar_rcpt_re_ipv4(smar_rcpt, n_addr, smar_rcpts->arrs_resolved == 0); if (sm_is_err(ret)) { sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RESOLVER, SM_LOG_ERROR, 0, "sev=ERROR, func=smar_rcpt_rslv, called=resolved, pa=%S, ip=%A, addr=%.256s, i=%d, ret=%m", smar_rcpt->arr_pa, smar_rcpt->arr_ipv4, addr, i, ret); SMARRS_SET_FLAG(smar_rcpts, SMARRS_FL_ERCB); /* XXX OK? need more cleanup? */ goto error; } } else if (smar_rcpt->arr_ipv4 != INADDR_NONE && !SMARRS_IS_FLAG(smar_rcpts, SMARRS_FL_NOSEND)) { /* Write answer to RCB */ ret = smar_rcpt_re_ipv4_more( &smar_rcpts->arrs_rcbe->rcbe_rcb, smar_rcpt->arr_ipv4); if (sm_is_err(ret)) { sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RESOLVER, SM_LOG_ERROR, 0, "sev=ERROR, func=smar_rcpt_rslv, called=more, pa=%S, ip=%A, i=%d, ret=%m", smar_rcpt->arr_pa, smar_rcpt->arr_ipv4, i, ret); SMARRS_SET_FLAG(smar_rcpts, SMARRS_FL_ERCB); /* XXX OK? need more cleanup? */ goto error; } } while (*addr != '\0' && *addr != SMAR_RHS_SEP_C && addr < eos) ++addr; } endcom: ++smar_rcpts->arrs_resolved; incremented = true; SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_rslv, status=resolved, pa=%S, ip=%A\n", smar_rcpt->arr_pa, (ipv4_T) smar_rcpt->arr_ipv4)); if (!SM_IS_FLAG(flags, SM_NOFREE)) { smar_rcpt_free(smar_rcpt, smar_rcpts); smar_rcpt = NULL; } if (smar_rcpts->arrs_resolved >= smar_rcpts->arrs_lst_n) { if (SMARRS_IS_FLAG(smar_rcpts, SMARRS_FL_NOSEND)) { SM_REQUIRE(smar_rcpts->arrs_cbf != NULL); ret = smar_rcpts->arrs_cbf(smar_rcpts, smar_rcpts->arrs_cb_ctx); } else { ret = sm_rcbcom_endrep(&smar_clt_ctx->smac_com_ctx, smar_clt_ctx->smac_com_ctx.rcbcom_tsk, false, &smar_rcpts->arrs_rcbe); } /* XXX add a flag "result already sent"? */ if (sm_is_err(ret)) goto error; if (!SM_IS_FLAG(flags, SM_NOFREE)) smar_rcpts_free(smar_rcpts); /* XXX */ SM_STR_FREE(mtstr); return ret; } continue; } /* no else, return is used in then part by default */ SMARR_SET_FLAG(smar_rcpt, SMARR_FL_NOMT); dns_mgr_ctx = smar_ctx->smar_dns_mgr_ctx; if (ipv4s != NULL && *ipv4s != '[') { /* do a "syntax" check for ipv4s? valid_domain()? */ q = sm_cstr_scpyn0((const uchar *) ipv4s, strlen(ipv4s)); } else { uchar *domain; /* q MUST be '\0' terminated! See dns_tsk_wr() */ domain = sm_str_getdata(smar_rcpt->arr_domain_pa); if (domain != NULL) { q = sm_cstr_crt(domain, sm_str_getlen(smar_rcpt->arr_domain_pa)); if (q != NULL) { /* Hack: give up "ownership" of data */ sm_str_data(smar_rcpt->arr_domain_pa) = NULL; SM_STR_SETLEN(smar_rcpt->arr_domain_pa, 0); } } else q = NULL; } if (NULL == q) goto enomem; /* ** Set flag before passing smar_rcpt to dns_req_add() ** because smar_rcpt_cb() will free() smar_rcpt. */ SMARR_SET_FLAG(smar_rcpt, SMARR_FL_A4MX); ret = dns_req_add(dns_mgr_ctx, q, T_MX, smar_rcpt->arr_timeout, smar_rcpt_cb, smar_rcpt); /* XXX check return? */ /* ** query has been "copied" by dns_req_add(). ** Maybe we should let dns_req_add() just "own" the query ** instead of "copying" it? */ #if 0 if (locked) { r = pthread_mutex_unlock(&smar_rcpts->arrs_mutex); SM_ASSERT(0 == r); if (0 == r) locked = false; } #endif /* 0 */ SM_CSTR_FREE(q); } SM_STR_FREE(mtstr); #if 0 if (sm_is_err(ret)) break; #endif return ret; enomem: ret = sm_error_temp(SM_EM_AR, ENOMEM); error: /* send a reply in any case? */ if (smar_rcpt != NULL) { if (smar_rcpts != NULL && SMARRS_IS_FLAG(smar_rcpts, SMARRS_FL_ERCB)) { SMARRS_CLR_FLAG(smar_rcpts, SMARRS_FL_INITRE); ret = smar_rcpts_re_err(smar_rcpts, ret); } else { /* don't overwrite ret */ (void) smar_rcpt_re_err(smar_rcpt, ret); } SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_rslv, smar_rcpt=%p\n", smar_rcpt)); if (SMARR_IS_FLAG(smar_rcpt, SMARR_FL_INRLST|SMARR_FL_INOWNLST) && !SM_IS_FLAG(flags, SM_NOFREE)) { smar_rcpt_free(smar_rcpt, smar_rcpts); smar_rcpt = NULL; } if (SMARRS_IS_FLAG(smar_rcpts, SMARRS_FL_NOSEND)) { SM_REQUIRE(smar_rcpts->arrs_cbf != NULL); (void) smar_rcpts->arrs_cbf(smar_rcpts, smar_rcpts->arrs_cb_ctx); } else { (void) sm_rcbcom_endrep(&smar_clt_ctx->smac_com_ctx, smar_clt_ctx->smac_com_ctx.rcbcom_tsk, false, &smar_rcpts->arrs_rcbe); } } if (!SM_IS_FLAG(flags, SM_NOFREE)) { smar_rcpts_free(smar_rcpts); /* XXX */ } #if 0 if (locked) { r = pthread_mutex_unlock(&smar_rcpts->arrs_mutex); SM_ASSERT(0 == r); if (0 == r) locked = false; } #endif /* 0 */ SM_STR_FREE(mtstr); sm_log_write(smar_ctx->smar_lctx, AR_LCAT_RESOLVER, AR_LMOD_RESOLVER, SM_LOG_ERROR, 0, "sev=ERROR, func=smar_rcpt_rslv, ret=%m", ret); return ret; }