void dns_compress_add(dns_compress_t *cctx, const dns_name_t *name, const dns_name_t *prefix, isc_uint16_t offset) { dns_name_t tname; unsigned int start; unsigned int n; unsigned int count; unsigned int hash; dns_compressnode_t *node; unsigned int length; unsigned int tlength; isc_uint16_t toffset; REQUIRE(VALID_CCTX(cctx)); REQUIRE(dns_name_isabsolute(name)); dns_name_init(&tname, NULL); n = dns_name_countlabels(name); count = dns_name_countlabels(prefix); if (dns_name_isabsolute(prefix)) count--; start = 0; length = name_length(name); while (count > 0) { if (offset >= 0x4000) break; dns_name_getlabelsequence(name, start, n, &tname); hash = dns_name_hash(&tname, ISC_FALSE) % DNS_COMPRESS_TABLESIZE; tlength = name_length(&tname); toffset = (isc_uint16_t)(offset + (length - tlength)); /* * Create a new node and add it. */ if (cctx->count < DNS_COMPRESS_INITIALNODES) node = &cctx->initialnodes[cctx->count]; else { node = isc_mem_get(cctx->mctx, sizeof(dns_compressnode_t)); if (node == NULL) return; } node->count = cctx->count++; node->offset = toffset; dns_name_toregion(&tname, &node->r); node->labels = (isc_uint8_t)dns_name_countlabels(&tname); node->next = cctx->table[hash]; cctx->table[hash] = node; start++; n--; count--; } }
/* * Find the longest match of name in the table. * If match is found return ISC_TRUE. prefix, suffix and offset are updated. * If no match is found return ISC_FALSE. */ isc_boolean_t dns_compress_findglobal(dns_compress_t *cctx, const dns_name_t *name, dns_name_t *prefix, isc_uint16_t *offset) { dns_name_t tname, nname; dns_compressnode_t *node = NULL; unsigned int labels, hash, n; REQUIRE(VALID_CCTX(cctx)); REQUIRE(dns_name_isabsolute(name) == ISC_TRUE); REQUIRE(offset != NULL); if (cctx->count == 0) return (ISC_FALSE); labels = dns_name_countlabels(name); INSIST(labels > 0); dns_name_init(&tname, NULL); dns_name_init(&nname, NULL); for (n = 0; n < labels - 1; n++) { dns_name_getlabelsequence(name, n, labels - n, &tname); hash = dns_name_hash(&tname, ISC_FALSE) % DNS_COMPRESS_TABLESIZE; for (node = cctx->table[hash]; node != NULL; node = node->next) { NODENAME(node, &nname); if ((cctx->allowed & DNS_COMPRESS_CASESENSITIVE) != 0) { if (dns_name_caseequal(&nname, &tname)) break; } else { if (dns_name_equal(&nname, &tname)) break; } } if (node != NULL) break; } /* * If node == NULL, we found no match at all. */ if (node == NULL) return (ISC_FALSE); if (n == 0) dns_name_reset(prefix); else dns_name_getlabelsequence(name, 0, n, prefix); *offset = node->offset; return (ISC_TRUE); }
void dns_badcache_flushname(dns_badcache_t *bc, dns_name_t *name) { dns_bcentry_t *bad, *prev, *next; isc_result_t result; isc_time_t now; unsigned int i; REQUIRE(VALID_BADCACHE(bc)); REQUIRE(name != NULL); LOCK(&bc->lock); result = isc_time_now(&now); if (result != ISC_R_SUCCESS) isc_time_settoepoch(&now); i = dns_name_hash(name, ISC_FALSE) % bc->size; prev = NULL; for (bad = bc->table[i]; bad != NULL; bad = next) { int n; next = bad->next; n = isc_time_compare(&bad->expire, &now); if (n < 0 || dns_name_equal(name, &bad->name)) { if (prev == NULL) bc->table[i] = bad->next; else prev->next = bad->next; isc_mem_put(bc->mctx, bad, sizeof(*bad) + bad->name.length); bc->count--; } else prev = bad; } UNLOCK(&bc->lock); }
isc_boolean_t dns_badcache_find(dns_badcache_t *bc, dns_name_t *name, dns_rdatatype_t type, isc_uint32_t *flagp, isc_time_t *now) { dns_bcentry_t *bad, *prev, *next; isc_boolean_t answer = ISC_FALSE; unsigned int i; REQUIRE(VALID_BADCACHE(bc)); REQUIRE(name != NULL); REQUIRE(now != NULL); LOCK(&bc->lock); /* * XXXMUKS: dns_name_equal() is expensive as it does a * octet-by-octet comparison, and it can be made better in two * ways here. First, lowercase the names (use * dns_name_downcase() instead of dns_name_copy() in * dns_badcache_add()) so that dns_name_caseequal() can be used * which the compiler will emit as SIMD instructions. Second, * don't put multiple copies of the same name in the chain (or * multiple names will have to be matched for equality), but use * name->link to store the type specific part. */ if (bc->count == 0) goto skip; i = dns_name_hash(name, ISC_FALSE) % bc->size; prev = NULL; for (bad = bc->table[i]; bad != NULL; bad = next) { next = bad->next; /* * Search the hash list. Clean out expired records as we go. */ if (isc_time_compare(&bad->expire, now) < 0) { if (prev != NULL) prev->next = bad->next; else bc->table[i] = bad->next; isc_mem_put(bc->mctx, bad, sizeof(*bad) + bad->name.length); bc->count--; continue; } if (bad->type == type && dns_name_equal(name, &bad->name)) { if (flagp != NULL) *flagp = bad->flags; answer = ISC_TRUE; break; } prev = bad; } skip: /* * Slow sweep to clean out stale records. */ i = bc->sweep++ % bc->size; bad = bc->table[i]; if (bad != NULL && isc_time_compare(&bad->expire, now) < 0) { bc->table[i] = bad->next; isc_mem_put(bc->mctx, bad, sizeof(*bad) + bad->name.length); bc->count--; } UNLOCK(&bc->lock); return (answer); }
void dns_badcache_add(dns_badcache_t *bc, dns_name_t *name, dns_rdatatype_t type, isc_boolean_t update, isc_uint32_t flags, isc_time_t *expire) { isc_result_t result; unsigned int i, hashval; dns_bcentry_t *bad, *prev, *next; isc_time_t now; REQUIRE(VALID_BADCACHE(bc)); REQUIRE(name != NULL); REQUIRE(expire != NULL); LOCK(&bc->lock); result = isc_time_now(&now); if (result != ISC_R_SUCCESS) isc_time_settoepoch(&now); hashval = dns_name_hash(name, ISC_FALSE); i = hashval % bc->size; prev = NULL; for (bad = bc->table[i]; bad != NULL; bad = next) { next = bad->next; if (bad->type == type && dns_name_equal(name, &bad->name)) { if (update) { bad->expire = *expire; bad->flags = flags; } break; } if (isc_time_compare(&bad->expire, &now) < 0) { if (prev == NULL) bc->table[i] = bad->next; else prev->next = bad->next; isc_mem_put(bc->mctx, bad, sizeof(*bad) + bad->name.length); bc->count--; } else prev = bad; } if (bad == NULL) { isc_buffer_t buffer; bad = isc_mem_get(bc->mctx, sizeof(*bad) + name->length); if (bad == NULL) goto cleanup; bad->type = type; bad->hashval = hashval; bad->expire = *expire; bad->flags = flags; isc_buffer_init(&buffer, bad + 1, name->length); dns_name_init(&bad->name, NULL); dns_name_copy(name, &bad->name, &buffer); bad->next = bc->table[i]; bc->table[i] = bad; bc->count++; if (bc->count > bc->size * 8) badcache_resize(bc, &now, ISC_TRUE); if (bc->count < bc->size * 2 && bc->size > bc->minsize) badcache_resize(bc, &now, ISC_FALSE); } else bad->expire = *expire; cleanup: UNLOCK(&bc->lock); }