/* * 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 */