/*
* Copyright (c) 2004-2006 Sendmail, Inc. and its suppliers.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*/
#include "sm/generic.h"
SM_RCSID("@(#)$Id: rcpts.c,v 1.82 2007/11/14 06:03:09 ca Exp $")
#include "sm/error.h"
#include "sm/assert.h"
#include "sm/rfc2821.h"
#include "sm/ctype.h"
#include "smar.h"
#include "sm/smardef.h"
#include "log.h"
#define MAX_ALIAS_RECURSION 5u
#if SM_ALIASES_LARGE
/* XXX RCB size? */
# define N_ALIASES 1031u
# define MAX_ALIASES 8191u
# ifndef MAXALIASLEN
# define MAXALIASLEN (256 * 1024)
# endif /* MAXALIASLEN */
#else /* SM_ALIASES_LARGE */
# define N_ALIASES 211u
# define MAX_ALIASES 1031u
# ifndef MAXALIASLEN
# define MAXALIASLEN (64 * 1024)
# endif /* MAXALIASLEN */
#endif /* SM_ALIASES_LARGE */
/*
** SMAR_RCPTS_NEW -- Create a SMAR RCPT LIST context
**
** Parameters:
** smar_ctx -- SMAR context
** smar_clt_ctx -- SMAR client context
** psmar_rcpt -- (pointer to) SMAR RCPT context (output)
**
** Returns:
** usual sm_error code
**
** Last code review: 2004-03-22 04:46:10
** Last code change:
*/
sm_ret_T
smar_rcpts_new(smar_ctx_P smar_ctx, smar_clt_ctx_P smar_clt_ctx, smar_rcpts_P *psmar_rcpts)
{
smar_rcpts_P smar_rcpts;
SM_REQUIRE(psmar_rcpts != NULL);
smar_rcpts = (smar_rcpts_P) sm_zalloc(sizeof(*smar_rcpts));
if (NULL == smar_rcpts)
return sm_error_temp(SM_EM_AR, ENOMEM);
smar_rcpts->arrs_rcpts = bht_new(N_ALIASES, MAX_ALIASES);
if (NULL == smar_rcpts->arrs_rcpts) {
sm_free_size(smar_rcpts, sizeof(*smar_rcpts));
return sm_error_temp(SM_EM_AR, ENOMEM);
}
RCPTS_INIT(smar_rcpts);
OWNER_INIT(smar_rcpts);
smar_rcpts->arrs_smar_ctx = smar_ctx;
smar_rcpts->arrs_smar_clt_ctx = smar_clt_ctx;
*psmar_rcpts = smar_rcpts;
smar_rcpts->sm_magic = SM_SMAR_RCPTS_MAGIC;
return SM_SUCCESS;
}
/*
** SMAR_RCPT_FREE_CB -- Callback for bht_destroy
**
** Parameters:
** value -- SMAR RCPT context
** key -- ignored
** ctx -- SMAR RCPT LIST context
**
** Returns:
** usual sm_error code
**
** Last code review: 2004-03-22 04:46:31
** Last code change: 2004-03-24 22:29:08
*/
static void
smar_rcpt_free_cb(void *value, void *key, void *ctx)
{
smar_rcpt_P smar_rcpt;
smar_rcpts_P smar_rcpts;
(void) key;
smar_rcpt = (smar_rcpt_P) value;
smar_rcpts = (smar_rcpts_P) ctx;
SMAR_LEV_DPRINTF(8, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_free_cb, smar_rcpt=%p\n", smar_rcpt));
(void) smar_rcpt_free(smar_rcpt, smar_rcpts);
}
/*
** SMAR_RCPTS_FREE -- Free a SMAR RCPT LIST context
** including all recipients in hash table and owner-list
**
** Parameters:
** smar_rcpts -- SMAR RCPT LIST context
**
** Returns:
** usual sm_error code
**
** Last code review: 2004-03-22 04:46:50; see below
** Last code change: 2005-10-02 18:07:53
*/
sm_ret_T
smar_rcpts_free(smar_rcpts_P smar_rcpts)
{
#if 0
int r;
#endif
smar_rcpt_P smar_rcpt, smar_rcpt_nxt;
if (NULL == smar_rcpts)
return SM_SUCCESS;
SM_IS_SMAR_RCPTS(smar_rcpts);
SMAR_LEV_DPRINTF(8, (SMAR_DEBFP, "sev=DBG, func=smar_rcpts_free, smar_rcpts=%p\n", smar_rcpts));
bht_destroy(smar_rcpts->arrs_rcpts, smar_rcpt_free_cb, smar_rcpts);
/*
** Note: arrs_rcpthd is NOT checked, list should be empty.
** arrs_pa is not free()d, it is just a pointer to ara_pa2
*/
if (smar_rcpts->arrs_rcbe != NULL) {
sm_rcbe_free(smar_rcpts->arrs_rcbe);
smar_rcpts->arrs_rcbe = NULL;
}
for (smar_rcpt = OWNER_FIRST(smar_rcpts);
smar_rcpt != OWNER_END(smar_rcpts);
smar_rcpt = smar_rcpt_nxt)
{
smar_rcpt_nxt = OWNER_NEXT(smar_rcpt);
smar_rcpt_free(smar_rcpt, smar_rcpts);
}
OWNER_INIT(smar_rcpts);
/*
** This requires that the pointer is set to NULL if the recipient
** is free()d or that the flag is cleared (note: a bogus pointer
** or a pointer to a free()d value that still has the flag set
** will cause problems too!). Can that be guaranteed??
*/
if (smar_rcpts->arrs_rcpt != NULL &&
SMARR_IS_FLAG(smar_rcpts->arrs_rcpt, SMARR_FL_ORCPT))
{
smar_rcpt_free(smar_rcpts->arrs_rcpt, NULL);
smar_rcpts->arrs_rcpt = NULL;
}
#if 0
r = pthread_mutex_destroy(&smar_rcpts->arrs_mutex);
smar_rcpts->arrs_smar_ctx = NULL;
smar_rcpts->arrs_smar_clt_ctx = NULL;
#endif
smar_rcpts->sm_magic = SM_MAGIC_NULL;
sm_free_size(smar_rcpts, sizeof(*smar_rcpts));
return SM_SUCCESS;
}
/*
** SMAR_RCPT_T2L_CB -- Callback for bht_walk in smar_rcpts_r2l()
**
** Parameters:
** entry -- SMAR RCPT context
** ctx -- smar_rcpts context
**
** Returns:
** usual sm_error code
**
** Last code review: 2004-03-22 04:52:02
** Last code change:
*/
static sm_ret_T
smar_rcpt_t2l_cb(bht_entry_P entry, void *ctx)
{
smar_rcpts_P smar_rcpts;
smar_rcpt_P smar_rcpt;
smar_rcpt = (smar_rcpt_P) entry->bhe_value;
smar_rcpts = (smar_rcpts_P) ctx;
SM_IS_SMAR_RCPTS(smar_rcpts);
SM_IS_SMAR_RCPT(smar_rcpt);
SMAR_LEV_DPRINTF(7, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_t2l_cb, smar_rcpt=%p, pa=%S, flags=%#x\n", smar_rcpt, smar_rcpt->arr_pa, smar_rcpt->arr_flags));
if (SMARR_IS_FLAG(smar_rcpt, SMARR_FL_ISOWN) ||
SMARR_IS_FLAG(smar_rcpt, SMARR_FL_ISVERP))
{
OWNER_APP(smar_rcpts, smar_rcpt);
++smar_rcpts->arrs_owners_n;
SMARR_CLR_FLAG(smar_rcpt, SMARR_FL_INHT);
SMARR_SET_FLAG(smar_rcpt, SMARR_FL_INOWNLST);
}
else if (!SMARR_IS_FLAG(smar_rcpt, SMARR_FL_EXPD))
{
RCPTS_APP(smar_rcpts, smar_rcpt);
++smar_rcpts->arrs_lst_n;
SMARR_CLR_FLAG(smar_rcpt, SMARR_FL_INHT);
SMARR_SET_FLAG(smar_rcpt, SMARR_FL_INRLST);
}
else
smar_rcpt_free(smar_rcpt, smar_rcpts);
return SM_SUCCESS;
}
/*
** SMAR_RCPTS_T2L -- Convert a SMAR RCPT table into a list
**
** Parameters:
** smar_rcpts -- SMAR RCPT LIST context
**
** Returns:
** usual sm_error code
**
** Last code review: 2004-03-24 18:18:32; see comments!
** Last code change:
*/
sm_ret_T
smar_rcpts_t2l(smar_rcpts_P smar_rcpts)
{
SM_IS_SMAR_RCPTS(smar_rcpts);
bht_walk(smar_rcpts->arrs_rcpts, smar_rcpt_t2l_cb, smar_rcpts);
/*
** Content is now in list, destroy table without free()ing elements.
** XXX why not use
bht_destroy(smar_rcpts->arrs_rcpts, smar_rcpt_t2l_cb, smar_rcpts);
** and get rid of bht_walk()?
*/
bht_destroy(smar_rcpts->arrs_rcpts, NULL, NULL);
smar_rcpts->arrs_rcpts = NULL;
return SM_SUCCESS;
}
/*
** SMAR_RCPTS_ADD2HT -- Add a rcpt to a SMAR rcpt hash table
**
** Parameters:
** smar_rcpt -- SMAR RCPT context
** smar_rcpts -- SMAR RCPT LIST context
**
** Returns:
** usual sm_error code,
** sm_error_perm(SM_EM_AR, EEXIST) if entry exists
**
** Last code review: 2004-03-22 04:54:51; see comments!
** Last code change:
*/
static sm_ret_T
smar_rcpts_add2ht(smar_rcpts_P smar_rcpts, smar_rcpt_P smar_rcpt)
{
sm_ret_T ret;
void *ptr;
SM_IS_SMAR_RCPT(smar_rcpt);
SM_IS_SMAR_RCPTS(smar_rcpts);
/*
** XXX Need "address equal" function:
** domain part: case insensitive
** local part: depends on configuration
** The entries in bht must be in "canonical" format...
** Note: if that is not the case, then it is not possible to use
** a hash table (well, because it's hashed; unless some
** "canonificaton" before hashing is done, e.g., lower case the key).
*/
ptr = bht_find(smar_rcpts->arrs_rcpts,
(const char *) sm_str_data(smar_rcpt->arr_pa),
sm_str_getlen(smar_rcpt->arr_pa));
if (NULL == ptr) {
bht_entry_P entry;
ret = bht_add(smar_rcpts->arrs_rcpts,
(char *) sm_str_data(smar_rcpt->arr_pa),
sm_str_getlen(smar_rcpt->arr_pa),
smar_rcpt, &entry);
if (SM_SUCCESS == ret) {
++smar_rcpts->arrs_ht_n;
smar_rcpt->arr_rcpts = smar_rcpts;
SMAR_LEV_DPRINTF(8, (SMAR_DEBFP, "sev=DBG, func=smar_rcpts_add2ht, smar_rcpt=%p, pa=%S, stat=inht\n", smar_rcpt, smar_rcpt->arr_pa));
SMARR_SET_FLAG(smar_rcpt, SMARR_FL_INHT);
}
/* else error code ret is returned to caller */
}
else
ret = sm_error_perm(SM_EM_AR, EEXIST);
return ret;
}
/*
** SMAR_RCPT_PARTS -- parse recipient parts (user/detail)
**
** Parameters:
** smar_ctx -- SMAR context
** smar_rcpt -- SMAR RCPT context
** prcpt -- parsed recipient
**
** Returns:
** usual sm_error code,
**
** Last code review:
** Last code change:
*/
static sm_ret_T
smar_rcpt_parts(smar_ctx_P smar_ctx, smar_rcpt_P smar_rcpt, sm_a2821_P prcpt)
{
sm_ret_T ret;
smar_rcpt->arr_user_pa = sm_str_new(NULL, MAXADDRLEN, MAXADDRLEN + 2);
if (NULL == smar_rcpt->arr_user_pa) goto enomem;
smar_rcpt->arr_detail_pa = sm_str_new(NULL, MAXADDRLEN >> 1, MAXADDRLEN);
if (NULL == smar_rcpt->arr_detail_pa) goto enomem;
/* extract localpart */
ret = t2821_parts(prcpt, smar_ctx->smar_cnf.smar_cnf_addr_delim,
true, smar_rcpt->arr_user_pa, smar_rcpt->arr_detail_pa, NULL,
&smar_rcpt->arr_delim);
if (sm_is_err(ret)) {
SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_parse, t2821_parts=%r\n", ret));
goto error;
}
if (ret > 0)
SMARR_SET_FLAG(smar_rcpt, SMARR_FL_HASDET);
return ret;
enomem:
ret = sm_error_temp(SM_EM_AR, ENOMEM);
error:
SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "sev=ERROR, func=smar_rcpt_parse, smar_rcpt=%p, flags=%#x\n", smar_rcpt, smar_rcpt == NULL ? 0xffffffff : smar_rcpt->arr_flags));
return ret;
}
/*
** SMAR_RCPT_EXPAND -- Expand RCPT address (add to hash table in smar_rcpts)
**
** Parameters:
** smar_rcpts -- SMAR RCPT LIST context
** smar_rcpt -- rcpt routing information
** owner_idx -- reference to owner (0: none)
** rflags -- flags
** level -- recursion level
**
** Returns:
** usual sm_error code
**
** Side Effects:
** creates and populates smar_rcpt->arr_domain_pa,
** smar_rcpt_rslv() relies on this.
** ...
**
** Note: the alias format is very restricted: it must be
local-part1: <full1@address1> <full2@address2> ...
local-part2: other-local-part
** i.e., delimiters are only whitespace, not comma.
**
** This function is called recursively.
**
** The entries are collected in a hash table, see smar_rcpts_t2l()
** how to get a list of ("valid") entries.
**
** Last code review: 2004-03-22 05:21:23; see comments!
** Last code change:
*/
#define SM_MAP_ENTRY_NOTFOUND(ret) \
(sm_error_perm(SM_EM_MAP, SM_E_NOTFOUND) == (ret) || \
sm_error_perm(SM_EM_MAP, SM_E_NOTIMPL) == (ret) || \
sm_error_perm(SM_EM_MAP, ENOENT) == (ret) || \
sm_error_perm(SM_EM_MAP, SM_E_UNAVAIL) == (ret) || \
sm_error_perm(SM_EM_MAP, SM_E_NOMAP) == (ret))
sm_ret_T
smar_rcpt_expand(smar_rcpts_P smar_rcpts, smar_rcpt_P smar_rcpt, rcpt_idx_T owner_idx, uint rflags, uint level)
{
sm_ret_T ret, hasdetail, offset;
uint idx, flags;
uint32_t lfl;
char *ipv4s;
sm_a2821_T a_rcpt, a_domain;
sm_a2821_P prcpt, pdomain;
sm_str_P mtstr, str, rhs, owner, ownerrhs;
smar_rcpt_P smar_rcpt_a;
smar_ctx_P smar_ctx;
bool islocaldomain;
/* address is identical to one already in hash table */
#define SRE_FL_IDENTICAL 0x01
#define SRE_FL_CHK_LU 0x02 /* check local user */
#define SRE_FL_USE_MAP 0x04 /* full match using a map */
SM_IS_SMAR_RCPT(smar_rcpt);
SM_IS_SMAR_RCPTS(smar_rcpts);
ret = SM_SUCCESS;
mtstr = str = rhs = owner = ownerrhs = NULL;
smar_ctx = smar_rcpts->arrs_smar_ctx;
pdomain = &a_domain; /* pdomain is just pointer to a_domain */
prcpt = &a_rcpt; /* prcpt is just pointer to a_rcpt */
A2821_INIT_RP(prcpt, NULL);
A2821_INIT_RP(pdomain, NULL);
smar_rcpt_a = NULL;
islocaldomain = false;
flags = 0;
hasdetail = 0;
SMAR_LEV_DPRINTF(7, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_expand, smar_rcpt=%p, level=%u\n", smar_rcpt, level));
if (level >= MAX_ALIAS_RECURSION) {
ret = sm_error_perm(SM_EM_AR, SM_E_ALIAS_REC);
goto error;
}
/* check recipient address */
ret = t2821_scan((sm_rdstr_P) smar_rcpt->arr_pa, prcpt, 0);
if (sm_is_err(ret)) goto error;
ret = t2821_parse(prcpt, R2821_CORRECT);
if (sm_is_err(ret)) goto error;
if (!SMARRS_IS_FLAG(smar_rcpts, SMARRS_FL_NOADD)) {
ret = smar_rcpts_add2ht(smar_rcpts, smar_rcpt);
if (ret == sm_error_perm(SM_EM_AR, EEXIST)) {
if (SMARR_IS_FLAG(smar_rcpt, SMARR_FL_TAKEIT)) {
smar_rcpt_free(smar_rcpt, smar_rcpts);
smar_rcpt = NULL;
}
a2821_free(pdomain);
a2821_free(prcpt);
return SM_SUCCESS;
}
if (sm_is_err(ret)) goto error;
SMARR_CLR_FLAG(smar_rcpt, SMARR_FL_TAKEIT);
}
/* assign next index */
smar_rcpt->arr_idx = ++smar_rcpts->arrs_idx;
if (owner_idx > 0)
smar_rcpt->arr_owner_idx = owner_idx;
if (SM_IS_FLAG(rflags, SMARR_FL_ISVERP)) {
SMARR_SET_FLAG(smar_rcpt, SMARR_FL_HASVERP);
rflags &= ~SMARR_FL_ISVERP;
}
SM_ASSERT(NULL == smar_rcpt->arr_domain_pa);
smar_rcpt->arr_domain_pa = sm_str_new(NULL, MAXDOMAINLEN, MAXDOMAINLEN + 2);
if (NULL == smar_rcpt->arr_domain_pa) goto enomem;
/* extract domain out of rcpt_a ... and look it up */
ret = t2821_extract_domain(NULL, prcpt, &pdomain);
if (sm_is_err(ret)) goto error;
ret = t2821_str(pdomain, smar_rcpt->arr_domain_pa, 0);
if (sm_is_err(ret)) goto error;
a2821_free(pdomain);
pdomain = NULL;
sm_str2lower(smar_rcpt->arr_domain_pa);
/* don't expand aliases? (just parse first recipient etc) */
if (!SMARRQ_IS_FLAG(smar_rcpt, SMARRQ_FL_ALIAS))
goto done;
/* XXX use different length? */
mtstr = sm_str_new(NULL, MAXADDRLEN, MAXADDRLEN + 2);
if (NULL == mtstr) goto enomem;
if (SMAR_MT_FULL_LOOKUP(smar_ctx) ||
SMARRQ_IS_FLAG(smar_rcpt, SMARRQ_FL_CONF))
{
hasdetail = smar_rcpt_parts(smar_ctx, smar_rcpt, prcpt);
if (sm_is_err(hasdetail)) {
SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_expand, smar_rcpt_parts=%r\n", hasdetail));
goto error;
}
}
if (SMARRQ_IS_FLAG(smar_rcpt, SMARRQ_FL_CONF) &&
smar_ctx->smar_access != NULL)
{
lfl = smar_rcpt->arr_cnf_lfl;
if (hasdetail <= 0)
lfl &= ~SMMAP_LFL_DET;
/* XXX use different length? */
smar_rcpt->arr_rhs_conf = sm_str_new(NULL, MAXADDRLEN, MAXADDRLEN + 2);
if (NULL == smar_rcpt->arr_rhs_conf) goto enomem;
/* slight abuse of mtstr... */
sm_str_scat(mtstr, SC_RCPT_CONF_TAG);
smar_rcpt->arr_maprescnf = sm_map_lookup_addr(smar_ctx->smar_access,
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 */ mtstr,
smar_rcpt->arr_delim,
lfl, smar_rcpt->arr_rhs_conf);
SMAR_LEV_DPRINTF(8, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_expand, pa=%S, lfl=%#x, conf=%#T, ret=%r\n", smar_rcpt->arr_pa, lfl, smar_rcpt->arr_rhs_conf, smar_rcpt->arr_maprescnf));
sm_str_clr(mtstr);
}
if (SMAR_MT_FULL_LOOKUP(smar_ctx)) {
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(smar_ctx->smar_mt_map, 0, smar_rcpt->arr_domain_pa,
mtstr);
if (SM_SUCCESS == ret)
ipv4s = (char *) sm_str_getdata(mtstr);
else {
ret = SM_SUCCESS;
ipv4s = NULL;
}
SMAR_LEV_DPRINTF(7, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_expand, pa=%S, domain=%S, ipv4s=%.256s, rq_flags=%#x, flags=%#x, ali_flags=%#x\n", smar_rcpt->arr_pa, smar_rcpt->arr_domain_pa, ipv4s, smar_rcpt->arr_rqflags, smar_rcpt->arr_flags, smar_ctx->smar_cnf.smar_cnf_alias_fl));
/* no entry found but domain is a literal? */
if (ipv4s == NULL && sm_str_rd_elem(smar_rcpt->arr_domain_pa, 0) == '[') {
/* use it... XXX what about free()ing data? */
ipv4s = (char *) sm_str_getdata(smar_rcpt->arr_domain_pa);
}
/* XXX HACK ahead, see also smtpc/smtpc.c */
islocaldomain = ipv4s != NULL &&
(strcmp(ipv4s, LMTP_IPV4_S2) == 0 || strcmp(ipv4s, LMTP_MT) == 0);
/*
** do alias expansion if
** asking for (local) alias and local address
** or
** asking for remote alias
** <=>
** do not perform alias expansion if
** !(asking for (local) alias and local address)
** and
** !(asking for remote alias)
*/
if (!(SM_IS_FLAG(smar_ctx->smar_cnf.smar_cnf_alias_fl, SMARCNF_FL_MAP_LP|SMARCNF_FL_MAP_LD)
&& islocaldomain)
&& !SM_IS_FLAG(smar_ctx->smar_cnf.smar_cnf_alias_fl, SMARCNF_FL_MAP_ALL))
{
if (islocaldomain && SMARRQ_IS_FLAG(smar_rcpt, SMARRQ_FL_CHK_LU))
SM_SET_FLAG(flags, SRE_FL_CHK_LU);
else
goto done;
}
if (NULL == smar_rcpt->arr_user_pa)
{
hasdetail = smar_rcpt_parts(smar_ctx, smar_rcpt, prcpt);
if (sm_is_err(hasdetail)) {
SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_expand, smar_rcpt_parts=%r\n", hasdetail));
goto error;
}
}
str = sm_str_new(NULL, MAXADDRLEN, MAXADDRLEN + 2);
if (NULL == str) goto enomem;
rhs = sm_str_new(NULL, MAXADDRLEN, MAXALIASLEN);
if (NULL == rhs) goto enomem;
if (SMARRQ_IS_FLAG(smar_rcpt, SMARRQ_FL_OWNER)) {
owner = sm_str_new(NULL, MAXADDRLEN, MAXADDRLEN);
if (NULL == owner) goto enomem;
ownerrhs = sm_str_new(NULL, MAXADDRLEN, MAXADDRLEN);
if (NULL == ownerrhs) goto enomem;
}
/*
** local address but no alias expansion?
** then check whether user exists.
*/
if (SM_IS_FLAG(flags, SRE_FL_CHK_LU)) {
uint32_t status;
status = SMTP_R_OK;
ret = smar_addr_lu(smar_ctx, smar_rcpt->arr_user_pa,
smar_rcpt->arr_delim,
(hasdetail > 0) ? smar_rcpt->arr_detail_pa : NULL,
smar_rcpt->arr_domain_pa, rhs, &status);
SM_CLR_FLAG(flags, SRE_FL_CHK_LU);
/* how to reject an address? */
if (status == SMTP_R_TEMP || status == SMTP_R_REJECT)
smar_rcpt->arr_ret = status;
goto done;
}
lfl = smar_ctx->smar_alias_lfl;
if (hasdetail <= 0)
lfl &= ~SMMAP_LFL_DET;
ret = sm_map_lookup_addr(smar_ctx->smar_aliases, smar_rcpt->arr_user_pa,
(hasdetail > 0) ? smar_rcpt->arr_detail_pa : NULL,
SM_IS_FLAG(smar_ctx->smar_cnf.smar_cnf_alias_fl,
SMARCNF_FL_MAP_LD|SMARCNF_FL_MAP_ALL)
? smar_rcpt->arr_domain_pa : NULL,
/* tag */ NULL,
smar_rcpt->arr_delim,
lfl, rhs);
SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_expand, local=%S, domain=%S, rhs=%.256S, len=%u, lfl=%#x, ret=%r\n", smar_rcpt->arr_user_pa, smar_rcpt->arr_domain_pa, rhs, sm_str_getlen(rhs), lfl, ret));
/* not found? XXX need to deal with tempfail etc! */
if (sm_is_err(ret)) {
if (!SM_MAP_ENTRY_NOTFOUND(ret)) goto error;
if (islocaldomain && SMARRQ_IS_FLAG(smar_rcpt, SMARRQ_FL_CHK_LU)) {
lfl = smar_ctx->smar_lum_lfl;
if (hasdetail == 0)
lfl &= ~SMMAP_LFL_DET;
ret = sm_map_lookup_addr(smar_ctx->smar_lum,
smar_rcpt->arr_user_pa,
(hasdetail > 0) ? smar_rcpt->arr_detail_pa : NULL,
(smar_rcpt->arr_domain_pa == NULL ||
sm_str_getlen(smar_rcpt->arr_domain_pa) == 0)
? NULL
: smar_rcpt->arr_domain_pa,
/* tag */ NULL,
smar_rcpt->arr_delim,
lfl, rhs);
SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "func=smar_rcpt_expand, local=%S, detail=%S, rhs=%.256S, local_map_lookup=%r\n", smar_rcpt->arr_user_pa, smar_rcpt->arr_detail_pa, rhs, ret));
if (SM_MAP_ENTRY_NOTFOUND(ret))
smar_rcpt->arr_ret = SMTP_R_REJECT;
else if (sm_is_err(ret)) goto error;
}
goto done;
}
SMARR_SET_FLAG(smar_rcpt, SMARR_FL_ISALIAS);
#define ADDR_IS_LOCAL "local:"
/* don't try further if rhs is ADDR_IS_LOCAL */
if (sm_str_getlen(rhs) == sizeof(ADDR_IS_LOCAL) - 1
&& strncasecmp((const char*) sm_str_data(rhs),
ADDR_IS_LOCAL, sm_str_getlen(rhs)) == 0)
{
SMARR_SET_FLAG(smar_rcpt, SMARR_FL_LU);
goto done;
}
/* check for error: (even before testing "NOEXP"?) */
if (sm_str_getlen(rhs) >= RHS_ERROR_LEN
&& strncasecmp((const char*) sm_str_data(rhs),
RHS_ERROR, RHS_ERROR_LEN) == 0)
{
/* don't accept mail for this address */
if (sm_str_getlen(rhs) >= RHS_ERROR_4_LEN
&& strncasecmp((const char*) sm_str_data(rhs),
RHS_ERROR_4, RHS_ERROR_4_LEN) == 0)
{
ret = SMTP_R_TEMP;
}
else
ret = SMTP_R_REJECT;
goto error;
}
/* don't rewrite address if no expand flag is set */
if (SMARRQ_IS_FLAG(smar_rcpt, SMARRQ_FL_NOEXP))
goto done;
#define SM_A_OWNER "owner-"
/* check for owner- */
if (SMARRQ_IS_FLAG(smar_rcpt, SMARRQ_FL_OWNER)) {
sm_str_clr(owner);
ret = sm_str_scat(owner, SM_A_OWNER);
if (sm_is_err(ret)) goto error;
ret = sm_str_cat(owner, smar_rcpt->arr_user_pa);
if (sm_is_err(ret)) goto error;
sm_str_clr(ownerrhs);
ret = sm_map_lookup_addr(smar_ctx->smar_aliases, owner,
(hasdetail > 0) ? smar_rcpt->arr_detail_pa : NULL,
SM_IS_FLAG(smar_ctx->smar_cnf.smar_cnf_alias_fl,
SMARCNF_FL_MAP_LD|SMARCNF_FL_MAP_ALL)
? smar_rcpt->arr_domain_pa : NULL,
/* tag */ NULL,
smar_rcpt->arr_delim,
lfl & ~SMMAP_LFL_DOMAIN, ownerrhs);
/* must match owner-, not just @domain */
SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_expand, owner=%S, domain=%S, ownerrhs=%.256S, ret=%r\n", owner, smar_rcpt->arr_domain_pa, ownerrhs, ret));
/* XXX need to deal with tempfail etc */
if (sm_is_success(ret) &&
!(sm_str_getlen(ownerrhs) >= RHS_ERROR_LEN
&& strncasecmp((const char*) sm_str_data(ownerrhs),
RHS_ERROR, RHS_ERROR_LEN) == 0))
{
SMARR_SET_FLAG(smar_rcpt, SMARR_FL_ISOWN);
owner_idx = smar_rcpt->arr_idx;
smar_rcpt->arr_owner_pa = sm_str_new(NULL, MAXADDRLEN,
MAXADDRLEN);
if (NULL == smar_rcpt->arr_owner_pa) goto enomem; /* XXX cleanup? */
ret = sm_strprintf(smar_rcpt->arr_owner_pa,
"<%s%S@%S>",
SM_A_OWNER, smar_rcpt->arr_user_pa,
smar_rcpt->arr_domain_pa);
if (sm_is_err(ret) || ret >= sm_str_getmax(smar_rcpt->arr_owner_pa))
goto error; /* XXX cleanup? */
}
}
#define SM_A_VERP "verp-"
/* check for verp- (almost identical to owner- check) */
if (SMARRQ_IS_FLAG(smar_rcpt, SMARRQ_FL_VERP) &&
!SMARR_IS_FLAG(smar_rcpt, SMARR_FL_ISOWN))
{
sm_str_clr(owner);
ret = sm_str_scat(owner, SM_A_VERP);
if (sm_is_err(ret)) goto error;
ret = sm_str_cat(owner, smar_rcpt->arr_user_pa);
if (sm_is_err(ret)) goto error;
sm_str_clr(ownerrhs);
ret = sm_map_lookup_addr(smar_ctx->smar_aliases, owner,
(hasdetail > 0) ? smar_rcpt->arr_detail_pa : NULL,
SM_IS_FLAG(smar_ctx->smar_cnf.smar_cnf_alias_fl,
SMARCNF_FL_MAP_LD|SMARCNF_FL_MAP_ALL)
? smar_rcpt->arr_domain_pa : NULL,
/* tag */ NULL,
smar_rcpt->arr_delim,
lfl & ~SMMAP_LFL_DOMAIN, ownerrhs);
/* must match owner-, not just @domain */
SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_expand, verp=%S, domain=%S, verprhs=%.256S, ret=%r\n", owner, smar_rcpt->arr_domain_pa, ownerrhs, ret));
/* XXX need to deal with tempfail etc */
if (sm_is_success(ret) &&
!(sm_str_getlen(ownerrhs) >= RHS_ERROR_LEN
&& strncasecmp((const char*) sm_str_data(ownerrhs),
RHS_ERROR, RHS_ERROR_LEN) == 0))
{
SMARR_SET_FLAG(smar_rcpt, SMARR_FL_ISVERP);
rflags |= SMARR_FL_ISVERP;
owner_idx = smar_rcpt->arr_idx;
smar_rcpt->arr_owner_pa = sm_str_new(NULL, MAXADDRLEN,
MAXADDRLEN);
if (NULL == smar_rcpt->arr_owner_pa) goto enomem; /* XXX cleanup? */
ret = sm_strprintf(smar_rcpt->arr_owner_pa,
"<%s%S@%S>",
SM_A_OWNER, smar_rcpt->arr_user_pa,
smar_rcpt->arr_domain_pa);
if (sm_is_err(ret) || ret >= sm_str_getmax(smar_rcpt->arr_owner_pa))
goto error; /* XXX cleanup? */
}
}
/* rewrite address... */
/*
** RHS must be an RFC2821 address or just a localpart.
*/
idx = 0;
SM_CLR_FLAG(flags, SRE_FL_IDENTICAL);
do {
/* skip over leading spaces */
while (sm_str_getlen(rhs) > idx && ISSPACE(sm_str_rd_elem(rhs, idx)))
++idx;
/* no more data? */
if (sm_str_getlen(rhs) <= idx)
break;
/*
** Convert localpart to <localpart@MY.HOST.NAME>
** (or use the domain of the original address)
** Note: This is broken for
** alias: local <other@address>
** A real address parser should be used which supports
** unqualified addresses!
*/
if (sm_str_getlen(rhs) > idx
&& sm_str_rd_elem(rhs, idx) != (uchar) '<')
{
sm_str_P exchg;
bool preserve_domain;
preserve_domain = SM_IS_FLAG(smar_ctx->smar_cnf.smar_cnf_alias_fl,
SMARCNF_FL_MAP_PD) &&
sm_str_getlen(smar_rcpt->arr_domain_pa) > 0;
sm_str_clr(str);
if (sm_str_put(str, (uchar) '<') != SM_SUCCESS
|| sm_str_cat(str, rhs) != SM_SUCCESS
|| sm_str_put(str, (uchar) '@') != SM_SUCCESS
|| sm_str_cat(str, preserve_domain ? smar_rcpt->arr_domain_pa
: smar_ctx->smar_hostname) != SM_SUCCESS
|| sm_str_put(str, (uchar) '>') != SM_SUCCESS)
{
SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_expand, convert_localpart=%m\n", ret));
goto error;
}
/* exchange rhs and str */
exchg = rhs;
rhs = str;
str = exchg;
}
a2821_free(prcpt);
A2821_INIT_RP(prcpt, NULL);
offset = t2821_scan((sm_rdstr_P) rhs, prcpt, idx);
if (sm_is_err(offset)) {
ret = offset;
sm_log_write(smar_ctx->smar_lctx,
AR_LCAT_RESOLVER, AR_LMOD_RESOLVER,
SM_LOG_ERROR, 3,
"sev=ERROR, func=smar_rcpt_expand, alias=%.256S, t2821_scan=%m"
, rhs, ret);
goto error;
}
if (sm_is_err(ret = t2821_parse(prcpt, R2821_CORRECT))) {
sm_log_write(smar_ctx->smar_lctx,
AR_LCAT_RESOLVER, AR_LMOD_RESOLVER,
SM_LOG_ERROR, 3,
"sev=ERROR, func=smar_rcpt_expand, alias=%.256S, t2821_parse=%m"
, rhs, ret);
goto error;
}
SM_ASSERT(offset >= 0);
idx = offset; /* set new index to end of current address */
if (smar_rcpts->arrs_addr != NULL &&
SMARA_IS_LFLAG(smar_rcpts->arrs_addr, SMARA_LFL_PROTMAP)
? T2821_FL_NOANGLE : 0)
SM_SET_FLAG(flags, SRE_FL_USE_MAP);
sm_str_clr(str);
ret = t2821_str(prcpt, str, SM_IS_FLAG(flags, SRE_FL_USE_MAP) ? T2821_FL_NOANGLE : 0);
if (sm_is_err(ret)) {
SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_expand, t2821_str=%r\n", ret));
goto error;
}
SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_expand, new=%.256S, len=%d, old=%.256S, len=%d, flags=%#x\n", str, sm_str_getlen(str), smar_rcpt->arr_pa, sm_str_getlen(smar_rcpt->arr_pa), flags));
/*
** Compare new address with old address?
** Stop if they are identical (instead of
** relying on recursion limit).
** XXX Need "address equal" function:
** domain part: case insensitive
** local part: depends on configuration
*/
if (sm_str_getlen(smar_rcpt->arr_pa) == sm_str_getlen(str) &&
strncmp((char *) sm_str_data(smar_rcpt->arr_pa),
(char *) sm_str_data(str), sm_str_getlen(str)) == 0)
{
SM_SET_FLAG(flags, SRE_FL_IDENTICAL);
continue;
}
if (SM_IS_FLAG(flags, SRE_FL_USE_MAP)) {
smar_addr_P smar_addr;
smar_addr = smar_rcpts->arrs_addr;
sm_str_clr(smar_addr->ara_rhs2);
ret = sm_map_setopt(smar_addr->ara_str_map, SMPO_STR, str, SMPO_END);
if (sm_is_err(ret)) goto error;
ret = sm_map_lookup_addr(smar_addr->ara_str_map,
smar_addr->ara_user2, smar_addr->ara_detail2,
smar_addr->ara_domain_pa2, NULL,
smar_addr->ara_delim,
/* Need to let caller (client) pass in other flags? */
SMMAP_LFL_ALL|SMMAP_LFL_NOAT|
(SMARA_IS_LFLAG(smar_addr, SMARA_LFL_PROTIMPLDET)
? SMMAP_LFL_IMPLDET : 0),
smar_addr->ara_rhs2);
SMAR_LEV_DPRINTF(9, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_expand, where=lookup, user2=%S, ret=%r\n", smar_addr->ara_user2, ret));
if (sm_is_success(ret)) {
SMARRS_SET_FLAG(smar_rcpts, SMARRS_FL_FOUND);
goto done;
}
ret = SM_SUCCESS;
continue;
}
if (SMARRS_IS_FLAG(smar_rcpts, SMARRS_FL_COMP)) {
SMAR_LEV_DPRINTF(9, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_expand, where=compare, pa=%#S, str=%#S\n", smar_rcpts->arrs_pa, str));
if (sm_str_getlen(smar_rcpts->arrs_pa) ==
sm_str_getlen(str) &&
strncasecmp((char *) sm_str_data(smar_rcpts->arrs_pa),
(char *) sm_str_data(str), sm_str_getlen(str)) == 0)
{
SMAR_LEV_DPRINTF(8, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_expand, where=compare, pa=%#S, str=%#S, status=found\n", smar_rcpts->arrs_pa, str));
SMARRS_SET_FLAG(smar_rcpts, SMARRS_FL_FOUND);
goto done;
}
ret = SM_SUCCESS;
continue;
}
if (SMARRS_IS_FLAG(smar_rcpts, SMARRS_FL_NOREC))
continue;
ret = smar_rcpt_new(&smar_rcpt_a);
if (sm_is_err(ret)) goto error; /* XXX more cleanup? */
SMAR_LEV_DPRINTF(7, (SMAR_DEBFP, "sev=DBG, func=smar_rcpt_expand, where=smar_rcpt_new, smar_rcpt_a=%p\n", smar_rcpt_a));
/* XXX fill in data... */
/*
XXX some of this data should only be in rcpts, not in each rcpt
that requires to rewrite the functions that access that data via rcpt
*/
smar_rcpt_a->arr_pa = str;
str = sm_str_new(NULL, MAXADDRLEN, MAXADDRLEN + 2);
if (NULL == str) goto enomem;
smar_rcpt_a->arr_timeout = smar_rcpt->arr_timeout;
smar_rcpt_a->arr_rqflags = smar_rcpt->arr_rqflags;
RCPT_ID_COPY(smar_rcpt_a->arr_id, smar_rcpt->arr_id);
SMARR_SET_FLAG(smar_rcpt_a, SMARR_FL_TAKEIT);
ret = smar_rcpt_expand(smar_rcpts, smar_rcpt_a, owner_idx, rflags, level + 1);
smar_rcpt_a = NULL;
if (sm_is_err(ret)) goto error;
} while (sm_is_success(ret) && sm_str_getlen(rhs) > idx);
/*
** The recipient has only been replaced if there wasn't an identical
** address on the RHS.
*/
if (!SM_IS_FLAG(flags, SRE_FL_IDENTICAL))
SMARR_SET_FLAG(smar_rcpt, SMARR_FL_EXPD);
done:
/*
* Hack: try to figure out whether smar_rcpt_parts() should be called.
* Note: prcpt can change but by then smar_rcpt_parts() was called and
* hence arr_user_pa is set.
*/
if (SMAR_MT_FULL_LOOKUP(smar_ctx) && NULL == smar_rcpt->arr_user_pa) {
ret = smar_rcpt_parts(smar_ctx, smar_rcpt, prcpt);
if (sm_is_err(ret)) goto error;
}
SM_STR_FREE(str);
SM_STR_FREE(rhs);
SM_STR_FREE(mtstr);
SM_STR_FREE(owner);
SM_STR_FREE(ownerrhs);
a2821_free(prcpt);
prcpt = NULL;
return SM_SUCCESS;
enomem:
ret = sm_error_temp(SM_EM_AR, ENOMEM);
error:
SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "sev=ERROR, func=smar_rcpt_expand, smar_rcpt=%p, flags=%#x\n", smar_rcpt, smar_rcpt == NULL ? 0xffffffff : smar_rcpt->arr_flags));
SM_STR_FREE(str);
SM_STR_FREE(rhs);
SM_STR_FREE(mtstr);
SM_STR_FREE(owner);
SM_STR_FREE(ownerrhs);
a2821_free(pdomain);
pdomain = NULL;
a2821_free(prcpt);
prcpt = NULL;
sm_log_write(smar_ctx->smar_lctx,
AR_LCAT_RESOLVER, AR_LMOD_RESOLVER,
SM_LOG_ERROR, 0,
"sev=ERROR, func=smar_rcpt_expand, rcpt_pa=%.256S, ret=%m"
, smar_rcpt == NULL ? 0 : smar_rcpt->arr_pa, ret);
if (smar_rcpt_a != NULL) {
smar_rcpt_free(smar_rcpt_a, smar_rcpts);
smar_rcpt_a = NULL;
}
if (smar_rcpt != NULL && SMARR_IS_FLAG(smar_rcpt, SMARR_FL_TAKEIT)) {
smar_rcpt_free(smar_rcpt, smar_rcpts);
smar_rcpt = NULL;
}
return ret;
}
syntax highlighted by Code2HTML, v. 0.9.1