/*
* Copyright (c) 2002-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.
*
* $Id: dnstsk.h,v 1.33 2007/11/05 05:48:20 ca Exp $
*/
#ifndef SM_DNS_TSK_H
#define SM_DNS_TSK_H 1
#include "sm/generic.h"
#include "sm/types.h"
#include "sm/limits.h"
#include "sm/time.h"
#include "sm/str.h"
#include "sm/bhtable.h"
#include "sm/dns.h"
#include "sm/log.h"
#if MTA_USE_PTHREADS
# include "sm/pthread.h"
# include "sm/evthr.h"
#endif
/*
** Not defined on HP-UX by default? How to get it? Should be in sm/net.h?
** requires _XOPEN_SOURCE_EXTENDED?
*/
#ifndef MSG_WAITALL
# define MSG_WAITALL 0x40
#endif
#define LOCALHOST_IP INADDR_LOOPBACK
#define DNS_PORT 53
/* maximum number of DNS tasks */
#define SM_DNS_MAX_TSKS 4u
#if SM_DNS_MAX_TSKS >= USHRT_MAX
# ERROR SM_DNS_MAX_TSKS _SM_DNS_MAX_TSKS >= USHRT_MAX _USHRT_MAX
#endif
/* Hash table size */
#define DNS_HT_SIZE 1023
#define DNS_HT_MAX 4095
/* Maximum length of query/answer (way too large?) */
#define MAX_QUERY_SIZE (64 * 1024)
/* list of requests can be SIMPLEQ */
TAILQ_HEAD(dns_rql_S, dns_req_S);
typedef struct dns_tsk_S dns_tsk_T, *dns_tsk_P;
/*
** DNS Manager Context
**
** The DNS manager uses a request list and a hash table to store DNS requests.
** The (incoming) request list contains (in arriving order) the list of
** unique requests.
** The hash table contains all requests. It is used for two purposes:
** 1. to avoid querying a DNS resolver for the same item again if
** there is already an open query.
** 2. to lookup requests after an answer is returned by a DNS
** resolver.
** The key for a hash table entry is the DNS query plus its type.
**
** The incoming request list acts as a FIFO buffer between the
** clients that send request to this DNS library and the DNS server:
** requests from clients are appended to the list.
** The wait list stores entries that are waiting for answers from a
** DNS server. It might not be necessary to keep the list sorted in
** case the list is short such that a search for expired entries is fast.
**
** Since there will be only one request per query, different timeouts
** don't work well. For example: ask for MX record for A with timeout 5s,
** then ask for MX record for A with timeout 20s: the second request
** will not be sent to a DNS server, hence if the requests times out
** after 6 seconds, the second request will be treated as (temp) failed too
** even though it might have succeeded.
*/
struct dns_mgr_ctx_S
{
/* magic? */
uint dnsmgr_flags;
sm_log_ctx_P dnsmgr_lctx; /* log context */
/* XXX should these be per DNS task? */
/* sorted list of incoming requests */
dns_rql_T dnsmgr_increq_hd;
/* sorted list of requests waiting for answers */
dns_rql_T dnsmgr_waitreq_hd;
/* hash table to store all requests */
bht_P dnsmgr_req_ht;
ushort dnsmgr_timeout; /* timeout for queries (s) */
ushort dnsmgr_retries;
ushort dnsmgr_ntsks; /* number of DNS tasks */
dns_tsk_P dnsmgr_dnstsks[SM_DNS_MAX_TSKS];
ushort dnsmgr_tskstatus[SM_DNS_MAX_TSKS];
/* last status change */
time_T dnsmgr_tskchg[SM_DNS_MAX_TSKS];
#if MTA_USE_PTHREADS
uint dnsmgr_ctsk; /* current DNS task to use for query */
sm_evthr_task_P dnsmgr_tsk[SM_DNS_MAX_TSKS];
sm_evthr_task_P dnsmgr_cleanup;
pthread_mutex_t dnsmgr_mutex; /* for the entire context? */
#endif
};
/* states for DNS tasks, must be ordered */
#define DNSTSK_ST_NONE 0x0000u
#define DNSTSK_ST_INIT 0x0001u
#define DNSTSK_ST_OK 0x0002u
#define DNSTSK_ST_DEAD 0x0004u
/* CONF! */
#define DNSTSK_CHGTM 32
/*
** DNS task context: this is bound to one DNS server
*/
struct dns_tsk_S
{
/* XXX int or statethreads socket */
int dnstsk_fd; /* socket */
dns_mgr_ctx_P dnstsk_mgr; /* DNS manager */
uint dnstsk_flags; /* operating flags */
/*
** counter for queries that timed out: if a limit is exceeded
** consider the server "dead" (unresponsive) and exclude it
** from the list
*/
uint dnstsk_timeouts;
uint dnstsk_maxtimeouts;
sockaddr_in_T dnstsk_sin; /* socket description */
sm_str_P dnstsk_rd; /* read buffer */
sm_str_P dnstsk_wr; /* write buffer */
ushort dnstsk_idx; /* index of this DNS task */
/* sorted list of incoming requests */
dns_rql_T dnstsk_increq_hd;
uint dnstsk_inc_reqs; /* number of incoming requests */
uint dnstsk_open_reqs; /* number of open requests */
#if MTA_USE_PTHREADS
pthread_mutex_t dnstsk_mutex; /* for list of incoming requests */
#endif
};
/*
** Locking order:
** 1. dns_mgr_ctx->dnsmgr_mutex
** 2. dns_tsk->dnstsk_mutex
*/
#define DNS_TSK_FL_USETCP 0x0001u /* use TCP instead of UDP */
#define DNS_TSK_FL_CONNECTUDP 0x0002u /* use connected UDP socket */
#define DNS_TSK_FL_OPT_MSK 0x000fu /* option flag */
#define DNS_TSK_FL_HAS_MTX 0x0010u /* mutex initialized */
#define DNS_TSK_FL_WR_EN 0x0020u /* write enabled */
#define DNS_TSK_FL_SHUTDOWN 0x1000u /* shutdown on next run */
/* 0x200u - 0x8000u are reserved! */
#define DNS_TSK_SET_FLAG(dns_tsk, fl) (dns_tsk)->dnstsk_flags |= (fl)
#define DNS_TSK_CLR_FLAG(dns_tsk, fl) (dns_tsk)->dnstsk_flags &= ~(fl)
#define DNS_TSK_IS_FLAG(dns_tsk, fl) (((dns_tsk)->dnstsk_flags & (fl)) != 0)
#define DNSTRQL_INIT(dns_tsk) TAILQ_INIT(&((dns_tsk)->dnstsk_increq_hd))
#define DNSTRQL_FIRST(dns_tsk) TAILQ_FIRST(&((dns_tsk)->dnstsk_increq_hd))
#define DNSTRQL_LAST(dns_tsk) TAILQ_LAST(&((dns_tsk)->dnstsk_increq_hd), dns_rql_S)
#define DNSTRQL_EMPTY(dns_tsk) TAILQ_EMPTY(&((dns_tsk)->dnstsk_increq_hd))
#define DNSTRQL_END(dns_tsk) TAILQ_END(&((dns_tsk)->dnstsk_increq_hd))
#define DNSTRQL_NEXT(dns_increq) TAILQ_NEXT(dns_increq, dnsreq_link)
#define DNSTRQL_PREV(dns_increq) TAILQ_PREV(dns_increq, dns_rql_S, dnsreq_link)
#define DNSTRQL_INSERT_TAIL(dns_tsk, dns_increq) \
do { \
TAILQ_INSERT_TAIL(&((dns_tsk)->dnstsk_increq_hd), \
dns_increq, dnsreq_link); \
++(dns_tsk)->dnstsk_inc_reqs; \
} while (0)
#define DNSTRQL_REMOVE(dns_tsk, dns_increq) \
do { \
TAILQ_REMOVE(&((dns_tsk)->dnstsk_increq_hd), \
dns_increq, dnsreq_link); \
SM_ASSERT((dns_tsk)->dnstsk_inc_reqs > 0); \
--(dns_tsk)->dnstsk_inc_reqs; \
} while (0)
sm_ret_T dns_mgr_ctx_del(dns_mgr_ctx_P _dns_mgr_ctx);
sm_ret_T dns_mgr_ctx_new(uint _flags, uint _timeout, uint _htsize, uint _htlimit, dns_mgr_ctx_P *_pdns_mgr_ctx);
sm_ret_T dns_mgr_set_timeout(dns_mgr_ctx_P _dns_mgr_ctx, uint _timeout);
sm_ret_T dns_tsk_new(dns_mgr_ctx_P _dns_mgr_ctx, uint _flags, ipv4_T ipv4, dns_tsk_P *_pdns_tsk);
#if MTA_USE_PTHREADS
sm_ret_T dns_tsk_start(dns_mgr_ctx_P _dns_mgr_ctx, sm_evthr_ctx_P _evthr_ctx);
sm_ret_T dns_comm_tsk(sm_evthr_task_P _tsk);
#else
/* sm_ret_T dns_comm_tsk(sm_evthr_task_P _tsk); */
#endif
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);
void dns_req_del(void *_value, void *_key, void *_ctx);
sm_ret_T dns_receive(dns_tsk_P dns_tsk);
sm_ret_T dns_send(dns_tsk_P dns_tsk);
#endif /* ! SM_DNS_TSK_H */
syntax highlighted by Code2HTML, v. 0.9.1