badcache.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2014  Internet Systems Consortium, Inc. ("ISC")
00003  *
00004  * Permission to use, copy, modify, and/or distribute this software for any
00005  * purpose with or without fee is hereby granted, provided that the above
00006  * copyright notice and this permission notice appear in all copies.
00007  *
00008  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
00009  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
00010  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
00011  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
00012  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
00013  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
00014  * PERFORMANCE OF THIS SOFTWARE.
00015  */
00016 
00017 /*! \file */
00018 
00019 #include <config.h>
00020 
00021 #include <isc/buffer.h>
00022 #include <isc/log.h>
00023 #include <isc/hash.h>
00024 #include <isc/mem.h>
00025 #include <isc/mutex.h>
00026 #include <isc/platform.h>
00027 #include <isc/print.h>
00028 #include <isc/string.h>
00029 #include <isc/time.h>
00030 #include <isc/util.h>
00031 
00032 #include <dns/badcache.h>
00033 #include <dns/name.h>
00034 #include <dns/rdatatype.h>
00035 #include <dns/types.h>
00036 
00037 typedef struct dns_bcentry dns_bcentry_t;
00038 
00039 struct dns_badcache {
00040         unsigned int            magic;
00041         isc_mutex_t             lock;
00042         isc_mem_t               *mctx;
00043 
00044         dns_bcentry_t           **table;
00045         unsigned int            count;
00046         unsigned int            minsize;
00047         unsigned int            size;
00048         unsigned int            sweep;
00049 };
00050 
00051 #define BADCACHE_MAGIC                   ISC_MAGIC('B', 'd', 'C', 'a')
00052 #define VALID_BADCACHE(m)                ISC_MAGIC_VALID(m, BADCACHE_MAGIC)
00053 
00054 struct dns_bcentry {
00055         dns_bcentry_t *         next;
00056         dns_rdatatype_t         type;
00057         isc_time_t              expire;
00058         isc_uint32_t            flags;
00059         unsigned int            hashval;
00060         dns_name_t              name;
00061 };
00062 
00063 static isc_result_t
00064 badcache_resize(dns_badcache_t *bc, isc_time_t *now, isc_boolean_t grow);
00065 
00066 isc_result_t
00067 dns_badcache_init(isc_mem_t *mctx, unsigned int size, dns_badcache_t **bcp) {
00068         isc_result_t result;
00069         dns_badcache_t *bc = NULL;
00070 
00071         REQUIRE(bcp != NULL && *bcp == NULL);
00072         REQUIRE(mctx != NULL);
00073 
00074         bc = isc_mem_get(mctx, sizeof(dns_badcache_t));
00075         if (bc == NULL)
00076                 return (ISC_R_NOMEMORY);
00077         memset(bc, 0, sizeof(dns_badcache_t));
00078 
00079         isc_mem_attach(mctx, &bc->mctx);
00080         result = isc_mutex_init(&bc->lock);
00081         if (result != ISC_R_SUCCESS)
00082                 goto cleanup;
00083 
00084         bc->table = isc_mem_get(bc->mctx, sizeof(*bc->table) * size);
00085         if (bc->table == NULL) {
00086                 result = ISC_R_NOMEMORY;
00087                 goto destroy_lock;
00088         }
00089 
00090         bc->size = bc->minsize = size;
00091         memset(bc->table, 0, bc->size * sizeof(dns_bcentry_t *));
00092 
00093         bc->count = 0;
00094         bc->sweep = 0;
00095         bc->magic = BADCACHE_MAGIC;
00096 
00097         *bcp = bc;
00098         return (ISC_R_SUCCESS);
00099 
00100  destroy_lock:
00101         DESTROYLOCK(&bc->lock);
00102  cleanup:
00103         isc_mem_putanddetach(&bc->mctx, bc, sizeof(dns_badcache_t));
00104         return (result);
00105 }
00106 
00107 void
00108 dns_badcache_destroy(dns_badcache_t **bcp) {
00109         dns_badcache_t *bc;
00110 
00111         REQUIRE(bcp != NULL && *bcp != NULL);
00112         bc = *bcp;
00113 
00114         dns_badcache_flush(bc);
00115 
00116         bc->magic = 0;
00117         DESTROYLOCK(&bc->lock);
00118         isc_mem_put(bc->mctx, bc->table, sizeof(dns_bcentry_t *) * bc->size);
00119         isc_mem_putanddetach(&bc->mctx, bc, sizeof(dns_badcache_t));
00120         *bcp = NULL;
00121 }
00122 
00123 static isc_result_t
00124 badcache_resize(dns_badcache_t *bc, isc_time_t *now, isc_boolean_t grow) {
00125         dns_bcentry_t **new, *bad, *next;
00126         unsigned int newsize, i;
00127 
00128         if (grow)
00129                 newsize = bc->size * 2 + 1;
00130         else
00131                 newsize = (bc->size - 1) / 2;
00132 
00133         new = isc_mem_get(bc->mctx, sizeof(dns_bcentry_t *) * newsize);
00134         if (new == NULL)
00135                 return (ISC_R_NOMEMORY);
00136         memset(new, 0, sizeof(dns_bcentry_t *) * newsize);
00137 
00138         for (i = 0; bc->count > 0 && i < bc->size; i++) {
00139                 for (bad = bc->table[i]; bad != NULL; bad = next) {
00140                         next = bad->next;
00141                         if (isc_time_compare(&bad->expire, now) < 0) {
00142                                 isc_mem_put(bc->mctx, bad,
00143                                             sizeof(*bad) + bad->name.length);
00144                                 bc->count--;
00145                         } else {
00146                                 bad->next = new[bad->hashval % newsize];
00147                                 new[bad->hashval % newsize] = bad;
00148                         }
00149                 }
00150                 bc->table[i] = NULL;
00151         }
00152 
00153         isc_mem_put(bc->mctx, bc->table, sizeof(*bc->table) * bc->size);
00154         bc->size = newsize;
00155         bc->table = new;
00156 
00157         return (ISC_R_SUCCESS);
00158 }
00159 
00160 void
00161 dns_badcache_add(dns_badcache_t *bc, dns_name_t *name,
00162                  dns_rdatatype_t type, isc_boolean_t update,
00163                  isc_uint32_t flags, isc_time_t *expire)
00164 {
00165         isc_result_t result;
00166         unsigned int i, hashval;
00167         dns_bcentry_t *bad, *prev, *next;
00168         isc_time_t now;
00169 
00170         REQUIRE(VALID_BADCACHE(bc));
00171         REQUIRE(name != NULL);
00172         REQUIRE(expire != NULL);
00173 
00174         LOCK(&bc->lock);
00175 
00176         result = isc_time_now(&now);
00177         if (result != ISC_R_SUCCESS)
00178                 isc_time_settoepoch(&now);
00179 
00180         hashval = dns_name_hash(name, ISC_FALSE);
00181         i = hashval % bc->size;
00182         prev = NULL;
00183         for (bad = bc->table[i]; bad != NULL; bad = next) {
00184                 next = bad->next;
00185                 if (bad->type == type && dns_name_equal(name, &bad->name)) {
00186                         if (update) {
00187                                 bad->expire = *expire;
00188                                 bad->flags = flags;
00189                         }
00190                         break;
00191                 }
00192                 if (isc_time_compare(&bad->expire, &now) < 0) {
00193                         if (prev == NULL)
00194                                 bc->table[i] = bad->next;
00195                         else
00196                                 prev->next = bad->next;
00197                         isc_mem_put(bc->mctx, bad,
00198                                     sizeof(*bad) + bad->name.length);
00199                         bc->count--;
00200                 } else
00201                         prev = bad;
00202         }
00203 
00204         if (bad == NULL) {
00205                 isc_buffer_t buffer;
00206                 bad = isc_mem_get(bc->mctx, sizeof(*bad) + name->length);
00207                 if (bad == NULL)
00208                         goto cleanup;
00209                 bad->type = type;
00210                 bad->hashval = hashval;
00211                 bad->expire = *expire;
00212                 bad->flags = flags;
00213                 isc_buffer_init(&buffer, bad + 1, name->length);
00214                 dns_name_init(&bad->name, NULL);
00215                 dns_name_copy(name, &bad->name, &buffer);
00216                 bad->next = bc->table[i];
00217                 bc->table[i] = bad;
00218                 bc->count++;
00219                 if (bc->count > bc->size * 8)
00220                         badcache_resize(bc, &now, ISC_TRUE);
00221                 if (bc->count < bc->size * 2 && bc->size > bc->minsize)
00222                         badcache_resize(bc, &now, ISC_FALSE);
00223         } else
00224                 bad->expire = *expire;
00225 
00226  cleanup:
00227         UNLOCK(&bc->lock);
00228 }
00229 
00230 isc_boolean_t
00231 dns_badcache_find(dns_badcache_t *bc, dns_name_t *name,
00232                   dns_rdatatype_t type, isc_uint32_t *flagp,
00233                   isc_time_t *now)
00234 {
00235         dns_bcentry_t *bad, *prev, *next;
00236         isc_boolean_t answer = ISC_FALSE;
00237         unsigned int i;
00238 
00239         REQUIRE(VALID_BADCACHE(bc));
00240         REQUIRE(name != NULL);
00241         REQUIRE(now != NULL);
00242 
00243         LOCK(&bc->lock);
00244 
00245         if (bc->count == 0)
00246                 goto skip;
00247 
00248         i = dns_name_hash(name, ISC_FALSE) % bc->size;
00249         prev = NULL;
00250         for (bad = bc->table[i]; bad != NULL; bad = next) {
00251                 next = bad->next;
00252                 /*
00253                  * Search the hash list. Clean out expired records as we go.
00254                  */
00255                 if (isc_time_compare(&bad->expire, now) < 0) {
00256                         if (prev != NULL)
00257                                 prev->next = bad->next;
00258                         else
00259                                 bc->table[i] = bad->next;
00260 
00261                         isc_mem_put(bc->mctx, bad, sizeof(*bad) +
00262                                     bad->name.length);
00263                         bc->count--;
00264                         continue;
00265                 }
00266                 if (bad->type == type && dns_name_equal(name, &bad->name)) {
00267                         if (flagp != NULL)
00268                                 *flagp = bad->flags;
00269                         answer = ISC_TRUE;
00270                         break;
00271                 }
00272                 prev = bad;
00273         }
00274  skip:
00275 
00276         /*
00277          * Slow sweep to clean out stale records.
00278          */
00279         i = bc->sweep++ % bc->size;
00280         bad = bc->table[i];
00281         if (bad != NULL && isc_time_compare(&bad->expire, now) < 0) {
00282                 bc->table[i] = bad->next;
00283                 isc_mem_put(bc->mctx, bad, sizeof(*bad) + bad->name.length);
00284                 bc->count--;
00285         }
00286 
00287         UNLOCK(&bc->lock);
00288         return (answer);
00289 }
00290 
00291 void
00292 dns_badcache_flush(dns_badcache_t *bc) {
00293         dns_bcentry_t *entry, *next;
00294         unsigned int i;
00295 
00296         REQUIRE(VALID_BADCACHE(bc));
00297 
00298         for (i = 0; bc->count > 0 && i < bc->size; i++) {
00299                 for (entry = bc->table[i]; entry != NULL; entry = next) {
00300                         next = entry->next;
00301                         isc_mem_put(bc->mctx, entry, sizeof(*entry) +
00302                                     entry->name.length);
00303                         bc->count--;
00304                 }
00305                 bc->table[i] = NULL;
00306         }
00307 }
00308 
00309 void
00310 dns_badcache_flushname(dns_badcache_t *bc, dns_name_t *name) {
00311         dns_bcentry_t *bad, *prev, *next;
00312         isc_result_t result;
00313         isc_time_t now;
00314         unsigned int i;
00315 
00316         REQUIRE(VALID_BADCACHE(bc));
00317         REQUIRE(name != NULL);
00318 
00319         LOCK(&bc->lock);
00320 
00321         result = isc_time_now(&now);
00322         if (result != ISC_R_SUCCESS)
00323                 isc_time_settoepoch(&now);
00324         i = dns_name_hash(name, ISC_FALSE) % bc->size;
00325         prev = NULL;
00326         for (bad = bc->table[i]; bad != NULL; bad = next) {
00327                 int n;
00328                 next = bad->next;
00329                 n = isc_time_compare(&bad->expire, &now);
00330                 if (n < 0 || dns_name_equal(name, &bad->name)) {
00331                         if (prev == NULL)
00332                                 bc->table[i] = bad->next;
00333                         else
00334                                 prev->next = bad->next;
00335 
00336                         isc_mem_put(bc->mctx, bad, sizeof(*bad) +
00337                                     bad->name.length);
00338                         bc->count--;
00339                 } else
00340                         prev = bad;
00341         }
00342 
00343         UNLOCK(&bc->lock);
00344 }
00345 
00346 void
00347 dns_badcache_flushtree(dns_badcache_t *bc, dns_name_t *name) {
00348         dns_bcentry_t *bad, *prev, *next;
00349         unsigned int i;
00350         int n;
00351         isc_time_t now;
00352         isc_result_t result;
00353 
00354         REQUIRE(VALID_BADCACHE(bc));
00355         REQUIRE(name != NULL);
00356 
00357         LOCK(&bc->lock);
00358 
00359         result = isc_time_now(&now);
00360         if (result != ISC_R_SUCCESS)
00361                 isc_time_settoepoch(&now);
00362 
00363         for (i = 0; bc->count > 0 && i < bc->size; i++) {
00364                 prev = NULL;
00365                 for (bad = bc->table[i]; bad != NULL; bad = next) {
00366                         next = bad->next;
00367                         n = isc_time_compare(&bad->expire, &now);
00368                         if (n < 0 || dns_name_issubdomain(&bad->name, name)) {
00369                                 if (prev == NULL)
00370                                         bc->table[i] = bad->next;
00371                                 else
00372                                         prev->next = bad->next;
00373 
00374                                 isc_mem_put(bc->mctx, bad, sizeof(*bad) +
00375                                             bad->name.length);
00376                                 bc->count--;
00377                         } else
00378                                 prev = bad;
00379                 }
00380         }
00381 
00382         UNLOCK(&bc->lock);
00383 }
00384 
00385 
00386 void
00387 dns_badcache_print(dns_badcache_t *bc, const char *cachename, FILE *fp) {
00388         char namebuf[DNS_NAME_FORMATSIZE];
00389         char typebuf[DNS_RDATATYPE_FORMATSIZE];
00390         dns_bcentry_t *bad, *next, *prev;
00391         isc_time_t now;
00392         unsigned int i;
00393         isc_uint64_t t;
00394 
00395         REQUIRE(VALID_BADCACHE(bc));
00396         REQUIRE(cachename != NULL);
00397         REQUIRE(fp != NULL);
00398 
00399         LOCK(&bc->lock);
00400         fprintf(fp, ";\n; %s\n;\n", cachename);
00401 
00402         TIME_NOW(&now);
00403         for (i = 0; bc->count > 0 && i < bc->size; i++) {
00404                 prev = NULL;
00405                 for (bad = bc->table[i]; bad != NULL; bad = next) {
00406                         next = bad->next;
00407                         if (isc_time_compare(&bad->expire, &now) < 0) {
00408                                 if (prev != NULL)
00409                                         prev->next = bad->next;
00410                                 else
00411                                         bc->table[i] = bad->next;
00412 
00413                                 isc_mem_put(bc->mctx, bad, sizeof(*bad) +
00414                                             bad->name.length);
00415                                 bc->count--;
00416                                 continue;
00417                         }
00418                         prev = bad;
00419                         dns_name_format(&bad->name, namebuf, sizeof(namebuf));
00420                         dns_rdatatype_format(bad->type, typebuf,
00421                                              sizeof(typebuf));
00422                         t = isc_time_microdiff(&bad->expire, &now);
00423                         t /= 1000;
00424                         fprintf(fp, "; %s/%s [ttl "
00425                                 "%" ISC_PLATFORM_QUADFORMAT "u]\n",
00426                                 namebuf, typebuf, t);
00427                 }
00428         }
00429         UNLOCK(&bc->lock);
00430 }

Generated on Tue Apr 28 17:40:55 2015 by Doxygen 1.5.4 for BIND9 Internals 9.11.0pre-alpha