/*
* Copyright (c) 2002-2006 Sendmail, Inc. and its suppliers.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*/
#include "sm/generic.h"
SM_RCSID("@(#)$Id: dnstsk.c,v 1.88 2006/12/29 01:25:35 ca Exp $")
#include "sm/ctype.h"
#include "sm/io.h" /* sm_fd_nonblock() */
#include "sm/dns.h"
#include "sm/dns-int.h"
#include "sm/dnstsk.h"
#include "dns.h"
#define LIBDNS_LOG_DEFINES 1
#include "log.h"
/*
** Note: for some unknown reasons the query in the answer packet
** does not contain a trailing dot even if the original query did.
** Hence the code that generates a key removes trailing dots.
** ToDo: It would be nice to put that key generation code into one place
** (macro/function).
*/
/* when to wakeup dns_tsk_cleanup; should be "never" by default, see comments */
#define TSK_CLEANUP_SLP 60
#define MIN_TSK_CLEANUP_SLP 1
static sm_ret_T dns_tsk_cleanup(sm_evthr_task_P _tsk);
#if !MTA_USE_PTHREADS
/*
** ToDo: Build a version for state threads... add macros to deal with the
** differences between event threads and state threads...
*/
/* HACK */
# define EVTHR_DEL 1
# define EVTHR_WAITQ 0
# define sm_evthr_task_P void *
#endif /* !MTA_USE_PTHREADS */
/*
** ToDo:
** - fix hash key (query + type); done, but it's still ugly.
** - deal with partial reads (requeue, store state in dns_tsk)
*/
/*
** DNS_TSK_DEL -- delete a DNS task
**
** Parameters:
** dns_tsk -- DNS task context
**
** Return value:
** usual sm_error code
*/
sm_ret_T
dns_tsk_del(dns_tsk_P dns_tsk)
{
if (NULL == dns_tsk)
return SM_SUCCESS;
if (is_valid_socket(dns_tsk->dnstsk_fd)) {
close(dns_tsk->dnstsk_fd);
dns_tsk->dnstsk_fd = INVALID_SOCKET;
}
SM_STR_FREE(dns_tsk->dnstsk_wr);
SM_STR_FREE(dns_tsk->dnstsk_rd);
#if MTA_USE_PTHREADS
if (DNS_TSK_IS_FLAG(dns_tsk, DNS_TSK_FL_HAS_MTX)) {
DNS_TSK_CLR_FLAG(dns_tsk, DNS_TSK_FL_HAS_MTX);
(void) pthread_mutex_destroy(&dns_tsk->dnstsk_mutex);
}
#endif
sm_free_size(dns_tsk, sizeof(*dns_tsk));
return SM_SUCCESS;
}
/*
** DNS_TSK_NEW -- make a dns_tsk
**
** Parameters:
** dns_mgr_ctx -- DNS manager context
** flags -- flags
** ipv4 -- IPv4 address of DNS resolver
** pdns_tsk -- (pointer to) DNS task context (output)
**
** Return value:
** usual sm_error code
*/
sm_ret_T
dns_tsk_new(dns_mgr_ctx_P dns_mgr_ctx, uint flags, uint32_t ipv4, dns_tsk_P *pdns_tsk)
{
sm_ret_T ret;
uint u;
dns_tsk_P dns_tsk;
#if MTA_USE_PTHREADS
int r;
#endif
SM_REQUIRE(pdns_tsk != NULL);
u = dns_mgr_ctx->dnsmgr_ntsks;
if (u >= SM_DNS_MAX_TSKS)
return sm_error_perm(SM_EM_DNS, SM_E_FULL);
dns_tsk = (dns_tsk_P) sm_zalloc(sizeof(*dns_tsk));
if (NULL == dns_tsk)
return sm_error_temp(SM_EM_DNS, ENOMEM);
dns_tsk->dnstsk_fd = INVALID_SOCKET;
dns_tsk->dnstsk_flags = flags & DNS_TSK_FL_OPT_MSK;
dns_tsk->dnstsk_mgr = dns_mgr_ctx;
dns_tsk->dnstsk_wr = sm_str_new(NULL, HFIXEDSZ + MAXPACKET,
MAX_QUERY_SIZE);
if (NULL == dns_tsk->dnstsk_wr) {
ret = sm_error_temp(SM_EM_DNS, ENOMEM);
goto error;
}
dns_tsk->dnstsk_rd = sm_str_new(NULL, HFIXEDSZ + MAXPACKET,
MAX_QUERY_SIZE);
if (NULL == dns_tsk->dnstsk_rd) {
ret = sm_error_temp(SM_EM_DNS, ENOMEM);
goto error;
}
/* Set up the socket description */
sm_memzero(&dns_tsk->dnstsk_sin, sizeof(dns_tsk->dnstsk_sin));
dns_tsk->dnstsk_sin.sin_family = AF_INET;
dns_tsk->dnstsk_sin.sin_port = htons(DNS_PORT);
sm_memcpy(&dns_tsk->dnstsk_sin.sin_addr.s_addr, &ipv4, sizeof(ipv4));
#if MTA_USE_PTHREADS
r = pthread_mutex_init(&dns_tsk->dnstsk_mutex, SM_PTHREAD_MUTEXATTR);
if (r != 0) {
ret = sm_error_perm(SM_EM_DNS, r);
goto error;
}
DNS_TSK_SET_FLAG(dns_tsk, DNS_TSK_FL_HAS_MTX);
#endif /* MTA_USE_PTHREADS */
dns_tsk->dnstsk_fd = socket(dns_tsk->dnstsk_sin.sin_family,
(flags & DNS_TSK_FL_USETCP) ? SOCK_STREAM
: SOCK_DGRAM, 0);
if (!is_valid_socket(dns_tsk->dnstsk_fd)) {
ret = sm_error_temp(SM_EM_DNS, errno);
goto error;
}
if ((flags & (DNS_TSK_FL_USETCP|DNS_TSK_FL_CONNECTUDP)) != 0) {
if (connect(dns_tsk->dnstsk_fd,
(sockaddr_P) &dns_tsk->dnstsk_sin,
sizeof(sockaddr_in_T)) != 0)
{
ret = sm_error_temp(SM_EM_DNS, errno);
goto error;
}
}
ret = sm_fd_nonblock(dns_tsk->dnstsk_fd, true);
if (sm_is_err(ret))
goto error;
DNSTRQL_INIT(dns_tsk);
dns_tsk->dnstsk_idx = u;
dns_tsk->dnstsk_maxtimeouts = DNS_MAXTIMEOUTS;
dns_mgr_ctx->dnsmgr_dnstsks[u] = dns_tsk;
dns_mgr_ctx->dnsmgr_tskstatus[u] = DNSTSK_ST_INIT;
++dns_mgr_ctx->dnsmgr_ntsks;
*pdns_tsk = dns_tsk;
return SM_SUCCESS;
error:
(void) dns_tsk_del(dns_tsk);
return ret;
}
/*
** DNS_TSK_START -- start all(?) DNS tasks
**
** Parameters:
** dns_mgr_ctx -- DNS manager context
** evthr_ctx -- Event thread system context
**
** Return value:
** usual sm_error code
*/
sm_ret_T
dns_tsk_start(dns_mgr_ctx_P dns_mgr_ctx, sm_evthr_ctx_P evthr_ctx)
{
sm_ret_T ret;
uint u;
sm_evthr_task_P task;
timeval_T sleept;
for (u = 0; u < dns_mgr_ctx->dnsmgr_ntsks; u++) {
task = NULL;
ret = evthr_task_new(evthr_ctx, &task, EVTHR_EV_RD,
dns_mgr_ctx->dnsmgr_dnstsks[u]->dnstsk_fd,
NULL, dns_comm_tsk,
(void *) dns_mgr_ctx->dnsmgr_dnstsks[u]);
if (sm_is_err(ret))
goto error;
dns_mgr_ctx->dnsmgr_tsk[u] = task;
dns_mgr_ctx->dnsmgr_tskstatus[u] = DNSTSK_ST_OK;
}
ret = evthr_timeval(evthr_ctx, &sleept);
sleept.tv_sec += TSK_CLEANUP_SLP;
ret = evthr_task_new(evthr_ctx, &task, EVTHR_EV_SL, INVALID_FD, &sleept,
dns_tsk_cleanup, (void *) dns_mgr_ctx);
if (sm_is_err(ret))
goto error;
dns_mgr_ctx->dnsmgr_cleanup = task;
return ret;
error:
/* Is this ok?? How to properly terminate task?? */
for (u = 0; u < dns_mgr_ctx->dnsmgr_ntsks; u++) {
task = dns_mgr_ctx->dnsmgr_tsk[u];
if (task != NULL) {
evthr_task_del(evthr_ctx, task, THR_LOCK_UNLOCK);
dns_mgr_ctx->dnsmgr_tsk[u] = NULL;
}
dns_mgr_ctx->dnsmgr_tskstatus[u] = DNSTSK_ST_NONE;
}
task = dns_mgr_ctx->dnsmgr_cleanup;
if (task != NULL) {
evthr_task_del(evthr_ctx, task, THR_LOCK_UNLOCK);
dns_mgr_ctx->dnsmgr_cleanup = NULL;
}
return ret;
}
/*
** DNS_REQ_FREE -- free a DNS request,
** remove it from list if dns_mgr_ctx is not NULL.
**
** Parameters:
** dns_req -- DNS request
** dns_mgr_ctx -- DNS manager context
**
** Return value:
** usual sm_error code
**
** Locking: dns_mgr_ctx must be locked by caller.
*/
static sm_ret_T
dns_req_free(dns_req_P dns_req, dns_mgr_ctx_P dns_mgr_ctx)
{
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"sev=DBG, func=dns_req_free, dns_req=%p, ctx=%p, inlist=%d\n",
dns_req, dns_mgr_ctx,
NULL == dns_mgr_ctx ? -1 : DNSREQ_IS_INANYLIST(dns_req)));
if (NULL == dns_req)
return SM_SUCCESS;
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"sev=DBG, func=dns_req_free, dns_req=%p, key=%p, query=%p\n",
dns_req, dns_req->dnsreq_key, dns_req->dnsreq_query));
SM_CSTR_FREE(dns_req->dnsreq_query);
SM_STR_FREE(dns_req->dnsreq_key);
if (dns_mgr_ctx != NULL) {
if (DNSREQ_IS_ININCLIST(dns_req))
DNSIRQL_REMOVE(dns_mgr_ctx, dns_req);
else if (DNSREQ_IS_INWAITLIST(dns_req))
DNSWRQL_REMOVE(dns_mgr_ctx, dns_req);
}
sm_free_size(dns_req, sizeof(*dns_req));
return SM_SUCCESS;
}
/*
** DNS_REQ_DEL -- free a DNS request
** (wrapper for bht_*(); still necessary?)
**
** Parameters:
** value -- DNS request
** key -- key (ignored)
** ctx -- DNS manager context
**
** Return value:
** usual sm_error code
**
** Locking: dns_mgr_ctx must be locked by caller.
*/
void
dns_req_del(void *value, void *key, void *ctx)
{
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"sev=DBG, func=dns_req_del, dns_req=%p\n",
value));
(void) dns_req_free((dns_req_P) value, (dns_mgr_ctx_P) ctx);
}
/*
** DNS_CRT_KEY -- create a key for a DNS query
**
** Parameters:
** query -- DNS query
** type -- DNS type
** pkey -- (pointer to) key (output)
** pkeylen -- (pointer to) keylen (output)
**
** Return value:
** usual sm_error code
*/
static sm_ret_T
dns_crt_key(sm_cstr_P query, dns_type_T type, sm_str_P *pkey, size_t *pkeylen)
{
sm_str_P key;
size_t keylen;
sm_ret_T ret;
SM_REQUIRE(pkey != NULL);
SM_REQUIRE(pkeylen != NULL);
keylen = sm_cstr_getlen(query) + sizeof(type);
key = sm_str_new(NULL, keylen, keylen + 4);
if (NULL == key) {
ret = sm_error_temp(SM_EM_DNS, ENOMEM);
goto error;
}
ret = sm_str_scatn(key, (const char *) sm_cstr_data(query),
sm_cstr_getlen(query));
if (sm_is_err(ret))
goto error;
ret = sm_str_rm_trail(key, ".");
if (sm_is_err(ret))
goto error;
keylen -= ret; /* decrement keylen if '.' has been removed */
/* always lower case?! */
sm_str2lower(key);
ret = sm_str_scatn(key, (const char *) &type, sizeof(type));
if (sm_is_err(ret))
goto error;
SM_ASSERT(keylen == sm_str_getlen(key));
*pkey = key;
*pkeylen = keylen;
return SM_SUCCESS;
error:
if (key != NULL)
sm_str_free(key);
*pkey = NULL;
*pkeylen = 0;
return ret;
}
/*
** DNS_TSK_SELECT -- select a DNS task
**
** Parameters:
** dns_mgr_ctx -- DNS manager context
** now -- current time
** wakeup -- "wake up" the selected DNS task?
** pidx -- (pointer to) selected DNS task (output)
** locktype -- kind of locking
**
** Return value:
** usual sm_error code
**
** Locking: locks dns_mgr_ctx if requested
*/
static sm_ret_T
dns_tsk_select(dns_mgr_ctx_P dns_mgr_ctx, time_T now, bool wakeup, ushort *pidx, thr_lock_T locktype)
{
sm_ret_T ret;
#if MTA_USE_PTHREADS
int r;
#endif
ushort u, j, i;
time_T chg;
#if MTA_USE_PTHREADS
if (thr_lock_it(locktype)) {
r = pthread_mutex_lock(&dns_mgr_ctx->dnsmgr_mutex);
SM_LOCK_OK(r);
if (r != 0) {
sm_log_write(dns_mgr_ctx->dnsmgr_lctx,
LIBDNS_LCAT_RESOLVER, LIBDNS_LMOD_RESOLVER,
SM_LOG_CRIT, 4,
"sev=CRIT, func=dns_tsk_select, lock=%d", r);
return sm_error_perm(SM_EM_DNS, r);
}
}
#endif /* MTA_USE_PTHREADS */
/* round robin?? or select server with least open queries? */
chg = TIME_T_MAX;
i = USHRT_MAX;
u = dns_mgr_ctx->dnsmgr_ctsk;
ret = SM_SUCCESS;
for (j = 0; j < SM_DNS_MAX_TSKS; j++) {
/* found a working DNS task? */
if (dns_mgr_ctx->dnsmgr_tsk[u] != NULL &&
dns_mgr_ctx->dnsmgr_tskstatus[u] == DNSTSK_ST_OK)
break;
if (dns_mgr_ctx->dnsmgr_tskchg[u] > 0 &&
dns_mgr_ctx->dnsmgr_tskstatus[u] == DNSTSK_ST_DEAD
&& dns_mgr_ctx->dnsmgr_tskchg[u] < chg)
{
chg = dns_mgr_ctx->dnsmgr_tskchg[u];
i = u;
}
u = ++dns_mgr_ctx->dnsmgr_ctsk;
if (u >= dns_mgr_ctx->dnsmgr_ntsks)
u = dns_mgr_ctx->dnsmgr_ctsk = 0;
}
if (j >= SM_DNS_MAX_TSKS) {
if (i < dns_mgr_ctx->dnsmgr_ntsks) {
u = i;
dns_mgr_ctx->dnsmgr_tskstatus[u] = DNSTSK_ST_OK;
dns_mgr_ctx->dnsmgr_tskchg[u] = now;
sm_log_write(dns_mgr_ctx->dnsmgr_lctx,
LIBDNS_LCAT_RESOLVER,
LIBDNS_LMOD_RESOLVER,
SM_LOG_WARN, 4,
"sev=WARN, func=dns_req_add, status=all dns servers considered unresponsive, action=selected one with oldest status change");
}
else {
u = dns_mgr_ctx->dnsmgr_ctsk;
sm_log_write(dns_mgr_ctx->dnsmgr_lctx,
LIBDNS_LCAT_RESOLVER,
LIBDNS_LMOD_RESOLVER,
SM_LOG_ERROR, 1,
"sev=ERROR, func=dns_req_add, status=all dns servers considered unresponsive, action=selected one round robin");
}
}
/*
** Ignore return value: in the worst case the request will
** not be sent to a DNS server but a timeout error will be
** returned to the caller, hence we'll ignore any error
** (for now).
*/
if (wakeup) {
dns_tsk_P dns_tsk;
dns_tsk = dns_mgr_ctx->dnsmgr_dnstsks[u];
#if MTA_USE_PTHREADS
if (thr_lock_it_s1(locktype)) {
r = pthread_mutex_lock(&dns_tsk->dnstsk_mutex);
SM_LOCK_OK(r);
if (r != 0) {
sm_log_write(dns_mgr_ctx->dnsmgr_lctx,
LIBDNS_LCAT_RESOLVER,
LIBDNS_LMOD_RESOLVER,
SM_LOG_CRIT, 4,
"sev=CRIT, func=dns_tsk_select, lock_tsk=%d"
, r);
ret = sm_error_perm(SM_EM_DNS, r);
goto unlock;
}
}
#endif /* MTA_USE_PTHREADS */
/* todo: access should be locked */
if (!DNS_TSK_IS_FLAG(dns_tsk, DNS_TSK_FL_WR_EN)) {
(void) evthr_en_wr(dns_mgr_ctx->dnsmgr_tsk[u]);
DNS_TSK_SET_FLAG(dns_tsk, DNS_TSK_FL_WR_EN);
}
#if MTA_USE_PTHREADS
if (thr_unl_always_s1(locktype)) {
r = pthread_mutex_unlock(&dns_tsk->dnstsk_mutex);
SM_ASSERT(r == 0);
}
#endif /* MTA_USE_PTHREADS */
}
#if MTA_USE_PTHREADS
unlock:
if (thr_unl_always(locktype)) {
r = pthread_mutex_unlock(&dns_mgr_ctx->dnsmgr_mutex);
SM_ASSERT(r == 0);
}
#endif /* MTA_USE_PTHREADS */
if (pidx != NULL)
*pidx = u;
return ret;
}
/*
** DNS_REQ_ADD -- Add a DNS request to the queue.
** This adds a DNS request to the hash table and if it isn't yet in the
** request list also to that.
**
** Parameters:
** dns_mgr_ctx -- DNS manager context
** query -- DNS query (MUST be '\0' terminated; SM_CSTR_DUP()ed)
** type -- DNS type
** timeout -- timeout (if 0: use default)
** fct -- callback to invoke with DNS result
** ctx -- context to pass to callback
**
** Return value:
** usual sm_error code: an error will only be returned
** if the request was NOT added to the request list and hence
** the caller can't expect a reply.
**
** Locking: dns_mgr_ctx is locked as necessary.
*/
sm_ret_T
dns_req_add(dns_mgr_ctx_P dns_mgr_ctx, sm_cstr_P query, dns_type_T type, uint timeout, dns_callback_F *fct, void *ctx)
{
sm_ret_T ret;
#if MTA_USE_PTHREADS
int r;
#endif
bool add2ht;
dns_req_P dns_req;
bht_entry_P bhte;
sm_str_P key;
size_t keylen;
time_T now;
timeval_T nowt;
SM_REQUIRE(query != NULL);
dns_req = (dns_req_P) sm_zalloc(sizeof(*dns_req));
if (NULL == dns_req)
return sm_error_temp(SM_EM_DNS, ENOMEM);
ret = SM_SUCCESS;
key = NULL;
add2ht = false;
dns_req->dnsreq_query = SM_CSTR_DUP(query);
dns_req->dnsreq_type = type;
(void) evthr_timeval(dns_mgr_ctx->dnsmgr_cleanup->evthr_t_ctx,
&nowt);
#if DNS_STATISTICS
dns_req->dnsreq_startt = nowt;
#endif
now = nowt.tv_sec;
dns_req->dnsreq_endt = now + ((timeout == 0)
? dns_mgr_ctx->dnsmgr_timeout
: timeout);
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"sev=DBG, func=dns_req_add, now=%ld, endt=%ld\n",
(long) now, (long) dns_req->dnsreq_endt));
/*
** Create key from query and type... this is still ugly...
** Note: we can't use the output of res_mkquery...
*/
ret = dns_crt_key(query, type, &key, &keylen);
if (sm_is_err(ret))
goto error;
dns_req->dnsreq_key = key;
dns_req->dnsreq_fct = fct;
dns_req->dnsreq_ctx = ctx;
#if MTA_USE_PTHREADS
r = pthread_mutex_lock(&dns_mgr_ctx->dnsmgr_mutex);
SM_LOCK_OK(r);
if (r != 0) {
sm_log_write(dns_mgr_ctx->dnsmgr_lctx,
LIBDNS_LCAT_RESOLVER, LIBDNS_LMOD_RESOLVER,
SM_LOG_CRIT, 4,
"sev=CRIT, func=dns_req_add, lock=%d", r);
ret = sm_error_perm(SM_EM_DNS, r);
goto error;
}
#endif
/* Is there already a request for this? */
bhte = bht_locate(dns_mgr_ctx->dnsmgr_req_ht,
(const char *) sm_str_data(key),
(uint) sm_str_getlen(key));
add2ht = (NULL == bhte);
/* Always add the request to the hash table so it can be answered */
ret = bht_add(dns_mgr_ctx->dnsmgr_req_ht,
(char *) sm_str_data(dns_req->dnsreq_key),
(uint) sm_str_getlen(dns_req->dnsreq_key),
dns_req, &bhte);
if (sm_is_err(ret)) {
sm_log_write(dns_mgr_ctx->dnsmgr_lctx,
LIBDNS_LCAT_RESOLVER, LIBDNS_LMOD_RESOLVER,
SM_LOG_ERROR, 4,
"sev=ERROR, func=dns_req_add, bhte=%p, bht_add=%r, key='%s'[%p], len=%d\n",
bhte, ret, sm_str_data(dns_req->dnsreq_key),
dns_req->dnsreq_key,
sm_str_getlen(dns_req->dnsreq_key));
}
else if (add2ht) {
/* entry was not in hash table: add it to the list */
DNSIRQL_INSERT_TAIL(dns_mgr_ctx, dns_req);
DNSREQ_SET_INLIST(dns_req, DNSREQ_FL_IN_INC);
}
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"sev=DBG, func=dns_req_add, bht_add bhte=%p, ret=%x, key='%s'[%p], len=%d\n",
bhte, ret, sm_str_data(dns_req->dnsreq_key),
dns_req->dnsreq_key, sm_str_getlen(dns_req->dnsreq_key)));
#if MTA_USE_PTHREADS
r = pthread_mutex_unlock(&dns_mgr_ctx->dnsmgr_mutex);
SM_ASSERT(r == 0);
/* ignore error; see Return values above for an explanation */
if (sm_is_success(ret) && add2ht)
(void) dns_tsk_select(dns_mgr_ctx, now, true, NULL,
THR_LOCK_UNLOCK|THR_LOCK_UNLOCK_S1);
if (sm_is_success(ret) && dns_mgr_ctx->dnsmgr_cleanup != NULL) {
timeval_T slpt, nowt, newt;
/* set timeout for cleanup task */
(void) evthr_timeval(dns_mgr_ctx->dnsmgr_cleanup->evthr_t_ctx,
&nowt);
slpt.tv_usec = 0;
slpt.tv_sec = dns_mgr_ctx->dnsmgr_timeout;
timeradd(&nowt, &slpt, &newt);
(void) evthr_new_sl(dns_mgr_ctx->dnsmgr_cleanup, newt, false);
}
#endif /* MTA_USE_PTHREADS */
return ret;
error:
if (dns_req != NULL)
dns_req_free(dns_req, NULL);
return ret;
}
/* context for dns_bht2reql_fct() */
struct bht2reql_S
{
dns_mgr_ctx_P b2r_dns_mgr_ctx;
dns_rql_P b2r_rql_hd;
};
typedef struct bht2reql_S bht2reql_T, *bht2reql_P;
/*
** DNS_BHT2REQL_FCT -- move DNS requests from hash table into local list.
** callback for bht_rm_all() used in dns_bht2reql() below.
**
** Parameters:
** value -- DNS request
** key -- key (ignored, for compatibility with bhfree_F)
** ctx -- context (see above: bht2reql_P)
**
** Return value:
** usual sm_error code
**
** Locking: dns_mgr_ctx must be locked by caller.
*/
static void
dns_bht2reql_fct(void *value, void *key, void *ctx)
{
dns_req_P dns_req;
bht2reql_P bht2reql;
dns_mgr_ctx_P dns_mgr_ctx;
dns_rql_P rql_hd;
dns_req = (dns_req_P) value;
bht2reql = (bht2reql_P) ctx;
if (NULL == dns_req || bht2reql == NULL) {
/* abort for now */
SM_ASSERT(dns_req != NULL);
SM_ASSERT(bht2reql != NULL);
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"sev=ERROR, func=dns_bht2reql_fct, dns_req=%p, bht2reql=%p\n",
dns_req, bht2reql));
return;
}
dns_mgr_ctx = bht2reql->b2r_dns_mgr_ctx;
rql_hd = bht2reql->b2r_rql_hd;
if (NULL == dns_mgr_ctx || rql_hd == NULL) {
/* abort for now */
SM_ASSERT(dns_mgr_ctx != NULL);
SM_ASSERT(rql_hd != NULL);
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"sev=ERROR, func=dns_bht2reql_fct, dns_mgr_ctx=%p, rql_hd=%p\n",
dns_mgr_ctx, rql_hd));
return;
}
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"sev=DBG, func=dns_bht2reql_fct, dns_req=%p, inlist=%d\n",
dns_req, DNSREQ_IS_INANYLIST(dns_req)));
/* Remove entry from dns_mgr list (if it is in that list) */
if (DNSREQ_IS_ININCLIST(dns_req)) {
DNSIRQL_REMOVE(dns_mgr_ctx, dns_req);
DNSREQ_CLR_INLIST(dns_req);
}
else if (DNSREQ_IS_INWAITLIST(dns_req)) { /* possible?? */
DNSWRQL_REMOVE(dns_mgr_ctx, dns_req);
DNSREQ_CLR_INLIST(dns_req);
}
/* Add to local list */
RQL_INSERT_TAIL(rql_hd, dns_req);
}
/*
** DNS_BHT2REQL -- move DNS requests that match key from hash table
** into local list.
**
** Parameters:
** dns_mgr_ctx -- DNS manager context
** key -- key to match
** keylen -- length of key
** rql_hd -- head of DNS request list to which items are moved
**
** Return value:
** usual sm_error code
**
** Locking: dns_mgr_ctx must be locked by caller.
*/
static sm_ret_T
dns_bht2reql(dns_mgr_ctx_P dns_mgr_ctx, sm_str_P key, size_t keylen, dns_rql_P rql_hd)
{
bht2reql_T bht2reql;
SM_ASSERT(dns_mgr_ctx != NULL);
SM_ASSERT(rql_hd != NULL);
SM_ASSERT(key != NULL);
bht2reql.b2r_dns_mgr_ctx = dns_mgr_ctx;
bht2reql.b2r_rql_hd = rql_hd;
/*
** Remove all entries that match key/keylen from hash table;
** remove them from DNS manager list, add to local list.
** Use the bht_rm_all() function callback to achieve this
** instead of accessing the data directly.
*/
bht_rm_all(dns_mgr_ctx->dnsmgr_req_ht,
(const char *) sm_str_data(key), keylen,
dns_bht2reql_fct, &bht2reql);
return SM_SUCCESS;
}
/*
** DNS_TSK_RD -- DNS communication task: read replies from DNS server
**
** Parameters:
** tsk -- evthr task
**
** Return value:
** usual sm_error code
**
** Locking: dns_mgr_ctx is locked when necessary.
*/
static sm_ret_T
dns_tsk_rd(sm_evthr_task_P tsk)
{
sm_ret_T ret, res;
int r;
dns_tsk_P dns_tsk;
dns_res_P dns_res;
dns_req_P dns_req, dns_req_next;
dns_mgr_ctx_P dns_mgr_ctx;
bht_entry_P bhte;
uchar query[MAXHOSTNAMELEN];
ushort type;
sm_str_P key;
size_t keylen;
dns_rql_T rql_hd;
#if MTA_USE_PTHREADS
SM_IS_EVTHR_TSK(tsk);
#endif
dns_tsk = (dns_tsk_P) tsk->evthr_t_actx;
if (NULL == dns_tsk)
return EVTHR_DEL;
if (!is_valid_fd(tsk->evthr_t_fd))
return EVTHR_DEL;
key = NULL;
res = dns_receive(dns_tsk);
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"sev=DBG, func=dns_tsk_rd, receive=%x\n", res));
if (sm_is_err(res)) {
/* XXX deal with it... */;
ret = res;
goto error;
}
if (res == 0)
goto done;
ret = dns_res_new(MAXRESHOSTS, &dns_res);
if (sm_is_err(ret)) {
/* XXX deal with it... */;
goto error;
}
ret = dns_decode(dns_tsk->dnstsk_rd, query, sizeof(query), &type, dns_res);
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"sev=DBG, func=dns_tsk_rd, decode=%x, query=%s\n", ret, query));
#if MTA_LIBDNS_TEST
/* introduce some errors for some requests; ToDo: add others! */
#define SMDT_ARPA_TEMP "190.0.0.127.in-addr.arpa"
#define SMDT_ARPA_PERM "191.0.0.127.in-addr.arpa"
#define SMDT_ARPA_EINVAL "192.0.0.127.in-addr.arpa"
#define SMDT_MX_EINVAL "mxinvalid.sm9.org"
if (sm_memeq(query, SMDT_ARPA_TEMP, sizeof(SMDT_ARPA_TEMP) - 1))
dns_res->dnsres_ret = DNSR_TEMP;
else if (sm_memeq(query, SMDT_ARPA_PERM, sizeof(SMDT_ARPA_PERM) - 1))
dns_res->dnsres_ret = DNSR_PERM;
else if (sm_memeq(query, SMDT_ARPA_EINVAL,
sizeof(SMDT_ARPA_EINVAL) - 1))
dns_res->dnsres_ret = sm_error_perm(SM_EM_DNS, EINVAL);
else if (sm_memeq(query, SMDT_MX_EINVAL, sizeof(SMDT_MX_EINVAL) - 1))
dns_res->dnsres_ret = DNSR_MXINVALID;
#endif /* MTA_LIBDNS_TEST */
dns_mgr_ctx = dns_tsk->dnstsk_mgr;
/* Create key from query and type... this is still ugly... */
keylen = strlen((char *) query) + sizeof(type);
key = sm_str_new(NULL, keylen, keylen + 4);
if (NULL == key) {
ret = sm_error_temp(SM_EM_DNS, ENOMEM);
goto error;
}
ret = sm_str_scatn(key, (const char *) query, strlen((char *) query));
if (sm_is_err(ret))
goto error;
ret = sm_str_rm_trail(key, ".");
if (sm_is_err(ret))
goto error;
keylen -= ret; /* decrement keylen if '.' has been removed */
/* always lower case?! */
sm_str2lower(key);
ret = sm_str_scatn(key, (const char *) &type, sizeof(type));
if (sm_is_err(ret))
goto error;
SM_ASSERT(keylen == sm_str_getlen(key));
RQL_INIT(&rql_hd);
#if MTA_USE_PTHREADS
r = pthread_mutex_lock(&dns_mgr_ctx->dnsmgr_mutex);
SM_LOCK_OK(r);
if (r != 0) {
sm_log_write(dns_mgr_ctx->dnsmgr_lctx,
LIBDNS_LCAT_RESOLVER, LIBDNS_LMOD_RESOLVER,
SM_LOG_CRIT, 4,
"sev=CRIT, func=dns_tsk_rd, lock=%d", r);
ret = sm_error_perm(SM_EM_DNS, r);
goto error;
}
#endif /* MTA_USE_PTHREADS */
/*
** A two step approach is required here:
** 1. Build a new list of requests (removing them from dns_mgr_ctx:
** request list and hash table)
** 2. Use that list to invoke the callbacks
** This is required to get around locking/concurrent access issues:
** the table/list can't be locked when invoking the callback
** since otherwise the callback can't invoke a function like
** dns_req_add() which locks the dns_mgr_ctx table/list.
**
** First lookup the entry and check whether the request id
** matches the result id. If it doesn't: ignore it.
** Note: bhtable prepends new entries, hence it is necessary to
** walk to the end of the chain. This is ugly and slow; a better
** method should be used!
*/
bhte = bht_locate(dns_mgr_ctx->dnsmgr_req_ht,
(const char *) sm_str_data(key), keylen);
dns_req = NULL;
while (bhte != NULL) {
dns_req = (dns_req_P) (bhte->bhe_value);
SM_ASSERT(dns_req != NULL);
if (dns_req->dnsreq_id == dns_res->dnsres_id)
break;
bhte = bhte->bhe_next;
#if 0
if (dns_req->dnsreq_id != 0) {
sm_io_fprintf(smioerr, "sev=WARN, func=dns_tsk_rd, dns_req=%p, dns_req_id=%d, dnsres_id=%d, status=id_mismatch, where=in_while\n",
dns_req, NULL == dns_req ? -1 : dns_req->dnsreq_id, dns_res->dnsres_id);
}
#endif
}
if (NULL == bhte || dns_req == NULL) {
#if MTA_USE_PTHREADS
r = pthread_mutex_unlock(&dns_mgr_ctx->dnsmgr_mutex);
SM_ASSERT(r == 0);
/* COMPLAIN if error */
#endif
if (dns_req != NULL) {
sm_io_fprintf(smioerr, "sev=WARN, func=dns_tsk_rd, dns_req=%p, dns_req_id=%d, dnsres_id=%d, status=id_mismatch/not_found, key=%#N\n",
dns_req, NULL == dns_req ? -1 : dns_req->dnsreq_id, dns_res->dnsres_id, key);
}
goto freeit;
}
ret = dns_bht2reql(dns_mgr_ctx, key, keylen, &rql_hd);
/* result ignored for now; it's always SUCCESS */
#if MTA_USE_PTHREADS
r = pthread_mutex_unlock(&dns_mgr_ctx->dnsmgr_mutex);
SM_ASSERT(r == 0);
/* COMPLAIN if error */
#endif
/* Go through list of "done" requests and invoke callbacks */
for (dns_req = RQL_FIRST(&rql_hd);
dns_req != RQL_END(&rql_hd);
dns_req = dns_req_next)
{
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"sev=DBG, func=dns_tsk_rd, dns_req=%p, inlist=%d\n",
dns_req, DNSREQ_IS_INANYLIST(dns_req)));
dns_req_next = RQL_NEXT(dns_req);
if (dns_req->dnsreq_id != 0 &&
dns_req->dnsreq_id != dns_res->dnsres_id)
{
sm_log_write(dns_mgr_ctx->dnsmgr_lctx,
LIBDNS_LCAT_RESOLVER, LIBDNS_LMOD_RESOLVER,
SM_LOG_WARN, 7,
"sev=WARN, func=dns_tsk_rd, dns_req_id=%d, dnsrpl_id=%d\n",
dns_req->dnsreq_id, dns_res->dnsres_id);
}
if (dns_req->dnsreq_fct != NULL) {
dns_res->dnsres_query = dns_req->dnsreq_query;
/* Ignore return value? */
dns_req->dnsreq_fct(dns_res, dns_req->dnsreq_ctx);
}
/* Unlink entry from list */
RQL_REMOVE(&rql_hd, dns_req);
ret = dns_req_free(dns_req, NULL);
}
freeit:
/* Free dns_res */
ret = dns_res_free(dns_res);
sm_str_free(key);
done:
return EVTHR_WAITQ;
error:
MTA_LIBDNS_DBG_DPRINTF((smioerr, "sev=ERROR, func=dns_tsk_rd, ret=%x\n",
ret));
if (key != NULL)
sm_str_free(key);
return ret;
}
/*
** DNS_REQ_INS_WRQL -- insert DNS request into wait list
**
** Parameters:
** dns_mgr_ctx -- DNS manager context
** dns_req -- DNS request
**
** Return value:
** SM_SUCCESS
**
** Locking: dns_mgr_ctx must be locked by caller.
*/
sm_ret_T
dns_req_ins_wrql(dns_mgr_ctx_P dns_mgr_ctx, dns_req_P dns_req)
{
dns_req_P dns_reqh;
if (DNSWRQL_EMPTY(dns_mgr_ctx)) {
DNSWRQL_INSERT_TAIL(dns_mgr_ctx, dns_req);
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"sev=DBG, func=dns_tsk_wr, writelist=empty\n"));
}
else {
/*
** Insert dns_req at the right place in the list:
** start from the back and go to the front until
** an entry is reached whose "end time" is not
** greater than the one of the entry to be inserted.
*/
dns_reqh = DNSWRQL_LAST(dns_mgr_ctx);
while (dns_reqh != DNSWRQL_FIRST(dns_mgr_ctx) &&
dns_req->dnsreq_endt < dns_reqh->dnsreq_endt)
{
dns_reqh = DNSWRQL_PREV(dns_reqh);
}
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"sev=DBG, func=dns_tsk_wr, req=%p, reqh=%p, req->endt=%ld, reqh->ednt=%ld\n",
dns_req, dns_reqh, (long) dns_req->dnsreq_endt,
(long) dns_reqh->dnsreq_endt));
if (dns_req->dnsreq_endt >= dns_reqh->dnsreq_endt)
DNSWRQL_INSERT_AFTER(dns_mgr_ctx, dns_reqh, dns_req);
else
DNSWRQL_INSERT_BEFORE(dns_mgr_ctx, dns_reqh, dns_req);
}
DNSREQ_SET_INLIST(dns_req, DNSREQ_FL_IN_WAIT);
return SM_SUCCESS;
}
/*
** DNS_TSK_WR -- DNS communication task: send queries to DNS server
**
** Parameters:
** tsk -- evthr task
**
** Return value:
** usual sm_error code
*/
static sm_ret_T
dns_tsk_wr(sm_evthr_task_P tsk)
{
sm_ret_T ret;
int r;
dns_tsk_P dns_tsk;
dns_mgr_ctx_P dns_mgr_ctx;
dns_req_P dns_req;
bool empty;
SM_IS_EVTHR_TSK(tsk);
dns_tsk = (dns_tsk_P) tsk->evthr_t_actx;
if (NULL == dns_tsk)
return EVTHR_DEL;
if (!is_valid_fd(tsk->evthr_t_fd))
return EVTHR_DEL;
dns_mgr_ctx = dns_tsk->dnstsk_mgr;
dns_req = NULL;
do {
/* get value alternatively from the two lists?? */
#if MTA_USE_PTHREADS
r = pthread_mutex_lock(&dns_mgr_ctx->dnsmgr_mutex);
SM_LOCK_OK(r);
if (r != 0) {
sm_log_write(dns_mgr_ctx->dnsmgr_lctx,
LIBDNS_LCAT_RESOLVER, LIBDNS_LMOD_RESOLVER,
SM_LOG_CRIT, 4,
"sev=CRIT, func=dns_tsk_wr, lock=%d", r);
ret = sm_error_perm(SM_EM_DNS, r);
goto error;
}
#endif
#if MTA_USE_DNSTSK_LIST
/*
** Disabled: currently no entries are added
** to the task-specific queues, hence we don't need to
** check them.
*/
#if MTA_USE_PTHREADS
r = pthread_mutex_lock(&dns_tsk->dnstsk_mutex);
SM_LOCK_OK(r);
if (r != 0) {
sm_log_write(dns_mgr_ctx->dnsmgr_lctx,
LIBDNS_LCAT_RESOLVER, LIBDNS_LMOD_RESOLVER,
SM_LOG_CRIT, 4,
"sev=CRIT, func=dns_tsk_wr, lock=%d", r);
ret = sm_error_perm(SM_EM_DNS, r);
goto error;
}
#endif
/* first: try to get entry from task-specific list */
empty = DNSTRQL_EMPTY(dns_tsk);
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"sev=DBG, func=dns_tsk_wr, empty=%d\n",
empty));
if (!empty) {
/* move first element from incoming to wait list */
dns_req = DNSTRQL_FIRST(dns_tsk);
DNSTRQL_REMOVE(dns_tsk, dns_req);
dns_req->dnsreq_idx = dns_tsk->dnstsk_idx;
(void) dns_req_ins_wrql(dns_mgr_ctx, dns_req);
}
#if MTA_USE_PTHREADS
r = pthread_mutex_unlock(&dns_tsk->dnstsk_mutex);
SM_ASSERT(r == 0);
/* COMPLAIN if error */
#endif
#else /* MTA_USE_DNSTSK_LIST */
empty = true;
#endif /* MTA_USE_DNSTSK_LIST */
if (empty) {
/* if task-specific list is empty, try "global" list */
empty = DNSIRQL_EMPTY(dns_mgr_ctx);
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"sev=DBG, func=dns_tsk_wr, empty=%d\n",
empty));
if (!empty) {
/* move element from incoming to wait list */
dns_req = DNSIRQL_FIRST(dns_mgr_ctx);
DNSIRQL_REMOVE(dns_mgr_ctx, dns_req);
dns_req->dnsreq_idx = dns_tsk->dnstsk_idx;
(void) dns_req_ins_wrql(dns_mgr_ctx, dns_req);
}
}
#if MTA_USE_PTHREADS
r = pthread_mutex_unlock(&dns_mgr_ctx->dnsmgr_mutex);
SM_ASSERT(r == 0);
/* COMPLAIN if error */
#endif
if (empty) {
#if MTA_USE_PTHREADS
r = pthread_mutex_lock(&dns_tsk->dnstsk_mutex);
SM_LOCK_OK(r);
if (r != 0) {
sm_log_write(dns_mgr_ctx->dnsmgr_lctx,
LIBDNS_LCAT_RESOLVER,
LIBDNS_LMOD_RESOLVER,
SM_LOG_CRIT, 4,
"sev=CRIT, func=dns_tsk_select, lock_tsk=%d"
, r);
}
DNS_TSK_CLR_FLAG(dns_tsk, DNS_TSK_FL_WR_EN);
r = pthread_mutex_unlock(&dns_tsk->dnstsk_mutex);
SM_ASSERT(r == 0);
#endif /* MTA_USE_PTHREADS */
/* Turn off WR, dns_req_add() will turn it on if req. */
return EVTHR_WAITQ|evthr_r_no(EVTHR_EV_WR);
}
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"sev=DBG, func=dns_wr, req=%p\n",
dns_req));
/* Extract query from dns_req and send it to DNS server */
do {
/* Note: dnsreq_query must be '\0' terminated! */
r = res_mkquery(QUERY,
(const char *) sm_cstr_data(
dns_req->dnsreq_query),
C_IN, dns_req->dnsreq_type, NULL, 0,
NULL, sm_str_data(dns_tsk->dnstsk_wr),
(int) sm_str_getsize(dns_tsk->dnstsk_wr));
if (r == -1) {
size_t len;
len = sm_str_getsize(dns_tsk->dnstsk_wr) * 2;
ret = sm_str_space(dns_tsk->dnstsk_wr, len);
if (sm_is_err(ret))
goto error;
}
else {
HEADER *hp;
hp = (HEADER *) sm_str_data(dns_tsk->dnstsk_wr);
dns_req->dnsreq_id = hp->id;
}
} while (r == -1);
SM_ASSERT(r >= 0);
SM_STR_SETLEN(dns_tsk->dnstsk_wr, (uint)r);
ret = dns_send(dns_tsk);
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"sev=DBG, func=dns_wr, send=%x\n",
ret));
} while (sm_is_success(ret) &&
0 == sm_write_wait(dns_tsk->dnstsk_fd, 0));
#if 0
/*
** Don't terminate the task just because dns_send() failed
** How to deal with this properly? (see below)
*/
if (sm_is_err(ret))
goto error;
#endif /* 0 */
return EVTHR_WAITQ;
error:
/*
** XXX need to determine whether this is "real" error,
** i.e., one that actually will persist and hence it makes
** sense to terminate this task.
*/
return EVTHR_WAITQ;
/* return ret; */
}
#if MTA_LIBDNS_DEBUG
sm_ret_T
printreq(bht_entry_P e, void *ctx)
{
if (e != NULL)
sm_io_fprintf(smioerr, "e=%p, ctx=%p\n", e, ctx);
return 0;
}
#endif
/*
** DNS_TSK_CLEANUP -- deal with requests that have not been answered.
** This should be run whenever a request times out.
**
** Parameters:
** tsk -- evthr task
**
** Return value:
** usual sm_error code
*/
static sm_ret_T
dns_tsk_cleanup(sm_evthr_task_P tsk)
{
sm_ret_T ret;
int r;
dns_mgr_ctx_P dns_mgr_ctx;
dns_req_P dns_req, dns_req_next;
dns_res_T dns_res;
time_T now;
timeval_T sleept;
uint delay;
sm_str_P key;
size_t keylen;
dns_rql_T rql_hd;
SM_IS_EVTHR_TSK(tsk);
dns_mgr_ctx = (dns_mgr_ctx_P) tsk->evthr_t_actx;
if (NULL == dns_mgr_ctx)
return EVTHR_DEL;
ret = evthr_timeval(tsk->evthr_t_ctx, &sleept);
now = sleept.tv_sec;
delay = 0;
tsk->evthr_t_sleep.tv_usec = sleept.tv_usec;
RQL_INIT(&rql_hd);
dns_req_next = NULL;
#if MTA_USE_PTHREADS
r = pthread_mutex_lock(&dns_mgr_ctx->dnsmgr_mutex);
SM_LOCK_OK(r);
if (r != 0) {
sm_log_write(dns_mgr_ctx->dnsmgr_lctx,
LIBDNS_LCAT_RESOLVER, LIBDNS_LMOD_RESOLVER,
SM_LOG_CRIT, 4,
"sev=CRIT, func=dns_tsk_cleanup, lock=%d", r);
ret = sm_error_perm(SM_EM_DNS, r);
goto error;
}
#endif
while (!DNSWRQL_EMPTY(dns_mgr_ctx)) {
dns_req = DNSWRQL_FIRST(dns_mgr_ctx);
if (dns_req == dns_req_next) {
sm_log_write(dns_mgr_ctx->dnsmgr_lctx,
LIBDNS_LCAT_RESOLVER, LIBDNS_LMOD_RESOLVER,
SM_LOG_ERROR, 4,
"sev=ERROR, func=dns_cleanup, status=loop, dns_req=%p, dns_req_next=%p\n",
dns_req, dns_req_next);
SM_ASSERT(dns_req != dns_req_next);
break;
}
dns_req_next = dns_req;
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"sev=DBG, func=dns_cleanup, dns_req=%p, dns_req_next=%p\n",
dns_req, dns_req_next));
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"sev=DBG, func=dns_cleanup, now=%ld, endt=%ld\n",
(long) now, (long) dns_req->dnsreq_endt));
/* Stop the loop if the timeout hasn't been reached yet */
if (now <= dns_req->dnsreq_endt) {
delay = dns_req->dnsreq_endt - now;
if (delay == 0)
delay = MIN_TSK_CLEANUP_SLP;
break;
}
if (dns_req->dnsreq_tries < dns_mgr_ctx->dnsmgr_retries) {
#if DNS_STATISTICS
dns_req->dnsreq_startt = sleept;
#endif
dns_req->dnsreq_endt = now +
dns_mgr_ctx->dnsmgr_timeout;
DNSWRQL_REMOVE(dns_mgr_ctx, dns_req);
DNSREQ_CLR_INLIST(dns_req);
DNSIRQL_INSERT_TAIL(dns_mgr_ctx, dns_req);
DNSREQ_SET_INLIST(dns_req, DNSREQ_FL_IN_INC);
++dns_req->dnsreq_tries;
/* how to avoid repeated activations? */
(void) dns_tsk_select(dns_mgr_ctx, now, true, NULL,
THR_NO_LOCK|THR_LOCK_UNLOCK_S1);
continue;
}
/* Create key from query and type... still ugly... */
ret = dns_crt_key(dns_req->dnsreq_query, dns_req->dnsreq_type,
&key, &keylen);
if (sm_is_err(ret))
goto errunl;
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"sev=DBG, func=dns_cleanup, dns_req=%p, key='%s', keylen=%d\n",
dns_req, sm_str_data(key), keylen));
/*
** Remove dns_req from hash table.
** Notice: this removes all entries that match the key
** and moves them to our local list.
**
** Note: this creates a problem: should we requeue
** those requests? This is just a timeout,
** maybe later on it actually works.
*/
ret = dns_bht2reql(dns_mgr_ctx, key, keylen, &rql_hd);
#if MTA_LIBDNS_DEBUG
if (dns_req == DNSWRQL_FIRST(dns_mgr_ctx))
bht_walk(dns_mgr_ctx->dnsmgr_req_ht, printreq, key);
#endif
SM_ASSERT(dns_req != DNSWRQL_FIRST(dns_mgr_ctx));
sm_str_free(key);
}
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"sev=DBG, func=dns_cleanup, empty=%d, delay=%d\n",
DNSWRQL_EMPTY(dns_mgr_ctx), delay));
if (delay == 0 && !DNSWRQL_EMPTY(dns_mgr_ctx)) {
dns_req = DNSWRQL_FIRST(dns_mgr_ctx);
/* make sure the result will be > 0 [unsigned arithmetic] */
if (now < dns_req->dnsreq_endt)
delay = dns_req->dnsreq_endt - now;
if (delay == 0)
delay = MIN_TSK_CLEANUP_SLP;
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"sev=DBG, func=dns_cleanup, where=not-empty, now=%ld, endt=%ld\n",
(long) now, (long) dns_req->dnsreq_endt));
}
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"sev=DBG, func=dns_cleanup, where=end, delay=%d\n",
delay));
if (delay == 0)
delay = TSK_CLEANUP_SLP;
#if MTA_USE_PTHREADS
r = pthread_mutex_unlock(&dns_mgr_ctx->dnsmgr_mutex);
SM_ASSERT(r == 0);
/* COMPLAIN if error */
#endif
/* Go through list of "done" requests and invoke callbacks */
for (dns_req = RQL_FIRST(&rql_hd);
dns_req != RQL_END(&rql_hd);
dns_req = dns_req_next)
{
ushort u;
dns_tsk_P dns_tsk;
dns_req_next = RQL_NEXT(dns_req);
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"sev=DBG, func=dns_cleanup, dns_req=%p, fct=%p\n",
dns_req, dns_req->dnsreq_fct));
if ((u = dns_req->dnsreq_idx) < dns_mgr_ctx->dnsmgr_ntsks &&
(dns_tsk = dns_mgr_ctx->dnsmgr_dnstsks[u]) != NULL &&
dns_tsk->dnstsk_maxtimeouts > 0 &&
(++dns_tsk->dnstsk_timeouts > dns_tsk->dnstsk_maxtimeouts))
{
dns_mgr_ctx->dnsmgr_tskstatus[u] = DNSTSK_ST_DEAD;
dns_mgr_ctx->dnsmgr_tskchg[u] = now;
dns_tsk->dnstsk_timeouts = 0;
}
/* Unlink entry from list */
if (dns_req->dnsreq_fct != NULL) {
/* Return a timeout to caller... fill in more data? */
sm_memzero(&dns_res, sizeof(dns_res));
dns_res.dnsres_query = dns_req->dnsreq_query;
dns_res.dnsres_ret = DNSR_TIMEOUT;
dns_res.dnsres_qtype = dns_req->dnsreq_type;
DRESL_INIT(&dns_res);
/* Ignore return value? */
dns_req->dnsreq_fct(&dns_res, dns_req->dnsreq_ctx);
}
RQL_REMOVE(&rql_hd, dns_req);
ret = dns_req_free(dns_req, NULL);
}
tsk->evthr_t_sleep.tv_sec = sleept.tv_sec + delay;
return EVTHR_SLPQ;
errunl:
#if MTA_USE_PTHREADS
r = pthread_mutex_unlock(&dns_mgr_ctx->dnsmgr_mutex);
SM_ASSERT(r == 0);
/* COMPLAIN if error */
#endif
error:
/* Cleanup? */
tsk->evthr_t_sleep.tv_sec = sleept.tv_sec + delay;
return EVTHR_SLPQ; /* XXX Always? */
}
/*
** DNS_COMM_TSK -- DNS communication task: calls functions to perform
** reads and writes.
** This runs as task in the event thread system.
**
** Parameters:
** tsk -- evthr task
**
** Return value:
** usual sm_error code
*/
sm_ret_T
dns_comm_tsk(sm_evthr_task_P tsk)
{
sm_ret_T ret;
dns_tsk_P dns_tsk;
SM_IS_EVTHR_TSK(tsk);
dns_tsk = (dns_tsk_P) tsk->evthr_t_actx;
MTA_LIBDNS_DBG_DPRINTF((smioerr,
"sev=DBG, func=dns_comm, dns_tsk=%p, want=%x, got=%x\n",
dns_tsk, tsk->evthr_t_rqevf, tsk->evthr_t_evocc));
if (NULL == dns_tsk)
return EVTHR_DEL;
if (is_valid_fd(tsk->evthr_t_fd)) {
ret = EVTHR_WAITQ;
if (evthr_got_wr(tsk))
ret = dns_tsk_wr(tsk); /* XXX check ret here? */
/* XXX This may turn off WR; change the order? */
if (evthr_got_rd(tsk))
ret = dns_tsk_rd(tsk);
if (sm_is_err(ret)) {
/* complain? do some cleanup? */
return ret;
}
return ret;
}
return EVTHR_DEL;
}
syntax highlighted by Code2HTML, v. 0.9.1