summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorInternet Software Consortium, Inc <@isc.org>2013-08-14 06:35:21 -0600
committerInternet Software Consortium, Inc <@isc.org>2013-08-14 06:35:21 -0600
commitebbc86ee1eae2231a10e23f4cda592085dbc7eef (patch)
tree8e373dd37c3b0a9fb113ff78f7a15dd19f6c0911 /lib
parent87c6fc212d37ddbeb388f8308377ae38de3061d9 (diff)
downloadbind9-ebbc86ee1eae2231a10e23f4cda592085dbc7eef.tar.gz
9.9.4b1
Diffstat (limited to 'lib')
-rw-r--r--lib/bind9/api2
-rw-r--r--lib/bind9/check.c5
-rw-r--r--lib/dns/Makefile.in12
-rw-r--r--lib/dns/acache.c3
-rw-r--r--lib/dns/api4
-rw-r--r--lib/dns/client.c27
-rw-r--r--lib/dns/gssapictx.c27
-rw-r--r--lib/dns/include/dns/log.h3
-rw-r--r--lib/dns/include/dns/rrl.h278
-rw-r--r--lib/dns/include/dns/view.h2
-rw-r--r--lib/dns/log.c3
-rw-r--r--lib/dns/name.c10
-rw-r--r--lib/dns/rbtdb.c38
-rw-r--r--lib/dns/resolver.c29
-rw-r--r--lib/dns/rrl.c1324
-rw-r--r--lib/dns/validator.c6
-rw-r--r--lib/dns/view.c43
-rw-r--r--lib/dns/win32/libdns.def3
-rw-r--r--lib/dns/win32/libdns.dsp8
-rw-r--r--lib/dns/win32/libdns.mak24
-rw-r--r--lib/dns/zone.c12
-rw-r--r--lib/isc/api6
-rw-r--r--lib/isc/app_api.c12
-rw-r--r--lib/isc/include/isc/app.h11
-rw-r--r--lib/isc/log.c2
-rw-r--r--lib/isc/unix/app.c31
-rw-r--r--lib/isc/unix/include/isc/Makefile.in4
-rw-r--r--lib/isc/win32/include/isc/Makefile.in4
-rw-r--r--lib/isc/win32/include/isc/net.h74
-rw-r--r--lib/isc/win32/netdb.h4
-rw-r--r--lib/isc/win32/stdio.c3
-rw-r--r--lib/isccfg/namedconf.c36
-rw-r--r--lib/lwres/api2
-rw-r--r--lib/lwres/win32/include/lwres/net.h106
-rw-r--r--lib/lwres/win32/include/lwres/netdb.h4
-rw-r--r--lib/tests/t_api.c16
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, &region);
+ 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);
}
}