00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include "spf_sys_config.h"
00017
00018 #ifdef STDC_HEADERS
00019 # include <stdio.h>
00020 # include <stdlib.h>
00021 #endif
00022
00023
00024 #ifdef HAVE_STRING_H
00025 # include <string.h>
00026 #else
00027 # ifdef HAVE_STRINGS_H
00028 # include <strings.h>
00029 # endif
00030 #endif
00031
00032 #ifdef HAVE_MEMORY_H
00033 #include <memory.h>
00034 #endif
00035 #if TIME_WITH_SYS_TIME
00036 # include <sys/time.h>
00037 # include <time.h>
00038 #else
00039 # if HAVE_SYS_TIME_H
00040 # include <sys/time.h>
00041 # else
00042 # include <time.h>
00043 # endif
00044 #endif
00045
00046 #ifdef HAVE_NETDB_H
00047 # include <netdb.h>
00048 #endif
00049
00050 #ifdef HAVE_PTHREAD_H
00051 # include <pthread.h>
00052 #endif
00053
00054 #include "spf.h"
00055 #include "spf_dns.h"
00056 #include "spf_internal.h"
00057 #include "spf_dns_internal.h"
00058 #include "spf_dns_cache.h"
00059
00060
00082 typedef
00083 struct _SPF_dns_cache_bucket_t {
00084 struct _SPF_dns_cache_bucket_t *next;
00085 SPF_dns_rr_t *rr;
00086 } SPF_dns_cache_bucket_t;
00087
00088 typedef struct
00089 {
00090 SPF_dns_cache_bucket_t **cache;
00091 int cache_size;
00092 pthread_mutex_t cache_lock;
00093
00094 int hash_mask;
00095 int max_hash_len;
00096
00097 #if 0
00098 int hit;
00099 int miss;
00100 #endif
00101
00102 time_t min_ttl;
00103 time_t err_ttl;
00104 time_t txt_ttl;
00105 time_t rdns_ttl;
00106
00107 int conserve_cache;
00108
00109
00110 } SPF_dns_cache_config_t;
00111
00112
00113 static inline SPF_dns_cache_config_t *SPF_voidp2spfhook( void *hook )
00114 { return (SPF_dns_cache_config_t *)hook; }
00115 static inline void *SPF_spfhook2voidp( SPF_dns_cache_config_t *spfhook )
00116 { return (void *)spfhook; }
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162 const unsigned int crc_32_tab[256] = {
00163 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
00164 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
00165 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
00166 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
00167 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
00168 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
00169 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
00170 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
00171 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
00172 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
00173 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
00174 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
00175 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
00176 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
00177 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
00178 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
00179 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
00180 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
00181 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
00182 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
00183 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
00184 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
00185 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
00186 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
00187 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
00188 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
00189 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
00190 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
00191 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
00192 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
00193 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
00194 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
00195 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
00196 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
00197 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
00198 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
00199 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
00200 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
00201 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
00202 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
00203 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
00204 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
00205 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
00206 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
00207 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
00208 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
00209 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
00210 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
00211 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
00212 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
00213 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
00214 0x2d02ef8dL
00215 };
00216
00217 static inline int
00218 crc32str(unsigned int accum, const char *str, int max_hash_len)
00219 {
00220 for( ; *str != '\0' && max_hash_len > 0; str++ ) {
00221 if ( *str == '.' )
00222 continue;
00223
00224 accum = crc_32_tab[ (unsigned char)accum ^ (unsigned char)*str ]
00225 ^ (unsigned char)(accum >> 8);
00226
00227 max_hash_len--;
00228 }
00229
00230
00231 return accum;
00232 }
00233
00234
00235 #define hash(h,s,a) crc32str(a,s,h->max_hash_len)
00236
00237
00238 static SPF_dns_cache_bucket_t *
00239 SPF_dns_cache_bucket_find(SPF_dns_cache_config_t *spfhook,
00240 const char *domain, ns_type rr_type, int idx)
00241 {
00242 SPF_dns_cache_bucket_t *bucket;
00243 SPF_dns_cache_bucket_t *prev;
00244 SPF_dns_rr_t *rr;
00245 time_t now;
00246
00247 bucket = spfhook->cache[idx];
00248 prev = NULL;
00249 time(&now);
00250
00251 while (bucket != NULL) {
00252 rr = bucket->rr;
00253
00254 if (rr->utc_ttl < now) {
00255
00256 if (prev != NULL)
00257 prev->next = bucket->next;
00258 else
00259 spfhook->cache[idx] = bucket->next;
00260
00261 if (bucket->rr)
00262 SPF_dns_rr_free(bucket->rr);
00263 free(bucket);
00264
00265 bucket = prev;
00266 }
00267 else if (rr->rr_type != rr_type) {
00268
00269 }
00270 else if (strcmp(rr->domain, domain) != 0) {
00271
00272 }
00273 else {
00274
00275 if (prev != NULL) {
00276 prev->next = bucket->next;
00277 bucket->next = spfhook->cache[idx];
00278 spfhook->cache[idx] = bucket;
00279 }
00280 return bucket;
00281 }
00282
00283 prev = bucket;
00284 if (bucket == NULL)
00285 bucket = spfhook->cache[idx];
00286 else
00287 bucket = bucket->next;
00288 }
00289
00290 return NULL;
00291 }
00292
00293
00294 static SPF_errcode_t
00295 SPF_dns_cache_bucket_add(SPF_dns_cache_config_t *spfhook,
00296 SPF_dns_rr_t *rr, int idx)
00297 {
00298 SPF_dns_cache_bucket_t *bucket;
00299
00300 bucket = (SPF_dns_cache_bucket_t *)
00301 malloc(sizeof(SPF_dns_cache_bucket_t));
00302 if (! bucket)
00303 return SPF_E_NO_MEMORY;
00304 bucket->next = spfhook->cache[idx];
00305 spfhook->cache[idx] = bucket;
00306 bucket->rr = rr;
00307 return SPF_E_SUCCESS;
00308 }
00309
00310
00314 static SPF_errcode_t
00315 SPF_dns_cache_rr_fixup(SPF_dns_cache_config_t *spfhook,
00316 SPF_dns_rr_t *cached_rr,
00317 const char *domain, ns_type rr_type)
00318 {
00319 char *p;
00320
00321
00322 if (cached_rr->rr_type == ns_t_any)
00323 cached_rr->rr_type = rr_type;
00324
00325
00326 if (cached_rr->domain == NULL || cached_rr->domain[0] != '\0') {
00327 char *new_domain;
00328 size_t new_len = strlen(domain) + 1;
00329
00330 if (cached_rr->domain_buf_len < new_len) {
00331 new_domain = realloc(cached_rr->domain, new_len);
00332 if (new_domain == NULL)
00333 return SPF_E_NO_MEMORY;
00334 cached_rr->domain = new_domain;
00335 cached_rr->domain_buf_len = new_len;
00336 }
00337 strcpy(cached_rr->domain, domain);
00338 }
00339
00340
00341 if ( cached_rr->ttl < spfhook->min_ttl )
00342 cached_rr->ttl = spfhook->min_ttl;
00343
00344 if ( cached_rr->ttl < spfhook->txt_ttl
00345 && cached_rr->rr_type == ns_t_txt )
00346 cached_rr->ttl = spfhook->txt_ttl;
00347
00348 if ( cached_rr->ttl < spfhook->err_ttl
00349 && cached_rr->herrno != NETDB_SUCCESS )
00350 cached_rr->ttl = spfhook->err_ttl;
00351
00352 if ( cached_rr->ttl < spfhook->rdns_ttl ) {
00353 p = strstr( cached_rr->domain, ".arpa" );
00354 if ( p && p[ sizeof( ".arpa" )-1 ] == '\0' )
00355 cached_rr->ttl = spfhook->rdns_ttl;
00356 }
00357
00358 cached_rr->utc_ttl = cached_rr->ttl + time(NULL);
00359
00360 return SPF_E_SUCCESS;
00361 }
00362
00363
00367 static SPF_dns_rr_t *
00368 SPF_dns_cache_lookup(SPF_dns_server_t *spf_dns_server,
00369 const char *domain, ns_type rr_type, int should_cache)
00370 {
00371 SPF_dns_cache_config_t *spfhook;
00372 SPF_dns_cache_bucket_t *bucket;
00373 SPF_dns_rr_t *cached_rr;
00374 SPF_dns_rr_t *rr;
00375 int idx;
00376
00377 spfhook = SPF_voidp2spfhook(spf_dns_server->hook);
00378
00379
00380
00381 idx = hash(spfhook, domain, 0 );
00382 idx &= (spfhook->cache_size - 1);
00383
00384 pthread_mutex_lock(&(spfhook->cache_lock));
00385
00386 bucket = SPF_dns_cache_bucket_find(spfhook, domain, rr_type, idx);
00387 if (bucket != NULL) {
00388 if (bucket->rr != NULL) {
00389 if (SPF_dns_rr_dup(&rr, bucket->rr) == SPF_E_SUCCESS) {
00390 pthread_mutex_unlock(&(spfhook->cache_lock));
00391 return rr;
00392 }
00393 else if (rr != NULL) {
00394 SPF_dns_rr_free(rr);
00395 }
00396 }
00397 }
00398
00399
00400
00401 bucket = NULL;
00402
00403 pthread_mutex_unlock(&(spfhook->cache_lock));
00404
00405 if (!spf_dns_server->layer_below)
00406 return SPF_dns_rr_new_nxdomain(spf_dns_server, domain);
00407
00408 rr = SPF_dns_lookup( spf_dns_server->layer_below,
00409 domain, rr_type, should_cache );
00410 if (spfhook->conserve_cache && !should_cache)
00411 return rr;
00412
00413 pthread_mutex_lock(&(spfhook->cache_lock));
00414
00415 if (SPF_dns_rr_dup(&cached_rr, rr) == SPF_E_SUCCESS) {
00416 if (SPF_dns_cache_rr_fixup(spfhook, cached_rr, domain, rr_type) == SPF_E_SUCCESS){
00417 if (SPF_dns_cache_bucket_add(spfhook, cached_rr, idx) == SPF_E_SUCCESS) {
00418 pthread_mutex_unlock(&(spfhook->cache_lock));
00419 return rr;
00420 }
00421 }
00422 }
00423
00424 pthread_mutex_unlock(&(spfhook->cache_lock));
00425
00426 if (cached_rr)
00427 SPF_dns_rr_free(cached_rr);
00428
00429 return rr;
00430
00431 }
00432
00433
00434 static void
00435 SPF_dns_cache_free( SPF_dns_server_t *spf_dns_server )
00436 {
00437 SPF_dns_cache_config_t *spfhook;
00438 SPF_dns_cache_bucket_t *bucket;
00439 SPF_dns_cache_bucket_t *prev;
00440 int i;
00441
00442 SPF_ASSERT_NOTNULL(spf_dns_server);
00443
00444 spfhook = SPF_voidp2spfhook( spf_dns_server->hook );
00445 if ( spfhook ) {
00446 pthread_mutex_lock(&(spfhook->cache_lock));
00447
00448 if (spfhook->cache) {
00449 for( i = 0; i < spfhook->cache_size; i++ ) {
00450 bucket = spfhook->cache[i];
00451 while (bucket != NULL) {
00452 prev = bucket;
00453 bucket = bucket->next;
00454
00455
00456 if (prev->rr)
00457 SPF_dns_rr_free(prev->rr);
00458 free(prev);
00459 }
00460 }
00461 free(spfhook->cache);
00462 spfhook->cache = NULL;
00463 }
00464
00465 pthread_mutex_unlock(&(spfhook->cache_lock));
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476 pthread_mutex_destroy(&(spfhook->cache_lock));
00477
00478 free(spfhook);
00479 }
00480
00481 free(spf_dns_server);
00482 }
00483
00484
00485
00486 SPF_dns_server_t *
00487 SPF_dns_cache_new(SPF_dns_server_t *layer_below,
00488 const char *name, int debug, int cache_bits)
00489 {
00490 SPF_dns_server_t *spf_dns_server;
00491 SPF_dns_cache_config_t *spfhook;
00492
00493 SPF_ASSERT_NOTNULL(layer_below);
00494
00495 if ( cache_bits < 1 || cache_bits > 16 )
00496 SPF_error( "cache bits out of range (1..16)." );
00497
00498
00499 spf_dns_server = malloc(sizeof(SPF_dns_server_t));
00500 if (spf_dns_server == NULL)
00501 return NULL;
00502 memset(spf_dns_server, 0, sizeof(SPF_dns_server_t));
00503
00504 spf_dns_server->hook = malloc(sizeof(SPF_dns_cache_config_t));
00505 if (spf_dns_server->hook == NULL) {
00506 free(spf_dns_server);
00507 return NULL;
00508 }
00509 memset(spf_dns_server->hook, 0, sizeof(SPF_dns_cache_config_t));
00510
00511 if (name == NULL)
00512 name = "cache";
00513
00514 spf_dns_server->destroy = SPF_dns_cache_free;
00515 spf_dns_server->lookup = SPF_dns_cache_lookup;
00516 spf_dns_server->get_spf = NULL;
00517 spf_dns_server->get_exp = NULL;
00518 spf_dns_server->add_cache = NULL;
00519 spf_dns_server->layer_below = layer_below;
00520 spf_dns_server->name = name;
00521 spf_dns_server->debug = debug;
00522
00523 spfhook = SPF_voidp2spfhook( spf_dns_server->hook );
00524
00525 spfhook->cache_size = 1 << cache_bits;
00526 spfhook->hash_mask = spfhook->cache_size - 1;
00527 spfhook->max_hash_len = cache_bits > 4 ? cache_bits * 2 : 8;
00528
00529 spfhook->cache = calloc(spfhook->cache_size,
00530 sizeof(*spfhook->cache));
00531
00532 #if 0
00533 spfhook->hit = 0;
00534 spfhook->miss = 0;
00535 #endif
00536
00537 spfhook->min_ttl = 30;
00538 spfhook->err_ttl = 30*60;
00539 spfhook->txt_ttl = 30*60;
00540 spfhook->rdns_ttl = 30*60;
00541 spfhook->conserve_cache = cache_bits < 12;
00542
00543 if (spfhook->cache == NULL) {
00544 free(spfhook);
00545 free(spf_dns_server);
00546 return NULL;
00547 }
00548
00549 pthread_mutex_init(&(spfhook->cache_lock),NULL);
00550
00551 return spf_dns_server;
00552 }
00553
00554 void
00555 SPF_dns_cache_set_ttl( SPF_dns_server_t *spf_dns_server,
00556 time_t min_ttl, time_t err_ttl,
00557 time_t txt_ttl, time_t rdns_ttl )
00558 {
00559 SPF_dns_cache_config_t *spfhook;
00560
00561 SPF_ASSERT_NOTNULL(spf_dns_server);
00562
00563 spfhook = SPF_voidp2spfhook( spf_dns_server->hook );
00564
00565 if (spfhook != NULL) {
00566 pthread_mutex_lock(&(spfhook->cache_lock));
00567 spfhook->min_ttl = min_ttl;
00568 spfhook->err_ttl = err_ttl;
00569 spfhook->txt_ttl = txt_ttl;
00570 spfhook->rdns_ttl = rdns_ttl;
00571 pthread_mutex_unlock(&(spfhook->cache_lock));
00572 }
00573 }
00574
00575
00576 void
00577 SPF_dns_set_conserve_cache( SPF_dns_server_t *spf_dns_server,
00578 int conserve_cache )
00579 {
00580 SPF_dns_cache_config_t *spfhook;
00581
00582 SPF_ASSERT_NOTNULL(spf_dns_server);
00583
00584 spfhook = SPF_voidp2spfhook( spf_dns_server->hook );
00585
00586
00587 if (spfhook != NULL)
00588 spfhook->conserve_cache = conserve_cache;
00589 }