/*
* 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;
}
syntax highlighted by Code2HTML, v. 0.9.1