/*
* 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: addr.c,v 1.78 2007/11/14 06:03:08 ca Exp $")
#include "sm/error.h"
#include "sm/assert.h"
#include "sm/io.h"
#include "sm/net.h"
#include "sm/rcb.h"
#include "sm/mta.h"
#include "sm/rfc2821.h"
#include "smar.h"
#include "log.h"
#include "sm/reccom.h"
/*
** Note: it might be useful to keep a small "cache" of these struct's
** around for reuse to avoid all those alloc()/free() calls.
*/
/*
** SMAR_ADDR_NEW -- Create a SMAR ADDR context
**
** Parameters:
** smar_clt_ctx -- SMAR client context
** psmar_addr -- (pointer to) SMAR ADDR context (output)
**
** Returns:
** usual sm_error code
**
** Last code review: 2004-03-11 17:57:46
** Last code change:
*/
sm_ret_T
smar_addr_new(smar_clt_ctx_P smar_clt_ctx, smar_addr_P *psmar_addr)
{
smar_addr_P smar_addr;
SM_REQUIRE(psmar_addr != NULL);
smar_addr = (smar_addr_P) sm_zalloc(sizeof(*smar_addr));
if (NULL == smar_addr)
return sm_error_temp(SM_EM_AR, ENOMEM);
smar_addr->ara_smar_clt_ctx = smar_clt_ctx;
smar_addr->ara_rpool = sm_rpool_new(NULL);
if (NULL == smar_addr->ara_rpool)
{
sm_free_size(smar_addr, sizeof(*smar_addr));
return sm_error_temp(SM_EM_AR, ENOMEM);
}
#if 0
/* set larger poolsize? */
sm_rpool_setsizes(smar_addr->ara_rpool, poolsize, 0, 0);
#endif
smar_addr->sm_magic = SM_SMAR_ADDR_MAGIC;
*psmar_addr = smar_addr;
return SM_SUCCESS;
}
/*
** SMAR_ADDR_FREE -- Free a SMAR ADDR context
**
** Parameters:
** smar_addr -- SMAR ADDR context
**
** Returns:
** usual sm_error code
**
** Locking: none, smar_addr must be under control of caller
**
** Last code review: 2004-03-11 18:00:01
** Last code change: 2006-03-31 06:24:40
*/
sm_ret_T
smar_addr_free(smar_addr_P smar_addr)
{
int r;
if (NULL == smar_addr)
return SM_SUCCESS;
SM_IS_SMAR_ADDR(smar_addr);
/* free other data... */
SM_STR_FREE(smar_addr->ara_pa);
SM_STR_FREE(smar_addr->ara_pa2);
SM_STR_FREE(smar_addr->ara_str);
SM_STR_FREE(smar_addr->ara_tag);
SM_STR_FREE(smar_addr->ara_detail);
SM_STR_FREE(smar_addr->ara_domain_pa);
SM_STR_FREE(smar_addr->ara_rhs);
SM_STR_FREE(smar_addr->ara_user2);
SM_STR_FREE(smar_addr->ara_detail2);
SM_STR_FREE(smar_addr->ara_domain_pa2);
SM_STR_FREE(smar_addr->ara_rhs2);
SM_STR_FREE(smar_addr->ara_rhs_conf);
SM_STR_FREE(smar_addr->ara_ipv4_str);
SM_CSTR_FREE(smar_addr->ara_rvrs_hostname);
for (r = 0; r < SM_MAX_DNSBL; r++)
SM_FREE(smar_addr->ara_dnsblres[r].sdbr_ipv4s);
if (smar_addr->ara_rcbe != NULL) {
sm_rcbe_free(smar_addr->ara_rcbe);
smar_addr->ara_rcbe = NULL;
}
if (smar_addr->ara_str_map != NULL) {
sm_map_close(smar_addr->ara_str_map, 0);
smar_addr->ara_str_map = NULL;
}
if (SMARA_IS_FLAG(smar_addr, SMARA_FL_HASMUT)) {
r = pthread_mutex_destroy(&smar_addr->ara_mutex);
SM_ASSERT(0 == r);
SMARA_CLR_FLAG(smar_addr, SMARA_FL_HASMUT);
}
if (SMARA_IS_FLAG(smar_addr, SMARA_FL_HASCOND)) {
r = pthread_cond_destroy(&smar_addr->ara_cond);
SM_ASSERT(0 == r);
SMARA_CLR_FLAG(smar_addr, SMARA_FL_HASCOND);
}
/*
** Note: do not free
** smar_addr->ara_rcpt nor smar_addr->ara_rcpts
** here!
*/
sm_rpool_delete(smar_addr->ara_rpool);
smar_addr->sm_magic = SM_MAGIC_NULL;
sm_free_size(smar_addr, sizeof(*smar_addr));
return SM_SUCCESS;
}
/*
** SMAR_ADDR_RE -- Send reply for an address check
**
** Parameters:
** smar_addr -- address context
** off -- possible offset in return string
**
** Returns:
** usual sm_error code
**
** Locking: none, smar_addr must be under control of caller
**
** Last code review: 2004-03-11 18:00:35
** Last code change:
*/
sm_ret_T
smar_addr_re(smar_addr_P smar_addr, uint off)
{
sm_rcb_P rcb;
sm_ret_T ret;
SM_IS_SMAR_ADDR(smar_addr);
rcb = &smar_addr->ara_rcbe->rcbe_rcb;
ret = sm_rcb_putv(rcb, RCB_PUTV_FIRST,
SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
SM_RCBV_INT, RT_A2S_ID, smar_addr->ara_id,
SM_RCBV_BUF, RT_A2S_TAID, smar_addr->ara_taid, SMTP_STID_SIZE,
/* dummy for consistency */
SM_RCBV_INT, RT_A2S_MAP_RES, SM_ACC_FOUND,
SM_RCBV_INT, RT_A2S_RCPT_ST, smar_addr->ara_status|smar_addr->ara_rflags,
SM_RCBV_END);
if (ret == SM_SUCCESS && smar_addr->ara_rhs != NULL
&& sm_str_getlen(smar_addr->ara_rhs) > 3)
{
if (off > 0) {
SM_ASSERT(sm_str_getlen(smar_addr->ara_rhs) > off);
ret = sm_rcb_putv(rcb, RCB_PUTV_NONE,
SM_RCBV_BUF, RT_A2S_STATT,
sm_str_getdata(smar_addr->ara_rhs) + off,
sm_str_getlen(smar_addr->ara_rhs) - off,
SM_RCBV_END);
}
else {
ret = sm_rcb_putv(rcb, RCB_PUTV_NONE,
SM_RCBV_STR, RT_A2S_STATT, smar_addr->ara_rhs,
SM_RCBV_END);
}
}
return ret;
}
/*
** SMAR_ADDR_CHK -- Check address
** If an address maps (via mt) to LMTP (see hack), then check
** whether the local part is in the "alias" or "local user" map.
**
** Parameters:
** smar_ctx -- SMAR context
** smar_addr -- address context
**
** Returns:
** usual sm_error code
** if ok: smar_addr->ara_status should have result of lookup(s)
**
** Called by:
**
** Locking: smar_addr must be under control of the caller
**
** Last code review: 2004-03-11 18:06:39; see comments
** Last code change:
*/
sm_ret_T
smar_addr_chk(smar_ctx_P smar_ctx, smar_addr_P smar_addr)
{
sm_ret_T ret;
smar_clt_ctx_P smar_clt_ctx;
char *ipv4s;
int flags;
bool islocal, hasdetail;
uchar delim;
sm_str_P str, rhs, detail, domain, mtstr;
sm_a2821_T a_rcpt, a_domain, a_local;
sm_a2821_P pdomain, plocal;
SM_IS_SMAR_ADDR(smar_addr);
SM_IS_SMAR_CTX(smar_ctx);
smar_clt_ctx = smar_addr->ara_smar_clt_ctx;
SM_IS_SMAR_CLT_CTX(smar_clt_ctx);
ret = SM_SUCCESS;
A2821_INIT_RP(&a_rcpt, smar_addr->ara_rpool);
A2821_INIT_RP(&a_domain, smar_addr->ara_rpool);
A2821_INIT_RP(&a_local, smar_addr->ara_rpool);
pdomain = &a_domain; /* pdomain is just pointer to a_domain! */
plocal = &a_local; /* plocal is just pointer to a_local! */
smar_addr->ara_status = SMTP_R_REJECT;
str = rhs = detail = domain = mtstr = NULL;
islocal = false;
hasdetail = false;
ret = t2821_scan((sm_rdstr_P) smar_addr->ara_pa, &a_rcpt, 0);
if (sm_is_err(ret)) goto done;
flags = R2821_CORRECT & ~R2821_AT;
ret = t2821_parse(&a_rcpt, flags);
if (sm_is_err(ret)) goto done;
/* fixme: should these be part of smar_addr? */
str = sm_str_new(smar_addr->ara_rpool, MAXADDRLEN, MAXADDRLEN + 2);
if (NULL == str) goto done; /* temp fail? */
domain = sm_str_new(smar_addr->ara_rpool, MAXADDRLEN, MAXADDRLEN + 2);
if (NULL == domain) goto done; /* temp fail? */
detail = sm_str_new(smar_addr->ara_rpool, MAXADDRLEN >> 1, MAXADDRLEN);
if (NULL == detail) goto done; /* temp fail? */
rhs = sm_str_new(smar_addr->ara_rpool, MAXADDRLEN, MAXADDRLEN + 2);
if (NULL == rhs) goto done; /* temp fail? */
/* fixme: use different length? */
mtstr = sm_str_new(smar_addr->ara_rpool, MAXADDRLEN, MAXADDRLEN + 2);
if (NULL == mtstr) goto done; /* temp fail? */
hasdetail = t2821_parts(&a_rcpt, smar_ctx->smar_cnf.smar_cnf_addr_delim,
true, str, detail, NULL, &delim);
if (sm_is_err(hasdetail)) {
SMAR_LEV_DPRINTF(5, (SMAR_DEBFP, "func=smar_addr_chk, t2821_parts=%r\n", hasdetail));
goto done;
}
/* extract domain out of rcpt_a ... and look it up */
ret = t2821_extract_domain(smar_addr->ara_rpool, &a_rcpt, &pdomain);
if (sm_is_err(ret)) {
if (ret == sm_error_perm(SM_EM_ADDR, R2821_ERR_FQDN))
islocal = true;
}
else {
ret = t2821_str(pdomain, domain, 0);
if (sm_is_err(ret)) goto done;
sm_str2lower(domain);
sm_str_clr(mtstr);
if (SMAR_MT_FULL_LOOKUP(smar_ctx))
{
uint32_t lfl;
lfl = smar_ctx->smar_mt_lfl;
if (hasdetail <= 0)
lfl &= ~SMMAP_LFL_DET;
ret = sm_map_lookup_addr(smar_ctx->smar_mt_map, str,
(hasdetail > 0) ? detail : NULL, domain, /* tag */ NULL,
delim, lfl, mtstr);
}
else
ret = sm_map_lookup(smar_ctx->smar_mt_map, 0, domain, mtstr);
if (SM_SUCCESS == ret)
ipv4s = (char *) sm_str_getdata(mtstr);
else
ipv4s = NULL;
SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "func=smar_addr_chk, domain=%S, ipv4s=%.256s\n", domain, ipv4s));
/* HACK ahead, see also smtpc/smtpc.c */
if (ipv4s == NULL ||
(strcmp(ipv4s, LMTP_IPV4_S2) != 0 && strcmp(ipv4s, LMTP_MT) != 0))
{
/* entry not found: "CONTINUE" further checks */
smar_addr->ara_status = SMTP_R_CONT;
}
else
islocal = true;
}
if (islocal) {
/*
** Extract localpart and look it up to decide whether it
** should be accepted.
*/
/*
** Check localpart.
** another map is required, or some different format, e.g.,
** To:localpart@ OK
** should be used (which, however, causes problems if
** a different map like passwd is used).
** Specify some bdb map?
** We could simply use aliases for now:
** if it exists: ok, otherwise: reject
** aliases can then also be used for the next step.
** ToDo: Enhance dealing with temporary errors.
*/
#if 0
/* lower case local parts! */
sm_str2lower(str);
sm_str2lower(detail);
#endif
ret = smar_addr_lu(smar_ctx, str, delim, hasdetail > 0 ? detail : NULL,
domain, rhs, &smar_addr->ara_status);
if (SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_RCPT_LCL_R))
SMAR_SET_RFL(smar_addr, SMAR_R_LOC_RCPT);
}
done:
SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "func=smar_addr_chk, where=reply, ret=%r, status=%d\n", ret, smar_addr->ara_status));
a2821_free(&a_domain);
a2821_free(&a_rcpt);
SM_STR_FREE(str);
SM_STR_FREE(domain);
SM_STR_FREE(detail);
SM_STR_FREE(rhs);
SM_STR_FREE(mtstr);
return SM_SUCCESS;
}
/*
** SMAR_ADDR_CHECK -- Check address; send reply
** If an address maps (via mt) to LMTP (see hack), then check
** whether the local part is in the "alias" or "local user" map.
**
** Parameters:
** smar_ctx -- SMAR context
** smar_addr -- address context
**
** Returns:
** usual sm_error code
**
** Called by: smar_react()
**
** Locking: smar_addr has been created by smar_react(),
** this is the only function that has been given access
**
** Last code review:
** Last code change:
*/
sm_ret_T
smar_addr_check(smar_ctx_P smar_ctx, smar_addr_P smar_addr)
{
sm_ret_T ret;
smar_clt_ctx_P smar_clt_ctx;
SM_IS_SMAR_ADDR(smar_addr);
SM_IS_SMAR_CTX(smar_ctx);
smar_clt_ctx = smar_addr->ara_smar_clt_ctx;
SM_IS_SMAR_CLT_CTX(smar_clt_ctx);
ret = smar_addr_chk(smar_ctx, smar_addr);
if (sm_is_err(ret)) goto error;
if (smar_addr->ara_status == SMTP_R_CONT &&
SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_RCPT_ACC|SMARA_LT_RCPT_PROT))
{
/* NOTE: This does double work, e.g., scanning, parsing */
ret = smar_access_chk(smar_ctx, smar_addr);
if (sm_is_err(ret)) goto error;
}
SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "func=smar_addr_check, where=reply, ret=%r, status=%d, ltype=%#x\n", ret, smar_addr->ara_status, smar_addr->ara_ltype));
ret = smar_addr_re(smar_addr, smar_addr->ara_rhstagoff);
ret = sm_rcbcom_endrep(&smar_clt_ctx->smac_com_ctx,
smar_clt_ctx->smac_com_ctx.rcbcom_tsk,
false, &smar_addr->ara_rcbe);
if (sm_is_err(ret)) goto error;
smar_addr_free(smar_addr);
return ret;
error:
smar_addr_free(smar_addr);
sm_log_write(smar_ctx->smar_lctx,
AR_LCAT_RESOLVER, AR_LMOD_RESOLVER,
SM_LOG_ERROR, 0,
"sev=ERROR, func=smar_addr_check, ret=%m", ret);
return ret;
}
syntax highlighted by Code2HTML, v. 0.9.1