diff options
| author | Internet Software Consortium, Inc <@isc.org> | 2013-08-14 06:35:21 -0600 |
|---|---|---|
| committer | Internet Software Consortium, Inc <@isc.org> | 2013-08-14 06:35:21 -0600 |
| commit | ebbc86ee1eae2231a10e23f4cda592085dbc7eef (patch) | |
| tree | 8e373dd37c3b0a9fb113ff78f7a15dd19f6c0911 /lib | |
| parent | 87c6fc212d37ddbeb388f8308377ae38de3061d9 (diff) | |
| download | bind9-ebbc86ee1eae2231a10e23f4cda592085dbc7eef.tar.gz | |
9.9.4b1
Diffstat (limited to 'lib')
36 files changed, 2073 insertions, 105 deletions
diff --git a/lib/bind9/api b/lib/bind9/api index a27437f4..1a861f8b 100644 --- a/lib/bind9/api +++ b/lib/bind9/api @@ -5,5 +5,5 @@ # 9.9: 90-109 # 9.9-sub: 130-139 LIBINTERFACE = 90 -LIBREVISION = 7 +LIBREVISION = 8 LIBAGE = 0 diff --git a/lib/bind9/check.c b/lib/bind9/check.c index 91f8bff1..61574e81 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -1801,8 +1801,9 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, obj = NULL; res1 = cfg_map_get(zoptions, "inline-signing", &obj); if ((tresult != ISC_R_SUCCESS && - (ztype == MASTERZONE || ztype == HINTZONE)) || - (ztype == SLAVEZONE && res1 == ISC_R_SUCCESS)) { + (ztype == MASTERZONE || ztype == HINTZONE || + (ztype == SLAVEZONE && res1 == ISC_R_SUCCESS && + cfg_obj_asboolean(obj))))) { cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR, "zone '%s': missing 'file' entry", znamestr); diff --git a/lib/dns/Makefile.in b/lib/dns/Makefile.in index b712ab1c..a3e3e9a4 100644 --- a/lib/dns/Makefile.in +++ b/lib/dns/Makefile.in @@ -55,6 +55,8 @@ DSTOBJS = @DST_EXTRA_OBJS@ @OPENSSLLINKOBJS@ \ dst_api.@O@ dst_lib.@O@ dst_parse.@O@ dst_result.@O@ \ gssapi_link.@O@ gssapictx.@O@ hmac_link.@O@ key.@O@ +RRLOBJS = rrl.@O@ + # Alphabetically DNSOBJS = acache.@O@ acl.@O@ adb.@O@ byaddr.@O@ \ cache.@O@ callbacks.@O@ clientinfo.@O@ compress.@O@ \ @@ -67,14 +69,14 @@ DNSOBJS = acache.@O@ acl.@O@ adb.@O@ byaddr.@O@ \ portlist.@O@ private.@O@ \ rbt.@O@ rbtdb.@O@ rbtdb64.@O@ rcode.@O@ rdata.@O@ \ rdatalist.@O@ rdataset.@O@ rdatasetiter.@O@ rdataslab.@O@ \ - request.@O@ resolver.@O@ result.@O@ rootns.@O@ rpz.@O@ \ - rriterator.@O@ sdb.@O@ \ + request.@O@ resolver.@O@ result.@O@ rootns.@O@ \ + rpz.@O@ rriterator.@O@ sdb.@O@ \ sdlz.@O@ soa.@O@ ssu.@O@ ssu_external.@O@ \ stats.@O@ tcpmsg.@O@ time.@O@ timer.@O@ tkey.@O@ \ tsec.@O@ tsig.@O@ ttl.@O@ update.@O@ validator.@O@ \ version.@O@ view.@O@ xfrin.@O@ zone.@O@ zonekey.@O@ zt.@O@ -OBJS= ${DNSOBJS} ${OTHEROBJS} ${DSTOBJS} +OBJS= ${DNSOBJS} ${OTHEROBJS} ${DSTOBJS} @RRLLINKOBJS@ # Alphabetically OPENSSLGOSTLINKSRCS = opensslgost_link.c @@ -101,7 +103,9 @@ DNSSRCS = acache.c acl.c adb.c byaddr.c \ tsec.c tsig.c ttl.c update.c validator.c \ version.c view.c xfrin.c zone.c zonekey.c zt.c ${OTHERSRCS} -SRCS = ${DSTSRCS} ${DNSSRCS} +RRLSRCS = rrl.c + +SRCS = ${DSTSRCS} ${DNSSRCS} @RRLLINKSRCS@ SUBDIRS = include TARGETS = include/dns/enumtype.h include/dns/enumclass.h \ diff --git a/lib/dns/acache.c b/lib/dns/acache.c index 6df9b983..d3d28f85 100644 --- a/lib/dns/acache.c +++ b/lib/dns/acache.c @@ -1669,13 +1669,14 @@ dns_acache_cancelentry(dns_acacheentry_t *entry) { REQUIRE(DNS_ACACHEENTRY_VALID(entry)); acache = entry->acache; - callback_active = ISC_TF(entry->cbarg != NULL); INSIST(DNS_ACACHE_VALID(entry->acache)); LOCK(&acache->lock); ACACHE_LOCK(&acache->entrylocks[entry->locknum], isc_rwlocktype_write); + callback_active = ISC_TF(entry->cbarg != NULL); + /* * Release dependencies stored in this entry as much as possible. * The main link cannot be released, since the acache object has diff --git a/lib/dns/api b/lib/dns/api index a8881101..09c345a9 100644 --- a/lib/dns/api +++ b/lib/dns/api @@ -4,6 +4,6 @@ # 9.8: 80-89, 120-129 # 9.9: 90-109 # 9.9-sub: 130-139 -LIBINTERFACE = 99 -LIBREVISION = 1 +LIBINTERFACE = 100 +LIBREVISION = 0 LIBAGE = 0 diff --git a/lib/dns/client.c b/lib/dns/client.c index fc551cf9..e9e8bde2 100644 --- a/lib/dns/client.c +++ b/lib/dns/client.c @@ -1094,11 +1094,23 @@ client_resfind(resctx_t *rctx, dns_fetchevent_t *event) { UNLOCK(&rctx->lock); } + +static void +suspend(isc_task_t *task, isc_event_t *event) { + isc_appctx_t *actx = event->ev_arg; + + UNUSED(task); + + isc_app_ctxsuspend(actx); + isc_event_free(&event); +} + static void resolve_done(isc_task_t *task, isc_event_t *event) { resarg_t *resarg = event->ev_arg; dns_clientresevent_t *rev = (dns_clientresevent_t *)event; dns_name_t *name; + isc_result_t result; UNUSED(task); @@ -1117,8 +1129,16 @@ resolve_done(isc_task_t *task, isc_event_t *event) { if (!resarg->canceled) { UNLOCK(&resarg->lock); - /* Exit from the internal event loop */ - isc_app_ctxsuspend(resarg->actx); + /* + * We may or may not be running. isc__appctx_onrun will + * fail if we are currently running otherwise we post a + * action to call isc_app_ctxsuspend when we do start + * running. + */ + result = isc_app_ctxonrun(resarg->actx, resarg->client->mctx, + task, suspend, resarg->actx); + if (result == ISC_R_ALREADYRUNNING) + isc_app_ctxsuspend(resarg->actx); } else { /* * We have already exited from the loop (due to some @@ -1310,9 +1330,8 @@ dns_client_startresolve(dns_client_t *client, dns_name_t *name, ISC_LIST_APPEND(client->resctxs, rctx, link); UNLOCK(&client->lock); - client_resfind(rctx, NULL); - *transp = (dns_clientrestrans_t *)rctx; + client_resfind(rctx, NULL); return (ISC_R_SUCCESS); diff --git a/lib/dns/gssapictx.c b/lib/dns/gssapictx.c index a8c5900e..aeaeb855 100644 --- a/lib/dns/gssapictx.c +++ b/lib/dns/gssapictx.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2000, 2001 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -252,12 +252,12 @@ dst_gssapi_acquirecred(dns_name_t *name, isc_boolean_t initiate, gss_cred_id_t *cred) { #ifdef GSSAPI + isc_result_t result; isc_buffer_t namebuf; gss_name_t gname; gss_buffer_desc gnamebuf; unsigned char array[DNS_NAME_MAXTEXT + 1]; OM_uint32 gret, minor; - gss_OID_set mechs; OM_uint32 lifetime; gss_cred_usage_t usage; char buf[1024]; @@ -304,16 +304,17 @@ dst_gssapi_acquirecred(dns_name_t *name, isc_boolean_t initiate, usage = GSS_C_ACCEPT; gret = gss_acquire_cred(&minor, gname, GSS_C_INDEFINITE, - &mech_oid_set, - usage, cred, &mechs, &lifetime); + &mech_oid_set, usage, cred, NULL, &lifetime); if (gret != GSS_S_COMPLETE) { gss_log(3, "failed to acquire %s credentials for %s: %s", initiate ? "initiate" : "accept", (gname != NULL) ? (char *)gnamebuf.value : "?", gss_error_tostring(gret, minor, buf, sizeof(buf))); - check_config((char *)array); - return (ISC_R_FAILURE); + if (gname != NULL) + check_config((char *)array); + result = ISC_R_FAILURE; + goto cleanup; } gss_log(4, "acquired %s credentials for %s", @@ -321,8 +322,18 @@ dst_gssapi_acquirecred(dns_name_t *name, isc_boolean_t initiate, (gname != NULL) ? (char *)gnamebuf.value : "?"); log_cred(*cred); + result = ISC_R_SUCCESS; + +cleanup: + if (gname != NULL) { + gret = gss_release_name(&minor, &gname); + if (gret != GSS_S_COMPLETE) + gss_log(3, "failed gss_release_name: %s", + gss_error_tostring(gret, minor, buf, + sizeof(buf))); + } - return (ISC_R_SUCCESS); + return (result); #else REQUIRE(cred != NULL && *cred == NULL); @@ -620,7 +631,6 @@ dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken, RETERR(isc_buffer_copyregion(outtoken, &r)); (void)gss_release_buffer(&minor, &gouttoken); } - (void)gss_release_name(&minor, &gname); if (gret == GSS_S_COMPLETE) result = ISC_R_SUCCESS; @@ -628,6 +638,7 @@ dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken, result = DNS_R_CONTINUE; out: + (void)gss_release_name(&minor, &gname); return (result); #else UNUSED(name); diff --git a/lib/dns/include/dns/log.h b/lib/dns/include/dns/log.h index 3c4df8a4..e8c8c105 100644 --- a/lib/dns/include/dns/log.h +++ b/lib/dns/include/dns/log.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2007, 2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2007, 2009, 2011-2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001, 2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -43,6 +43,7 @@ LIBDNS_EXTERNAL_DATA extern isc_logmodule_t dns_modules[]; #define DNS_LOGCATEGORY_DELEGATION_ONLY (&dns_categories[10]) #define DNS_LOGCATEGORY_EDNS_DISABLED (&dns_categories[11]) #define DNS_LOGCATEGORY_RPZ (&dns_categories[12]) +#define DNS_LOGCATEGORY_RRL (&dns_categories[13]) /* Backwards compatibility. */ #define DNS_LOGCATEGORY_GENERAL ISC_LOGCATEGORY_GENERAL diff --git a/lib/dns/include/dns/rrl.h b/lib/dns/include/dns/rrl.h new file mode 100644 index 00000000..ef6b72b4 --- /dev/null +++ b/lib/dns/include/dns/rrl.h @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + + +#ifndef DNS_RRL_H +#define DNS_RRL_H 1 + +/* + * Rate limit DNS responses. + */ + +#include <isc/lang.h> + +#include <dns/fixedname.h> +#include <dns/rdata.h> +#include <dns/types.h> + +ISC_LANG_BEGINDECLS + + +/* + * Memory allocation or other failures. + */ +#define DNS_RRL_LOG_FAIL ISC_LOG_WARNING +/* + * dropped or slipped responses. + */ +#define DNS_RRL_LOG_DROP ISC_LOG_INFO +/* + * Major events in dropping or slipping. + */ +#define DNS_RRL_LOG_DEBUG1 ISC_LOG_DEBUG(3) +/* + * Limit computations. + */ +#define DNS_RRL_LOG_DEBUG2 ISC_LOG_DEBUG(4) +/* + * Even less interesting. + */ +#define DNS_RRL_LOG_DEBUG3 ISC_LOG_DEBUG(9) + + +#define DNS_RRL_LOG_ERR_LEN 64 +#define DNS_RRL_LOG_BUF_LEN (sizeof("would continue limiting") + \ + DNS_RRL_LOG_ERR_LEN + \ + sizeof(" responses to ") + \ + ISC_NETADDR_FORMATSIZE + \ + sizeof("/128 for IN ") + \ + DNS_RDATATYPE_FORMATSIZE + \ + DNS_NAME_FORMATSIZE) + + +typedef struct dns_rrl_hash dns_rrl_hash_t; + +/* + * Response types. + */ +typedef enum { + DNS_RRL_RTYPE_FREE = 0, + DNS_RRL_RTYPE_QUERY, + DNS_RRL_RTYPE_REFERRAL, + DNS_RRL_RTYPE_NODATA, + DNS_RRL_RTYPE_NXDOMAIN, + DNS_RRL_RTYPE_ERROR, + DNS_RRL_RTYPE_ALL, + DNS_RRL_RTYPE_TCP, +} dns_rrl_rtype_t; + +/* + * A rate limit bucket key. + * This should be small to limit the total size of the database. + * The hash of the qname should be wide enough to make the probability + * of collisions among requests from a single IP address block less than 50%. + * We need a 32-bit hash value for 10000 qps (e.g. random qnames forged + * by attacker) to collide with legitimate qnames from the target with + * probability at most 1%. + */ +#define DNS_RRL_MAX_PREFIX 64 +typedef union dns_rrl_key dns_rrl_key_t; +union dns_rrl_key { + struct { + isc_uint32_t ip[DNS_RRL_MAX_PREFIX/32]; + isc_uint32_t qname_hash; + dns_rdatatype_t qtype; + isc_uint8_t qclass; + dns_rrl_rtype_t rtype :4; /* 3 bits + sign bit */ + isc_boolean_t ipv6 :1; + } s; + isc_uint16_t w[1]; +}; + +/* + * A rate-limit entry. + * This should be small to limit the total size of the table of entries. + */ +typedef struct dns_rrl_entry dns_rrl_entry_t; +typedef ISC_LIST(dns_rrl_entry_t) dns_rrl_bin_t; +struct dns_rrl_entry { + ISC_LINK(dns_rrl_entry_t) lru; + ISC_LINK(dns_rrl_entry_t) hlink; + dns_rrl_key_t key; +# define DNS_RRL_RESPONSE_BITS 24 + signed int responses :DNS_RRL_RESPONSE_BITS; +# define DNS_RRL_QNAMES_BITS 8 + unsigned int log_qname :DNS_RRL_QNAMES_BITS; + +# define DNS_RRL_TS_GEN_BITS 2 + unsigned int ts_gen :DNS_RRL_TS_GEN_BITS; + isc_boolean_t ts_valid :1; +# define DNS_RRL_HASH_GEN_BITS 1 + unsigned int hash_gen :DNS_RRL_HASH_GEN_BITS; + isc_boolean_t logged :1; +# define DNS_RRL_LOG_BITS 11 + unsigned int log_secs :DNS_RRL_LOG_BITS; + +# define DNS_RRL_TS_BITS 12 + unsigned int ts :DNS_RRL_TS_BITS; + +# define DNS_RRL_MAX_SLIP 10 + unsigned int slip_cnt :4; +}; + +#define DNS_RRL_MAX_TIME_TRAVEL 5 +#define DNS_RRL_FOREVER (1<<DNS_RRL_TS_BITS) +#define DNS_RRL_MAX_TS (DNS_RRL_FOREVER - 1) + +#define DNS_RRL_MAX_RESPONSES ((1<<(DNS_RRL_RESPONSE_BITS-1))-1) +#define DNS_RRL_MAX_WINDOW 3600 +#if DNS_RRL_MAX_WINDOW >= DNS_RRL_MAX_TS +#error "DNS_RRL_MAX_WINDOW is too large" +#endif +#define DNS_RRL_MAX_RATE 1000 +#if DNS_RRL_MAX_RATE >= (DNS_RRL_MAX_RESPONSES / DNS_RRL_MAX_WINDOW) +#error "DNS_RRL_MAX_rate is too large" +#endif + +#if (1<<DNS_RRL_LOG_BITS) >= DNS_RRL_FOREVER +#error DNS_RRL_LOG_BITS is too big +#endif +#define DNS_RRL_MAX_LOG_SECS 1800 +#if DNS_RRL_MAX_LOG_SECS >= (1<<DNS_RRL_LOG_BITS) +#error "DNS_RRL_MAX_LOG_SECS is too large" +#endif +#define DNS_RRL_STOP_LOG_SECS 60 +#if DNS_RRL_STOP_LOG_SECS >= (1<<DNS_RRL_LOG_BITS) +#error "DNS_RRL_STOP_LOG_SECS is too large" +#endif + + +/* + * A hash table of rate-limit entries. + */ +struct dns_rrl_hash { + isc_stdtime_t check_time; + unsigned int gen :DNS_RRL_HASH_GEN_BITS; + int length; + dns_rrl_bin_t bins[1]; +}; + +/* + * A block of rate-limit entries. + */ +typedef struct dns_rrl_block dns_rrl_block_t; +struct dns_rrl_block { + ISC_LINK(dns_rrl_block_t) link; + int size; + dns_rrl_entry_t entries[1]; +}; + +/* + * A rate limited qname buffer. + */ +typedef struct dns_rrl_qname_buf dns_rrl_qname_buf_t; +struct dns_rrl_qname_buf { + ISC_LINK(dns_rrl_qname_buf_t) link; + const dns_rrl_entry_t *e; + unsigned int index; + dns_fixedname_t qname; +}; + +typedef struct dns_rrl_rate dns_rrl_rate_t; +struct dns_rrl_rate { + int r; + int scaled; + const char *str; +}; + +/* + * Per-view query rate limit parameters and a pointer to database. + */ +typedef struct dns_rrl dns_rrl_t; +struct dns_rrl { + isc_mutex_t lock; + isc_mem_t *mctx; + + isc_boolean_t log_only; + dns_rrl_rate_t responses_per_second; + dns_rrl_rate_t referrals_per_second; + dns_rrl_rate_t nodata_per_second; + dns_rrl_rate_t nxdomains_per_second; + dns_rrl_rate_t errors_per_second; + dns_rrl_rate_t all_per_second; + dns_rrl_rate_t slip; + int window; + double qps_scale; + int max_entries; + + dns_acl_t *exempt; + + int num_entries; + + int qps_responses; + isc_stdtime_t qps_time; + double qps; + + unsigned int probes; + unsigned int searches; + + ISC_LIST(dns_rrl_block_t) blocks; + ISC_LIST(dns_rrl_entry_t) lru; + + dns_rrl_hash_t *hash; + dns_rrl_hash_t *old_hash; + unsigned int hash_gen; + + unsigned int ts_gen; +# define DNS_RRL_TS_BASES (1<<DNS_RRL_TS_GEN_BITS) + isc_stdtime_t ts_bases[DNS_RRL_TS_BASES]; + + int ipv4_prefixlen; + isc_uint32_t ipv4_mask; + int ipv6_prefixlen; + isc_uint32_t ipv6_mask[4]; + + isc_stdtime_t log_stops_time; + dns_rrl_entry_t *last_logged; + int num_logged; + int num_qnames; + ISC_LIST(dns_rrl_qname_buf_t) qname_free; +# define DNS_RRL_QNAMES (1<<DNS_RRL_QNAMES_BITS) + dns_rrl_qname_buf_t *qnames[DNS_RRL_QNAMES]; +}; + +typedef enum { + DNS_RRL_RESULT_OK, + DNS_RRL_RESULT_DROP, + DNS_RRL_RESULT_SLIP, +} dns_rrl_result_t; + +dns_rrl_result_t +dns_rrl(dns_view_t *view, + const isc_sockaddr_t *client_addr, isc_boolean_t is_tcp, + dns_rdataclass_t rdclass, dns_rdatatype_t qtype, + dns_name_t *qname, isc_result_t resp_result, isc_stdtime_t now, + isc_boolean_t wouldlog, char *log_buf, unsigned int log_buf_len); + +void +dns_rrl_view_destroy(dns_view_t *view); + +isc_result_t +dns_rrl_init(dns_rrl_t **rrlp, dns_view_t *view, int min_entries); + +ISC_LANG_ENDDECLS + +#endif /* DNS_RRL_H */ diff --git a/lib/dns/include/dns/view.h b/lib/dns/include/dns/view.h index d0c1931d..704e5fe3 100644 --- a/lib/dns/include/dns/view.h +++ b/lib/dns/include/dns/view.h @@ -73,6 +73,7 @@ #include <dns/acl.h> #include <dns/fixedname.h> +#include <dns/rrl.h> #include <dns/rdatastruct.h> #include <dns/rpz.h> #include <dns/types.h> @@ -142,6 +143,7 @@ struct dns_view { dns_rbt_t * answeracl_exclude; dns_rbt_t * denyanswernames; dns_rbt_t * answernames_exclude; + dns_rrl_t * rrl; isc_boolean_t provideixfr; isc_boolean_t requestnsid; dns_ttl_t maxcachettl; diff --git a/lib/dns/log.c b/lib/dns/log.c index c4d644e3..75e0d79b 100644 --- a/lib/dns/log.c +++ b/lib/dns/log.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2007, 2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2007, 2009, 2011-2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001, 2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -45,6 +45,7 @@ LIBDNS_EXTERNAL_DATA isc_logcategory_t dns_categories[] = { { "delegation-only", 0 }, { "edns-disabled", 0 }, { "rpz", 0 }, + { "rate-limit", 0 }, { NULL, 0 } }; diff --git a/lib/dns/name.c b/lib/dns/name.c index 7fb21e13..4fcabb11 100644 --- a/lib/dns/name.c +++ b/lib/dns/name.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -578,6 +578,11 @@ dns_name_fullcompare(const dns_name_t *name1, const dns_name_t *name2, REQUIRE((name1->attributes & DNS_NAMEATTR_ABSOLUTE) == (name2->attributes & DNS_NAMEATTR_ABSOLUTE)); + if (name1 == name2) { + *orderp = 0; + return (dns_namereln_equal); + } + SETUP_OFFSETS(name1, offsets1, odata1); SETUP_OFFSETS(name2, offsets2, odata2); @@ -691,6 +696,9 @@ dns_name_equal(const dns_name_t *name1, const dns_name_t *name2) { REQUIRE((name1->attributes & DNS_NAMEATTR_ABSOLUTE) == (name2->attributes & DNS_NAMEATTR_ABSOLUTE)); + if (name1 == name2) + return (ISC_TRUE); + if (name1->length != name2->length) return (ISC_FALSE); diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index bff52b87..d468d312 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -4168,7 +4168,7 @@ cache_zonecut_callback(dns_rbtnode_t *node, dns_name_t *name, void *arg) { header_prev = NULL; for (header = node->data; header != NULL; header = header_next) { header_next = header->next; - if (header->rdh_ttl <= search->now) { + if (header->rdh_ttl < search->now) { /* * This rdataset is stale. If no one else is * using the node, we can clean it up right @@ -4176,7 +4176,7 @@ cache_zonecut_callback(dns_rbtnode_t *node, dns_name_t *name, void *arg) { * the node as dirty, so it will get cleaned * up later. */ - if ((header->rdh_ttl <= search->now - RBTDB_VIRTUAL) && + if ((header->rdh_ttl < search->now - RBTDB_VIRTUAL) && (locktype == isc_rwlocktype_write || NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) { /* @@ -4292,7 +4292,7 @@ find_deepest_zonecut(rbtdb_search_t *search, dns_rbtnode_t *node, header != NULL; header = header_next) { header_next = header->next; - if (header->rdh_ttl <= search->now) { + if (header->rdh_ttl < search->now) { /* * This rdataset is stale. If no one else is * using the node, we can clean it up right @@ -4300,7 +4300,7 @@ find_deepest_zonecut(rbtdb_search_t *search, dns_rbtnode_t *node, * the node as dirty, so it will get cleaned * up later. */ - if ((header->rdh_ttl <= search->now - + if ((header->rdh_ttl < search->now - RBTDB_VIRTUAL) && (locktype == isc_rwlocktype_write || NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) { @@ -4469,7 +4469,7 @@ find_coveringnsec(rbtdb_search_t *search, dns_dbnode_t **nodep, header != NULL; header = header_next) { header_next = header->next; - if (header->rdh_ttl <= now) { + if (header->rdh_ttl < now) { /* * This rdataset is stale. If no one else is * using the node, we can clean it up right @@ -4477,7 +4477,7 @@ find_coveringnsec(rbtdb_search_t *search, dns_dbnode_t **nodep, * node as dirty, so it will get cleaned up * later. */ - if ((header->rdh_ttl <= now - RBTDB_VIRTUAL) && + if ((header->rdh_ttl < now - RBTDB_VIRTUAL) && (locktype == isc_rwlocktype_write || NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) { /* @@ -4876,14 +4876,14 @@ cache_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, header_prev = NULL; for (header = node->data; header != NULL; header = header_next) { header_next = header->next; - if (header->rdh_ttl <= now) { + if (header->rdh_ttl < now) { /* * This rdataset is stale. If no one else is using the * node, we can clean it up right now, otherwise we * mark it as stale, and the node as dirty, so it will * get cleaned up later. */ - if ((header->rdh_ttl <= now - RBTDB_VIRTUAL) && + if ((header->rdh_ttl < now - RBTDB_VIRTUAL) && (locktype == isc_rwlocktype_write || NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) { /* @@ -5183,14 +5183,14 @@ cache_findzonecut(dns_db_t *db, dns_name_t *name, unsigned int options, header_prev = NULL; for (header = node->data; header != NULL; header = header_next) { header_next = header->next; - if (header->rdh_ttl <= now) { + if (header->rdh_ttl < now) { /* * This rdataset is stale. If no one else is using the * node, we can clean it up right now, otherwise we * mark it as stale, and the node as dirty, so it will * get cleaned up later. */ - if ((header->rdh_ttl <= now - RBTDB_VIRTUAL) && + if ((header->rdh_ttl < now - RBTDB_VIRTUAL) && (locktype == isc_rwlocktype_write || NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) { /* @@ -5672,8 +5672,8 @@ cache_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, for (header = rbtnode->data; header != NULL; header = header_next) { header_next = header->next; - if (header->rdh_ttl <= now) { - if ((header->rdh_ttl <= now - RBTDB_VIRTUAL) && + if (header->rdh_ttl < now) { + if ((header->rdh_ttl < now - RBTDB_VIRTUAL) && (locktype == isc_rwlocktype_write || NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) { /* @@ -5981,7 +5981,7 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion, } } if (topheader != NULL && EXISTS(topheader) && - topheader->rdh_ttl > now) { + topheader->rdh_ttl >= now) { /* * Found one. */ @@ -6047,7 +6047,7 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion, * has no effect, provided that the cache data isn't stale. */ if (rbtversion == NULL && trust < header->trust && - (header->rdh_ttl > now || header_nx)) { + (header->rdh_ttl >= now || header_nx)) { free_rdataset(rbtdb, rbtdb->common.mctx, newheader); if (addedrdataset != NULL) bind_rdataset(rbtdb, rbtnode, header, now, @@ -6117,7 +6117,7 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion, * Don't lower trust of existing record if the * update is forced. */ - if (IS_CACHE(rbtdb) && header->rdh_ttl > now && + if (IS_CACHE(rbtdb) && header->rdh_ttl >= now && header->type == dns_rdatatype_ns && !header_nx && !newheader_nx && header->trust >= newheader->trust && @@ -6153,7 +6153,7 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion, * to be no more than the current NS RRset's TTL. This * ensures the delegations that are withdrawn are honoured. */ - if (IS_CACHE(rbtdb) && header->rdh_ttl > now && + if (IS_CACHE(rbtdb) && header->rdh_ttl >= now && header->type == dns_rdatatype_ns && !header_nx && !newheader_nx && header->trust <= newheader->trust) { @@ -6161,7 +6161,7 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion, newheader->rdh_ttl = header->rdh_ttl; } } - if (IS_CACHE(rbtdb) && header->rdh_ttl > now && + if (IS_CACHE(rbtdb) && header->rdh_ttl >= now && (header->type == dns_rdatatype_a || header->type == dns_rdatatype_aaaa || header->type == dns_rdatatype_ds || @@ -6564,7 +6564,7 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, cleanup_dead_nodes(rbtdb, rbtnode->locknum); header = isc_heap_element(rbtdb->heaps[rbtnode->locknum], 1); - if (header && header->rdh_ttl <= now - RBTDB_VIRTUAL) + if (header && header->rdh_ttl < now - RBTDB_VIRTUAL) expire_header(rbtdb, header, tree_locked); /* @@ -9289,7 +9289,7 @@ overmem_purge(dns_rbtdb_t *rbtdb, unsigned int locknum_start, isc_rwlocktype_write); header = isc_heap_element(rbtdb->heaps[locknum], 1); - if (header && header->rdh_ttl <= now - RBTDB_VIRTUAL) { + if (header && header->rdh_ttl < now - RBTDB_VIRTUAL) { expire_header(rbtdb, header, tree_locked); purgecount--; } diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index 10d1f75f..27d15b93 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -4395,7 +4395,7 @@ fctx_log(void *arg, int level, const char *fmt, ...) { static inline isc_result_t findnoqname(fetchctx_t *fctx, dns_name_t *name, dns_rdatatype_t type, - dns_name_t **noqname) + dns_name_t **noqnamep) { dns_rdataset_t *nrdataset, *next, *sigrdataset; dns_rdata_rrsig_t rrsig; @@ -4408,10 +4408,12 @@ findnoqname(fetchctx_t *fctx, dns_name_t *name, dns_rdatatype_t type, dns_fixedname_t fclosest; dns_name_t *nearest; dns_fixedname_t fnearest; + dns_rdatatype_t found = dns_rdatatype_none; + dns_name_t *noqname = NULL; FCTXTRACE("findnoqname"); - REQUIRE(noqname != NULL && *noqname == NULL); + REQUIRE(noqnamep != NULL && *noqnamep == NULL); /* * Find the SIG for this rdataset, if we have it. @@ -4480,8 +4482,10 @@ findnoqname(fetchctx_t *fctx, dns_name_t *name, dns_rdatatype_t type, &data, NULL, fctx_log, fctx))) { - if (!exists) - *noqname = nsec; + if (!exists) { + noqname = nsec; + found = dns_rdatatype_nsec; + } } if (nrdataset->type == dns_rdatatype_nsec3 && @@ -4494,13 +4498,26 @@ findnoqname(fetchctx_t *fctx, dns_name_t *name, dns_rdatatype_t type, closest, nearest, fctx_log, fctx))) { - if (!exists && setnearest) - *noqname = nsec; + if (!exists && setnearest) { + noqname = nsec; + found = dns_rdatatype_nsec3; + } } } } if (result == ISC_R_NOMORE) result = ISC_R_SUCCESS; + if (noqname != NULL) { + for (sigrdataset = ISC_LIST_HEAD(noqname->list); + sigrdataset != NULL; + sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) { + if (sigrdataset->type == dns_rdatatype_rrsig && + sigrdataset->covers == found) + break; + } + if (sigrdataset != NULL) + *noqnamep = noqname; + } return (result); } diff --git a/lib/dns/rrl.c b/lib/dns/rrl.c new file mode 100644 index 00000000..96c55fef --- /dev/null +++ b/lib/dns/rrl.c @@ -0,0 +1,1324 @@ +/* + * Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/*! \file */ + +/* + * Rate limit DNS responses. + */ + +/* #define ISC_LIST_CHECKINIT */ + +#include <config.h> +#include <isc/mem.h> +#include <isc/net.h> +#include <isc/netaddr.h> +#include <isc/print.h> + +#include <dns/result.h> +#include <dns/rcode.h> +#include <dns/rdatatype.h> +#include <dns/rdataclass.h> +#include <dns/log.h> +#include <dns/rrl.h> +#include <dns/view.h> + +static void +log_end(dns_rrl_t *rrl, dns_rrl_entry_t *e, isc_boolean_t early, + char *log_buf, unsigned int log_buf_len); + +/* + * Get a modulus for a hash function that is tolerably likely to be + * relatively prime to most inputs. Of course, we get a prime for for initial + * values not larger than the square of the last prime. We often get a prime + * after that. + * This works well in practice for hash tables up to at least 100 + * times the square of the last prime and better than a multiplicative hash. + */ +static int +hash_divisor(unsigned int initial) { + static isc_uint16_t primes[] = { + 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, + 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, +#if 0 + 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, + 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, + 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, + 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, + 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, + 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, + 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, + 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, + 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, + 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, + 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, + 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997,1009, +#endif + }; + int divisions, tries; + unsigned int result; + isc_uint16_t *pp, p; + + result = initial; + + if (primes[sizeof(primes)/sizeof(primes[0])-1] >= result) { + pp = primes; + while (*pp < result) + ++pp; + return (*pp); + } + + if ((result & 1) == 0) + ++result; + + divisions = 0; + tries = 1; + pp = primes; + do { + p = *pp++; + ++divisions; + if ((result % p) == 0) { + ++tries; + result += 2; + pp = primes; + } + } while (pp < &primes[sizeof(primes) / sizeof(primes[0])]); + + if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DEBUG3)) + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL, + DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DEBUG3, + "%d hash_divisor() divisions in %d tries" + " to get %d from %d", + divisions, tries, result, initial); + + return (result); +} + +/* + * Convert a timestamp to a number of seconds in the past. + */ +static inline int +delta_rrl_time(isc_stdtime_t ts, isc_stdtime_t now) { + int delta; + + delta = now - ts; + if (delta >= 0) + return (delta); + + /* + * The timestamp is in the future. That future might result from + * re-ordered requests, because we use timestamps on requests + * instead of consulting a clock. Timestamps in the distant future are + * assumed to result from clock changes. When the clock changes to + * the past, make existing timestamps appear to be in the past. + */ + if (delta < -DNS_RRL_MAX_TIME_TRAVEL) + return (DNS_RRL_FOREVER); + return (0); +} + +static inline int +get_age(const dns_rrl_t *rrl, const dns_rrl_entry_t *e, isc_stdtime_t now) { + if (!e->ts_valid) + return (DNS_RRL_FOREVER); + return (delta_rrl_time(e->ts + rrl->ts_bases[e->ts_gen], now)); +} + +static inline void +set_age(dns_rrl_t *rrl, dns_rrl_entry_t *e, isc_stdtime_t now) { + dns_rrl_entry_t *e_old; + unsigned int ts_gen; + int i, ts; + + ts_gen = rrl->ts_gen; + ts = now - rrl->ts_bases[ts_gen]; + if (ts < 0) { + if (ts < -DNS_RRL_MAX_TIME_TRAVEL) + ts = DNS_RRL_FOREVER; + else + ts = 0; + } + + /* + * Make a new timestamp base if the current base is too old. + * All entries older than DNS_RRL_MAX_WINDOW seconds are ancient, + * useless history. Their timestamps can be treated as if they are + * all the same. + * We only do arithmetic on more recent timestamps, so bases for + * older timestamps can be recycled provided the old timestamps are + * marked as ancient history. + * This loop is almost always very short because most entries are + * recycled after one second and any entries that need to be marked + * are older than (DNS_RRL_TS_BASES)*DNS_RRL_MAX_TS seconds. + */ + if (ts >= DNS_RRL_MAX_TS) { + ts_gen = (ts_gen + 1) % DNS_RRL_TS_BASES; + for (e_old = ISC_LIST_TAIL(rrl->lru), i = 0; + e_old != NULL && (e_old->ts_gen == ts_gen || + !ISC_LINK_LINKED(e_old, hlink)); + e_old = ISC_LIST_PREV(e_old, lru), ++i) + { + e_old->ts_valid = ISC_FALSE; + } + if (i != 0) + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL, + DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DEBUG1, + "rrl new time base scanned %d entries" + " at %d for %d %d %d %d", + i, now, rrl->ts_bases[ts_gen], + rrl->ts_bases[(ts_gen + 1) % + DNS_RRL_TS_BASES], + rrl->ts_bases[(ts_gen + 2) % + DNS_RRL_TS_BASES], + rrl->ts_bases[(ts_gen + 3) % + DNS_RRL_TS_BASES]); + rrl->ts_gen = ts_gen; + rrl->ts_bases[ts_gen] = now; + ts = 0; + } + + e->ts_gen = ts_gen; + e->ts = ts; + e->ts_valid = ISC_TRUE; +} + +static isc_result_t +expand_entries(dns_rrl_t *rrl, int new) { + unsigned int bsize; + dns_rrl_block_t *b; + dns_rrl_entry_t *e; + double rate; + int i; + + if (rrl->num_entries + new >= rrl->max_entries && + rrl->max_entries != 0) + { + new = rrl->max_entries - rrl->num_entries; + if (new <= 0) + return (ISC_R_SUCCESS); + } + + /* + * Log expansions so that the user can tune max-table-size + * and min-table-size. + */ + if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DROP) && + rrl->hash != NULL) { + rate = rrl->probes; + if (rrl->searches != 0) + rate /= rrl->searches; + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL, + DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DROP, + "increase from %d to %d RRL entries with" + " %d bins; average search length %.1f", + rrl->num_entries, rrl->num_entries+new, + rrl->hash->length, rate); + } + + bsize = sizeof(dns_rrl_block_t) + (new-1)*sizeof(dns_rrl_entry_t); + b = isc_mem_get(rrl->mctx, bsize); + if (b == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL, + DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_FAIL, + "isc_mem_get(%d) failed for RRL entries", + bsize); + return (ISC_R_NOMEMORY); + } + memset(b, 0, bsize); + b->size = bsize; + + e = b->entries; + for (i = 0; i < new; ++i, ++e) { + ISC_LINK_INIT(e, hlink); + ISC_LIST_INITANDAPPEND(rrl->lru, e, lru); + } + rrl->num_entries += new; + ISC_LIST_INITANDAPPEND(rrl->blocks, b, link); + + return (ISC_R_SUCCESS); +} + +static inline dns_rrl_bin_t * +get_bin(dns_rrl_hash_t *hash, unsigned int hval) { + return (&hash->bins[hval % hash->length]); +} + +static void +free_old_hash(dns_rrl_t *rrl) { + dns_rrl_hash_t *old_hash; + dns_rrl_bin_t *old_bin; + dns_rrl_entry_t *e, *e_next; + + old_hash = rrl->old_hash; + for (old_bin = &old_hash->bins[0]; + old_bin < &old_hash->bins[old_hash->length]; + ++old_bin) + { + for (e = ISC_LIST_HEAD(*old_bin); e != NULL; e = e_next) { + e_next = ISC_LIST_NEXT(e, hlink); + ISC_LINK_INIT(e, hlink); + } + } + + isc_mem_put(rrl->mctx, old_hash, + sizeof(*old_hash) + + (old_hash->length - 1) * sizeof(old_hash->bins[0])); + rrl->old_hash = NULL; +} + +static isc_result_t +expand_rrl_hash(dns_rrl_t *rrl, isc_stdtime_t now) { + dns_rrl_hash_t *hash; + int old_bins, new_bins, hsize; + double rate; + + if (rrl->old_hash != NULL) + free_old_hash(rrl); + + /* + * Most searches fail and so go to the end of the chain. + * Use a small hash table load factor. + */ + old_bins = (rrl->hash == NULL) ? 0 : rrl->hash->length; + new_bins = old_bins/8 + old_bins; + if (new_bins < rrl->num_entries) + new_bins = rrl->num_entries; + new_bins = hash_divisor(new_bins); + + hsize = sizeof(dns_rrl_hash_t) + (new_bins-1)*sizeof(hash->bins[0]); + hash = isc_mem_get(rrl->mctx, hsize); + if (hash == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL, + DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_FAIL, + "isc_mem_get(%d) failed for" + " RRL hash table", + hsize); + return (ISC_R_NOMEMORY); + } + memset(hash, 0, hsize); + hash->length = new_bins; + rrl->hash_gen ^= 1; + hash->gen = rrl->hash_gen; + + if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DROP) && old_bins != 0) { + rate = rrl->probes; + if (rrl->searches != 0) + rate /= rrl->searches; + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL, + DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DROP, + "increase from %d to %d RRL bins for" + " %d entries; average search length %.1f", + old_bins, new_bins, rrl->num_entries, rate); + } + + rrl->old_hash = rrl->hash; + if (rrl->old_hash != NULL) + rrl->old_hash->check_time = now; + rrl->hash = hash; + + return (ISC_R_SUCCESS); +} + +static void +ref_entry(dns_rrl_t *rrl, dns_rrl_entry_t *e, int probes, isc_stdtime_t now) { + /* + * Make the entry most recently used. + */ + if (ISC_LIST_HEAD(rrl->lru) != e) { + if (e == rrl->last_logged) + rrl->last_logged = ISC_LIST_PREV(e, lru); + ISC_LIST_UNLINK(rrl->lru, e, lru); + ISC_LIST_PREPEND(rrl->lru, e, lru); + } + + /* + * Expand the hash table if it is time and necessary. + * This will leave the newly referenced entry in a chain in the + * old hash table. It will migrate to the new hash table the next + * time it is used or be cut loose when the old hash table is destroyed. + */ + rrl->probes += probes; + ++rrl->searches; + if (rrl->searches > 100 && + delta_rrl_time(rrl->hash->check_time, now) > 1) { + if (rrl->probes/rrl->searches > 2) + expand_rrl_hash(rrl, now); + rrl->hash->check_time = now; + rrl->probes = 0; + rrl->searches = 0; + } +} + +static inline isc_boolean_t +key_cmp(const dns_rrl_key_t *a, const dns_rrl_key_t *b) { + if (memcmp(a, b, sizeof(dns_rrl_key_t)) == 0) + return (ISC_TRUE); + return (ISC_FALSE); +} + +static inline isc_uint32_t +hash_key(const dns_rrl_key_t *key) { + isc_uint32_t hval; + int i; + + hval = key->w[0]; + for (i = sizeof(*key) / sizeof(key->w[0]) - 1; i >= 0; --i) { + hval = key->w[i] + (hval<<1); + } + return (hval); +} + +/* + * Construct the hash table key. + * Use a hash of the DNS query name to save space in the database. + * Collisions result in legitimate rate limiting responses for one + * query name also limiting responses for other names to the + * same client. This is rare and benign enough given the large + * space costs compared to keeping the entire name in the database + * entry or the time costs of dynamic allocation. + */ +static void +make_key(const dns_rrl_t *rrl, dns_rrl_key_t *key, + const isc_sockaddr_t *client_addr, + dns_rdatatype_t qtype, dns_name_t *qname, dns_rdataclass_t qclass, + dns_rrl_rtype_t rtype) +{ + dns_name_t base; + dns_offsets_t base_offsets; + int labels, i; + + memset(key, 0, sizeof(*key)); + + key->s.rtype = rtype; + if (rtype == DNS_RRL_RTYPE_QUERY) { + key->s.qtype = qtype; + key->s.qclass = qclass & 0xff; + } else if (rtype == DNS_RRL_RTYPE_REFERRAL || + rtype == DNS_RRL_RTYPE_NODATA) { + /* + * Because there is no qtype in the empty answer sections of + * referral and NODATA responses, count them as the same. + */ + key->s.qclass = qclass & 0xff; + } + + if (qname != NULL && qname->labels != 0) { + /* + * Ignore the first label of wildcards. + */ + if ((qname->attributes & DNS_NAMEATTR_WILDCARD) != 0 && + (labels = dns_name_countlabels(qname)) > 1) + { + dns_name_init(&base, base_offsets); + dns_name_getlabelsequence(qname, 1, labels-1, &base); + key->s.qname_hash = dns_name_hashbylabel(&base, + ISC_FALSE); + } else { + key->s.qname_hash = dns_name_hashbylabel(qname, + ISC_FALSE); + } + } + + switch (client_addr->type.sa.sa_family) { + case AF_INET: + key->s.ip[0] = (client_addr->type.sin.sin_addr.s_addr & + rrl->ipv4_mask); + break; + case AF_INET6: + key->s.ipv6 = ISC_TRUE; + memcpy(key->s.ip, &client_addr->type.sin6.sin6_addr, + sizeof(key->s.ip)); + for (i = 0; i < DNS_RRL_MAX_PREFIX/32; ++i) + key->s.ip[i] &= rrl->ipv6_mask[i]; + break; + } +} + +static inline dns_rrl_rate_t * +get_rate(dns_rrl_t *rrl, dns_rrl_rtype_t rtype) { + switch (rtype) { + case DNS_RRL_RTYPE_QUERY: + return (&rrl->responses_per_second); + case DNS_RRL_RTYPE_REFERRAL: + return (&rrl->referrals_per_second); + case DNS_RRL_RTYPE_NODATA: + return (&rrl->nodata_per_second); + case DNS_RRL_RTYPE_NXDOMAIN: + return (&rrl->nxdomains_per_second); + case DNS_RRL_RTYPE_ERROR: + return (&rrl->errors_per_second); + case DNS_RRL_RTYPE_ALL: + return (&rrl->all_per_second); + default: + INSIST(0); + } + return (NULL); +} + +static int +response_balance(dns_rrl_t *rrl, const dns_rrl_entry_t *e, int age) { + dns_rrl_rate_t *ratep; + int balance, rate; + + if (e->key.s.rtype == DNS_RRL_RTYPE_TCP) { + rate = 1; + } else { + ratep = get_rate(rrl, e->key.s.rtype); + rate = ratep->scaled; + } + + balance = e->responses + age * rate; + if (balance > rate) + balance = rate; + return (balance); +} + +/* + * Search for an entry for a response and optionally create it. + */ +static dns_rrl_entry_t * +get_entry(dns_rrl_t *rrl, const isc_sockaddr_t *client_addr, + dns_rdataclass_t qclass, dns_rdatatype_t qtype, dns_name_t *qname, + dns_rrl_rtype_t rtype, isc_stdtime_t now, isc_boolean_t create, + char *log_buf, unsigned int log_buf_len) +{ + dns_rrl_key_t key; + isc_uint32_t hval; + dns_rrl_entry_t *e; + dns_rrl_hash_t *hash; + dns_rrl_bin_t *new_bin, *old_bin; + int probes, age; + + make_key(rrl, &key, client_addr, qtype, qname, qclass, rtype); + hval = hash_key(&key); + + /* + * Look for the entry in the current hash table. + */ + new_bin = get_bin(rrl->hash, hval); + probes = 1; + e = ISC_LIST_HEAD(*new_bin); + while (e != NULL) { + if (key_cmp(&e->key, &key)) { + ref_entry(rrl, e, probes, now); + return (e); + } + ++probes; + e = ISC_LIST_NEXT(e, hlink); + } + + /* + * Look in the old hash table. + */ + if (rrl->old_hash != NULL) { + old_bin = get_bin(rrl->old_hash, hval); + e = ISC_LIST_HEAD(*old_bin); + while (e != NULL) { + if (key_cmp(&e->key, &key)) { + ISC_LIST_UNLINK(*old_bin, e, hlink); + ISC_LIST_PREPEND(*new_bin, e, hlink); + e->hash_gen = rrl->hash_gen; + ref_entry(rrl, e, probes, now); + return (e); + } + e = ISC_LIST_NEXT(e, hlink); + } + + /* + * Discard prevous hash table when all of its entries are old. + */ + age = delta_rrl_time(rrl->old_hash->check_time, now); + if (age > rrl->window) + free_old_hash(rrl); + } + + if (!create) + return (NULL); + + /* + * The entry does not exist, so create it by finding a free entry. + * Keep currently penalized and logged entries. + * Try to make more entries if none are idle. + * Steal the oldest entry if we cannot create more. + */ + for (e = ISC_LIST_TAIL(rrl->lru); + e != NULL; + e = ISC_LIST_PREV(e, lru)) + { + if (!ISC_LINK_LINKED(e, hlink)) + break; + age = get_age(rrl, e, now); + if (age <= 1) { + e = NULL; + break; + } + if (!e->logged && response_balance(rrl, e, age) > 0) + break; + } + if (e == NULL) { + expand_entries(rrl, ISC_MIN((rrl->num_entries+1)/2, 1000)); + e = ISC_LIST_TAIL(rrl->lru); + } + if (e->logged) + log_end(rrl, e, ISC_TRUE, log_buf, log_buf_len); + if (ISC_LINK_LINKED(e, hlink)) { + if (e->hash_gen == rrl->hash_gen) + hash = rrl->hash; + else + hash = rrl->old_hash; + old_bin = get_bin(hash, hash_key(&e->key)); + ISC_LIST_UNLINK(*old_bin, e, hlink); + } + ISC_LIST_PREPEND(*new_bin, e, hlink); + e->hash_gen = rrl->hash_gen; + e->key = key; + e->ts_valid = ISC_FALSE; + ref_entry(rrl, e, probes, now); + return (e); +} + +static void +debit_log(const dns_rrl_entry_t *e, int age, const char *action) { + char buf[sizeof("age=12345678")]; + const char *age_str; + + if (age == DNS_RRL_FOREVER) { + age_str = ""; + } else { + snprintf(buf, sizeof(buf), "age=%d", age); + age_str = buf; + } + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL, + DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DEBUG3, + "rrl %08x %6s responses=%-3d %s", + hash_key(&e->key), age_str, e->responses, action); +} + +static inline dns_rrl_result_t +debit_rrl_entry(dns_rrl_t *rrl, dns_rrl_entry_t *e, double qps, double scale, + const isc_sockaddr_t *client_addr, isc_stdtime_t now, + char *log_buf, unsigned int log_buf_len) +{ + int rate, new_rate, slip, new_slip, age, log_secs, min; + dns_rrl_rate_t *ratep; + dns_rrl_entry_t const *credit_e; + + /* + * Pick the rate counter. + * Optionally adjust the rate by the estimated query/second rate. + */ + ratep = get_rate(rrl, e->key.s.rtype); + rate = ratep->r; + if (rate == 0) + return (DNS_RRL_RESULT_OK); + + if (scale < 1.0) { + /* + * The limit for clients that have used TCP is not scaled. + */ + credit_e = get_entry(rrl, client_addr, + 0, dns_rdatatype_none, NULL, + DNS_RRL_RTYPE_TCP, now, ISC_FALSE, + log_buf, log_buf_len); + if (credit_e != NULL) { + age = get_age(rrl, e, now); + if (age < rrl->window) + scale = 1.0; + } + } + if (scale < 1.0) { + new_rate = (int) (rate * scale); + if (new_rate < 1) + new_rate = 1; + if (ratep->scaled != new_rate) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL, + DNS_LOGMODULE_REQUEST, + DNS_RRL_LOG_DEBUG1, + "%d qps scaled %s by %.2f" + " from %d to %d", + (int)qps, ratep->str, scale, + rate, new_rate); + rate = new_rate; + ratep->scaled = rate; + } + } + + min = -rrl->window * rate; + + /* + * Treat time jumps into the recent past as no time. + * Treat entries older than the window as if they were just created + * Credit other entries. + */ + age = get_age(rrl, e, now); + if (age > 0) { + /* + * Credit tokens earned during elapsed time. + */ + if (age > rrl->window) { + e->responses = rate; + e->slip_cnt = 0; + } else { + e->responses += rate*age; + if (e->responses > rate) { + e->responses = rate; + e->slip_cnt = 0; + } + } + /* + * Find the seconds since last log message without overflowing + * small counter. This counter is reset when an entry is + * created. It is not necessarily reset when some requests + * are answered provided other requests continue to be dropped + * or slipped. This can happen when the request rate is just + * at the limit. + */ + if (e->logged) { + log_secs = e->log_secs; + log_secs += age; + if (log_secs > DNS_RRL_MAX_LOG_SECS || log_secs < 0) + log_secs = DNS_RRL_MAX_LOG_SECS; + e->log_secs = log_secs; + } + } + set_age(rrl, e, now); + + /* + * Debit the entry for this response. + */ + if (--e->responses >= 0) { + if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DEBUG3)) + debit_log(e, age, ""); + return (DNS_RRL_RESULT_OK); + } + + if (e->responses < min) + e->responses = min; + + /* + * Drop this response unless it should slip or leak. + */ + slip = rrl->slip.r; + if (slip > 2 && scale < 1.0) { + new_slip = (int) (slip * scale); + if (new_slip < 2) + new_slip = 2; + if (rrl->slip.scaled != new_slip) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL, + DNS_LOGMODULE_REQUEST, + DNS_RRL_LOG_DEBUG1, + "%d qps scaled slip" + " by %.2f from %d to %d", + (int)qps, scale, + slip, new_slip); + slip = new_slip; + rrl->slip.scaled = slip; + } + } + if (slip != 0 && e->key.s.rtype != DNS_RRL_RTYPE_ALL) { + if (e->slip_cnt++ == 0) { + if ((int) e->slip_cnt >= slip) + e->slip_cnt = 0; + if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DEBUG3)) + debit_log(e, age, "slip"); + return (DNS_RRL_RESULT_SLIP); + } else if ((int) e->slip_cnt >= slip) { + e->slip_cnt = 0; + } + } + + if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DEBUG3)) + debit_log(e, age, "drop"); + return (DNS_RRL_RESULT_DROP); +} + +static inline dns_rrl_qname_buf_t * +get_qname(dns_rrl_t *rrl, const dns_rrl_entry_t *e) { + dns_rrl_qname_buf_t *qbuf; + + qbuf = rrl->qnames[e->log_qname]; + if (qbuf == NULL || qbuf->e != e) + return (NULL); + return (qbuf); +} + +static inline void +free_qname(dns_rrl_t *rrl, dns_rrl_entry_t *e) { + dns_rrl_qname_buf_t *qbuf; + + qbuf = get_qname(rrl, e); + if (qbuf != NULL) { + qbuf->e = NULL; + ISC_LIST_APPEND(rrl->qname_free, qbuf, link); + } +} + +static void +add_log_str(isc_buffer_t *lb, const char *str, unsigned int str_len) { + isc_region_t region; + + isc_buffer_availableregion(lb, ®ion); + if (str_len >= region.length) { + if (region.length <= 0) + return; + str_len = region.length; + } + memcpy(region.base, str, str_len); + isc_buffer_add(lb, str_len); +} + +#define ADD_LOG_CSTR(eb, s) add_log_str(eb, s, sizeof(s)-1) + +/* + * Build strings for the logs + */ +static void +make_log_buf(dns_rrl_t *rrl, dns_rrl_entry_t *e, + const char *str1, const char *str2, isc_boolean_t plural, + dns_name_t *qname, isc_boolean_t save_qname, + dns_rrl_result_t rrl_result, isc_result_t resp_result, + char *log_buf, unsigned int log_buf_len) +{ + isc_buffer_t lb; + dns_rrl_qname_buf_t *qbuf; + isc_netaddr_t cidr; + char strbuf[ISC_MAX(sizeof("/123"), sizeof(" (12345678)"))]; + const char *rstr; + isc_result_t msg_result; + + if (log_buf_len <= 1) { + if (log_buf_len == 1) + log_buf[0] = '\0'; + return; + } + isc_buffer_init(&lb, log_buf, log_buf_len-1); + + if (str1 != NULL) + add_log_str(&lb, str1, strlen(str1)); + if (str2 != NULL) + add_log_str(&lb, str2, strlen(str2)); + + switch (rrl_result) { + case DNS_RRL_RESULT_OK: + break; + case DNS_RRL_RESULT_DROP: + ADD_LOG_CSTR(&lb, "drop "); + break; + case DNS_RRL_RESULT_SLIP: + ADD_LOG_CSTR(&lb, "slip "); + break; + default: + INSIST(0); + break; + } + + switch (e->key.s.rtype) { + case DNS_RRL_RTYPE_QUERY: + break; + case DNS_RRL_RTYPE_REFERRAL: + ADD_LOG_CSTR(&lb, "referral "); + break; + case DNS_RRL_RTYPE_NODATA: + ADD_LOG_CSTR(&lb, "NODATA "); + break; + case DNS_RRL_RTYPE_NXDOMAIN: + ADD_LOG_CSTR(&lb, "NXDOMAIN "); + break; + case DNS_RRL_RTYPE_ERROR: + if (resp_result == ISC_R_SUCCESS) { + ADD_LOG_CSTR(&lb, "error "); + } else { + rstr = isc_result_totext(resp_result); + add_log_str(&lb, rstr, strlen(rstr)); + ADD_LOG_CSTR(&lb, " error "); + } + break; + case DNS_RRL_RTYPE_ALL: + ADD_LOG_CSTR(&lb, "all "); + break; + default: + INSIST(0); + } + + if (plural) + ADD_LOG_CSTR(&lb, "responses to "); + else + ADD_LOG_CSTR(&lb, "response to "); + + memset(&cidr, 0, sizeof(cidr)); + if (e->key.s.ipv6) { + snprintf(strbuf, sizeof(strbuf), "/%d", rrl->ipv6_prefixlen); + cidr.family = AF_INET6; + memset(&cidr.type.in6, 0, sizeof(cidr.type.in6)); + memcpy(&cidr.type.in6, e->key.s.ip, sizeof(e->key.s.ip)); + } else { + snprintf(strbuf, sizeof(strbuf), "/%d", rrl->ipv4_prefixlen); + cidr.family = AF_INET; + cidr.type.in.s_addr = e->key.s.ip[0]; + } + msg_result = isc_netaddr_totext(&cidr, &lb); + if (msg_result != ISC_R_SUCCESS) + ADD_LOG_CSTR(&lb, "?"); + add_log_str(&lb, strbuf, strlen(strbuf)); + + if (e->key.s.rtype == DNS_RRL_RTYPE_QUERY || + e->key.s.rtype == DNS_RRL_RTYPE_REFERRAL || + e->key.s.rtype == DNS_RRL_RTYPE_NODATA || + e->key.s.rtype == DNS_RRL_RTYPE_NXDOMAIN) { + qbuf = get_qname(rrl, e); + if (save_qname && qbuf == NULL && + qname != NULL && dns_name_isabsolute(qname)) { + /* + * Capture the qname for the "stop limiting" message. + */ + qbuf = ISC_LIST_TAIL(rrl->qname_free); + if (qbuf != NULL) { + ISC_LIST_UNLINK(rrl->qname_free, qbuf, link); + } else if (rrl->num_qnames < DNS_RRL_QNAMES) { + qbuf = isc_mem_get(rrl->mctx, sizeof(*qbuf)); + if (qbuf != NULL) { + memset(qbuf, 0, sizeof(*qbuf)); + ISC_LINK_INIT(qbuf, link); + qbuf->index = rrl->num_qnames; + rrl->qnames[rrl->num_qnames++] = qbuf; + } else { + isc_log_write(dns_lctx, + DNS_LOGCATEGORY_RRL, + DNS_LOGMODULE_REQUEST, + DNS_RRL_LOG_FAIL, + "isc_mem_get(%d)" + " failed for RRL qname", + (int)sizeof(*qbuf)); + } + } + if (qbuf != NULL) { + e->log_qname = qbuf->index; + qbuf->e = e; + dns_fixedname_init(&qbuf->qname); + dns_name_copy(qname, + dns_fixedname_name(&qbuf->qname), + NULL); + } + } + if (qbuf != NULL) + qname = dns_fixedname_name(&qbuf->qname); + if (qname != NULL) { + ADD_LOG_CSTR(&lb, " for "); + (void)dns_name_totext(qname, ISC_TRUE, &lb); + } else { + ADD_LOG_CSTR(&lb, " for (?)"); + } + if (e->key.s.rtype != DNS_RRL_RTYPE_NXDOMAIN) { + ADD_LOG_CSTR(&lb, " "); + (void)dns_rdataclass_totext(e->key.s.qclass, &lb); + if (e->key.s.rtype == DNS_RRL_RTYPE_QUERY) { + ADD_LOG_CSTR(&lb, " "); + (void)dns_rdatatype_totext(e->key.s.qtype, &lb); + } + } + snprintf(strbuf, sizeof(strbuf), " (%08x)", + e->key.s.qname_hash); + add_log_str(&lb, strbuf, strlen(strbuf)); + } + + /* + * We saved room for '\0'. + */ + log_buf[isc_buffer_usedlength(&lb)] = '\0'; +} + +static void +log_end(dns_rrl_t *rrl, dns_rrl_entry_t *e, isc_boolean_t early, + char *log_buf, unsigned int log_buf_len) +{ + if (e->logged) { + make_log_buf(rrl, e, + early ? "*" : NULL, + rrl->log_only ? "would stop limiting " + : "stop limiting ", + ISC_TRUE, NULL, ISC_FALSE, + DNS_RRL_RESULT_OK, ISC_R_SUCCESS, + log_buf, log_buf_len); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL, + DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DROP, + "%s", log_buf); + free_qname(rrl, e); + e->logged = ISC_FALSE; + --rrl->num_logged; + } +} + +/* + * Log messages for streams that have stopped being rate limited. + */ +static void +log_stops(dns_rrl_t *rrl, isc_stdtime_t now, int limit, + char *log_buf, unsigned int log_buf_len) +{ + dns_rrl_entry_t *e; + int age; + + for (e = rrl->last_logged; e != NULL; e = ISC_LIST_PREV(e, lru)) { + if (!e->logged) + continue; + if (now != 0) { + age = get_age(rrl, e, now); + if (age < DNS_RRL_STOP_LOG_SECS || + response_balance(rrl, e, age) < 0) + break; + } + + log_end(rrl, e, now == 0, log_buf, log_buf_len); + if (rrl->num_logged <= 0) + break; + + /* + * Too many messages could stall real work. + */ + if (--limit < 0) { + rrl->last_logged = ISC_LIST_PREV(e, lru); + return; + } + } + if (e == NULL) { + INSIST(rrl->num_logged == 0); + rrl->log_stops_time = now; + } + rrl->last_logged = e; +} + +/* + * Main rate limit interface. + */ +dns_rrl_result_t +dns_rrl(dns_view_t *view, + const isc_sockaddr_t *client_addr, isc_boolean_t is_tcp, + dns_rdataclass_t qclass, dns_rdatatype_t qtype, + dns_name_t *qname, isc_result_t resp_result, isc_stdtime_t now, + isc_boolean_t wouldlog, char *log_buf, unsigned int log_buf_len) +{ + dns_rrl_t *rrl; + dns_rrl_rtype_t rtype; + dns_rrl_entry_t *e; + isc_netaddr_t netclient; + int secs; + double qps, scale; + int exempt_match; + isc_result_t result; + dns_rrl_result_t rrl_result; + + INSIST(log_buf != NULL && log_buf_len > 0); + + rrl = view->rrl; + if (rrl->exempt != NULL) { + isc_netaddr_fromsockaddr(&netclient, client_addr); + result = dns_acl_match(&netclient, NULL, rrl->exempt, + &view->aclenv, &exempt_match, NULL); + if (result == ISC_R_SUCCESS && exempt_match > 0) + return (DNS_RRL_RESULT_OK); + } + + LOCK(&rrl->lock); + + /* + * Estimate total query per second rate when scaling by qps. + */ + if (rrl->qps_scale == 0) { + qps = 0.0; + scale = 1.0; + } else { + ++rrl->qps_responses; + secs = delta_rrl_time(rrl->qps_time, now); + if (secs <= 0) { + qps = rrl->qps; + } else { + qps = (1.0*rrl->qps_responses) / secs; + if (secs >= rrl->window) { + if (isc_log_wouldlog(dns_lctx, + DNS_RRL_LOG_DEBUG3)) + isc_log_write(dns_lctx, + DNS_LOGCATEGORY_RRL, + DNS_LOGMODULE_REQUEST, + DNS_RRL_LOG_DEBUG3, + "%d responses/%d seconds" + " = %d qps", + rrl->qps_responses, secs, + (int)qps); + rrl->qps = qps; + rrl->qps_responses = 0; + rrl->qps_time = now; + } else if (qps < rrl->qps) { + qps = rrl->qps; + } + } + scale = rrl->qps_scale / qps; + } + + /* + * Do maintenance once per second. + */ + if (rrl->num_logged > 0 && rrl->log_stops_time != now) + log_stops(rrl, now, 8, log_buf, log_buf_len); + + /* + * Notice TCP responses when scaling limits by qps. + * Do not try to rate limit TCP responses. + */ + if (is_tcp) { + if (scale < 1.0) { + e = get_entry(rrl, client_addr, + 0, dns_rdatatype_none, NULL, + DNS_RRL_RTYPE_TCP, now, ISC_TRUE, + log_buf, log_buf_len); + if (e != NULL) { + e->responses = -(rrl->window+1); + set_age(rrl, e, now); + } + } + UNLOCK(&rrl->lock); + return (ISC_R_SUCCESS); + } + + /* + * Find the right kind of entry, creating it if necessary. + * If that is impossible, then nothing more can be done + */ + switch (resp_result) { + case ISC_R_SUCCESS: + rtype = DNS_RRL_RTYPE_QUERY; + break; + case DNS_R_DELEGATION: + rtype = DNS_RRL_RTYPE_REFERRAL; + break; + case DNS_R_NXRRSET: + rtype = DNS_RRL_RTYPE_NODATA; + break; + case DNS_R_NXDOMAIN: + rtype = DNS_RRL_RTYPE_NXDOMAIN; + break; + default: + rtype = DNS_RRL_RTYPE_ERROR; + break; + } + e = get_entry(rrl, client_addr, qclass, qtype, qname, rtype, + now, ISC_TRUE, log_buf, log_buf_len); + if (e == NULL) { + UNLOCK(&rrl->lock); + return (DNS_RRL_RESULT_OK); + } + + if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DEBUG1)) { + /* + * Do not worry about speed or releasing the lock. + * This message appears before messages from debit_rrl_entry(). + */ + make_log_buf(rrl, e, "consider limiting ", NULL, ISC_FALSE, + qname, ISC_FALSE, DNS_RRL_RESULT_OK, resp_result, + log_buf, log_buf_len); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL, + DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DEBUG1, + "%s", log_buf); + } + + rrl_result = debit_rrl_entry(rrl, e, qps, scale, client_addr, now, + log_buf, log_buf_len); + + if (rrl->all_per_second.r != 0) { + /* + * We must debit the all-per-second token bucket if we have + * an all-per-second limit for the IP address. + * The all-per-second limit determines the log message + * when both limits are hit. + * The response limiting must continue if the + * all-per-second limiting lapses. + */ + dns_rrl_entry_t *e_all; + dns_rrl_result_t rrl_all_result; + + e_all = get_entry(rrl, client_addr, + 0, dns_rdatatype_none, NULL, + DNS_RRL_RTYPE_ALL, now, ISC_TRUE, + log_buf, log_buf_len); + if (e_all == NULL) { + UNLOCK(&rrl->lock); + return (DNS_RRL_RESULT_OK); + } + rrl_all_result = debit_rrl_entry(rrl, e_all, qps, scale, + client_addr, now, + log_buf, log_buf_len); + if (rrl_all_result != DNS_RRL_RESULT_OK) { + int level; + + e = e_all; + rrl_result = rrl_all_result; + if (rrl_result == DNS_RRL_RESULT_OK) + level = DNS_RRL_LOG_DEBUG2; + else + level = DNS_RRL_LOG_DEBUG1; + if (isc_log_wouldlog(dns_lctx, level)) { + make_log_buf(rrl, e, + "prefer all-per-second limiting ", + NULL, ISC_TRUE, qname, ISC_FALSE, + DNS_RRL_RESULT_OK, resp_result, + log_buf, log_buf_len); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL, + DNS_LOGMODULE_REQUEST, level, + "%s", log_buf); + } + } + } + + if (rrl_result == DNS_RRL_RESULT_OK) { + UNLOCK(&rrl->lock); + return (DNS_RRL_RESULT_OK); + } + + /* + * Log occassionally in the rate-limit category. + */ + if ((!e->logged || e->log_secs >= DNS_RRL_MAX_LOG_SECS) && + isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DROP)) { + make_log_buf(rrl, e, rrl->log_only ? "would " : NULL, + e->logged ? "continue limiting " : "limit ", + ISC_TRUE, qname, ISC_TRUE, + DNS_RRL_RESULT_OK, resp_result, + log_buf, log_buf_len); + if (!e->logged) { + e->logged = ISC_TRUE; + if (++rrl->num_logged <= 1) + rrl->last_logged = e; + } + e->log_secs = 0; + + /* + * Avoid holding the lock. + */ + if (!wouldlog) { + UNLOCK(&rrl->lock); + e = NULL; + } + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL, + DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DROP, + "%s", log_buf); + } + + /* + * Make a log message for the caller. + */ + if (wouldlog) + make_log_buf(rrl, e, + rrl->log_only ? "would rate limit " : "rate limit ", + NULL, ISC_FALSE, qname, ISC_FALSE, + rrl_result, resp_result, log_buf, log_buf_len); + + if (e != NULL) { + /* + * Do not save the qname unless we might need it for + * the ending log message. + */ + if (!e->logged) + free_qname(rrl, e); + UNLOCK(&rrl->lock); + } + + return (rrl_result); +} + +void +dns_rrl_view_destroy(dns_view_t *view) { + dns_rrl_t *rrl; + dns_rrl_block_t *b; + dns_rrl_hash_t *h; + char log_buf[DNS_RRL_LOG_BUF_LEN]; + int i; + + rrl = view->rrl; + if (rrl == NULL) + return; + view->rrl = NULL; + + /* + * Assume the caller takes care of locking the view and anything else. + */ + + if (rrl->num_logged > 0) + log_stops(rrl, 0, ISC_INT32_MAX, log_buf, sizeof(log_buf)); + + for (i = 0; i < DNS_RRL_QNAMES; ++i) { + if (rrl->qnames[i] == NULL) + break; + isc_mem_put(rrl->mctx, rrl->qnames[i], sizeof(*rrl->qnames[i])); + } + + if (rrl->exempt != NULL) + dns_acl_detach(&rrl->exempt); + + DESTROYLOCK(&rrl->lock); + + while (!ISC_LIST_EMPTY(rrl->blocks)) { + b = ISC_LIST_HEAD(rrl->blocks); + ISC_LIST_UNLINK(rrl->blocks, b, link); + isc_mem_put(rrl->mctx, b, b->size); + } + + h = rrl->hash; + if (h != NULL) + isc_mem_put(rrl->mctx, h, + sizeof(*h) + (h->length - 1) * sizeof(h->bins[0])); + + h = rrl->old_hash; + if (h != NULL) + isc_mem_put(rrl->mctx, h, + sizeof(*h) + (h->length - 1) * sizeof(h->bins[0])); + + isc_mem_putanddetach(&rrl->mctx, rrl, sizeof(*rrl)); +} + +isc_result_t +dns_rrl_init(dns_rrl_t **rrlp, dns_view_t *view, int min_entries) { + dns_rrl_t *rrl; + isc_result_t result; + + *rrlp = NULL; + + rrl = isc_mem_get(view->mctx, sizeof(*rrl)); + if (rrl == NULL) + return (ISC_R_NOMEMORY); + memset(rrl, 0, sizeof(*rrl)); + isc_mem_attach(view->mctx, &rrl->mctx); + result = isc_mutex_init(&rrl->lock); + if (result != ISC_R_SUCCESS) { + isc_mem_putanddetach(&rrl->mctx, rrl, sizeof(*rrl)); + return (result); + } + isc_stdtime_get(&rrl->ts_bases[0]); + + view->rrl = rrl; + + result = expand_entries(rrl, min_entries); + if (result != ISC_R_SUCCESS) { + dns_rrl_view_destroy(view); + return (result); + } + result = expand_rrl_hash(rrl, 0); + if (result != ISC_R_SUCCESS) { + dns_rrl_view_destroy(view); + return (result); + } + + *rrlp = rrl; + return (ISC_R_SUCCESS); +} diff --git a/lib/dns/validator.c b/lib/dns/validator.c index 8cf7f665..d7982caa 100644 --- a/lib/dns/validator.c +++ b/lib/dns/validator.c @@ -3753,8 +3753,7 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type, val->keytable = NULL; result = dns_view_getsecroots(val->view, &val->keytable); if (result != ISC_R_SUCCESS) - return (result); - + goto cleanup_mutex; val->keynode = NULL; val->key = NULL; val->siginfo = NULL; @@ -3787,6 +3786,9 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type, return (ISC_R_SUCCESS); + cleanup_mutex: + DESTROYLOCK(&val->lock); + cleanup_event: isc_task_detach(&tclone); isc_event_free(ISC_EVENT_PTR(&event)); diff --git a/lib/dns/view.c b/lib/dns/view.c index 9c1a201a..7daf64fa 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -49,6 +49,7 @@ #include <dns/masterdump.h> #include <dns/order.h> #include <dns/peer.h> +#include <dns/rrl.h> #include <dns/rbt.h> #include <dns/rdataset.h> #include <dns/request.h> @@ -184,6 +185,7 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, view->answeracl_exclude = NULL; view->denyanswernames = NULL; view->answernames_exclude = NULL; + view->rrl = NULL; view->provideixfr = ISC_TRUE; view->maxcachettl = 7 * 24 * 3600; view->maxncachettl = 3 * 3600; @@ -335,10 +337,16 @@ destroy(dns_view_t *view) { dns_acache_detach(&view->acache); } dns_rpz_view_destroy(view); -#else +#ifdef USE_RRL + dns_rrl_view_destroy(view); +#else /* USE_RRL */ + INSIST(view->rrl == NULL); +#endif /* USE_RRL */ +#else /* BIND9 */ INSIST(view->acache == NULL); INSIST(ISC_LIST_EMPTY(view->rpz_zones)); -#endif + INSIST(view->rrl == NULL); +#endif /* BIND9 */ if (view->requestmgr != NULL) dns_requestmgr_detach(&view->requestmgr); if (view->task != NULL) @@ -560,6 +568,8 @@ dialup(dns_zone_t *zone, void *dummy) { void dns_view_dialup(dns_view_t *view) { REQUIRE(DNS_VIEW_VALID(view)); + REQUIRE(view->zonetable != NULL); + (void)dns_zt_apply(view->zonetable, ISC_FALSE, dialup, NULL); } #endif @@ -868,6 +878,7 @@ dns_view_addzone(dns_view_t *view, dns_zone_t *zone) { REQUIRE(DNS_VIEW_VALID(view)); REQUIRE(!view->frozen); + REQUIRE(view->zonetable != NULL); result = dns_zt_mount(view->zonetable, zone); @@ -882,6 +893,7 @@ dns_view_findzone(dns_view_t *view, dns_name_t *name, dns_zone_t **zonep) { REQUIRE(DNS_VIEW_VALID(view)); + LOCK(&view->lock); if (view->zonetable != NULL) { result = dns_zt_find(view->zonetable, name, 0, NULL, zonep); if (result == DNS_R_PARTIALMATCH) { @@ -890,6 +902,7 @@ dns_view_findzone(dns_view_t *view, dns_name_t *name, dns_zone_t **zonep) { } } else result = ISC_R_NOTFOUND; + UNLOCK(&view->lock); return (result); } @@ -952,7 +965,12 @@ dns_view_find2(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type, is_staticstub_zone = ISC_FALSE; #ifdef BIND9 zone = NULL; - result = dns_zt_find(view->zonetable, name, 0, NULL, &zone); + LOCK(&view->lock); + if (view->zonetable != NULL) + result = dns_zt_find(view->zonetable, name, 0, NULL, &zone); + else + result = ISC_R_NOTFOUND; + UNLOCK(&view->lock); if (zone != NULL && dns_zone_gettype(zone) == dns_zone_staticstub && !use_static_stub) { result = ISC_R_NOTFOUND; @@ -1223,9 +1241,14 @@ dns_view_findzonecut2(dns_view_t *view, dns_name_t *name, dns_name_t *fname, */ #ifdef BIND9 zone = NULL; - result = dns_zt_find(view->zonetable, name, 0, NULL, &zone); + LOCK(&view->lock); + if (view->zonetable != NULL) + result = dns_zt_find(view->zonetable, name, 0, NULL, &zone); + else + result = ISC_R_NOTFOUND; if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) result = dns_zone_getdb(zone, &db); + UNLOCK(&view->lock); #else result = ISC_R_NOTFOUND; #endif @@ -1415,7 +1438,13 @@ dns_viewlist_findzone(dns_viewlist_t *list, dns_name_t *name, * treat it as not found. */ zp = (zone1 == NULL) ? &zone1 : &zone2; - result = dns_zt_find(view->zonetable, name, 0, NULL, zp); + LOCK(&view->lock); + if (view->zonetable != NULL) + result = dns_zt_find(view->zonetable, name, 0, + NULL, zp); + else + result = ISC_R_NOTFOUND; + UNLOCK(&view->lock); INSIST(result == ISC_R_SUCCESS || result == ISC_R_NOTFOUND || result == DNS_R_PARTIALMATCH); @@ -1706,13 +1735,17 @@ dns_view_getrootdelonly(dns_view_t *view) { #ifdef BIND9 isc_result_t dns_view_freezezones(dns_view_t *view, isc_boolean_t value) { + REQUIRE(DNS_VIEW_VALID(view)); + REQUIRE(view->zonetable != NULL); + return (dns_zt_freezezones(view->zonetable, value)); } #endif void dns_view_setresstats(dns_view_t *view, isc_stats_t *stats) { + REQUIRE(DNS_VIEW_VALID(view)); REQUIRE(!view->frozen); REQUIRE(view->resstats == NULL); diff --git a/lib/dns/win32/libdns.def b/lib/dns/win32/libdns.def index 3446d1d4..46327635 100644 --- a/lib/dns/win32/libdns.def +++ b/lib/dns/win32/libdns.def @@ -657,6 +657,9 @@ dns_rriterator_init dns_rriterator_next dns_rriterator_nextrrset dns_rriterator_pause +dns_rrl +dns_rrl_init +dns_rrl_view_destroy dns_sdb_putnamedrr dns_sdb_putrdata dns_sdb_putrr diff --git a/lib/dns/win32/libdns.dsp b/lib/dns/win32/libdns.dsp index 6a0c2c91..3ed7d5a9 100644 --- a/lib/dns/win32/libdns.dsp +++ b/lib/dns/win32/libdns.dsp @@ -346,6 +346,10 @@ SOURCE=..\include\dns\rpz.h # End Source File
# Begin Source File
+SOURCE=..\include\dns\rrl.h
+# End Source File
+# Begin Source File
+
SOURCE=..\include\dns\rriterator.h
# End Source File
# Begin Source File
@@ -650,6 +654,10 @@ SOURCE=..\rpz.c # End Source File
# Begin Source File
+SOURCE=..\rrl.c
+# End Source File
+# Begin Source File
+
SOURCE=..\rriterator.c
# End Source File
# Begin Source File
diff --git a/lib/dns/win32/libdns.mak b/lib/dns/win32/libdns.mak index c0c37ddb..8de50581 100644 --- a/lib/dns/win32/libdns.mak +++ b/lib/dns/win32/libdns.mak @@ -184,6 +184,7 @@ CLEAN : -@erase "$(INTDIR)\result.obj"
-@erase "$(INTDIR)\rootns.obj"
-@erase "$(INTDIR)\rpz.obj"
+ -@erase "$(INTDIR)\rrl.obj"
-@erase "$(INTDIR)\sdb.obj"
-@erase "$(INTDIR)\sdlz.obj"
-@erase "$(INTDIR)\soa.obj"
@@ -309,6 +310,7 @@ LINK32_OBJS= \ "$(INTDIR)\result.obj" \
"$(INTDIR)\rootns.obj" \
"$(INTDIR)\rpz.obj" \
+ "$(INTDIR)\rrl.obj" \
"$(INTDIR)\rriterator.obj" \
"$(INTDIR)\sdb.obj" \
"$(INTDIR)\sdlz.obj" \
@@ -505,6 +507,8 @@ CLEAN : -@erase "$(INTDIR)\rootns.sbr"
-@erase "$(INTDIR)\rpz.obj"
-@erase "$(INTDIR)\rpz.sbr"
+ -@erase "$(INTDIR)\rrl.obj"
+ -@erase "$(INTDIR)\rrl.sbr"
-@erase "$(INTDIR)\rriterator.obj"
-@erase "$(INTDIR)\rriterator.sbr"
-@erase "$(INTDIR)\sdb.obj"
@@ -651,6 +655,7 @@ BSC32_SBRS= \ "$(INTDIR)\result.sbr" \
"$(INTDIR)\rootns.sbr" \
"$(INTDIR)\rpz.sbr" \
+ "$(INTDIR)\rrl.sbr" \
"$(INTDIR)\rriterator.sbr" \
"$(INTDIR)\sdb.sbr" \
"$(INTDIR)\sdlz.sbr" \
@@ -748,6 +753,7 @@ LINK32_OBJS= \ "$(INTDIR)\result.obj" \
"$(INTDIR)\rootns.obj" \
"$(INTDIR)\rpz.obj" \
+ "$(INTDIR)\rrl.obj" \
"$(INTDIR)\rriterator.obj" \
"$(INTDIR)\sdb.obj" \
"$(INTDIR)\sdlz.obj" \
@@ -1726,6 +1732,24 @@ SOURCE=..\rpz.c !ENDIF
+SOURCE=..\rrl.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\rrl.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\rrl.obj" "$(INTDIR)\rrl.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
SOURCE=..\rriterator.c
!IF "$(CFG)" == "libdns - Win32 Release"
diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 10ba807c..bd2ad74a 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -13885,8 +13885,18 @@ forward_callback(isc_task_t *task, isc_event_t *event) { case dns_rcode_yxrrset: case dns_rcode_nxrrset: case dns_rcode_refused: - case dns_rcode_nxdomain: + case dns_rcode_nxdomain: { + char rcode[128]; + isc_buffer_t rb; + + isc_buffer_init(&rb, rcode, sizeof(rcode)); + (void)dns_rcode_totext(msg->rcode, &rb); + dns_zone_log(zone, ISC_LOG_INFO, + "forwarded dynamic update: " + "master %s returned: %.*s", + master, (int)rb.used, rcode); break; + } /* These should not occur if the masters/zone are valid. */ case dns_rcode_notzone: diff --git a/lib/isc/api b/lib/isc/api index 48bc766d..38d3b8f9 100644 --- a/lib/isc/api +++ b/lib/isc/api @@ -4,6 +4,6 @@ # 9.8: 80-89, 120-129 # 9.9: 90-109 # 9.9-sub: 130-139 -LIBINTERFACE = 95 -LIBREVISION = 1 -LIBAGE = 0 +LIBINTERFACE = 96 +LIBREVISION = 0 +LIBAGE = 1 diff --git a/lib/isc/app_api.c b/lib/isc/app_api.c index ce767d17..709f2f25 100644 --- a/lib/isc/app_api.c +++ b/lib/isc/app_api.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2009, 2013 Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -91,6 +91,16 @@ isc_app_ctxrun(isc_appctx_t *ctx) { } isc_result_t +isc_app_ctxonrun(isc_appctx_t *ctx, isc_mem_t *mctx, + isc_task_t *task, isc_taskaction_t action, + void *arg) +{ + REQUIRE(ISCAPI_APPCTX_VALID(ctx)); + + return (ctx->methods->ctxonrun(ctx, mctx, task, action, arg)); +} + +isc_result_t isc_app_ctxsuspend(isc_appctx_t *ctx) { REQUIRE(ISCAPI_APPCTX_VALID(ctx)); diff --git a/lib/isc/include/isc/app.h b/lib/isc/include/isc/app.h index e0be7906..53810859 100644 --- a/lib/isc/include/isc/app.h +++ b/lib/isc/include/isc/app.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2007, 2009 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2007, 2009, 2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -117,6 +117,9 @@ typedef struct isc_appmethods { isc_socketmgr_t *timermgr); void (*settimermgr)(isc_appctx_t *ctx, isc_timermgr_t *timermgr); + isc_result_t (*ctxonrun)(isc_appctx_t *ctx, isc_mem_t *mctx, + isc_task_t *task, isc_taskaction_t action, + void *arg); } isc_appmethods_t; /*% @@ -153,10 +156,13 @@ isc_app_start(void); * close to the beginning of the application as possible. * * Requires: - * 'ctx' is a valid application context (for app_ctxstart()). + *\li 'ctx' is a valid application context (for app_ctxstart()). */ isc_result_t +isc_app_ctxonrun(isc_appctx_t *ctx, isc_mem_t *mctx, isc_task_t *task, + isc_taskaction_t action, void *arg); +isc_result_t isc_app_onrun(isc_mem_t *mctx, isc_task_t *task, isc_taskaction_t action, void *arg); /*!< @@ -164,6 +170,7 @@ isc_app_onrun(isc_mem_t *mctx, isc_task_t *task, isc_taskaction_t action, * * Requires: *\li isc_app_start() has been called. + *\li 'ctx' is a valid application context (for app_ctxonrun()). * * Returns: * ISC_R_SUCCESS diff --git a/lib/isc/log.c b/lib/isc/log.c index 024d97c6..35204cfb 100644 --- a/lib/isc/log.c +++ b/lib/isc/log.c @@ -767,7 +767,7 @@ isc_log_createchannel(isc_logconfig_t *lcfg, const char *name, break; default: - isc_mem_put(mctx, channel->name, strlen(channel->name) + 1); + isc_mem_free(mctx, channel->name); isc_mem_put(mctx, channel, sizeof(*channel)); return (ISC_R_UNEXPECTED); } diff --git a/lib/isc/unix/app.c b/lib/isc/unix/app.c index 5393be94..d97d7c6b 100644 --- a/lib/isc/unix/app.c +++ b/lib/isc/unix/app.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007-2009 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007-2009, 2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -107,6 +107,11 @@ ISC_APPFUNC_SCOPE void isc__appctx_setsocketmgr(isc_appctx_t *ctx, isc_socketmgr_t *socketmgr); ISC_APPFUNC_SCOPE void isc__appctx_settimermgr(isc_appctx_t *ctx, isc_timermgr_t *timermgr); +ISC_APPFUNC_SCOPE isc_result_t isc__app_ctxonrun(isc_appctx_t *ctx, + isc_mem_t *mctx, + isc_task_t *task, + isc_taskaction_t action, + void *arg); /* * The application context of this module. This implementation actually @@ -148,8 +153,8 @@ static struct { * The following are defined just for avoiding unused static functions. */ #ifndef BIND9 - void *run, *shutdown, *start, *onrun, *reload, *finish, - *block, *unblock; + void *run, *shutdown, *start, *onrun, + *reload, *finish, *block, *unblock; #endif } appmethods = { { @@ -161,7 +166,8 @@ static struct { isc__app_ctxfinish, isc__appctx_settaskmgr, isc__appctx_setsocketmgr, - isc__appctx_settimermgr + isc__appctx_settimermgr, + isc__app_ctxonrun } #ifndef BIND9 , @@ -387,13 +393,22 @@ ISC_APPFUNC_SCOPE isc_result_t isc__app_onrun(isc_mem_t *mctx, isc_task_t *task, isc_taskaction_t action, void *arg) { + return (isc__app_ctxonrun((isc_appctx_t *)&isc_g_appctx, mctx, + task, action, arg)); +} + +isc_result_t +isc__app_ctxonrun(isc_appctx_t *ctx0, isc_mem_t *mctx, isc_task_t *task, + isc_taskaction_t action, void *arg) +{ + isc__appctx_t *ctx = (isc__appctx_t *)ctx0; isc_event_t *event; isc_task_t *cloned_task = NULL; isc_result_t result; - LOCK(&isc_g_appctx.lock); + LOCK(&ctx->lock); - if (isc_g_appctx.running) { + if (ctx->running) { result = ISC_R_ALREADYRUNNING; goto unlock; } @@ -410,12 +425,12 @@ isc__app_onrun(isc_mem_t *mctx, isc_task_t *task, isc_taskaction_t action, goto unlock; } - ISC_LIST_APPEND(isc_g_appctx.on_run, event, ev_link); + ISC_LIST_APPEND(ctx->on_run, event, ev_link); result = ISC_R_SUCCESS; unlock: - UNLOCK(&isc_g_appctx.lock); + UNLOCK(&ctx->lock); return (result); } diff --git a/lib/isc/unix/include/isc/Makefile.in b/lib/isc/unix/include/isc/Makefile.in index d3b50842..6acad006 100644 --- a/lib/isc/unix/include/isc/Makefile.in +++ b/lib/isc/unix/include/isc/Makefile.in @@ -1,4 +1,4 @@ -# Copyright (C) 2004, 2007, 2012 Internet Systems Consortium, Inc. ("ISC") +# Copyright (C) 2004, 2007, 2012, 2013 Internet Systems Consortium, Inc. ("ISC") # Copyright (C) 1998-2001 Internet Software Consortium. # # Permission to use, copy, modify, and/or distribute this software for any @@ -21,7 +21,7 @@ top_srcdir = @top_srcdir@ @BIND9_VERSION@ -HEADERS = dir.h int.h net.h netdb.h offset.h stdtime.h \ +HEADERS = dir.h int.h net.h netdb.h offset.h stat.h stdtime.h \ syslog.h time.h SUBDIRS = diff --git a/lib/isc/win32/include/isc/Makefile.in b/lib/isc/win32/include/isc/Makefile.in index 6b5bcea2..997de397 100644 --- a/lib/isc/win32/include/isc/Makefile.in +++ b/lib/isc/win32/include/isc/Makefile.in @@ -1,4 +1,4 @@ -# Copyright (C) 2004, 2007, 2012 Internet Systems Consortium, Inc. ("ISC") +# Copyright (C) 2004, 2007, 2012, 2013 Internet Systems Consortium, Inc. ("ISC") # Copyright (C) 1999-2001 Internet Software Consortium. # # Permission to use, copy, modify, and/or distribute this software for any @@ -22,7 +22,7 @@ top_srcdir = @top_srcdir@ @BIND9_VERSION@ HEADERS = dir.h int.h mutex.h net.h netdb.h once.h \ - stdtime.h thread.h time.h + stat.h stdtime.h thread.h time.h SUBDIRS = TARGETS = diff --git a/lib/isc/win32/include/isc/net.h b/lib/isc/win32/include/isc/net.h index 43e424d8..5ac325b8 100644 --- a/lib/isc/win32/include/isc/net.h +++ b/lib/isc/win32/include/isc/net.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007, 2008, 2012 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007, 2008, 2012, 2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -189,41 +189,113 @@ typedef isc_uint16_t in_port_t; * Use the WSA constants instead. */ +#include <errno.h> + +#ifndef EWOULDBLOCK #define EWOULDBLOCK WSAEWOULDBLOCK +#endif +#ifndef EINPROGRESS #define EINPROGRESS WSAEINPROGRESS +#endif +#ifndef EALREADY #define EALREADY WSAEALREADY +#endif +#ifndef ENOTSOCK #define ENOTSOCK WSAENOTSOCK +#endif +#ifndef EDESTADDRREQ #define EDESTADDRREQ WSAEDESTADDRREQ +#endif +#ifndef EMSGSIZE #define EMSGSIZE WSAEMSGSIZE +#endif +#ifndef EPROTOTYPE #define EPROTOTYPE WSAEPROTOTYPE +#endif +#ifndef ENOPROTOOPT #define ENOPROTOOPT WSAENOPROTOOPT +#endif +#ifndef EPROTONOSUPPORT #define EPROTONOSUPPORT WSAEPROTONOSUPPORT +#endif +#ifndef ESOCKTNOSUPPORT #define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT +#endif +#ifndef EOPNOTSUPP #define EOPNOTSUPP WSAEOPNOTSUPP +#endif +#ifndef EPFNOSUPPORT #define EPFNOSUPPORT WSAEPFNOSUPPORT +#endif +#ifndef EAFNOSUPPORT #define EAFNOSUPPORT WSAEAFNOSUPPORT +#endif +#ifndef EADDRINUSE #define EADDRINUSE WSAEADDRINUSE +#endif +#ifndef EADDRNOTAVAIL #define EADDRNOTAVAIL WSAEADDRNOTAVAIL +#endif +#ifndef ENETDOWN #define ENETDOWN WSAENETDOWN +#endif +#ifndef ENETUNREACH #define ENETUNREACH WSAENETUNREACH +#endif +#ifndef ENETRESET #define ENETRESET WSAENETRESET +#endif +#ifndef ECONNABORTED #define ECONNABORTED WSAECONNABORTED +#endif +#ifndef ECONNRESET #define ECONNRESET WSAECONNRESET +#endif +#ifndef ENOBUFS #define ENOBUFS WSAENOBUFS +#endif +#ifndef EISCONN #define EISCONN WSAEISCONN +#endif +#ifndef ENOTCONN #define ENOTCONN WSAENOTCONN +#endif +#ifndef ESHUTDOWN #define ESHUTDOWN WSAESHUTDOWN +#endif +#ifndef ETOOMANYREFS #define ETOOMANYREFS WSAETOOMANYREFS +#endif +#ifndef ETIMEDOUT #define ETIMEDOUT WSAETIMEDOUT +#endif +#ifndef ECONNREFUSED #define ECONNREFUSED WSAECONNREFUSED +#endif +#ifndef ELOOP #define ELOOP WSAELOOP +#endif +#ifndef EHOSTDOWN #define EHOSTDOWN WSAEHOSTDOWN +#endif +#ifndef EHOSTUNREACH #define EHOSTUNREACH WSAEHOSTUNREACH +#endif +#ifndef EPROCLIM #define EPROCLIM WSAEPROCLIM +#endif +#ifndef EUSERS #define EUSERS WSAEUSERS +#endif +#ifndef EDQUOT #define EDQUOT WSAEDQUOT +#endif +#ifndef ESTALE #define ESTALE WSAESTALE +#endif +#ifndef EREMOTE #define EREMOTE WSAEREMOTE +#endif /*** diff --git a/lib/isc/win32/netdb.h b/lib/isc/win32/netdb.h index f8d936a9..02d3c668 100644 --- a/lib/isc/win32/netdb.h +++ b/lib/isc/win32/netdb.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2006, 2007, 2009 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2006, 2007, 2009, 2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2000, 2001 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -27,6 +27,7 @@ * Define if <netdb.h> does not declare struct addrinfo. */ +#if _MSC_VER < 1600 struct addrinfo { int ai_flags; /* AI_PASSIVE, AI_CANONNAME */ int ai_family; /* PF_xxx */ @@ -37,6 +38,7 @@ struct addrinfo { struct sockaddr *ai_addr; /* Binary address */ struct addrinfo *ai_next; /* Next structure in linked list */ }; +#endif /* diff --git a/lib/isc/win32/stdio.c b/lib/isc/win32/stdio.c index 427a8e1e..69a1c6f0 100644 --- a/lib/isc/win32/stdio.c +++ b/lib/isc/win32/stdio.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2007, 2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2000, 2001 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -50,6 +50,7 @@ isc_stdio_close(FILE *f) { isc_result_t isc_stdio_seek(FILE *f, long offset, int whence) { + /* based on the fact off_t is typedef to long */ int r; r = fseek(f, offset, whence); diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 431af746..15fd5549 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -1269,6 +1269,39 @@ static cfg_type_t cfg_type_rpz = { rpz_fields }; +#ifdef USE_RRL +/* + * rate-limit + */ +static cfg_clausedef_t rrl_clauses[] = { + { "responses-per-second", &cfg_type_uint32, 0 }, + { "referrals-per-second", &cfg_type_uint32, 0 }, + { "nodata-per-second", &cfg_type_uint32, 0 }, + { "nxdomains-per-second", &cfg_type_uint32, 0 }, + { "errors-per-second", &cfg_type_uint32, 0 }, + { "all-per-second", &cfg_type_uint32, 0 }, + { "slip", &cfg_type_uint32, 0 }, + { "window", &cfg_type_uint32, 0 }, + { "log-only", &cfg_type_boolean, 0 }, + { "qps-scale", &cfg_type_uint32, 0 }, + { "ipv4-prefix-length", &cfg_type_uint32, 0 }, + { "ipv6-prefix-length", &cfg_type_uint32, 0 }, + { "exempt-clients", &cfg_type_bracketed_aml, 0 }, + { "max-table-size", &cfg_type_uint32, 0 }, + { "min-table-size", &cfg_type_uint32, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_clausedef_t *rrl_clausesets[] = { + rrl_clauses, + NULL +}; + +static cfg_type_t cfg_type_rrl = { + "rate-limit", cfg_parse_map, cfg_print_map, cfg_doc_map, + &cfg_rep_map, rrl_clausesets +}; +#endif /* USE_RRL */ /*% * dnssec-lookaside @@ -1423,6 +1456,9 @@ view_clauses[] = { CFG_CLAUSEFLAG_NOTCONFIGURED }, #endif { "response-policy", &cfg_type_rpz, 0 }, +#ifdef USE_RRL + { "rate-limit", &cfg_type_rrl, 0 }, +#endif /* USE_RRL */ { NULL, NULL, 0 } }; diff --git a/lib/lwres/api b/lib/lwres/api index 95bd2046..51898234 100644 --- a/lib/lwres/api +++ b/lib/lwres/api @@ -5,5 +5,5 @@ # 9.9: 90-109 # 9.9-sub: 130-139 LIBINTERFACE = 90 -LIBREVISION = 4 +LIBREVISION = 5 LIBAGE = 0 diff --git a/lib/lwres/win32/include/lwres/net.h b/lib/lwres/win32/include/lwres/net.h index 94e4af43..42a061ff 100644 --- a/lib/lwres/win32/include/lwres/net.h +++ b/lib/lwres/win32/include/lwres/net.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2007, 2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2000, 2001 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -78,15 +78,15 @@ #define FD_CLR(fd, set) do { \ u_int __i; \ for (__i = 0; __i < ((fd_set FAR *)(set))->fd_count; __i++) { \ - if (((fd_set FAR *)(set))->fd_array[__i] == (SOCKET) fd) { \ - while (__i < ((fd_set FAR *)(set))->fd_count-1) { \ - ((fd_set FAR *)(set))->fd_array[__i] = \ - ((fd_set FAR *)(set))->fd_array[__i+1]; \ - __i++; \ - } \ - ((fd_set FAR *)(set))->fd_count--; \ - break; \ - } \ + if (((fd_set FAR *)(set))->fd_array[__i] == (SOCKET) fd) { \ + while (__i < ((fd_set FAR *)(set))->fd_count-1) { \ + ((fd_set FAR *)(set))->fd_array[__i] = \ + ((fd_set FAR *)(set))->fd_array[__i+1]; \ + __i++; \ + } \ + ((fd_set FAR *)(set))->fd_count--; \ + break; \ + } \ } \ } while (0) @@ -94,15 +94,15 @@ #define FD_SET(fd, set) do { \ u_int __i; \ for (__i = 0; __i < ((fd_set FAR *)(set))->fd_count; __i++) { \ - if (((fd_set FAR *)(set))->fd_array[__i] == (SOCKET)(fd)) { \ - break; \ - } \ + if (((fd_set FAR *)(set))->fd_array[__i] == (SOCKET)(fd)) { \ + break; \ + } \ } \ if (__i == ((fd_set FAR *)(set))->fd_count) { \ - if (((fd_set FAR *)(set))->fd_count < FD_SETSIZE) { \ - ((fd_set FAR *)(set))->fd_array[__i] = (SOCKET)(fd); \ - ((fd_set FAR *)(set))->fd_count++; \ - } \ + if (((fd_set FAR *)(set))->fd_count < FD_SETSIZE) { \ + ((fd_set FAR *)(set))->fd_array[__i] = (SOCKET)(fd); \ + ((fd_set FAR *)(set))->fd_count++; \ + } \ } \ } while (0) @@ -112,41 +112,113 @@ * Use the WSA constants instead. */ +#include <errno.h> + +#ifndef EWOULDBLOCK #define EWOULDBLOCK WSAEWOULDBLOCK +#endif +#ifndef EINPROGRESS #define EINPROGRESS WSAEINPROGRESS +#endif +#ifndef EALREADY #define EALREADY WSAEALREADY +#endif +#ifndef ENOTSOCK #define ENOTSOCK WSAENOTSOCK +#endif +#ifndef EDESTADDRREQ #define EDESTADDRREQ WSAEDESTADDRREQ +#endif +#ifndef EMSGSIZE #define EMSGSIZE WSAEMSGSIZE +#endif +#ifndef EPROTOTYPE #define EPROTOTYPE WSAEPROTOTYPE +#endif +#ifndef ENOPROTOOPT #define ENOPROTOOPT WSAENOPROTOOPT +#endif +#ifndef EPROTONOSUPPORT #define EPROTONOSUPPORT WSAEPROTONOSUPPORT +#endif +#ifndef ESOCKTNOSUPPORT #define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT +#endif +#ifndef EOPNOTSUPP #define EOPNOTSUPP WSAEOPNOTSUPP +#endif +#ifndef EPFNOSUPPORT #define EPFNOSUPPORT WSAEPFNOSUPPORT +#endif +#ifndef EAFNOSUPPORT #define EAFNOSUPPORT WSAEAFNOSUPPORT +#endif +#ifndef EADDRINUSE #define EADDRINUSE WSAEADDRINUSE +#endif +#ifndef EADDRNOTAVAIL #define EADDRNOTAVAIL WSAEADDRNOTAVAIL +#endif +#ifndef ENETDOWN #define ENETDOWN WSAENETDOWN +#endif +#ifndef ENETUNREACH #define ENETUNREACH WSAENETUNREACH +#endif +#ifndef ENETRESET #define ENETRESET WSAENETRESET +#endif +#ifndef ECONNABORTED #define ECONNABORTED WSAECONNABORTED +#endif +#ifndef ECONNRESET #define ECONNRESET WSAECONNRESET +#endif +#ifndef ENOBUFS #define ENOBUFS WSAENOBUFS +#endif +#ifndef EISCONN #define EISCONN WSAEISCONN +#endif +#ifndef ENOTCONN #define ENOTCONN WSAENOTCONN +#endif +#ifndef ESHUTDOWN #define ESHUTDOWN WSAESHUTDOWN +#endif +#ifndef ETOOMANYREFS #define ETOOMANYREFS WSAETOOMANYREFS +#endif +#ifndef ETIMEDOUT #define ETIMEDOUT WSAETIMEDOUT +#endif +#ifndef ECONNREFUSED #define ECONNREFUSED WSAECONNREFUSED +#endif +#ifndef ELOOP #define ELOOP WSAELOOP +#endif +#ifndef EHOSTDOWN #define EHOSTDOWN WSAEHOSTDOWN +#endif +#ifndef EHOSTUNREACH #define EHOSTUNREACH WSAEHOSTUNREACH +#endif +#ifndef EPROCLIM #define EPROCLIM WSAEPROCLIM +#endif +#ifndef EUSERS #define EUSERS WSAEUSERS +#endif +#ifndef EDQUOT #define EDQUOT WSAEDQUOT +#endif +#ifndef ESTALE #define ESTALE WSAESTALE +#endif +#ifndef EREMOTE #define EREMOTE WSAEREMOTE +#endif LWRES_LANG_BEGINDECLS diff --git a/lib/lwres/win32/include/lwres/netdb.h b/lib/lwres/win32/include/lwres/netdb.h index 41146c88..d6a988b1 100644 --- a/lib/lwres/win32/include/lwres/netdb.h +++ b/lib/lwres/win32/include/lwres/netdb.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2006, 2007 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2006, 2007, 2013 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2000, 2001 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -31,7 +31,9 @@ /* * Define if <netdb.h> does not declare struct addrinfo. */ +#if _MSC_VER < 1600 #define ISC_LWRES_NEEDADDRINFO 1 +#endif #ifdef ISC_LWRES_NEEDADDRINFO struct addrinfo { diff --git a/lib/tests/t_api.c b/lib/tests/t_api.c index 40113751..26406315 100644 --- a/lib/tests/t_api.c +++ b/lib/tests/t_api.c @@ -514,12 +514,12 @@ t_fgetbs(FILE *fp) { int c; size_t n; size_t size; - char *buf; + char *buf, *old; char *p; - n = 0; - size = T_BUFSIZ; - buf = (char *) malloc(T_BUFSIZ * sizeof(char)); + n = 0; + size = T_BUFSIZ; + old = buf = (char *) malloc(T_BUFSIZ * sizeof(char)); if (buf != NULL) { p = buf; @@ -535,7 +535,8 @@ t_fgetbs(FILE *fp) { buf = (char *)realloc(buf, size * sizeof(char)); if (buf == NULL) - break; + goto err; + old = buf; p = buf + n; } } @@ -546,7 +547,10 @@ t_fgetbs(FILE *fp) { } return (buf); } else { - fprintf(stderr, "malloc failed %d", errno); + err: + if (old != NULL) + free(old); + fprintf(stderr, "malloc/realloc failed %d", errno); return(NULL); } } |
