result[0] = result.size() - 1;
}
+void
+stringToCharStringData(const MasterToken::StringRegion& str_region,
+ CharStringData& result)
+{
+ bool escape = false;
+ const char* s = str_region.beg;
+ const char* const s_end = str_region.beg + str_region.len;
+
+ for (size_t n = str_region.len; n != 0; --n, ++s) {
+ int c = (*s & 0xff);
+ if (escape && std::isdigit(c) != 0) {
+ c = decimalToNumber(s, s_end);
+ assert(n >= 3);
+ n -= 2;
+ s += 2;
+ } else if (!escape && c == '\\') {
+ escape = true;
+ continue;
+ }
+ escape = false;
+ result.push_back(c);
+ }
+ if (escape) { // terminated by non-escaped '\'
+ isc_throw(InvalidRdataText, "character-string ends with '\\'");
+ }
+}
+
std::string
charStringToString(const CharString& char_string) {
std::string s;
return (s);
}
+std::string
+charStringDataToString(const CharStringData& char_string) {
+ std::string s;
+ for (CharString::const_iterator it = char_string.begin();
+ it != char_string.end(); ++it) {
+ const uint8_t ch = *it;
+ if ((ch < 0x20) || (ch >= 0x7f)) {
+ // convert to escaped \xxx (decimal) format
+ s.push_back('\\');
+ s.push_back('0' + ((ch / 100) % 10));
+ s.push_back('0' + ((ch / 10) % 10));
+ s.push_back('0' + (ch % 10));
+ continue;
+ }
+ if ((ch == '"') || (ch == ';') || (ch == '\\')) {
+ s.push_back('\\');
+ }
+ s.push_back(ch);
+ }
+
+ return (s);
+}
+
int compareCharStrings(const detail::CharString& self,
const detail::CharString& other) {
if (self.size() == 0 && other.size() == 0) {
}
}
+int compareCharStringDatas(const detail::CharStringData& self,
+ const detail::CharStringData& other) {
+ if (self.size() == 0 && other.size() == 0) {
+ return (0);
+ }
+ if (self.size() == 0) {
+ return (-1);
+ }
+ if (other.size() == 0) {
+ return (1);
+ }
+ const size_t self_len = self.size();
+ const size_t other_len = other.size();
+ const size_t cmp_len = std::min(self_len, other_len);
+ const int cmp = std::memcmp(&self[0], &other[0], cmp_len);
+ if (cmp < 0) {
+ return (-1);
+ } else if (cmp > 0) {
+ return (1);
+ } else if (self_len < other_len) {
+ return (-1);
+ } else if (self_len > other_len) {
+ return (1);
+ } else {
+ return (0);
+ }
+}
+
size_t
bufferToCharString(isc::util::InputBuffer& buffer, size_t rdata_len,
CharString& target) {
/// be the bare char basis.
typedef std::vector<uint8_t> CharString;
+/// \brief Type for DNS character string without the length prefix.
+typedef std::vector<uint8_t> CharStringData;
+
/// \brief Convert a DNS character-string into corresponding binary data.
///
/// This helper function takes a string object that is expected to be a
void stringToCharString(const MasterToken::StringRegion& str_region,
CharString& result);
+/// \brief Convert a DNS character-string into corresponding binary data.
+///
+/// This method functions similar to \c stringToCharString() except it
+/// does not include the 1-octet length prefix in the \c result, and the
+/// result is not limited to MAX_CHARSTRING_LEN octets.
+///
+/// \throw InvalidRdataText Upon syntax errors.
+///
+/// \brief str_region A string that represents a character-string.
+/// \brief result A placeholder vector where the resulting data are to be
+/// stored. Expected to be empty, but it's not checked.
+void stringToCharStringData(const MasterToken::StringRegion& str_region,
+ CharStringData& result);
+
/// \brief Convert a CharString into a textual DNS character-string.
///
/// This method converts a binary 8-bit representation of a DNS
/// \return A string representation of \c char_string.
std::string charStringToString(const CharString& char_string);
+/// \brief Convert a CharStringData into a textual DNS character-string.
+///
+/// Reverse of \c stringToCharStringData(). See \c stringToCharString()
+/// vs. \c stringToCharStringData().
+///
+/// \param char_string The \c CharStringData to convert.
+/// \return A string representation of \c char_string.
+std::string charStringDataToString(const CharStringData& char_string);
+
/// \brief Compare two CharString objects
///
/// \param self The CharString field to compare
/// 0 if \c self and \c other are equal
int compareCharStrings(const CharString& self, const CharString& other);
+/// \brief Compare two CharStringData objects
+///
+/// \param self The CharStringData field to compare
+/// \param other The CharStringData field to compare to
+///
+/// \return -1 if \c self would be sorted before \c other
+/// 1 if \c self would be sorted after \c other
+/// 0 if \c self and \c other are equal
+int compareCharStringDatas(const CharStringData& self,
+ const CharStringData& other);
+
/// \brief Convert a buffer containing a character-string to CharString
///
/// This method reads one character-string from the given buffer (in wire
--- /dev/null
+// Copyright (C) 2014 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.
+
+#include <util/unittests/wiredata.h>
+
+#include <dns/exceptions.h>
+#include <dns/rdata.h>
+#include <dns/rdata/generic/detail/char_string.h>
+#include <util/buffer.h>
+
+#include <gtest/gtest.h>
+
+#include <string>
+#include <vector>
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using isc::dns::rdata::generic::detail::CharStringData;
+using isc::dns::rdata::generic::detail::stringToCharStringData;
+using isc::dns::rdata::generic::detail::charStringDataToString;
+using isc::dns::rdata::generic::detail::compareCharStringDatas;
+using isc::util::unittests::matchWireData;
+
+namespace {
+const uint8_t test_charstr[] = {
+ 'T', 'e', 's', 't', ' ', 'S', 't', 'r', 'i', 'n', 'g'
+};
+
+class CharStringDataTest : public ::testing::Test {
+protected:
+ CharStringDataTest() :
+ // char-string representation for test data using two types of escape
+ // ('r' = 114)
+ test_str("Test\\ St\\114ing")
+ {
+ str_region.beg = &test_str[0];
+ str_region.len = test_str.size();
+ }
+ CharStringData chstr; // place holder
+ const std::string test_str;
+ MasterToken::StringRegion str_region;
+};
+
+MasterToken::StringRegion
+createStringRegion(const std::string& str) {
+ MasterToken::StringRegion region;
+ region.beg = &str[0]; // note std ensures this works even if str is empty
+ region.len = str.size();
+ return (region);
+}
+
+TEST_F(CharStringDataTest, normalConversion) {
+ uint8_t tmp[3]; // placeholder for expected sequence
+
+ stringToCharStringData(str_region, chstr);
+ matchWireData(test_charstr, sizeof(test_charstr), &chstr[0], chstr.size());
+
+ // Empty string
+ chstr.clear();
+ stringToCharStringData(createStringRegion(""), chstr);
+ EXPECT_TRUE(chstr.empty());
+
+ // Possible largest char string
+ chstr.clear();
+ std::string long_str(255, 'x');
+ stringToCharStringData(createStringRegion(long_str), chstr);
+ std::vector<uint8_t> expected;
+ expected.insert(expected.end(), long_str.begin(), long_str.end());
+ matchWireData(&expected[0], expected.size(), &chstr[0], chstr.size());
+
+ // Escaped '\'
+ chstr.clear();
+ tmp[0] = '\\';
+ stringToCharStringData(createStringRegion("\\\\"), chstr);
+ matchWireData(tmp, 1, &chstr[0], chstr.size());
+
+ // Boundary values for \DDD
+ chstr.clear();
+ tmp[0] = 0;
+ stringToCharStringData(createStringRegion("\\000"), chstr);
+ matchWireData(tmp, 1, &chstr[0], chstr.size());
+
+ chstr.clear();
+ stringToCharStringData(createStringRegion("\\255"), chstr);
+ tmp[0] = 255;
+ matchWireData(tmp, 1, &chstr[0], chstr.size());
+
+ // Another digit follows DDD; it shouldn't cause confusion
+ chstr.clear();
+ stringToCharStringData(createStringRegion("\\2550"), chstr);
+ tmp[1] = '0';
+ matchWireData(tmp, 2, &chstr[0], chstr.size());
+}
+
+TEST_F(CharStringDataTest, badConversion) {
+ // input string ending with (non escaped) '\'
+ chstr.clear();
+ EXPECT_THROW(stringToCharStringData(createStringRegion("foo\\"), chstr),
+ InvalidRdataText);
+}
+
+TEST_F(CharStringDataTest, badDDD) {
+ // Check various type of bad form of \DDD
+
+ // Not a number
+ EXPECT_THROW(stringToCharStringData(createStringRegion("\\1a2"), chstr),
+ InvalidRdataText);
+ EXPECT_THROW(stringToCharStringData(createStringRegion("\\12a"), chstr),
+ InvalidRdataText);
+
+ // Not in the range of uint8_t
+ EXPECT_THROW(stringToCharStringData(createStringRegion("\\256"), chstr),
+ InvalidRdataText);
+
+ // Short buffer
+ EXPECT_THROW(stringToCharStringData(createStringRegion("\\42"), chstr),
+ InvalidRdataText);
+}
+
+const struct TestData {
+ const char *data;
+ const char *expected;
+} conversion_data[] = {
+ {"Test\"Test", "Test\\\"Test"},
+ {"Test;Test", "Test\\;Test"},
+ {"Test\\Test", "Test\\\\Test"},
+ {"Test\x1fTest", "Test\\031Test"},
+ {"Test ~ Test", "Test ~ Test"},
+ {"Test\x7fTest", "Test\\127Test"},
+ {NULL, NULL}
+};
+
+TEST_F(CharStringDataTest, charStringDataToString) {
+ for (const TestData* cur = conversion_data; cur->data != NULL; ++cur) {
+ uint8_t idata[32];
+ size_t length = std::strlen(cur->data);
+ assert(sizeof(idata) >= length);
+ std::memcpy(idata, cur->data, length);
+ const CharStringData test_data(idata, idata + length);
+ EXPECT_EQ(cur->expected, charStringDataToString(test_data));
+ }
+}
+
+TEST_F(CharStringDataTest, compareCharStringData) {
+ CharStringData charstr;
+ CharStringData charstr2;
+ CharStringData charstr_small1;
+ CharStringData charstr_small2;
+ CharStringData charstr_large1;
+ CharStringData charstr_large2;
+ CharStringData charstr_empty;
+
+ stringToCharStringData(createStringRegion("test string"), charstr);
+ stringToCharStringData(createStringRegion("test string"), charstr2);
+ stringToCharStringData(createStringRegion("test strin"), charstr_small1);
+ stringToCharStringData(createStringRegion("test strina"), charstr_small2);
+ stringToCharStringData(createStringRegion("test stringa"), charstr_large1);
+ stringToCharStringData(createStringRegion("test strinz"), charstr_large2);
+
+ EXPECT_EQ(0, compareCharStringDatas(charstr, charstr2));
+ EXPECT_EQ(0, compareCharStringDatas(charstr2, charstr));
+ EXPECT_EQ(1, compareCharStringDatas(charstr, charstr_small1));
+ EXPECT_EQ(1, compareCharStringDatas(charstr, charstr_small2));
+ EXPECT_EQ(-1, compareCharStringDatas(charstr, charstr_large1));
+ EXPECT_EQ(-1, compareCharStringDatas(charstr, charstr_large2));
+ EXPECT_EQ(-1, compareCharStringDatas(charstr_small1, charstr));
+ EXPECT_EQ(-1, compareCharStringDatas(charstr_small2, charstr));
+ EXPECT_EQ(1, compareCharStringDatas(charstr_large1, charstr));
+ EXPECT_EQ(1, compareCharStringDatas(charstr_large2, charstr));
+
+ EXPECT_EQ(-1, compareCharStringDatas(charstr_empty, charstr));
+ EXPECT_EQ(1, compareCharStringDatas(charstr, charstr_empty));
+ EXPECT_EQ(0, compareCharStringDatas(charstr_empty, charstr_empty));
+}
+
+} // unnamed namespace