00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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
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
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 }