"Fossies" - the Fresh Open Source Software Archive

Member "libspf2-1.2.10/src/libspf2/spf_dns_cache.c" (28 Jan 2012, 17417 Bytes) of package /linux/privat/libspf2-1.2.10.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "spf_dns_cache.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2  * This program is free software; you can redistribute it and/or modify
    3  * it under the terms of either:
    4  *
    5  *   a) The GNU Lesser General Public License as published by the Free
    6  *      Software Foundation; either version 2.1, or (at your option) any
    7  *      later version,
    8  *
    9  *   OR
   10  *
   11  *   b) The two-clause BSD license.
   12  *
   13  * These licenses can be found with the distribution in the file LICENSES
   14  */
   15 
   16 #include "spf_sys_config.h"
   17 
   18 #ifdef STDC_HEADERS
   19 # include <stdio.h>        /* stdin / stdout */
   20 # include <stdlib.h>       /* malloc / free */
   21 #endif
   22 
   23 
   24 #ifdef HAVE_STRING_H
   25 # include <string.h>       /* strstr / strdup */
   26 #else
   27 # ifdef HAVE_STRINGS_H
   28 #  include <strings.h>       /* strstr / strdup */
   29 # endif
   30 #endif
   31 
   32 #ifdef HAVE_MEMORY_H
   33 #include <memory.h>
   34 #endif
   35 #if TIME_WITH_SYS_TIME
   36 # include <sys/time.h>
   37 # include <time.h>
   38 #else
   39 # if HAVE_SYS_TIME_H
   40 #  include <sys/time.h>
   41 # else
   42 #  include <time.h>
   43 # endif
   44 #endif
   45 
   46 #ifdef HAVE_NETDB_H
   47 # include <netdb.h>
   48 #endif
   49 
   50 #ifdef HAVE_PTHREAD_H
   51 # include <pthread.h>
   52 #endif
   53 
   54 #include "spf.h"
   55 #include "spf_dns.h"
   56 #include "spf_internal.h"
   57 #include "spf_dns_internal.h"
   58 #include "spf_dns_cache.h"
   59 
   60 
   61 /**
   62  * @file
   63  *
   64  * Implements a simple cache using a list hash. There is no reclaim
   65  * list, since GNU malloc has clue.
   66  *
   67  * This original description from Wayne is no longer true:
   68  *
   69  * This is really little more than a proof-of-concept cache.
   70  *
   71  * The cache size is fixed and uses the CRC-32 function as a hash
   72  * generator.  Little is done about hash collisions, no alternate hash
   73  * functions, no buckets, no linked lists, etc.  There is a small
   74  * reclaim list and if you add multiple DNS cache layers of different
   75  * sizes you get slightly different hash functions. (The CRC-32
   76  * function was chosen because I had a copy handy, it is pretty fast,
   77  * and single bit changes are guarenteed to give a different hash.
   78  * So, mx1.foo.com and mx2.foo.com will not collide)
   79  */
   80 
   81 
   82 typedef
   83 struct _SPF_dns_cache_bucket_t {
   84     struct _SPF_dns_cache_bucket_t  *next;
   85     SPF_dns_rr_t                    *rr;
   86 } SPF_dns_cache_bucket_t;
   87 
   88 typedef struct
   89 {
   90     SPF_dns_cache_bucket_t  **cache;
   91     int                       cache_size;
   92     pthread_mutex_t           cache_lock;
   93 
   94     int             hash_mask;
   95     int             max_hash_len;
   96 
   97 #if 0
   98     int             hit;
   99     int             miss;
  100 #endif
  101 
  102     time_t          min_ttl;
  103     time_t          err_ttl;
  104     time_t          txt_ttl;
  105     time_t          rdns_ttl;
  106 
  107     int             conserve_cache;
  108 
  109     
  110 } SPF_dns_cache_config_t;
  111 
  112 
  113 static inline SPF_dns_cache_config_t *SPF_voidp2spfhook( void *hook )
  114     { return (SPF_dns_cache_config_t *)hook; }
  115 static inline void *SPF_spfhook2voidp( SPF_dns_cache_config_t *spfhook )
  116     { return (void *)spfhook; }
  117 
  118 
  119 /*
  120 ** calculate CRC-32 stuff.
  121 */
  122 
  123 /*
  124  *  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or
  125  *  code or tables extracted from it, as desired without restriction.
  126  *
  127  *  First, the polynomial itself and its table of feedback terms.  The
  128  *  polynomial is
  129  *  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
  130  *
  131  *  Note that we take it "backwards" and put the highest-order term in
  132  *  the lowest-order bit.  The X^32 term is "implied"; the LSB is the
  133  *  X^31 term, etc.  The X^0 term (usually shown as "+1") results in
  134  *  the MSB being 1
  135  *
  136  *  Note that the usual hardware shift register implementation, which
  137  *  is what we're using (we're merely optimizing it by doing eight-bit
  138  *  chunks at a time) shifts bits into the lowest-order term.  In our
  139  *  implementation, that means shifting towards the right.  Why do we
  140  *  do it this way?  Because the calculated CRC must be transmitted in
  141  *  order from highest-order term to lowest-order term.  UARTs transmit
  142  *  characters in order from LSB to MSB.  By storing the CRC this way
  143  *  we hand it to the UART in the order low-byte to high-byte; the UART
  144  *  sends each low-bit to hight-bit; and the result is transmission bit
  145  *  by bit from highest- to lowest-order term without requiring any bit
  146  *  shuffling on our part.  Reception works similarly
  147  *
  148  *  The feedback terms table consists of 256, 32-bit entries.  Notes
  149  *
  150  *      The table can be generated at runtime if desired; code to do so
  151  *      is shown later.  It might not be obvious, but the feedback
  152  *      terms simply represent the results of eight shift/xor opera
  153  *      tions for all combinations of data and CRC register values
  154  *
  155  *      The values must be right-shifted by eight bits by the "updcrc
  156  *      logic; the shift must be unsigned (bring in zeroes).  On some
  157  *      hardware you could probably optimize the shift in assembler by
  158  *      using byte-swap instructions
  159  *      polynomial $edb88320
  160  */
  161 
  162 const unsigned int crc_32_tab[256] = {
  163     0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
  164     0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
  165     0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
  166     0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
  167     0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
  168     0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
  169     0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
  170     0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
  171     0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
  172     0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
  173     0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
  174     0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
  175     0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
  176     0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
  177     0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
  178     0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
  179     0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
  180     0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
  181     0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
  182     0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
  183     0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
  184     0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
  185     0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
  186     0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
  187     0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
  188     0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
  189     0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
  190     0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
  191     0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
  192     0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
  193     0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
  194     0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
  195     0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
  196     0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
  197     0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
  198     0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
  199     0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
  200     0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
  201     0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
  202     0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
  203     0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
  204     0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
  205     0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
  206     0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
  207     0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
  208     0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
  209     0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
  210     0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
  211     0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
  212     0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
  213     0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
  214     0x2d02ef8dL
  215 };
  216 
  217 static inline int
  218 crc32str(unsigned int accum, const char *str, int max_hash_len)
  219 {
  220     for( ; *str != '\0' && max_hash_len > 0; str++ ) {
  221         if ( *str == '.' )
  222             continue;
  223 
  224         accum = crc_32_tab[ (unsigned char)accum ^ (unsigned char)*str ]
  225             ^ (unsigned char)(accum >> 8);
  226 
  227         max_hash_len--;
  228     }
  229 
  230 
  231     return accum;
  232 }
  233 
  234 // #define hash(h,s,a) (crc32str(a,s,h->max_hash_len) & (h->hash_mask))
  235 #define hash(h,s,a) crc32str(a,s,h->max_hash_len)
  236 
  237 /* This must be called with the lock held. */
  238 static SPF_dns_cache_bucket_t *
  239 SPF_dns_cache_bucket_find(SPF_dns_cache_config_t *spfhook,
  240                 const char *domain, ns_type rr_type, int idx)
  241 {
  242     SPF_dns_cache_bucket_t  *bucket;
  243     SPF_dns_cache_bucket_t  *prev;
  244     SPF_dns_rr_t            *rr;
  245     time_t                   now;
  246 
  247     bucket = spfhook->cache[idx];
  248     prev = NULL;
  249     time(&now);
  250 
  251     while (bucket != NULL) {
  252         rr = bucket->rr;
  253 
  254         if (rr->utc_ttl < now) {
  255             /* Unlink the bucket. */
  256             if (prev != NULL)
  257                 prev->next = bucket->next;
  258             else
  259                 spfhook->cache[idx] = bucket->next;
  260             /* Free the bucket. */
  261             if (bucket->rr)
  262                 SPF_dns_rr_free(bucket->rr);
  263             free(bucket);
  264             /* Set iterator back one step. */
  265             bucket = prev;  /* Might be NULL */
  266         }
  267         else if (rr->rr_type != rr_type) {
  268             /* Types differ */
  269         }
  270         else if (strcmp(rr->domain, domain) != 0) {
  271             /* Domains differ */
  272         }
  273         else {
  274             /* Move the bucket to the top of the chain. */
  275             if (prev != NULL) {
  276                 prev->next = bucket->next;
  277                 bucket->next = spfhook->cache[idx];
  278                 spfhook->cache[idx] = bucket;
  279             }
  280             return bucket;
  281         }
  282 
  283         prev = bucket;      /* Might be NULL */
  284         if (bucket == NULL) /* After an unlink */
  285             bucket = spfhook->cache[idx];
  286         else
  287             bucket = bucket->next;
  288     }
  289 
  290     return NULL;
  291 }
  292 
  293 /* This must be called with the lock held. */
  294 static SPF_errcode_t
  295 SPF_dns_cache_bucket_add(SPF_dns_cache_config_t *spfhook,
  296                 SPF_dns_rr_t *rr, int idx)
  297 {
  298     SPF_dns_cache_bucket_t  *bucket;
  299 
  300     bucket = (SPF_dns_cache_bucket_t *)
  301                 malloc(sizeof(SPF_dns_cache_bucket_t));
  302     if (! bucket)
  303         return SPF_E_NO_MEMORY;
  304     bucket->next = spfhook->cache[idx];
  305     spfhook->cache[idx] = bucket;
  306     bucket->rr = rr;
  307     return SPF_E_SUCCESS;
  308 }
  309 
  310 
  311 /**
  312  * Patches up an rr for insertion into the cache.
  313  */
  314 static SPF_errcode_t
  315 SPF_dns_cache_rr_fixup(SPF_dns_cache_config_t *spfhook,
  316                 SPF_dns_rr_t *cached_rr,
  317                 const char *domain, ns_type rr_type)
  318 {
  319     char            *p;
  320 
  321     /* make sure the RR has enough data to be useful for caching */
  322     if (cached_rr->rr_type == ns_t_any)
  323         cached_rr->rr_type = rr_type;
  324 
  325     /* XXX I'm still not sure about this bit. */
  326     if (cached_rr->domain == NULL || cached_rr->domain[0] != '\0') {
  327         char    *new_domain;
  328         size_t   new_len = strlen(domain) + 1;
  329 
  330         if (cached_rr->domain_buf_len < new_len) {
  331             new_domain = realloc(cached_rr->domain, new_len);
  332             if (new_domain == NULL)
  333                 return SPF_E_NO_MEMORY;
  334             cached_rr->domain = new_domain;
  335             cached_rr->domain_buf_len = new_len;
  336         }
  337         strcpy(cached_rr->domain, domain);
  338     }
  339 
  340     /* set up the ttl values */
  341     if ( cached_rr->ttl < spfhook->min_ttl )
  342         cached_rr->ttl = spfhook->min_ttl;
  343 
  344     if ( cached_rr->ttl < spfhook->txt_ttl
  345             && cached_rr->rr_type == ns_t_txt )
  346         cached_rr->ttl = spfhook->txt_ttl;
  347 
  348     if ( cached_rr->ttl < spfhook->err_ttl
  349             && cached_rr->herrno != NETDB_SUCCESS )
  350         cached_rr->ttl = spfhook->err_ttl;
  351 
  352     if ( cached_rr->ttl < spfhook->rdns_ttl ) {
  353         p = strstr( cached_rr->domain, ".arpa" );
  354         if ( p && p[ sizeof( ".arpa" )-1 ] == '\0' )
  355             cached_rr->ttl = spfhook->rdns_ttl;
  356     }
  357 
  358     cached_rr->utc_ttl = cached_rr->ttl + time(NULL);
  359 
  360     return SPF_E_SUCCESS;
  361 }
  362 
  363 
  364 /**
  365  * Can return NULL on out-of-memory condition.
  366  */
  367 static SPF_dns_rr_t *
  368 SPF_dns_cache_lookup(SPF_dns_server_t *spf_dns_server,
  369                 const char *domain, ns_type rr_type, int should_cache)
  370 {
  371     SPF_dns_cache_config_t  *spfhook;
  372     SPF_dns_cache_bucket_t  *bucket;
  373     SPF_dns_rr_t            *cached_rr;
  374     SPF_dns_rr_t            *rr;
  375     int                      idx;
  376 
  377     spfhook = SPF_voidp2spfhook(spf_dns_server->hook);
  378 
  379     /* max_hash_len and cache_size are constant, so this be done
  380      * outside the lock. */
  381     idx = hash(spfhook, domain, 0 /* spfhook->hash_mask+rr_type */);
  382     idx &= (spfhook->cache_size - 1);
  383 
  384     pthread_mutex_lock(&(spfhook->cache_lock));
  385 
  386     bucket = SPF_dns_cache_bucket_find(spfhook, domain, rr_type, idx);
  387     if (bucket != NULL) {
  388         if (bucket->rr != NULL) {
  389             if (SPF_dns_rr_dup(&rr, bucket->rr) == SPF_E_SUCCESS) {
  390                 pthread_mutex_unlock(&(spfhook->cache_lock));
  391                 return rr;
  392             }
  393             else if (rr != NULL) {
  394                 SPF_dns_rr_free(rr);    /* Within the lock. :-( */
  395             }
  396         }
  397     }
  398 
  399     /* Make sure we don't hang onto this outside the lock.
  400      * idx is presumably safe. */
  401     bucket = NULL;
  402 
  403     pthread_mutex_unlock(&(spfhook->cache_lock));
  404 
  405     if (!spf_dns_server->layer_below)
  406         return SPF_dns_rr_new_nxdomain(spf_dns_server, domain);
  407 
  408     rr = SPF_dns_lookup( spf_dns_server->layer_below,
  409                     domain, rr_type, should_cache );
  410     if (spfhook->conserve_cache && !should_cache)
  411         return rr;
  412 
  413     pthread_mutex_lock(&(spfhook->cache_lock));
  414 
  415     if (SPF_dns_rr_dup(&cached_rr, rr) == SPF_E_SUCCESS) {
  416         if (SPF_dns_cache_rr_fixup(spfhook, cached_rr, domain, rr_type) == SPF_E_SUCCESS){
  417             if (SPF_dns_cache_bucket_add(spfhook, cached_rr, idx) == SPF_E_SUCCESS) {
  418                 pthread_mutex_unlock(&(spfhook->cache_lock));
  419                 return rr;
  420             }
  421         }
  422     }
  423 
  424     pthread_mutex_unlock(&(spfhook->cache_lock));
  425 
  426     if (cached_rr)
  427         SPF_dns_rr_free(cached_rr);
  428 
  429     return rr;
  430 
  431 }
  432 
  433 
  434 static void
  435 SPF_dns_cache_free( SPF_dns_server_t *spf_dns_server )
  436 {
  437     SPF_dns_cache_config_t  *spfhook;
  438     SPF_dns_cache_bucket_t  *bucket;
  439     SPF_dns_cache_bucket_t  *prev;
  440     int                      i;
  441 
  442     SPF_ASSERT_NOTNULL(spf_dns_server);
  443 
  444     spfhook = SPF_voidp2spfhook( spf_dns_server->hook );
  445     if ( spfhook ) {
  446         pthread_mutex_lock(&(spfhook->cache_lock));
  447     
  448         if (spfhook->cache) {
  449             for( i = 0; i < spfhook->cache_size; i++ ) {
  450                 bucket = spfhook->cache[i];
  451                 while (bucket != NULL) {
  452                     prev = bucket;
  453                     bucket = bucket->next;
  454 
  455                     /* Free the bucket. */
  456                     if (prev->rr)
  457                         SPF_dns_rr_free(prev->rr);
  458                     free(prev);
  459                 }
  460             }
  461             free(spfhook->cache);
  462             spfhook->cache = NULL;
  463         }
  464 
  465         pthread_mutex_unlock(&(spfhook->cache_lock));
  466 
  467         /* 
  468          * There is a risk that something might grab the mutex
  469          * here and try to look things up and try to resolve
  470          * stuff from a mashed cache it might happen but that's
  471          * what you get for trying to simultaneously free and
  472          * use a resource destroy will then return EBUSY but
  473          * it'll probably segfault so there ain't much to be
  474          * done really.
  475          */
  476         pthread_mutex_destroy(&(spfhook->cache_lock));
  477 
  478         free(spfhook);
  479     }
  480 
  481     free(spf_dns_server);
  482 }
  483 
  484 
  485 
  486 SPF_dns_server_t *
  487 SPF_dns_cache_new(SPF_dns_server_t *layer_below,
  488                 const char *name, int debug, int cache_bits)
  489 {
  490     SPF_dns_server_t        *spf_dns_server;
  491     SPF_dns_cache_config_t  *spfhook;
  492 
  493     SPF_ASSERT_NOTNULL(layer_below);
  494 
  495     if ( cache_bits < 1 || cache_bits > 16 )
  496         SPF_error( "cache bits out of range (1..16)." );
  497 
  498 
  499     spf_dns_server = malloc(sizeof(SPF_dns_server_t));
  500     if (spf_dns_server == NULL)
  501         return NULL;
  502     memset(spf_dns_server, 0, sizeof(SPF_dns_server_t));
  503 
  504     spf_dns_server->hook = malloc(sizeof(SPF_dns_cache_config_t));
  505     if (spf_dns_server->hook == NULL) {
  506         free(spf_dns_server);
  507         return NULL;
  508     }
  509     memset(spf_dns_server->hook, 0, sizeof(SPF_dns_cache_config_t));
  510 
  511     if (name == NULL)
  512         name = "cache";
  513 
  514     spf_dns_server->destroy     = SPF_dns_cache_free;
  515     spf_dns_server->lookup      = SPF_dns_cache_lookup;
  516     spf_dns_server->get_spf     = NULL;
  517     spf_dns_server->get_exp     = NULL;
  518     spf_dns_server->add_cache   = NULL;
  519     spf_dns_server->layer_below = layer_below;
  520     spf_dns_server->name        = name;
  521     spf_dns_server->debug       = debug;
  522 
  523     spfhook = SPF_voidp2spfhook( spf_dns_server->hook );
  524 
  525     spfhook->cache_size = 1 << cache_bits;
  526     spfhook->hash_mask  = spfhook->cache_size - 1;
  527     spfhook->max_hash_len = cache_bits > 4 ? cache_bits * 2 : 8;
  528 
  529     spfhook->cache = calloc(spfhook->cache_size,
  530                                     sizeof(*spfhook->cache));
  531 
  532 #if 0
  533     spfhook->hit        = 0;
  534     spfhook->miss       = 0;
  535 #endif
  536 
  537     spfhook->min_ttl    = 30;
  538     spfhook->err_ttl    = 30*60;
  539     spfhook->txt_ttl    = 30*60;
  540     spfhook->rdns_ttl   = 30*60;
  541     spfhook->conserve_cache  = cache_bits < 12;
  542 
  543     if (spfhook->cache == NULL) {
  544         free(spfhook);
  545         free(spf_dns_server);
  546         return NULL;
  547     }
  548 
  549     pthread_mutex_init(&(spfhook->cache_lock),NULL);
  550 
  551     return spf_dns_server;
  552 }
  553 
  554 void
  555 SPF_dns_cache_set_ttl( SPF_dns_server_t *spf_dns_server,
  556                 time_t min_ttl, time_t err_ttl,
  557                 time_t txt_ttl, time_t rdns_ttl )
  558 {
  559     SPF_dns_cache_config_t *spfhook;
  560 
  561     SPF_ASSERT_NOTNULL(spf_dns_server);
  562 
  563     spfhook = SPF_voidp2spfhook( spf_dns_server->hook );
  564 
  565     if (spfhook != NULL) {
  566         pthread_mutex_lock(&(spfhook->cache_lock));
  567         spfhook->min_ttl  = min_ttl;
  568         spfhook->err_ttl  = err_ttl;
  569         spfhook->txt_ttl  = txt_ttl;
  570         spfhook->rdns_ttl = rdns_ttl;
  571         pthread_mutex_unlock(&(spfhook->cache_lock));
  572     }
  573 }
  574 
  575 
  576 void
  577 SPF_dns_set_conserve_cache( SPF_dns_server_t *spf_dns_server,
  578                 int conserve_cache )
  579 {
  580     SPF_dns_cache_config_t *spfhook;
  581 
  582     SPF_ASSERT_NOTNULL(spf_dns_server);
  583 
  584     spfhook = SPF_voidp2spfhook( spf_dns_server->hook );
  585     /* This is a boolean and it doesn't matter if it
  586      * changes suddenly, thus no lock. */
  587     if (spfhook != NULL)
  588         spfhook->conserve_cache = conserve_cache;
  589 }