?? namecachemain.c
字號(hào):
/****************************************************************\Copyright 2004 Enzo MichelangeliThis file is part of the KadC library.KadC is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 2 of the License, or(at your option) any later version.KadC is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with KadC; if not, write to the Free SoftwareFoundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USAIn addition, closed-source licenses for this software may be grantedby the copyright owner on commercial basis, with conditions negotiatedcase by case. Interested parties may contact Enzo Michelangeli at oneof the following e-mail addresses (replace "(at)" with "@"): em(at)em.no-ip.com em(at)i-t-vision.com\****************************************************************/#define DEBUG 1#define arraysize(a) (sizeof(a)/sizeof(a[0]))#include <pthread.h>#include <stdio.h>#include <string.h>#include <ctype.h>#include <time.h>#include <stdlib.h>#include <unistd.h>#include <errno.h>#include <stdlib.h>#include <signal.h>#include <assert.h>#ifdef __WIN32__#include <winsock.h>#define socklen_t int#else /* __WIN32__ */#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <netdb.h>#define min(a,b) (((a)<(b))?(a):(b))#include <errno.h>#endif /* __WIN32__ */#include <Debug_pthreads.h>#include <int128.h>#include <rbt.h>#include <queue.h>#include <KadCalloc.h>#include <KadClog.h>#include <millisleep.h>#include <net.h> /* for htoa() */#include <KadCapi.h>#include <dns.h>#include <droppriv.h>#include <config.h>#define DNS_PORT 53typedef struct _DNSIO { int fd; unsigned long int ip; unsigned long int port; unsigned long int upstream[4]; /* upstream DNS servers array */ int nupstream; /* number of upstream DNS servers */ pthread_mutex_t mutex; queue *fifo; int err; int shutdown_flag; /* set by signal handler, protected by mutex */#if !defined(__WIN32__) && !defined(__CYGWIN__) int uid; /* uid to fall back to in order to drop root privileges after bind() under UNIX */ int gid; /* gid to fall back to in order to drop root privileges after bind() under UNIX */#endif} DNSIO;typedef struct _processing_thread_params { KadCcontext *pkcc; DNSIO *pdnsio; char *tlpd[100]; int ntlpd; char *my_pseudo[100]; int nmy_pseudo; void *pending_q_rbt; /* table of raw_qa structs for pending questions by question */ void *cache_q_rbt; /* table indexing raw_qa (question/answer pairs) by question */ int cache_maxentries; /* typically 4096 */ pthread_mutex_t mutex;} processing_thread_params;/* a structure holding DNS client's address/port, a pointer to a buffer containing the raw DNS message as it is in the UDP payload, and the number of bytes in that buffer. For packets exchanged over TCP, fd contains the fd of the TCP connection, otherwise it's -1. */typedef struct _DNSpacket { unsigned long int remoteip; unsigned short int remoteport; int fd; int bufsize; unsigned char *buf; int qsize; /* length of questions area inside buf */} DNSpacket;/* a structure containing one "query" and one "response" DNSpackets, and an expiration timestamp */typedef struct _raw_qa { DNSpacket *q; DNSpacket *a; time_t expiry; time_t last_accessed; int being_refreshed; /* the fields below are used only by detached queries (which call the P2P backend) */ int isdetached; pthread_t thread; /* the thread servicing the P2P query */ pthread_mutex_t mutex; /* protects critical areas in thread routine code */ processing_thread_params *pptp; /* useful to access e.g. the fifo in the DNSIO block */ int done; /* set by thread routine just before terminating: tells that pthread_join won't block for long */} raw_qa;/* header is 12 byte long, and the question(s) come next */#define HS 12/* compare DNSpacket structures by raw_question area (1st question only) *//* if the sizes are the same, compare the content; else return false */static int DNSpacket_q_eq(DNSpacket *pdnp1, DNSpacket *pdnp2) { if(pdnp1->qsize != pdnp2->qsize) return 0; else return (memcmp(pdnp1->buf + HS, pdnp2->buf + HS, pdnp1->qsize) == 0);}/* if the sizes are the same, compare the content; else compare question sizes */static int DNSpacket_q_lt(DNSpacket *pdnp1, DNSpacket *pdnp2) { if(pdnp1->qsize != pdnp2->qsize) return pdnp1->qsize < pdnp2->qsize; else return (memcmp(pdnp1->buf + HS, pdnp2->buf + HS, pdnp1->qsize) < 0);}/* compare raw_qa structures by raw_question AND by ID (important for pending queries table!) *//* if the sizes are the same, compare the content; else return false */static int DNSpacket_qi_eq(DNSpacket *pdnp1, DNSpacket *pdnp2) { if(pdnp1->qsize != pdnp2->qsize) return 0; else return (memcmp(pdnp1->buf + HS, pdnp2->buf + HS, pdnp1->qsize) == 0 && memcmp(pdnp1->buf, pdnp2->buf, 2) == 0);}/* if the sizes are the same, compare the content (incl. ID); else compare question sizes */static int DNSpacket_qi_lt(DNSpacket *pdnp1, DNSpacket *pdnp2) { if(pdnp1->qsize != pdnp2->qsize) return pdnp1->qsize < pdnp2->qsize; else { int compare_id = memcmp(pdnp1->buf, pdnp2->buf, 2); if(compare_id != 0) return compare_id; else return (memcmp(pdnp1->buf + HS, pdnp2->buf + HS, pdnp1->qsize) < 0); }}static int qa_rbt_destroy(void *rbt, int destroy_data_too) { void *iter; raw_qa *prqa; rbt_StatusEnum rbt_status; for(;;) { iter = rbt_begin(rbt); if(iter == NULL) break; prqa = rbt_value(iter); rbt_erase(rbt, iter); if(destroy_data_too && (prqa != NULL)) { if(prqa->q != NULL) { if(prqa->q->buf != NULL) free(prqa->q->buf); free(prqa->q); } if(prqa->a != NULL) { if(prqa->a->buf != NULL) free(prqa->a->buf); free(prqa->a); free(prqa); } } } rbt_status = rbt_destroy(rbt); if(rbt_status == RBT_STATUS_OK) return 0; else return 1;}/* conversion routines between DNSpacket (which contains a packed buffer together with peer information which is not retained) and the parsed dns_msg defined in dns.h */static dns_msg *DNSpacket2msg(DNSpacket *pdnp) { return dns_parse((char *)pdnp->buf, pdnp->bufsize);}static DNSpacket *DNSmsg2packet(dns_msg *pdm, unsigned long int ip, unsigned short int port, int fd) { DNSpacket *pdnp; int status; pdnp = malloc(sizeof(DNSpacket)); assert(pdnp != NULL); pdnp->bufsize = 4096; /* preliminary, vastly oversized */ pdnp->buf = malloc(pdnp->bufsize); assert(pdnp->buf != NULL); status = dns_pack((char *)pdnp->buf, pdnp->bufsize, pdm); assert(status > 0);/* we should also check if status > 512, and, if so, limit to 512 and set the truncation bit: */#if 1 if(status > 512) {#ifdef DEBUG KadC_log("Truncation occurred: %d bytes reduced to 512\n", status);#endif status = 512; pdnp->buf[2] |= (1 << 1); }#endif pdnp->bufsize = status; pdnp->buf = realloc(pdnp->buf, pdnp->bufsize); /* trim down to what it's necessary */ pdnp->remoteip = ip; pdnp->remoteport = port; pdnp->fd = fd; return pdnp;}static void DNSpacket_destroy(DNSpacket *pdnp) { if(pdnp != NULL) { if(pdnp->buf != NULL) free(pdnp->buf); free(pdnp); }}/* for detached P2P queries, it also reaps the thread used in the search */static void raw_qa_destroy(raw_qa *pqa) { if(pqa != NULL) { if(pqa->isdetached) { pthread_join(pqa->thread, NULL); } pthread_mutex_destroy(&pqa->mutex); if(pqa->q != NULL) DNSpacket_destroy(pqa->q); if(pqa->a != NULL) DNSpacket_destroy(pqa->a); free(pqa); }}static int trim_qa_rbt(void *rbt, int maxentries) { int removed_entries = 0; rbt_StatusEnum rbt_status; while(rbt_size(rbt) > maxentries) { /* cache full: let's expire the oldest record */ void *iter = NULL; raw_qa *soonest_to_expire_qa = NULL; for(iter=rbt_begin(rbt); iter != NULL; iter = rbt_next(rbt, iter)) { raw_qa *pqa = rbt_value(iter); if(soonest_to_expire_qa == NULL || pqa->expiry < soonest_to_expire_qa->expiry) soonest_to_expire_qa = pqa; } if(soonest_to_expire_qa != NULL) { rbt_status = rbt_eraseKey(rbt, soonest_to_expire_qa->q); assert(rbt_status == RBT_STATUS_OK);#ifdef DEBUG KadC_log("Expunging the soonest_to_expire cached record; %d remain in cache\n", rbt_size(rbt));#endif raw_qa_destroy(soonest_to_expire_qa); removed_entries++; } else { break; /* should only be executed if maxentries <= 0... */ } } return removed_entries;}static int purge_expired(void *qarbt) { void *iter; int removed_entries = 0; rbt_StatusEnum rbt_status; int rbt_is_cache = 0; do { for(iter = rbt_begin(qarbt); iter != NULL; iter = rbt_next(qarbt, iter)) { raw_qa *pqa = rbt_value(iter); /* only for cache rbt (where pqa->a != NULL) implement "predictive caching": if the entry is going to expire soon (e.g., 1 minute), and it has been accessed recently, (e.g., less than 5 minutes ago) then perform a refresh so that the next access will find the data already in cache. This is implemented posting to the DNSIO fifo queue a dummy DNSpacket obtained by cloning the ->q member in the cached qa. The clone's remoteip will be set to 0, so to suppress the DNS reply otherwise issued by the processing thread. */ if(pqa->a != NULL && /* i.e., it's a cached qa, not a pending one... */ pqa->expiry < time(NULL) + 60 && /* ...expiring within 60 seconds */ pqa->last_accessed > time(NULL) - 300 && /* ...and accessed less that 300 seconds ago... */ pqa->being_refreshed == 0) { /* ...and we haven't yet taken care of it... */ DNSIO *pdnsio = pqa->pptp->pdnsio; /* allocate a DNSpacket */ DNSpacket *dp = malloc(sizeof(DNSpacket)); assert(dp != NULL); pqa->being_refreshed = 1; *dp = *(pqa->q); /* shallow clone of cached question packet */ dp->remoteip = 0; /* identify it as a locally-generated psudo-packet */ dp->remoteport = 0; /* same */ dp->fd = -1; /* means "packet not arrived over TCP" */ /* allocate a copy of buffer referenced by DNSpacket */ dp->buf = malloc(dp->bufsize); assert(dp->buf != NULL); /* copy there the question's buf: this makes the cloning deep */ memcpy(dp->buf, pqa->q->buf, dp->bufsize);#ifdef VERBOSE_DEBUG /* DEBUG ONLY */ { int i; KadC_log("Predictive caching: enqueuing pseudo-packet from %s:%d %d bytes:", htoa(dp->remoteip), dp->remoteport, dp->bufsize); for(i=0; i < dp->bufsize /* && i < 48 */; i++) { if((i % 16) == 0) KadC_log("\n"); KadC_log("%02x ", dp->buf[i]); } } KadC_log("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");#endif /* enqueue the cloned DNSpacket */ if(pdnsio->fifo->enq(pdnsio->fifo, dp) != 0) { /* if FIFO full, drop the packet? */ free(dp->buf); free(dp); } } if(pqa->expiry < time(NULL)) { if(pqa->a != NULL) rbt_is_cache = 1; /* only qa's in cache have a non-NULL a */ if(pqa->isdetached == 0 || pqa->done != 0) { /* if detached and not done, skip it */ rbt_status = rbt_erase(qarbt, iter); removed_entries++; assert(rbt_status == RBT_STATUS_OK); raw_qa_destroy(pqa); break; /* restart scan after erase because iter becomes invalid */ } } } } while(iter != NULL); /* until end of table is reached without deletions during a scan */#ifdef DEBUG if(removed_entries > 0) KadC_log("Expunging from %s %d expired record; %d remain in table\n", (rbt_is_cache ? "cache" : "pending requests"), removed_entries, rbt_size(qarbt));#endif return removed_entries;}/* static global variables and prototypes */static pthread_mutex_t mutex_initializer = PTHREAD_MUTEX_INITIALIZER;static void *processing_thread(void *p);static void *publishing_thread(void *p);static int udp_dns_init(DNSIO *pdnsio);void *dns_udp_recv_thread(void *p);static void ConsoleLoop(processing_thread_params *pptp);static int DNSquery(DNSIO *pd, DNSpacket *pdnp, unsigned long int ip);static int DNSreply(DNSIO *pd, DNSpacket *pdnpreply);void usage(char *prog) {#if !defined(__WIN32__) && !defined(__CYGWIN__) KadC_log("usage: %s [-k inifile.ini] [-i ip_to_bind_to] [-p port_to_bind_to] [-s DNSserver]* -c cache_maxentries [-d my_pseudodomain[=dotted.quad.ip.addr]]* [-t toplevelpseudodomain]* [-u UID] [-g GID]\n", prog);#else KadC_log("usage: %s [-k inifile.ini] [-i ip_to_bind_to] [-p port_to_bind_to] [-s DNSserver]* -c cache_maxentries [-d my_pseudodomain[=dotted.quad.ip.addr]]* [-t toplevelpseudodomain]*\n", prog);#endif}static DNSIO *glob_pdnsio; /* used by sighandler */#ifdef OLD_STYLE_SIGNALvoid sighandler(int sig) { DNSIO *pdnsio = glob_pdnsio; pthread_mutex_lock(&pdnsio->mutex);#ifdef DEBUG KadC_log("Signal %d caught, shutdown_flag set to 1\n", sig);#endif pdnsio->shutdown_flag = 1; pthread_mutex_unlock(&pdnsio->mutex);
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -