Browse Source

[2522] add MasterLexer constructor for TSIG

Paul Selkirk 12 years ago
parent
commit
1a39094273

+ 4 - 1
src/lib/dns/gen-rdatacode.py.in

@@ -42,18 +42,21 @@ new_rdata_factory_users = [('a', 'in'),
                            ('dnskey', 'generic'),
                            ('ds', 'generic'),
                            ('hinfo', 'generic'),
-                           ('naptr', 'generic'),
+                           ('minfo', 'generic'),
                            ('mx', 'generic'),
+                           ('naptr', 'generic'),
                            ('ns', 'generic'),
                            ('nsec', 'generic'),
                            ('nsec3', 'generic'),
                            ('nsec3param', 'generic'),
                            ('opt', 'generic'),
                            ('ptr', 'generic'),
+                           ('rp', 'generic'),
                            ('rrsig', 'generic'),
                            ('soa', 'generic'),
                            ('spf', 'generic'),
                            ('srv', 'in'),
+                           ('tsig', 'any'),
                            ('txt', 'generic')
                           ]
 

+ 126 - 45
src/lib/dns/rdata/any_255/tsig_250.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2010-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
@@ -26,19 +26,23 @@
 #include <dns/name.h>
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>
+#include <dns/rcode.h>
 #include <dns/tsigerror.h>
+#include <dns/rdata/generic/detail/lexer_util.h>
 
 using namespace std;
 using boost::lexical_cast;
 using namespace isc::util;
 using namespace isc::util::encode;
 using namespace isc::util::str;
+using namespace isc::dns;
+using isc::dns::rdata::generic::detail::createNameFromLexer;
 
 // BEGIN_ISC_NAMESPACE
 // BEGIN_RDATA_NAMESPACE
 
-/// This is a straightforward representation of TSIG RDATA fields.
-struct TSIG::TSIGImpl {
+// straightforward representation of TSIG RDATA fields
+struct TSIGImpl {
     TSIGImpl(const Name& algorithm, uint64_t time_signed, uint16_t fudge,
              vector<uint8_t>& mac, uint16_t original_id, uint16_t error,
              vector<uint8_t>& other_data) :
@@ -68,6 +72,86 @@ struct TSIG::TSIGImpl {
     const vector<uint8_t> other_data_;
 };
 
+// helper function for string and lexer constructors
+TSIGImpl*
+TSIG::constructFromLexer(MasterLexer& lexer) {
+    // RFC2845 defines Algorithm Name to be "in domain name syntax",
+    // but it's not actually a domain name, so we allow it to be not
+    // fully qualified.
+    const Name root(".");
+    const Name algorithm = createNameFromLexer(lexer, &root);
+
+    const string time_str = lexer.getNextToken(MasterToken::STRING).getString();
+    uint64_t time_signed;
+    try {
+        time_signed = boost::lexical_cast<uint64_t>(time_str);
+    } catch (const boost::bad_lexical_cast&) {
+        isc_throw(InvalidRdataText, "Invalid TSIG Time");
+    }
+    if (time_signed > 0xffffffffffff) {
+        isc_throw(InvalidRdataText, "TSIG Time out of range");
+    }
+
+    const int32_t fudge = lexer.getNextToken(MasterToken::NUMBER).getNumber();
+    if (fudge > 0xffff) {
+        isc_throw(InvalidRdataText, "TSIG Fudge out of range");
+    }
+    const int32_t macsize = lexer.getNextToken(MasterToken::NUMBER).getNumber();
+    if (macsize > 0xffff) {
+        isc_throw(InvalidRdataText, "TSIG MAC Size out of range");
+    }
+
+    const string mac_txt = (macsize > 0) ?
+            lexer.getNextToken(MasterToken::STRING).getString() : "";
+    vector<uint8_t> mac;
+    decodeBase64(mac_txt, mac);
+    if (mac.size() != macsize) {
+        isc_throw(InvalidRdataText, "TSIG MAC Size and data are inconsistent");
+    }
+
+    const int32_t orig_id = lexer.getNextToken(MasterToken::NUMBER).getNumber();
+    if (orig_id > 0xffff) {
+        isc_throw(InvalidRdataText, "TSIG Original ID out of range");
+    }
+
+    const string error_txt = lexer.getNextToken(MasterToken::STRING).getString();
+    uint16_t error = 0;
+    // XXX: In the initial implementation we hardcode the mnemonics.
+    // We'll soon generalize this.
+    if (error_txt == "NOERROR") {
+        error = Rcode::NOERROR_CODE;
+    } else if (error_txt == "BADSIG") {
+        error = TSIGError::BAD_SIG_CODE;
+    } else if (error_txt == "BADKEY") {
+        error = TSIGError::BAD_KEY_CODE;
+    } else if (error_txt == "BADTIME") {
+        error = TSIGError::BAD_TIME_CODE;
+    } else {
+        try {
+            error = boost::lexical_cast<uint16_t>(error_txt);
+        } catch (const boost::bad_lexical_cast&) {
+            isc_throw(InvalidRdataText, "Invalid TSIG Error");
+        }
+    }
+
+    const int32_t otherlen = lexer.getNextToken(MasterToken::NUMBER).getNumber();
+    if (otherlen > 0xffff) {
+        isc_throw(InvalidRdataText, "TSIG Other Len out of range");
+    }
+    const string otherdata_txt = (otherlen > 0) ? 
+            lexer.getNextToken(MasterToken::STRING).getString() : "";
+    vector<uint8_t> other_data;
+    decodeBase64(otherdata_txt, other_data);
+    if (other_data.size() != otherlen) {
+        isc_throw(InvalidRdataText,
+                  "TSIG Other Data length does not match Other Len");
+    }
+    // also verify Error == BADTIME?
+
+    return new TSIGImpl(algorithm, time_signed, fudge, mac, orig_id,
+                        error, other_data);
+}
+
 /// \brief Constructor from string.
 ///
 /// \c tsig_str must be formatted as follows:
@@ -113,54 +197,51 @@ struct TSIG::TSIGImpl {
 /// This constructor internally involves resource allocation, and if it fails
 /// a corresponding standard exception will be thrown.
 TSIG::TSIG(const std::string& tsig_str) : impl_(NULL) {
-    istringstream iss(tsig_str);
+    // We use auto_ptr here because if there is an exception in this
+    // constructor, the destructor is not called and there could be a
+    // leak of the DNSKEYImpl that constructFromLexer() returns.
+    std::auto_ptr<TSIGImpl> impl_ptr(NULL);
 
     try {
-        const Name algorithm(getToken(iss));
-        const int64_t time_signed = tokenToNum<int64_t, 48>(getToken(iss));
-        const int32_t fudge = tokenToNum<int32_t, 16>(getToken(iss));
-        const int32_t macsize = tokenToNum<int32_t, 16>(getToken(iss));
-
-        const string mac_txt = (macsize > 0) ? getToken(iss) : "";
-        vector<uint8_t> mac;
-        decodeBase64(mac_txt, mac);
-        if (mac.size() != macsize) {
-            isc_throw(InvalidRdataText, "TSIG MAC size and data are inconsistent");
-        }
-
-        const int32_t orig_id = tokenToNum<int32_t, 16>(getToken(iss));
-
-        const string error_txt = getToken(iss);
-        int32_t error = 0;
-        // XXX: In the initial implementation we hardcode the mnemonics.
-        // We'll soon generalize this.
-        if (error_txt == "BADSIG") {
-            error = 16;
-        } else if (error_txt == "BADKEY") {
-            error = 17;
-        } else if (error_txt == "BADTIME") {
-            error = 18;
-        } else {
-            error = tokenToNum<int32_t, 16>(error_txt);
-        }
+        std::istringstream ss(tsig_str);
+        MasterLexer lexer;
+        lexer.pushSource(ss);
 
-        const int32_t otherlen = tokenToNum<int32_t, 16>(getToken(iss));
-        const string otherdata_txt = (otherlen > 0) ? getToken(iss) : "";
-        vector<uint8_t> other_data;
-        decodeBase64(otherdata_txt, other_data);
+        impl_ptr.reset(constructFromLexer(lexer));
 
-        if (!iss.eof()) {
-            isc_throw(InvalidRdataText, "Unexpected input for TSIG RDATA: " <<
-                    tsig_str);
+        if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) {
+            isc_throw(InvalidRdataText,
+                      "Extra input text for TSIG: " << tsig_str);
         }
+    } catch (const MasterLexer::LexerError& ex) {
+        isc_throw(InvalidRdataText,
+                  "Failed to construct TSIG from '" << tsig_str << "': "
+                  << ex.what());
+    }
 
-        impl_ = new TSIGImpl(algorithm, time_signed, fudge, mac, orig_id,
-                            error, other_data);
+    impl_ = impl_ptr.release();
+}
 
-    } catch (const StringTokenError& ste) {
-        isc_throw(InvalidRdataText, "Invalid TSIG text: " << ste.what() <<
-                  ": " << tsig_str);
-    }
+/// \brief Constructor with a context of MasterLexer.
+///
+/// The \c lexer should point to the beginning of valid textual
+/// representation of an TSIG RDATA.
+///
+/// See \c TSIG::TSIG(const std::string&) for description of the
+/// expected RDATA fields.
+///
+/// \throw MasterLexer::LexerError General parsing error such as
+/// missing field.
+/// \throw InvalidRdataText if any fields are out of their valid range,
+/// or are incorrect.
+///
+/// \param lexer A \c MasterLexer object parsing a master file for the
+/// RDATA to be created
+TSIG::TSIG(MasterLexer& lexer, const Name*,
+               MasterLoader::Options, MasterLoaderCallbacks&) :
+    impl_(NULL)
+{
+    impl_ = constructFromLexer(lexer);
 }
 
 /// \brief Constructor from wire-format data.
@@ -298,7 +379,7 @@ TSIG::toText() const {
 // toWire().
 template <typename Output>
 void
-TSIG::TSIGImpl::toWireCommon(Output& output) const {
+TSIGImpl::toWireCommon(Output& output) const {
     output.writeUint16(time_signed_ >> 32);
     output.writeUint32(time_signed_ & 0xffffffff);
     output.writeUint16(fudge_);

+ 6 - 8
src/lib/dns/rdata/any_255/tsig_250.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2010-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
@@ -18,14 +18,9 @@
 
 #include <string>
 
+#include <dns/name.h>
 #include <dns/rdata.h>
 
-namespace isc {
-namespace dns {
-class Name;
-}
-}
-
 // BEGIN_ISC_NAMESPACE
 
 // BEGIN_COMMON_DECLARATIONS
@@ -33,6 +28,8 @@ class Name;
 
 // BEGIN_RDATA_NAMESPACE
 
+struct TSIGImpl;
+
 /// \brief \c rdata::TSIG class represents the TSIG RDATA as defined %in
 /// RFC2845.
 ///
@@ -145,7 +142,8 @@ public:
     /// This method never throws an exception.
     const void* getOtherData() const;
 private:
-    struct TSIGImpl;
+    TSIGImpl* constructFromLexer(isc::dns::MasterLexer& lexer);
+
     TSIGImpl* impl_;
 };
 

+ 3 - 2
src/lib/dns/tests/rdata_tsig_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2010-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
@@ -23,6 +23,7 @@
 #include <dns/rdataclass.h>
 #include <dns/rrclass.h>
 #include <dns/rrtype.h>
+#include <dns/tsigerror.h>
 
 #include <gtest/gtest.h>
 
@@ -72,7 +73,7 @@ TEST_F(Rdata_TSIG_Test, createFromText) {
 
     any::TSIG tsig2((string(valid_text2)));
     EXPECT_EQ(12, tsig2.getMACSize());
-    EXPECT_EQ(16, tsig2.getError()); // TODO: use constant
+    EXPECT_EQ(TSIGError::BAD_SIG_CODE, tsig2.getError());
 
     any::TSIG tsig3((string(valid_text3)));
     EXPECT_EQ(6, tsig3.getOtherLen());