From: W.C.A. Wijngaards Date: Mon, 15 Jun 2026 13:45:03 +0000 (+0200) Subject: - Fix to add `max-transfer-size` and `max-transfer-time` that X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=153f8d5353a3cea65d161574b8e3dd35df662bcb;p=thirdparty%2Funbound.git - Fix to add `max-transfer-size` and `max-transfer-time` that limit auth-zone and rpz transfer amount and time taken. Default is disabled. This hardens against unbounded transfers. Thanks to Qifan Zhang, Palo Alto Networks, for the report. --- diff --git a/daemon/remote.c b/daemon/remote.c index dc241b09c..8a8edb8eb 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -5461,6 +5461,23 @@ xfr_masterlist_equal(struct auth_master* list1, struct auth_master* list2) return 0; } +/** See if configuration has changed. */ +static int +xfr_config_equal(struct auth_xfer* xfr1, struct auth_xfer* xfr2) +{ + if(xfr1 == NULL && xfr2 == NULL) + return 1; + if(xfr1 == NULL && xfr2 != NULL) + return 0; + if(xfr1 != NULL && xfr2 == NULL) + return 0; + if(xfr1->max_transfer_size != xfr2->max_transfer_size) + return 0; + if(xfr1->max_transfer_time != xfr2->max_transfer_time) + return 0; + return 1; +} + /** See if the list of masters has changed. */ static int xfr_masters_equal(struct auth_xfer* xfr1, struct auth_xfer* xfr2) @@ -5572,6 +5589,7 @@ auth_zones_check_changes(struct fast_reload_thread* fr, */ if(have_old != have_new || old_serial != new_serial || !xfr_masters_equal(old_xfr, new_xfr) + || !xfr_config_equal(old_xfr, new_xfr) || old_z->zonemd_callback_env != NULL) { /* The zone has been changed. */ if(!fr_add_auth_zone_change(fr, old_z, new_z, @@ -7702,6 +7720,9 @@ auth_xfr_pickup_config(struct auth_xfer* loadxfr, struct auth_xfer* xfr) log_assert(loadxfr->namelabs == xfr->namelabs); log_assert(loadxfr->dclass == xfr->dclass); + xfr->max_transfer_size = loadxfr->max_transfer_size; + xfr->max_transfer_time = loadxfr->max_transfer_time; + /* The lists can be swapped in, the other xfr struct will be deleted * afterwards. */ probe_masters = xfr->task_probe->masters; diff --git a/doc/Changelog b/doc/Changelog index e97466738..f9396cb2b 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,10 @@ +15 June 2026: Wouter + - Fix to add `max-transfer-size` and `max-transfer-time` that + limit auth-zone and rpz transfer amount and time taken. + Default is disabled. This hardens against unbounded + transfers. Thanks to Qifan Zhang, Palo Alto Networks, + for the report. + 12 June 2026: Wouter - Fix that for auth-zone and rpz zones the allow-notify addresses and netblocks are available from start, and diff --git a/doc/example.conf.in b/doc/example.conf.in index 2c6d63409..5250ef456 100644 --- a/doc/example.conf.in +++ b/doc/example.conf.in @@ -1287,6 +1287,9 @@ remote-control: # zonemd-check: no # zonemd-reject-absence: no # zonefile: "example.org.zone" +# max-transfer-size: 0 +# max-transfer-time: 0 + # Views # Create named views. Name must be unique. @@ -1453,3 +1456,5 @@ remote-control: # rpz-signal-nxdomain-ra: no # for-downstream: no # tags: "example" +# max-transfer-size: 0 +# max-transfer-time: 0 diff --git a/doc/unbound.conf.rst b/doc/unbound.conf.rst index 97fc738a4..cfd78806e 100644 --- a/doc/unbound.conf.rst +++ b/doc/unbound.conf.rst @@ -4025,6 +4025,31 @@ fallback activates to fetch from the upstream instead of the SERVFAIL. If the file does not exist or is empty, Unbound will attempt to fetch zone data (eg. from the primary servers). + +@@UAHL@unbound.conf.auth@max-transfer-size@@: ** + Number of bytes size of the maximum zone transfer size. + Larger transfers, over AXFR, IXFR and HTTP, are not allowed. + A plain number is in bytes, append 'k', 'm' or 'g' for kilobytes, megabytes + or gigabytes (1024*1024 bytes in a megabyte). + The value ``0`` disables the feature. + + Only consider for untrusted/misbehaving primaries that could hog resources + and bring down the resolver. + + Default: 0 + + +@@UAHL@unbound.conf.auth@max-transfer-time@@: ** + Maximum time in milliseconds that a zone transfer is allowed to take from + the start. + The value ``0`` disables the feature. + + Only consider for untrusted/misbehaving primaries that could hog resources + and bring down the resolver. + + Default: 0 + + .. _unbound.conf.view: View Options @@ -5205,6 +5230,31 @@ answer queries with that content. If no tags are specified the policies from this section will be applied for all clients. + +@@UAHL@unbound.conf.rpz@max-transfer-size@@: ** + Number of bytes size of the maximum zone transfer size. + Larger transfers, over AXFR, IXFR and HTTP, are not allowed. + A plain number is in bytes, append 'k', 'm' or 'g' for kilobytes, megabytes + or gigabytes (1024*1024 bytes in a megabyte). + The value ``0`` disables the feature. + + Only consider for untrusted/misbehaving primaries that could hog resources + and bring down the resolver. + + Default: 0 + + +@@UAHL@unbound.conf.rpz@max-transfer-time@@: ** + Maximum time in milliseconds that a zone transfer is allowed to take from + the start. + The value ``0`` disables the feature. + + Only consider for untrusted/misbehaving primaries that could hog resources + and bring down the resolver. + + Default: 0 + + Memory Control Example ---------------------- diff --git a/services/authzone.c b/services/authzone.c index 3fc3f174e..1ab2ec1a4 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -55,6 +55,7 @@ #include "util/log.h" #include "util/module.h" #include "util/random.h" +#include "util/timeval_func.h" #include "services/cache/dns.h" #include "services/outside_network.h" #include "services/listen_dnsport.h" @@ -2145,6 +2146,9 @@ auth_zones_cfg(struct auth_zones* az, struct config_auth* c) } return 0; } + /* Populate the xfer related options early since we may create one now */ + z->max_transfer_size = c->max_transfer_size; + z->max_transfer_time = c->max_transfer_time; if(c->masters || c->urls) { if(!(x=auth_zones_find_or_add_xfer(az, z))) { lock_rw_unlock(&az->lock); @@ -2327,6 +2331,7 @@ auth_chunks_delete(struct auth_transfer* at) } at->chunks_first = NULL; at->chunks_last = NULL; + at->chunks_total = 0; } /** free master addr list */ @@ -5592,6 +5597,7 @@ xfr_transfer_init_fetch(struct auth_xfer* xfr, struct module_env* env) t.tv_sec = timeout/1000; t.tv_usec = (timeout%1000)*1000; #endif + xfr->task_transfer->start_time = *env->now_tv; if(master->http) { /* perform http fetch */ @@ -6146,6 +6152,7 @@ xfer_link_data(sldns_buffer* pkt, struct auth_xfer* xfr) if(xfr->task_transfer->chunks_last) xfr->task_transfer->chunks_last->next = e; xfr->task_transfer->chunks_last = e; + xfr->task_transfer->chunks_total += e->len; return 1; } @@ -6241,6 +6248,15 @@ auth_xfer_transfer_timer_callback(void* arg) xfr_transfer_nexttarget_or_end(xfr, env); } +/** return the time taken by the transfer */ +static int +auth_xfer_transfer_time_taken(struct auth_xfer* xfr, struct module_env* env) +{ + struct timeval delta; + timeval_subtract(&delta, env->now_tv, &xfr->task_transfer->start_time); + return ((int)delta.tv_sec)*1000 + ((int)delta.tv_usec)/1000; +} + /** callback for task_transfer tcp connections */ int auth_xfer_transfer_tcp_callback(struct comm_point* c, void* arg, int err, @@ -6307,6 +6323,15 @@ auth_xfer_transfer_tcp_callback(struct comm_point* c, void* arg, int err, xfr->task_transfer->master->host); goto failed; } + if(xfr->max_transfer_size > 0 && + xfr->task_transfer->chunks_total > xfr->max_transfer_size) { + char zname[LDNS_MAX_DOMAINLEN]; + dname_str(xfr->name, zname); + log_err("auth zone %s transfer from %s exceeded %u bytes, aborting", + zname, xfr->task_transfer->master->host, + (unsigned)xfr->max_transfer_size); + goto failed; + } /* if the transfer is done now, disconnect and process the list */ if(transferdone) { comm_point_delete(xfr->task_transfer->cp); @@ -6315,6 +6340,16 @@ auth_xfer_transfer_tcp_callback(struct comm_point* c, void* arg, int err, return 0; } + if(xfr->max_transfer_time > 0 && + auth_xfer_transfer_time_taken(xfr, env) > xfr->max_transfer_time) { + char zname[LDNS_MAX_DOMAINLEN]; + dname_str(xfr->name, zname); + log_err("auth zone %s transfer from %s exceeded %u msec total running time, aborting", + zname, xfr->task_transfer->master->host, + (unsigned)xfr->max_transfer_time); + goto failed; + } + /* if we want to read more messages, setup the commpoint to read * a DNS packet, and the timeout */ lock_basic_unlock(&xfr->lock); @@ -6370,6 +6405,16 @@ auth_xfer_transfer_http_callback(struct comm_point* c, void* arg, int err, xfr->task_transfer->master->host); goto failed; } + if(xfr->max_transfer_size > 0 && + xfr->task_transfer->chunks_total > xfr->max_transfer_size) { + char zname[LDNS_MAX_DOMAINLEN]; + dname_str(xfr->name, zname); + log_err("auth zone %s http %s/%s exceeded %u bytes, aborting", + zname, xfr->task_transfer->master->host, + xfr->task_transfer->master->file, + (unsigned)xfr->max_transfer_size); + goto failed; + } } /* if the transfer is done now, disconnect and process the list */ if(err == NETEVENT_DONE) { @@ -6381,6 +6426,17 @@ auth_xfer_transfer_http_callback(struct comm_point* c, void* arg, int err, return 0; } + if(xfr->max_transfer_time > 0 && + auth_xfer_transfer_time_taken(xfr, env) > xfr->max_transfer_time) { + char zname[LDNS_MAX_DOMAINLEN]; + dname_str(xfr->name, zname); + log_err("auth zone %s transfer http %s/%s exceeded %u msec total running time, aborting", + zname, xfr->task_transfer->master->host, + xfr->task_transfer->master->file, + (unsigned)xfr->max_transfer_time); + goto failed; + } + /* if we want to read more messages, setup the commpoint to read * a DNS packet, and the timeout */ lock_basic_unlock(&xfr->lock); @@ -7172,6 +7228,8 @@ auth_xfer_new(struct auth_zone* z) xfr->namelen = z->namelen; xfr->namelabs = z->namelabs; xfr->dclass = z->dclass; + xfr->max_transfer_size = z->max_transfer_size; + xfr->max_transfer_time = z->max_transfer_time; xfr->task_nextprobe = (struct auth_nextprobe*)calloc(1, sizeof(struct auth_nextprobe)); diff --git a/services/authzone.h b/services/authzone.h index 2086a99ee..3862c6431 100644 --- a/services/authzone.h +++ b/services/authzone.h @@ -153,6 +153,10 @@ struct auth_zone { struct auth_zone* rpz_az_next; /** previous auth zone containing RPZ data, or NULL */ struct auth_zone* rpz_az_prev; + /** The maximum auth zone transfer size, in bytes. */ + size_t max_transfer_size; + /** The maximum auth zone transfer time taken, in msec. */ + int max_transfer_time; }; /** @@ -283,6 +287,11 @@ struct auth_xfer { * this is renewed every SOA probe and transfer. On zone load * from zonefile it is also set (with probe set soon to check) */ time_t lease_time; + + /** The maximum auth zone transfer size, in bytes. */ + size_t max_transfer_size; + /** The maximum auth zone transfer time taken, in msec. */ + int max_transfer_time; }; /** @@ -379,6 +388,10 @@ struct auth_transfer { struct auth_chunk* chunks_first; /** last element in chunks list (to append new data at the end) */ struct auth_chunk* chunks_last; + /** running total of bytes held in chunks_first..chunks_last */ + size_t chunks_total; + /** start time of the transfer */ + struct timeval start_time; /** list of upstream masters for this zone, from config */ struct auth_master* masters; diff --git a/testdata/auth_transfer_limit.tdir/auth_transfer_limit.conf b/testdata/auth_transfer_limit.tdir/auth_transfer_limit.conf new file mode 100644 index 000000000..94b4475b9 --- /dev/null +++ b/testdata/auth_transfer_limit.tdir/auth_transfer_limit.conf @@ -0,0 +1,32 @@ +server: + verbosity: 7 + # num-threads: 1 + interface: 127.0.0.1 + port: @PORT@ + use-syslog: no + directory: "" + pidfile: "unbound.pid" + chroot: "" + username: "" + do-not-query-localhost: no + use-caps-for-id: no + +auth-zone: + name: "example.com" + for-upstream: yes + for-downstream: yes + master: "127.0.0.1@@TOPORT@" + max-transfer-size: 512 + max-transfer-time: 2000 +auth-zone: + name: "example2.com" + for-upstream: yes + for-downstream: yes + master: "127.0.0.1@@TOPORT@" + max-transfer-size: 512 + max-transfer-time: 2000 +remote-control: + control-enable: yes + control-interface: @CONTROL_PATH@/controlpipe.@CONTROL_PID@ + control-use-cert: no + diff --git a/testdata/auth_transfer_limit.tdir/auth_transfer_limit.dsc b/testdata/auth_transfer_limit.tdir/auth_transfer_limit.dsc new file mode 100644 index 000000000..926099e19 --- /dev/null +++ b/testdata/auth_transfer_limit.tdir/auth_transfer_limit.dsc @@ -0,0 +1,16 @@ +BaseName: auth_transfer_limit +Version: 1.0 +Description: Test limit of authority zone transfer. +CreationDate: Tue May 12 03:00:00 PM CEST 2026 +Maintainer: dr. W.C.A. Wijngaards +Category: +Component: +CmdDepends: +Depends: +Help: +Pre: auth_transfer_limit.pre +Post: auth_transfer_limit.post +Test: auth_transfer_limit.test +AuxFiles: +Passed: +Failure: diff --git a/testdata/auth_transfer_limit.tdir/auth_transfer_limit.post b/testdata/auth_transfer_limit.tdir/auth_transfer_limit.post new file mode 100644 index 000000000..4a4d8342a --- /dev/null +++ b/testdata/auth_transfer_limit.tdir/auth_transfer_limit.post @@ -0,0 +1,14 @@ +# #-- auth_transfer_limit.post --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# source the test var file when it's there +[ -f .tpkg.var.test ] && source .tpkg.var.test +# +# do your teardown here +. ../common.sh +kill_pid $FWD_PID +kill_pid $UNBOUND_PID +rm -f $CONTROL_PATH/controlpipe.$CONTROL_PID +echo "> cat logfiles" +cat fwd.log +cat unbound.log diff --git a/testdata/auth_transfer_limit.tdir/auth_transfer_limit.pre b/testdata/auth_transfer_limit.tdir/auth_transfer_limit.pre new file mode 100644 index 000000000..399101ff5 --- /dev/null +++ b/testdata/auth_transfer_limit.tdir/auth_transfer_limit.pre @@ -0,0 +1,42 @@ +# #-- auth_transfer_limit.pre--# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test + +PRE="../.." +. ../common.sh +if grep -e "define HAVE_PTHREAD 1" -e "define HAVE_SOLARIS_THREADS 1" -e "define HAVE_WINDOWS_THREADS 1" $PRE/config.h; then + TEST_FAST_RELOAD="yes" +else + TEST_FAST_RELOAD="no" +fi +echo "TEST_FAST_RELOAD=$TEST_FAST_RELOAD" >> .tpkg.var.test + +get_random_port 2 +UNBOUND_PORT=$RND_PORT +FWD_PORT=$(($RND_PORT + 1)) +echo "UNBOUND_PORT=$UNBOUND_PORT" >> .tpkg.var.test +echo "FWD_PORT=$FWD_PORT" >> .tpkg.var.test + +# start forwarder +get_ldns_testns +$LDNS_TESTNS -p $FWD_PORT auth_transfer_limit.testns >fwd.log 2>&1 & +FWD_PID=$! +echo "FWD_PID=$FWD_PID" >> .tpkg.var.test + +# make config file +CONTROL_PATH=/tmp +CONTROL_PID=$$ +sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's/@TOPORT\@/'$FWD_PORT'/' -e 's?@CONTROL_PATH\@?'$CONTROL_PATH'?' -e 's/@CONTROL_PID@/'$CONTROL_PID'/' < auth_transfer_limit.conf > ub.conf +# start unbound in the background +$PRE/unbound -d -c ub.conf >unbound.log 2>&1 & +UNBOUND_PID=$! +echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test +echo "CONTROL_PATH=$CONTROL_PATH" >> .tpkg.var.test +echo "CONTROL_PID=$CONTROL_PID" >> .tpkg.var.test + +cat .tpkg.var.test +wait_ldns_testns_up fwd.log +wait_unbound_up unbound.log + diff --git a/testdata/auth_transfer_limit.tdir/auth_transfer_limit.test b/testdata/auth_transfer_limit.tdir/auth_transfer_limit.test new file mode 100644 index 000000000..d4a0131ec --- /dev/null +++ b/testdata/auth_transfer_limit.tdir/auth_transfer_limit.test @@ -0,0 +1,100 @@ +# #-- auth_transfer_limit.test --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test + +PRE="../.." +. ../common.sh +# do the test + +teststep "wait for unbound to transfer" +sleep 3 + +teststep "check log for max-transfer-size" +if grep "auth zone example.com. transfer.*exceeded 512 bytes" unbound.log; then + echo "OK" +else + echo "Not OK" + exit 1 +fi + +teststep "check log for max-transfer-time" +if grep "auth zone example2.com. transfer.*exceeded 2000 msec" unbound.log; then + echo "OK" +else + echo "Not OK" + exit 1 +fi + +if test "$TEST_FAST_RELOAD" == "yes"; then + teststep "Testing with fast_reload" + cp ub.conf ub.conf.old + sed -e 's/max-transfer-size: 512/max-transfer-size: 500/' -e 's/max-transfer-time: 2000/max-transfer-time: 1000/' < ub.conf.old > ub.conf + + teststep "unbound-control status" + $PRE/unbound-control -c ub.conf status + if test $? -ne 0; then + echo "wrong exit value." + exit 1 + else + echo "exit value: OK" + fi + + teststep "unbound-control fast_reload +vvdp" + $PRE/unbound-control -c ub.conf fast_reload +vvdp 2>&1 | tee output + if test $? -ne 0; then + echo "wrong exit value." + exit 1 + else + echo "exit value: OK" + fi + wait_logfile unbound.log "start fast reload thread" 60 + wait_logfile unbound.log "stop fast reload thread" 60 + wait_logfile unbound.log "joined with fastreload thread" 60 + + if grep "ok" output; then + echo "OK" + else + echo "output not correct" + exit 1 + fi + + teststep "wait for unbound to transfer" + sleep 3 + + $PRE/unbound-control -c ub.conf auth_zone_transfer example.com 2>&1 + if test $? -ne 0; then + echo "wrong exit value." + exit 1 + else + echo "exit value: OK" + fi + $PRE/unbound-control -c ub.conf auth_zone_transfer example2.com 2>&1 + if test $? -ne 0; then + echo "wrong exit value." + exit 1 + else + echo "exit value: OK" + fi + teststep "wait for unbound to transfer" + sleep 3 + + teststep "check log for max-transfer-size" + if grep "auth zone example.com. transfer.*exceeded 500 bytes" unbound.log; then + echo "OK" + else + echo "Not OK" + exit 1 + fi + + teststep "check log for max-transfer-time" + if grep "auth zone example2.com. transfer.*exceeded 1000 msec" unbound.log; then + echo "OK" + else + echo "Not OK" + exit 1 + fi +fi + +exit 0 diff --git a/testdata/auth_transfer_limit.tdir/auth_transfer_limit.testns b/testdata/auth_transfer_limit.tdir/auth_transfer_limit.testns new file mode 100644 index 000000000..3847d723f --- /dev/null +++ b/testdata/auth_transfer_limit.tdir/auth_transfer_limit.testns @@ -0,0 +1,74 @@ +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +example.com. IN SOA +SECTION ANSWER +example.com. IN SOA ns.example.com. hostmaster.example.com. 1 3600 900 86400 3600 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +example.com. IN AXFR +SECTION ANSWER +example.com. IN SOA ns.example.com. hostmaster.example.com. 1 3600 900 86400 3600 +example.com. IN NS ns.example.net. +; too big! +EXTRA_PACKET +REPLY QR AA NOERROR +SECTION QUESTION +example.com. IN AXFR +SECTION ANSWER +large01.example.com. IN TXT "123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789" +large02.example.com. IN TXT "123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789" +large03.example.com. IN TXT "123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789" +large04.example.com. IN TXT "123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789" +large05.example.com. IN TXT "123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789" +EXTRA_PACKET +REPLY QR AA NOERROR +SECTION QUESTION +example.com. IN AXFR +SECTION ANSWER +www.example.com. IN A 1.2.3.4 +example.com. IN SOA ns.example.com. hostmaster.example.com. 1 3600 900 86400 3600 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +example2.com. IN SOA +SECTION ANSWER +example2.com. IN SOA ns.example2.com. hostmaster.example2.com. 1 3600 900 86400 3600 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +example2.com. IN AXFR +SECTION ANSWER +example2.com. IN SOA ns.example2.com. hostmaster.example2.com. 1 3600 900 86400 3600 +example2.com. IN NS ns.example2.net. +EXTRA_PACKET +REPLY QR AA NOERROR +; too slow +ADJUST packet_sleep=3 +SECTION QUESTION +example2.com. IN AXFR +SECTION ANSWER +extra.example2.com. IN A 1.2.3.5 +EXTRA_PACKET +REPLY QR AA NOERROR +SECTION QUESTION +example2.com. IN AXFR +SECTION ANSWER +www.example2.com. IN A 1.2.3.4 +example2.com. IN SOA ns.example2.com. hostmaster.example2.com. 1 3600 900 86400 3600 +ENTRY_END diff --git a/util/config_file.h b/util/config_file.h index 6dadf0b72..c2b45759f 100644 --- a/util/config_file.h +++ b/util/config_file.h @@ -884,6 +884,10 @@ struct config_auth { int zonemd_check; /** Reject absence of ZONEMD records, zone must have one */ int zonemd_reject_absence; + /** The maximum auth zone transfer size, in bytes. */ + size_t max_transfer_size; + /** The maximum auth zone transfer time taken, in msec. */ + int max_transfer_time; }; /** diff --git a/util/configlexer.lex b/util/configlexer.lex index d6222cdf9..196575bdb 100644 --- a/util/configlexer.lex +++ b/util/configlexer.lex @@ -608,6 +608,8 @@ iter-scrub-ns{COLON} { YDVAR(1, VAR_ITER_SCRUB_NS) } iter-scrub-cname{COLON} { YDVAR(1, VAR_ITER_SCRUB_CNAME) } iter-scrub-rrsig{COLON} { YDVAR(1, VAR_ITER_SCRUB_RRSIG) } max-global-quota{COLON} { YDVAR(1, VAR_MAX_GLOBAL_QUOTA) } +max-transfer-size{COLON} { YDVAR(1, VAR_MAX_TRANSFER_SIZE) } +max-transfer-time{COLON} { YDVAR(1, VAR_MAX_TRANSFER_TIME) } iter-scrub-promiscuous{COLON} { YDVAR(1, VAR_ITER_SCRUB_PROMISCUOUS) } {NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++; } diff --git a/util/configparser.y b/util/configparser.y index 6ea214ea5..64a4e589d 100644 --- a/util/configparser.y +++ b/util/configparser.y @@ -216,6 +216,7 @@ extern struct config_parser_state* cfg_parser; %token VAR_LOG_DESTADDR VAR_CACHEDB_CHECK_WHEN_SERVE_EXPIRED %token VAR_COOKIE_SECRET_FILE VAR_ITER_SCRUB_NS VAR_ITER_SCRUB_CNAME %token VAR_ITER_SCRUB_RRSIG +%token VAR_MAX_TRANSFER_SIZE VAR_MAX_TRANSFER_TIME %token VAR_MAX_GLOBAL_QUOTA VAR_HARDEN_UNVERIFIED_GLUE VAR_LOG_TIME_ISO %token VAR_ITER_SCRUB_PROMISCUOUS VAR_LOG_THREAD_ID @@ -459,6 +460,8 @@ authstart: VAR_AUTH_ZONE s->zonemd_check = 0; s->zonemd_reject_absence = 0; s->isrpz = 0; + s->max_transfer_size = 0; + s->max_transfer_time = 0; } else { yyerror("out of memory"); } @@ -468,7 +471,8 @@ contents_auth: contents_auth content_auth | ; content_auth: auth_name | auth_zonefile | auth_master | auth_url | auth_for_downstream | auth_for_upstream | auth_fallback_enabled | - auth_allow_notify | auth_zonemd_check | auth_zonemd_reject_absence + auth_allow_notify | auth_zonemd_check | auth_zonemd_reject_absence | + auth_max_transfer_size | auth_max_transfer_time ; rpz_tag: VAR_TAGS STRING_ARG @@ -556,6 +560,8 @@ rpzstart: VAR_RPZ s->for_upstream = 0; s->fallback_enabled = 0; s->isrpz = 1; + s->max_transfer_size = 0; + s->max_transfer_time = 0; } else { yyerror("out of memory"); } @@ -565,7 +571,8 @@ contents_rpz: contents_rpz content_rpz | ; content_rpz: auth_name | auth_zonefile | rpz_tag | auth_master | auth_url | auth_allow_notify | rpz_action_override | rpz_cname_override | - rpz_log | rpz_log_name | rpz_signal_nxdomain_ra | auth_for_downstream + rpz_log | rpz_log_name | rpz_signal_nxdomain_ra | auth_for_downstream | + auth_max_transfer_size | auth_max_transfer_time ; server_num_threads: VAR_NUM_THREADS STRING_ARG { @@ -3341,6 +3348,23 @@ auth_fallback_enabled: VAR_FALLBACK_ENABLED STRING_ARG free($2); } ; +auth_max_transfer_size: VAR_MAX_TRANSFER_SIZE STRING_ARG + { + OUTYY(("P(max-transfer-size:%s)\n", $2)); + if(!cfg_parse_memsize($2, &cfg_parser->cfg->auths->max_transfer_size)) + yyerror("memory size expected"); + free($2); + } + ; +auth_max_transfer_time: VAR_MAX_TRANSFER_TIME STRING_ARG + { + OUTYY(("P(max-transfer-time:%s)\n", $2)); + if(atoi($2) == 0 && strcmp($2, "0") != 0) + yyerror("number expected"); + else cfg_parser->cfg->auths->max_transfer_time = atoi($2); + free($2); + } + ; view_name: VAR_NAME STRING_ARG { OUTYY(("P(name:%s)\n", $2));