Browse Source

Merge branch 'trac2796'

Conflicts:
	ChangeLog
Yoshitaka Aharen 12 years ago
parent
commit
3d291f42cd

+ 2 - 0
src/bin/auth/auth_srv.cc

@@ -521,6 +521,8 @@ AuthSrv::processMessage(const IOMessage& io_message, Message& message,
         return;
         return;
     }
     }
 
 
+    stats_attrs.setRequestRD(message.getHeaderFlag(Message::HEADERFLAG_RD));
+
     const Opcode& opcode = message.getOpcode();
     const Opcode& opcode = message.getOpcode();
     // Get opcode at this point; for all requests regardless of message body
     // Get opcode at this point; for all requests regardless of message body
     // sanity check.
     // sanity check.

+ 13 - 1
src/bin/auth/b10-auth.xml.pre

@@ -20,7 +20,7 @@
 <refentry>
 <refentry>
 
 
   <refentryinfo>
   <refentryinfo>
-    <date>February 5, 2013</date>
+    <date>May 22, 2013</date>
   </refentryinfo>
   </refentryinfo>
 
 
   <refmeta>
   <refmeta>
@@ -248,6 +248,18 @@
         but remember that if there's any error related to TSIG, some
         but remember that if there's any error related to TSIG, some
         of the counted opcode may not be trustworthy.
         of the counted opcode may not be trustworthy.
       </para>
       </para>
+
+      <para>
+	The <quote>qryrecursion</quote> counter is limited to queries
+	(requests of opcode 0) even though the RD bit is not specific
+	to queries.  In practice, this bit is generally just ignored for
+	other types of requests, while DNS servers behave differently
+	for queries depending on this bit.  It is also known that
+	some authoritative-only servers receive a non negligible
+	number of queries with the RD bit being set, so it would be
+	of particular interest to have a specific counters for such
+	requests.
+      </para>
     </note>
     </note>
 
 
   </refsect1>
   </refsect1>

+ 8 - 1
src/bin/auth/statistics.cc.pre

@@ -138,7 +138,14 @@ Counters::incRequest(const MessageAttributes& msgattrs) {
     // if a short message which does not contain DNS header is received, or
     // if a short message which does not contain DNS header is received, or
     // a response message (i.e. QR bit is set) is received.
     // a response message (i.e. QR bit is set) is received.
     if (opcode) {
     if (opcode) {
-        server_msg_counter_.inc(opcode_to_msgcounter[opcode.get().getCode()]);
+        server_msg_counter_.inc(opcode_to_msgcounter[opcode->getCode()]);
+
+        if (opcode.get() == Opcode::QUERY()) {
+            // Recursion Desired bit
+            if (msgattrs.requestHasRD()) {
+                server_msg_counter_.inc(MSG_QRYRECURSION);
+            }
+        }
     }
     }
 
 
     // TSIG
     // TSIG

+ 18 - 0
src/bin/auth/statistics.h

@@ -66,6 +66,8 @@ private:
     enum BitAttributes {
     enum BitAttributes {
         REQ_WITH_EDNS_0,            // request with EDNS ver.0
         REQ_WITH_EDNS_0,            // request with EDNS ver.0
         REQ_WITH_DNSSEC_OK,         // DNSSEC OK (DO) bit is set in request
         REQ_WITH_DNSSEC_OK,         // DNSSEC OK (DO) bit is set in request
+        REQ_WITH_RD,                // Recursion Desired (RD) bit is set in
+                                    // request
         REQ_TSIG_SIGNED,            // request is signed with valid TSIG
         REQ_TSIG_SIGNED,            // request is signed with valid TSIG
         REQ_BADSIG,                 // request is signed but bad signature
         REQ_BADSIG,                 // request is signed but bad signature
         RES_IS_TRUNCATED,           // response is truncated
         RES_IS_TRUNCATED,           // response is truncated
@@ -170,6 +172,22 @@ public:
         bit_attributes_[REQ_WITH_DNSSEC_OK] = with_dnssec_ok;
         bit_attributes_[REQ_WITH_DNSSEC_OK] = with_dnssec_ok;
     }
     }
 
 
+    /// \brief Return Recursion Desired (RD) bit of the request.
+    ///
+    /// \return true if Recursion Desired (RD) bit of the request is set
+    /// \throw None
+    bool requestHasRD() const {
+        return (bit_attributes_[REQ_WITH_RD]);
+    }
+
+    /// \brief Set Recursion Desired (RD) bit of the request.
+    ///
+    /// \param with_rd true if Recursion Desired (RD)bit of the request is set
+    /// \throw None
+    void setRequestRD(const bool with_rd) {
+        bit_attributes_[REQ_WITH_RD] = with_rd;
+    }
+
     /// \brief Return whether the request is TSIG signed or not.
     /// \brief Return whether the request is TSIG signed or not.
     ///
     ///
     /// \return true if the request is TSIG signed
     /// \return true if the request is TSIG signed

+ 1 - 0
src/bin/auth/statistics_msg_items.def

@@ -31,6 +31,7 @@ qrynoauthans	MSG_QRYNOAUTHANS		Number of queries received by the b10-auth server
 qryreferral	MSG_QRYREFERRAL			Number of queries received by the b10-auth server resulted in referral answer.
 qryreferral	MSG_QRYREFERRAL			Number of queries received by the b10-auth server resulted in referral answer.
 qrynxrrset	MSG_QRYNXRRSET			Number of queries received by the b10-auth server resulted in NoError and AA bit is set in the response, but the number of answer RR == 0.
 qrynxrrset	MSG_QRYNXRRSET			Number of queries received by the b10-auth server resulted in NoError and AA bit is set in the response, but the number of answer RR == 0.
 authqryrej	MSG_QRYREJECT			Number of authoritative queries rejected by the b10-auth server.
 authqryrej	MSG_QRYREJECT			Number of authoritative queries rejected by the b10-auth server.
+qryrecursion	MSG_QRYRECURSION		Number of queries received by the b10-auth server with "Recursion Desired" (RD) bit was set.
 rcode		msg_counter_rcode	Rcode statistics	=
 rcode		msg_counter_rcode	Rcode statistics	=
 	noerror		MSG_RCODE_NOERROR	Number of requests received by the b10-auth server resulted in RCODE = 0 (NoError).
 	noerror		MSG_RCODE_NOERROR	Number of requests received by the b10-auth server resulted in RCODE = 0 (NoError).
 	formerr		MSG_RCODE_FORMERR	Number of requests received by the b10-auth server resulted in RCODE = 1 (FormErr).
 	formerr		MSG_RCODE_FORMERR	Number of requests received by the b10-auth server resulted in RCODE = 1 (FormErr).

+ 58 - 0
src/bin/auth/tests/statistics_unittest.cc.pre

@@ -361,6 +361,64 @@ TEST_F(CountersTest, incrementTSIG) {
     }
     }
 }
 }
 
 
+TEST_F(CountersTest, incrementRD) {
+    Message response(Message::RENDER);
+    MessageAttributes msgattrs;
+    std::map<std::string, int> expect;
+
+    // Test these patterns:
+    //     OpCode         Recursion Desired
+    //    ---------------------------
+    //     0 (Query)      false
+    //     0 (Query)      true
+    //     2 (Status)     false
+    //     2 (Status)     true
+    //  Make sure the counter will be incremented only for the requests with
+    //  OpCode=Query and Recursion Desired (RD) bit=1.
+    int count_opcode_query = 0;
+    int count_opcode_status = 0;
+    for (int i = 0; i < 4; ++i) {
+        const bool is_recursion_desired = i & 1;
+        const uint8_t opcode_code = i & 0x2;
+        const Opcode opcode(opcode_code);
+        buildSkeletonMessage(msgattrs);
+        msgattrs.setRequestRD(is_recursion_desired);
+        msgattrs.setRequestOpCode(opcode);
+
+        response.setRcode(Rcode::REFUSED());
+        response.addQuestion(Question(Name("example.com"),
+                                      RRClass::IN(), RRType::AAAA()));
+        response.setHeaderFlag(Message::HEADERFLAG_QR);
+
+        counters.inc(msgattrs, response, true);
+
+        if (opcode == Opcode::QUERY()) {
+            ++count_opcode_query;
+        } else {
+            ++count_opcode_status;
+        }
+
+        expect.clear();
+        expect["opcode.query"] = count_opcode_query;
+        expect["opcode.status"] = count_opcode_status;
+        expect["request.v4"] = i+1;
+        expect["request.udp"] = i+1;
+        expect["request.edns0"] = i+1;
+        expect["request.dnssec_ok"] = i+1;
+        expect["responses"] = i+1;
+        // qryrecursion will (only) be incremented if i == 1: OpCode=Query and
+        // RD bit=1
+        expect["qryrecursion"] = (i == 0) ? 0 : 1;
+        expect["rcode.refused"] = i+1;
+        // these counters are for queries; the value will be equal to the
+        // number of requests with OpCode=Query
+        expect["qrynoauthans"] = count_opcode_query;
+        expect["authqryrej"] = count_opcode_query;
+        checkStatisticsCounters(counters.get()->get("zones")->get("_SERVER_"),
+                                expect);
+    }
+}
+
 TEST_F(CountersTest, incrementOpcode) {
 TEST_F(CountersTest, incrementOpcode) {
     Message response(Message::RENDER);
     Message response(Message::RENDER);
     MessageAttributes msgattrs;
     MessageAttributes msgattrs;

+ 1 - 1
tests/lettuce/features/auth_badzone.feature

@@ -24,7 +24,7 @@ Feature: Authoritative DNS server with a bad zone
         And bind10 module Resolver should not be running
         And bind10 module Resolver should not be running
 
 
         A query for www.example.org should have rcode NOERROR
         A query for www.example.org should have rcode NOERROR
-        The last query response should have flags qr aa rd
+        The last query response should have flags qr aa
         The last query response should have ancount 1
         The last query response should have ancount 1
         The last query response should have nscount 2
         The last query response should have nscount 2
         The last query response should have adcount 2
         The last query response should have adcount 2

+ 1 - 1
tests/lettuce/features/example.feature

@@ -120,7 +120,7 @@ Feature: Example feature
         The last query response should have adcount 0
         The last query response should have adcount 0
         # When checking flags, we must pass them exactly as they appear in
         # When checking flags, we must pass them exactly as they appear in
         # the output of dig.
         # the output of dig.
-        The last query response should have flags qr aa rd
+        The last query response should have flags qr aa
 
 
         A query for www.example.org type TXT should have rcode NOERROR
         A query for www.example.org type TXT should have rcode NOERROR
         The last query response should have ancount 0
         The last query response should have ancount 0

+ 14 - 14
tests/lettuce/features/nsec3_auth.feature

@@ -25,7 +25,7 @@ Feature: NSEC3 Authoritative service
         And bind10 module StatsHttpd should not be running
         And bind10 module StatsHttpd should not be running
 
 
         A dnssec query for a.c.x.w.example. should have rcode NXDOMAIN
         A dnssec query for a.c.x.w.example. should have rcode NXDOMAIN
-        The last query response should have flags qr aa rd
+        The last query response should have flags qr aa
         The last query response should have edns_flags do
         The last query response should have edns_flags do
         The last query response should have ancount 0
         The last query response should have ancount 0
         The last query response should have nscount 8
         The last query response should have nscount 8
@@ -57,7 +57,7 @@ Feature: NSEC3 Authoritative service
         And bind10 module StatsHttpd should not be running
         And bind10 module StatsHttpd should not be running
 
 
         A dnssec query for ns1.example. type MX should have rcode NOERROR
         A dnssec query for ns1.example. type MX should have rcode NOERROR
-        The last query response should have flags qr aa rd
+        The last query response should have flags qr aa
         The last query response should have edns_flags do
         The last query response should have edns_flags do
         The last query response should have ancount 0
         The last query response should have ancount 0
         The last query response should have nscount 4
         The last query response should have nscount 4
@@ -85,7 +85,7 @@ Feature: NSEC3 Authoritative service
         And bind10 module StatsHttpd should not be running
         And bind10 module StatsHttpd should not be running
 
 
         A dnssec query for y.w.example. should have rcode NOERROR
         A dnssec query for y.w.example. should have rcode NOERROR
-        The last query response should have flags qr aa rd
+        The last query response should have flags qr aa
         The last query response should have edns_flags do
         The last query response should have edns_flags do
         The last query response should have ancount 0
         The last query response should have ancount 0
         The last query response should have nscount 4
         The last query response should have nscount 4
@@ -113,7 +113,7 @@ Feature: NSEC3 Authoritative service
         And bind10 module StatsHttpd should not be running
         And bind10 module StatsHttpd should not be running
 
 
         A dnssec query for mc.c.example. type MX should have rcode NOERROR
         A dnssec query for mc.c.example. type MX should have rcode NOERROR
-        The last query response should have flags qr rd
+        The last query response should have flags qr
         The last query response should have edns_flags do
         The last query response should have edns_flags do
         The last query response should have ancount 0
         The last query response should have ancount 0
         The last query response should have nscount 6
         The last query response should have nscount 6
@@ -148,7 +148,7 @@ Feature: NSEC3 Authoritative service
         And bind10 module StatsHttpd should not be running
         And bind10 module StatsHttpd should not be running
 
 
         A dnssec query for a.z.w.example. type MX should have rcode NOERROR
         A dnssec query for a.z.w.example. type MX should have rcode NOERROR
-        The last query response should have flags qr aa rd
+        The last query response should have flags qr aa
         The last query response should have edns_flags do
         The last query response should have edns_flags do
         The last query response should have ancount 2
         The last query response should have ancount 2
         The last query response should have nscount 5
         The last query response should have nscount 5
@@ -195,7 +195,7 @@ Feature: NSEC3 Authoritative service
         And bind10 module StatsHttpd should not be running
         And bind10 module StatsHttpd should not be running
 
 
         A dnssec query for a.z.w.example. type AAAA should have rcode NOERROR
         A dnssec query for a.z.w.example. type AAAA should have rcode NOERROR
-        The last query response should have flags qr aa rd
+        The last query response should have flags qr aa
         The last query response should have edns_flags do
         The last query response should have edns_flags do
         The last query response should have ancount 0
         The last query response should have ancount 0
         The last query response should have nscount 8
         The last query response should have nscount 8
@@ -227,7 +227,7 @@ Feature: NSEC3 Authoritative service
         And bind10 module StatsHttpd should not be running
         And bind10 module StatsHttpd should not be running
 
 
         A dnssec query for example. type DS should have rcode NOERROR
         A dnssec query for example. type DS should have rcode NOERROR
-        The last query response should have flags qr aa rd
+        The last query response should have flags qr aa
         The last query response should have edns_flags do
         The last query response should have edns_flags do
         The last query response should have ancount 0
         The last query response should have ancount 0
         The last query response should have nscount 4
         The last query response should have nscount 4
@@ -259,7 +259,7 @@ Feature: NSEC3 Authoritative service
         And bind10 module StatsHttpd should not be running
         And bind10 module StatsHttpd should not be running
 
 
         A dnssec query for b.x.w.example. should have rcode NXDOMAIN
         A dnssec query for b.x.w.example. should have rcode NXDOMAIN
-        The last query response should have flags qr aa rd
+        The last query response should have flags qr aa
         The last query response should have edns_flags do
         The last query response should have edns_flags do
         The last query response should have ancount 0
         The last query response should have ancount 0
         The last query response should have nscount 6
         The last query response should have nscount 6
@@ -289,7 +289,7 @@ Feature: NSEC3 Authoritative service
         And bind10 module StatsHttpd should not be running
         And bind10 module StatsHttpd should not be running
 
 
         A dnssec query for a.w.example. should have rcode NOERROR
         A dnssec query for a.w.example. should have rcode NOERROR
-        The last query response should have flags qr aa rd
+        The last query response should have flags qr aa
         The last query response should have edns_flags do
         The last query response should have edns_flags do
         The last query response should have ancount 0
         The last query response should have ancount 0
         The last query response should have nscount 6
         The last query response should have nscount 6
@@ -319,7 +319,7 @@ Feature: NSEC3 Authoritative service
         And bind10 module StatsHttpd should not be running
         And bind10 module StatsHttpd should not be running
 
 
         A dnssec query for *.w.example. type MX should have rcode NOERROR
         A dnssec query for *.w.example. type MX should have rcode NOERROR
-        The last query response should have flags qr aa rd
+        The last query response should have flags qr aa
         The last query response should have edns_flags do
         The last query response should have edns_flags do
         The last query response should have ancount 2
         The last query response should have ancount 2
         The last query response should have nscount 3
         The last query response should have nscount 3
@@ -362,7 +362,7 @@ Feature: NSEC3 Authoritative service
         And bind10 module StatsHttpd should not be running
         And bind10 module StatsHttpd should not be running
 
 
         A dnssec query for *.w.example. type A should have rcode NOERROR
         A dnssec query for *.w.example. type A should have rcode NOERROR
-        The last query response should have flags qr aa rd
+        The last query response should have flags qr aa
         The last query response should have edns_flags do
         The last query response should have edns_flags do
         The last query response should have ancount 0
         The last query response should have ancount 0
         The last query response should have nscount 4
         The last query response should have nscount 4
@@ -390,7 +390,7 @@ Feature: NSEC3 Authoritative service
         And bind10 module StatsHttpd should not be running
         And bind10 module StatsHttpd should not be running
 
 
         A dnssec query for 0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. type NSEC3 should have rcode NXDOMAIN
         A dnssec query for 0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. type NSEC3 should have rcode NXDOMAIN
-        The last query response should have flags qr aa rd
+        The last query response should have flags qr aa
         The last query response should have edns_flags do
         The last query response should have edns_flags do
         The last query response should have ancount 0
         The last query response should have ancount 0
         The last query response should have nscount 8
         The last query response should have nscount 8
@@ -422,7 +422,7 @@ Feature: NSEC3 Authoritative service
         And bind10 module StatsHttpd should not be running
         And bind10 module StatsHttpd should not be running
 
 
         A dnssec query for ai.example. type DS should have rcode NOERROR
         A dnssec query for ai.example. type DS should have rcode NOERROR
-        The last query response should have flags qr aa rd
+        The last query response should have flags qr aa
         The last query response should have edns_flags do
         The last query response should have edns_flags do
         The last query response should have ancount 0
         The last query response should have ancount 0
         The last query response should have nscount 4
         The last query response should have nscount 4
@@ -450,7 +450,7 @@ Feature: NSEC3 Authoritative service
         And bind10 module StatsHttpd should not be running
         And bind10 module StatsHttpd should not be running
 
 
         A dnssec query for c.example. type DS should have rcode NOERROR
         A dnssec query for c.example. type DS should have rcode NOERROR
-        The last query response should have flags qr aa rd
+        The last query response should have flags qr aa
         The last query response should have edns_flags do
         The last query response should have edns_flags do
         The last query response should have ancount 0
         The last query response should have ancount 0
         The last query response should have nscount 6
         The last query response should have nscount 6

+ 6 - 5
tests/lettuce/features/queries.feature

@@ -75,7 +75,7 @@ Feature: Querying feature
         The statistics counters are 0 in category .Auth.zones._SERVER_
         The statistics counters are 0 in category .Auth.zones._SERVER_
 
 
         A query for www.example.org should have rcode NOERROR
         A query for www.example.org should have rcode NOERROR
-        The last query response should have flags qr aa rd
+        The last query response should have flags qr aa
         The last query response should have ancount 1
         The last query response should have ancount 1
         The last query response should have nscount 2
         The last query response should have nscount 2
         The last query response should have adcount 2
         The last query response should have adcount 2
@@ -121,7 +121,7 @@ Feature: Querying feature
 
 
         # Repeat of the above
         # Repeat of the above
         A query for www.example.org should have rcode NOERROR
         A query for www.example.org should have rcode NOERROR
-        The last query response should have flags qr aa rd
+        The last query response should have flags qr aa
         The last query response should have ancount 1
         The last query response should have ancount 1
         The last query response should have nscount 2
         The last query response should have nscount 2
         The last query response should have adcount 2
         The last query response should have adcount 2
@@ -165,7 +165,7 @@ Feature: Querying feature
           | rcode.noerror |          2 |
           | rcode.noerror |          2 |
 
 
         # And now query something completely different
         # And now query something completely different
-        A query for nosuchname.example.org should have rcode NXDOMAIN
+        A recursive query for nosuchname.example.org should have rcode NXDOMAIN
         The last query response should have flags qr aa rd
         The last query response should have flags qr aa rd
         The last query response should have ancount 0
         The last query response should have ancount 0
         The last query response should have nscount 1
         The last query response should have nscount 1
@@ -196,6 +196,7 @@ Feature: Querying feature
           | responses      |          3 |
           | responses      |          3 |
           | qrysuccess     |          2 |
           | qrysuccess     |          2 |
           | qryauthans     |          3 |
           | qryauthans     |          3 |
+          | qryrecursion   |          1 |
           | rcode.noerror  |          2 |
           | rcode.noerror  |          2 |
           | rcode.nxdomain |          1 |
           | rcode.nxdomain |          1 |
 
 
@@ -225,7 +226,7 @@ Feature: Querying feature
         The statistics counters are 0 in category .Auth.zones._SERVER_
         The statistics counters are 0 in category .Auth.zones._SERVER_
 
 
         A query for example.org type ANY should have rcode NOERROR
         A query for example.org type ANY should have rcode NOERROR
-        The last query response should have flags qr aa rd
+        The last query response should have flags qr aa
         The last query response should have ancount 4
         The last query response should have ancount 4
         The last query response should have nscount 0
         The last query response should have nscount 0
         The last query response should have adcount 3
         The last query response should have adcount 3
@@ -284,7 +285,7 @@ Feature: Querying feature
         The statistics counters are 0 in category .Auth.zones._SERVER_
         The statistics counters are 0 in category .Auth.zones._SERVER_
 
 
         A dnssec query for www.sub.example.org type AAAA should have rcode NOERROR
         A dnssec query for www.sub.example.org type AAAA should have rcode NOERROR
-        The last query response should have flags qr rd
+        The last query response should have flags qr
         The last query response should have edns_flags do
         The last query response should have edns_flags do
         The last query response should have ancount 0
         The last query response should have ancount 0
         The last query response should have nscount 1
         The last query response should have nscount 1

+ 3 - 3
tests/lettuce/features/resolver_basic.feature

@@ -24,13 +24,13 @@ Feature: Basic Resolver
         And bind10 module StatsHttpd should not be running
         And bind10 module StatsHttpd should not be running
 
 
         # The ACL is set to reject any queries
         # The ACL is set to reject any queries
-        A query for l.root-servers.net. should have rcode REFUSED
+        A recursive query for l.root-servers.net. should have rcode REFUSED
 
 
         # Test whether acl ACCEPT works
         # Test whether acl ACCEPT works
         When I set bind10 configuration Resolver/query_acl[0] to {"action": "ACCEPT", "from": "127.0.0.1"}
         When I set bind10 configuration Resolver/query_acl[0] to {"action": "ACCEPT", "from": "127.0.0.1"}
         # This address is currently hardcoded, so shouldn't cause outside traffic
         # This address is currently hardcoded, so shouldn't cause outside traffic
-        A query for l.root-servers.net. should have rcode NOERROR
+        A recursive query for l.root-servers.net. should have rcode NOERROR
 
 
         # Check whether setting the ACL to reject again works
         # Check whether setting the ACL to reject again works
         When I set bind10 configuration Resolver/query_acl[0] to {"action": "REJECT", "from": "127.0.0.1"}
         When I set bind10 configuration Resolver/query_acl[0] to {"action": "REJECT", "from": "127.0.0.1"}
-        A query for l.root-servers.net. should have rcode REFUSED
+        A recursive query for l.root-servers.net. should have rcode REFUSED

+ 10 - 2
tests/lettuce/features/terrain/querying.py

@@ -200,14 +200,19 @@ class QueryResult(object):
         """
         """
         pass
         pass
 
 
-@step('A (dnssec )?query for ([\S]+) (?:type ([A-Z0-9]+) )?' +
+@step('A (dnssec )?(recursive )?query for ([\S]+) (?:type ([A-Z0-9]+) )?' +
       '(?:class ([A-Z]+) )?(?:to ([^:]+|\[[0-9a-fA-F:]+\])(?::([0-9]+))? )?' +
       '(?:class ([A-Z]+) )?(?:to ([^:]+|\[[0-9a-fA-F:]+\])(?::([0-9]+))? )?' +
       'should have rcode ([\w.]+)')
       'should have rcode ([\w.]+)')
-def query(step, dnssec, query_name, qtype, qclass, addr, port, rcode):
+def query(step, dnssec, recursive, query_name, qtype, qclass, addr, port,
+          rcode):
     """
     """
     Run a query, check the rcode of the response, and store the query
     Run a query, check the rcode of the response, and store the query
     result in world.last_query_result.
     result in world.last_query_result.
     Parameters:
     Parameters:
+    dnssec ('dnssec'): DO bit is set in the query.
+                       Defaults to unset (no DNSSEC).
+    recursive ('recursive'): RD bit is set in the query.
+                             Defaults to unset (no recursion).
     query_name ('query for <name>'): The domain name to query.
     query_name ('query for <name>'): The domain name to query.
     qtype ('type <type>', optional): The RR type to query. Defaults to A.
     qtype ('type <type>', optional): The RR type to query. Defaults to A.
     qclass ('class <class>', optional): The RR class to query. Defaults to IN.
     qclass ('class <class>', optional): The RR class to query. Defaults to IN.
@@ -234,6 +239,9 @@ def query(step, dnssec, query_name, qtype, qclass, addr, port, rcode):
         # additional counts, so unless we need dnssec, explicitly
         # additional counts, so unless we need dnssec, explicitly
         # disable edns0
         # disable edns0
         additional_arguments.append("+noedns")
         additional_arguments.append("+noedns")
+    # dig sets RD bit by default.
+    if recursive is None:
+        additional_arguments.append("+norecurse")
     query_result = QueryResult(query_name, qtype, qclass, addr, port,
     query_result = QueryResult(query_name, qtype, qclass, addr, port,
                                additional_arguments)
                                additional_arguments)
     assert query_result.rcode == rcode,\
     assert query_result.rcode == rcode,\