]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3860] More vsa
authorFrancis Dupont <fdupont@isc.org>
Thu, 21 Aug 2025 07:39:23 +0000 (09:39 +0200)
committerFrancis Dupont <fdupont@isc.org>
Fri, 12 Sep 2025 21:44:54 +0000 (23:44 +0200)
12 files changed:
src/hooks/dhcp/radius/client_attribute.cc
src/hooks/dhcp/radius/client_attribute.h
src/hooks/dhcp/radius/client_dictionary.cc
src/hooks/dhcp/radius/data/dictionary
src/hooks/dhcp/radius/radius_parsers.cc
src/hooks/dhcp/radius/tests/access_unittests.cc
src/hooks/dhcp/radius/tests/accounting_unittests.cc
src/hooks/dhcp/radius/tests/attribute_test.cc
src/hooks/dhcp/radius/tests/attribute_unittests.cc
src/hooks/dhcp/radius/tests/config_unittests.cc
src/hooks/dhcp/radius/tests/dictionary_unittests.cc
src/hooks/dhcp/radius/tests/request_unittests.h

index 41aac45726ed3366b55305a05be8a2c9b51430ff..7840bd4b97f85d62a124c86c6ec62098d2dbf8e7 100644 (file)
@@ -24,36 +24,6 @@ using namespace std;
 namespace isc {
 namespace radius {
 
-AttributePtr
-Attribute::fromText(const string& repr) {
-    if (repr.empty()) {
-        isc_throw(BadValue, "empty text attribute");
-    }
-    string trimed = str::trim(repr);
-    if (trimed.empty()) {
-        isc_throw(BadValue, "blank text attribute '" << repr << "'");
-    }
-    size_t equal = trimed.find('=');
-    if (equal == string::npos) {
-        isc_throw(BadValue, "can't find '=' in text attribute '"
-                  << repr << "'");
-    }
-    string name = str::trim(trimed.substr(0, equal));
-    if (name.empty()) {
-        isc_throw(BadValue, "empty attribute name in '" << repr << "'");
-    }
-    string value = str::trim(trimed.substr(equal + 1));
-    if (value.empty()) {
-        isc_throw(BadValue, "empty attribute value in '" << repr << "'");
-    }
-    AttrDefPtr def = AttrDefs::instance().getByName(name);
-    if (!def) {
-        isc_throw(NotFound, "can't find attribute definition for '"
-                  << name << "'");
-    }
-    return (Attribute::fromText(def, value));
-}
-
 AttributePtr
 Attribute::fromText(const AttrDefPtr& def, const string& value) {
     if (!def) {
@@ -81,7 +51,7 @@ Attribute::fromText(const AttrDefPtr& def, const string& value) {
     case PW_TYPE_IPV6PREFIX:
         return (AttrIpv6Prefix::fromText(def->type_, value));
     case PW_TYPE_VSA:
-        return (AttrVSA::fromText(def->type_, value));
+        return (AttrVsa::fromText(def->type_, value));
     default:
         // Impossible case.
         isc_throw(OutOfRange, "unknown value type "
@@ -134,7 +104,7 @@ Attribute::fromBytes(const AttrDefPtr& def, const vector<uint8_t>& value) {
     case PW_TYPE_IPV6PREFIX:
         return (AttrIpv6Prefix::fromBytes(def->type_, value));
     case PW_TYPE_VSA:
-        return (AttrVSA::fromBytes(def->type_, value));
+        return (AttrVsa::fromBytes(def->type_, value));
     default:
         // Impossible case.
         isc_throw(OutOfRange, "unknown value type "
@@ -179,15 +149,27 @@ Attribute::fromIpv6Prefix(const uint8_t type, const uint8_t len,
     return (AttributePtr(new AttrIpv6Prefix(type, len, value)));
 }
 
+AttributePtr
+Attribute::fromVsa(const uint8_t type, const uint32_t vendor,
+                   const std::string& value) {
+    return (AttributePtr(new AttrVsa(type, vendor, value)));
+}
+
+AttributePtr
+Attribute::fromVsa(const uint8_t type, const uint32_t vendor,
+                   const std::vector<uint8_t>& value) {
+    return (AttributePtr(new AttrVsa(type, vendor, value)));
+}
+
 string
 Attribute::toString() const {
-    isc_throw(TypeError, "the attribute value type must be string or vsa, not "
+    isc_throw(TypeError, "the attribute value type must be string, not "
               << attrValueTypeToText(getValueType()));
 }
 
 vector<uint8_t>
 Attribute::toBinary() const {
-    isc_throw(TypeError, "the attribute value type must be string or vsa, not "
+    isc_throw(TypeError, "the attribute value type must be string, not "
               << attrValueTypeToText(getValueType()));
 }
 
@@ -227,8 +209,8 @@ Attribute::toVendorId() const {
               << attrValueTypeToText(getValueType()));
 }
 
-void
-Attribute::setVendorId(const uint32_t vendor) {
+string
+Attribute::toVsaData() const {
     isc_throw(TypeError, "the attribute value type must be vsa, not "
               << attrValueTypeToText(getValueType()));
 }
@@ -276,7 +258,17 @@ AttrString::toText(size_t indent) const {
     for (size_t i = 0; i < indent; i++) {
         output << " ";
     }
-    output << AttrDefs::instance().getName(getType()) << '=' << value_;
+    output << AttrDefs::instance().getName(getType()) << '=';
+    if (str::isPrintable(value_)) {
+        output << "'" << value_ << "'";
+    } else {
+        vector<uint8_t> binary;
+        binary.resize(value_.size());
+        if (binary.size() > 0) {
+            memmove(&binary[0], value_.c_str(), binary.size());
+        }
+        output << "0x" << encode::encodeHex(binary);
+    }
     return (output.str());
 }
 
@@ -628,7 +620,7 @@ AttrIpv6Prefix::toElement() const {
     return (output);
 }
 
-AttrVSA::AttrVSA(const uint8_t type, const int32_t vendor,
+AttrVsa::AttrVsa(const uint8_t type, const uint32_t vendor,
                  const vector<uint8_t>& value)
     : Attribute(type), vendor_(vendor), value_() {
     if (value.empty()) {
@@ -643,12 +635,12 @@ AttrVSA::AttrVSA(const uint8_t type, const int32_t vendor,
 }
 
 AttributePtr
-AttrVSA::fromText(const uint8_t type, const string& repr) {
-    isc_throw(NotImplemented, "Can't decode VSA from text");
+AttrVsa::fromText(const uint8_t type, const string& repr) {
+    isc_throw(NotImplemented, "Can't decode vsa from text");
 }
 
 AttributePtr
-AttrVSA::fromBytes(const uint8_t type, const vector<uint8_t>& bytes) {
+AttrVsa::fromBytes(const uint8_t type, const vector<uint8_t>& bytes) {
     if (bytes.empty()) {
         isc_throw(BadValue, "empty attribute value");
     }
@@ -667,16 +659,28 @@ AttrVSA::fromBytes(const uint8_t type, const vector<uint8_t>& bytes) {
     if (value.size() > 0) {
         memmove(&value[0], &bytes[4], value.size());
     }
-    return (AttributePtr(new AttrVSA(type, vendor, value)));
+    return (AttributePtr(new AttrVsa(type, vendor, value)));
 }
 
 string
-AttrVSA::toText(size_t indent) const {
-    isc_throw(NotImplemented, "Can't encode VSA into text");
+AttrVsa::toText(size_t indent) const {
+    ostringstream output;
+    for (size_t i = 0; i < indent; i++) {
+        output << " ";
+    }
+    output << AttrDefs::instance().getName(getType()) << "=["
+           << vendor_ << "]";
+    vector<uint8_t> binary;
+    binary.resize(value_.size());
+    if (binary.size() > 0) {
+        memmove(&binary[0], value_.c_str(), binary.size());
+    }
+    output << "0x" << encode::encodeHex(binary);
+    return (output.str());
 }
 
 std::vector<uint8_t>
-AttrVSA::toBytes() const {
+AttrVsa::toBytes() const {
     vector<uint8_t> output;
     output.resize(2 + getValueLen());
     output[0] = getType();
@@ -691,18 +695,8 @@ AttrVSA::toBytes() const {
     return (output);
 }
 
-std::vector<uint8_t>
-AttrVSA::toBinary() const {
-    vector<uint8_t> binary;
-    binary.resize(getValueLen() - 4);
-    if (binary.size() > 0) {
-        memmove(&binary[0], &value_[0], binary.size());
-    }
-    return (binary);
-}
-
 ElementPtr
-AttrVSA::toElement() const {
+AttrVsa::toElement() const {
     ElementPtr output = Element::createMap();
     AttrDefPtr def = AttrDefs::instance().getByType(getType());
     if (def) {
index ac13cfbd790e7e10fe69da5173a3c2500aae256f..d98ee9d601312520ce39d538a888a9b242e7368a 100644 (file)
@@ -69,13 +69,6 @@ public:
 
     /// Generic factories.
 
-    /// @brief From text.
-    ///
-    /// @param repr name=value representation.
-    /// @return pointer to the attribute.
-    /// @throw NotFound if the definition can't be found.
-    static AttributePtr fromText(const std::string& repr);
-
     /// @brief From bytes (wire format).
     ///
     /// @param bytes binary attribute.
@@ -164,12 +157,12 @@ public:
 
     /// @brief From Vendor ID and string data with type.
     ///
-    /// @note Requires the type to be of the Vendor Specific attribute (26).
+    /// @note Requires the type to be of a standard vsa attribute.
     ///
     /// @param type type of attribute.
     /// @param vendor vendor id.
     /// @param value vsa data.
-    static AttributePtr fromVSA(const uint8_t type,
+    static AttributePtr fromVsa(const uint8_t type,
                                 const uint32_t vendor,
                                 const std::string& value);
 
@@ -180,7 +173,7 @@ public:
     /// @param type type of attribute.
     /// @param vendor vendor id.
     /// @param value vsa data.
-    static AttributePtr fromVSA(const uint8_t type,
+    static AttributePtr fromVsa(const uint8_t type,
                                 const uint32_t vendor,
                                 const std::vector<uint8_t>& value);
 
@@ -209,13 +202,13 @@ public:
     /// @brief To string.
     ///
     /// @return the string value.
-    /// @throw TypeError if the attribute is not a string or vsa one.
+    /// @throw TypeError if the attribute is not a string one.
     virtual std::string toString() const;
 
     /// @brief To binary.
     ///
     /// @return the string value as a binary.
-    /// @throw TypeError if the attribute is not a string or vsa one.
+    /// @throw TypeError if the attribute is not a string one.
     virtual std::vector<uint8_t> toBinary() const;
 
     /// @brief To integer.
@@ -254,13 +247,11 @@ public:
     /// @throw TypeError if the attribute is not a vsa one.
     virtual uint32_t toVendorId() const;
 
-    /// Generic set methods.
-
-    /// @brief Set vendor id.
+    /// @brief To vsa data.
     ///
-    /// @param vendor vendor id.
+    /// @return the vsa data.
     /// @throw TypeError if the attribute is not a vsa one.
-    virtual void setVendorId(const uint32_t vendor);
+    virtual std::string toVsaData() const;
 
     /// @brief Type.
     const uint8_t type_;
@@ -689,7 +680,7 @@ private:
 };
 
 /// @brief RADIUS attribute holding vsa.
-class AttrVSA : public Attribute {
+class AttrVsa : public Attribute {
 protected:
 
     /// @brief Constructor.
@@ -697,7 +688,8 @@ protected:
     /// @param type attribute type.
     /// @param vendor vendor id.
     /// @param value string vsa data.
-    AttrVSA(const uint8_t type, const int32_t vendor, const std::string& value)
+    AttrVsa(const uint8_t type, const uint32_t vendor,
+            const std::string& value)
         : Attribute(type), vendor_(vendor), value_(value) {
         if (value.empty()) {
             isc_throw(BadValue, "value is empty");
@@ -713,7 +705,7 @@ protected:
     /// @param type attribute type.
     /// @param vendor vendor id.
     /// @param value binary vsa data.
-    AttrVSA(const uint8_t type, const int32_t vendor,
+    AttrVsa(const uint8_t type, const uint32_t vendor,
             const std::vector<uint8_t>& value);
 
     /// @brief From text.
@@ -721,6 +713,7 @@ protected:
     /// @param type attribute type.
     /// @param repr value representation.
     /// @return pointer to the attribute or null.
+    /// @throw NotImplemented
     static AttributePtr fromText(const uint8_t type, const std::string& repr);
 
     /// @brief From bytes.
@@ -761,18 +754,6 @@ public:
     /// @return binary representation.
     virtual std::vector<uint8_t> toBytes() const override;
 
-    /// @brief To string.
-    ///
-    /// @return the string value.
-    virtual std::string toString() const override {
-        return (value_);
-    }
-
-    /// @brief To binary.
-    ///
-    /// @return the string value as a binary.
-    virtual std::vector<uint8_t> toBinary() const override;
-
     /// @brief To vendor id.
     ///
     /// @return the vendor id.
@@ -780,11 +761,11 @@ public:
         return (vendor_);
     }
 
-    /// @brief Set vendor id.
+    /// @brief To vsa data.
     ///
-    /// @param vendor vendor id.
-    virtual void setVendorId(const uint32_t vendor) override {
-        vendor_ = vendor;
+    /// @return the vsa data.
+    virtual std::string toVsaData() const override {
+        return (value_);
     }
 
     /// @brief Unparse attribute.
index 988ab2f2ff19068c5c468af9e039606588b5522c..479db62e0dfdbbecb0846a8eadccde0636314aaa 100644 (file)
@@ -291,6 +291,9 @@ AttrDefs::parseLine(const string& line, unsigned int depth) {
         } catch (...) {
             isc_throw(Unexpected, "can't parse integer value " << value_str);
         }
+        if (value == 0) {
+            isc_throw(Unexpected, "0 is reserved");
+        }
         IntCstDefPtr def(new IntCstDef(PW_VENDOR_SPECIFIC, name, value));
         add(def);
         return;
index f06dde01b3d3159fd65738375d86f40b44ca7d3e..94ed4716bcfc8ef7ec9e1081fc176df151c448e3 100644 (file)
@@ -26,7 +26,7 @@ ATTRIBUTE     Framed-Route            22      string
 ATTRIBUTE      Framed-IPX-Network      23      ipaddr
 ATTRIBUTE      State                   24      string
 ATTRIBUTE      Class                   25      string
-ATTRIBUTE      Vendor-Specific         26      string
+ATTRIBUTE      Vendor-Specific         26      vsa
 ATTRIBUTE      Session-Timeout         27      integer
 ATTRIBUTE      Idle-Timeout            28      integer
 ATTRIBUTE      Termination-Action      29      integer
index 1e98f413db6c6e107ce15e10473631d56a0715ef..20c89d1053e84f67c773dde40cd8daecf74b85e7 100644 (file)
@@ -74,6 +74,7 @@ const AttrDefList RadiusConfigParser::USED_STANDARD_ATTR_DEFS = {
     { PW_FRAMED_IP_ADDRESS,     "Framed-IP-Address",     PW_TYPE_IPADDR },
     { PW_REPLY_MESSAGE,         "Reply-Message",         PW_TYPE_STRING },
     { PW_CLASS,                 "Class",                 PW_TYPE_STRING },
+    { PW_VENDOR_SPECIFIC,       "Vendor-Specific",       PW_TYPE_VSA },
     { PW_CALLING_STATION_ID,    "Calling-Station-Id",    PW_TYPE_STRING },
     { PW_ACCT_STATUS_TYPE,      "Acct-Status-Type",      PW_TYPE_INTEGER },
     { PW_ACCT_DELAY_TIME,       "Acct-Delay-Time",       PW_TYPE_INTEGER },
index a42da6cde89f3cb63b69d02fe89eec0c5d512452..d97fe16f14ded7fc0af44837276545819df53b4e 100644 (file)
@@ -1755,12 +1755,12 @@ TEST_F(AccessTest, buildHWAddr4) {
     EXPECT_LE(2, handler->env_.send_attrs_->size());
     ConstAttributePtr user_name = handler->env_.send_attrs_->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    string expected = "User-Name=" + text;
+    string expected = "User-Name='" + text + "'";
     EXPECT_EQ(expected, user_name->toText());
     ConstAttributePtr calling_station_id =
         handler->env_.send_attrs_->get(PW_CALLING_STATION_ID);
     ASSERT_TRUE(calling_station_id);
-    EXPECT_EQ("Calling-Station-Id=20:e5:2a:b8:15:14",
+    EXPECT_EQ("Calling-Station-Id='20:e5:2a:b8:15:14'",
               calling_station_id->toText());
 }
 
@@ -1783,12 +1783,12 @@ TEST_F(AccessTest, buildHWAddr6) {
     EXPECT_LE(2, handler->env_.send_attrs_->size());
     ConstAttributePtr user_name = handler->env_.send_attrs_->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    string expected = "User-Name=" + text;
+    string expected = "User-Name='" + text + "'";
     EXPECT_EQ(expected, user_name->toText());
     ConstAttributePtr calling_station_id =
         handler->env_.send_attrs_->get(PW_CALLING_STATION_ID);
     ASSERT_TRUE(calling_station_id);
-    EXPECT_EQ("Calling-Station-Id=08:00:27:58:f1:e8",
+    EXPECT_EQ("Calling-Station-Id='08:00:27:58:f1:e8'",
               calling_station_id->toText());
 }
 
@@ -1809,7 +1809,7 @@ TEST_F(AccessTest, buildCanonHWAddr4) {
     ConstAttributePtr calling_station_id =
         handler->env_.send_attrs_->get(PW_CALLING_STATION_ID);
     ASSERT_TRUE(calling_station_id);
-    EXPECT_EQ("Calling-Station-Id=20-e5-2a-b8-15-14",
+    EXPECT_EQ("Calling-Station-Id='20-e5-2a-b8-15-14'",
               calling_station_id->toText());
 }
 
@@ -1830,7 +1830,7 @@ TEST_F(AccessTest, buildCanonHWAddr6) {
     ConstAttributePtr calling_station_id =
         handler->env_.send_attrs_->get(PW_CALLING_STATION_ID);
     ASSERT_TRUE(calling_station_id);
-    EXPECT_EQ("Calling-Station-Id=08-00-27-58-f1-e8",
+    EXPECT_EQ("Calling-Station-Id='08-00-27-58-f1-e8'",
               calling_station_id->toText());
 }
 
index 2521a200de97858c12c1580d849ab57442661b5a..9af3040647782684e24b52c99382593f73e35a30 100644 (file)
@@ -743,10 +743,10 @@ void AccountingTest::testBuildAcctLease4() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
+    EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
     ConstAttributePtr calling_station_id = attrs->get(PW_CALLING_STATION_ID);
     ASSERT_TRUE(calling_station_id);
-    EXPECT_EQ("Calling-Station-Id=20:e5:2a:b8:15:14",
+    EXPECT_EQ("Calling-Station-Id='20:e5:2a:b8:15:14'",
               calling_station_id->toText());
     ConstAttributePtr framed_ip_address = attrs->get(PW_FRAMED_IP_ADDRESS);
     ASSERT_TRUE(framed_ip_address);
@@ -782,7 +782,7 @@ void AccountingTest::testBuildAcctLease4canon() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr calling_station_id = attrs->get(PW_CALLING_STATION_ID);
     ASSERT_TRUE(calling_station_id);
-    EXPECT_EQ("Calling-Station-Id=20-e5-2a-b8-15-14",
+    EXPECT_EQ("Calling-Station-Id='20-e5-2a-b8-15-14'",
               calling_station_id->toText());
     EXPECT_EQ(0, attrs->count(PW_CLASS));
 }
@@ -805,7 +805,7 @@ void AccountingTest::testBuildAcctLease4noDuid() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
+    EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
     EXPECT_EQ(0, attrs->count(PW_CLASS));
 }
 
@@ -827,7 +827,7 @@ void AccountingTest::testBuildAcctLease4noPop0() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
+    EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
     EXPECT_EQ(0, attrs->count(PW_CLASS));
 }
 
@@ -849,7 +849,7 @@ void AccountingTest::testBuildAcctLease4notPrintable() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
+    EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
     EXPECT_EQ(0, attrs->count(PW_CLASS));
 }
 
@@ -873,7 +873,7 @@ void AccountingTest::testBuildAcctLease4Duid() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=05:06:07:08:09", user_name->toText());
+    EXPECT_EQ("User-Name='05:06:07:08:09'", user_name->toText());
     EXPECT_EQ(0, attrs->count(PW_CLASS));
 }
 
@@ -898,7 +898,7 @@ void AccountingTest::testBuildAcctLease4DuidPrintable() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=Foobar", user_name->toText());
+    EXPECT_EQ("User-Name='Foobar'", user_name->toText());
     EXPECT_EQ(0, attrs->count(PW_CLASS));
 }
 
@@ -920,7 +920,7 @@ void AccountingTest::testBuildAcctLease4Pop0() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
+    EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
     EXPECT_EQ(0, attrs->count(PW_CLASS));
 }
 
@@ -942,7 +942,7 @@ void AccountingTest::testBuildAcctLease4Pop0Printable() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=Foobar", user_name->toText());
+    EXPECT_EQ("User-Name='Foobar'", user_name->toText());
     EXPECT_EQ(0, attrs->count(PW_CLASS));
 }
 
@@ -962,7 +962,7 @@ void AccountingTest::testBuildAcctLease4noClientId() {
     ASSERT_LE(4, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=20:e5:2a:b8:15:14", user_name->toText());
+    EXPECT_EQ("User-Name='20:e5:2a:b8:15:14'", user_name->toText());
     ConstAttributePtr calling_station_id = attrs->get(PW_CALLING_STATION_ID);
     EXPECT_FALSE(calling_station_id);
     EXPECT_EQ(0, attrs->count(PW_CLASS));
@@ -1166,7 +1166,7 @@ void AccountingTest::testBuildAcctLease4ClassClientID() {
     ASSERT_LE(6, attrs->size());
     ConstAttributePtr cclass = attrs->get(PW_CLASS);
     ASSERT_TRUE(cclass);
-    EXPECT_EQ("Class=foobar", cclass->toText());
+    EXPECT_EQ("Class='foobar'", cclass->toText());
 }
 
 /// Verify that buildAcct on IPv4/HwAddr can get the Class from host cache.
@@ -1200,7 +1200,7 @@ void AccountingTest::testBuildAcctLease4ClassHwAddr() {
     ASSERT_LE(6, attrs->size());
     ConstAttributePtr cclass = attrs->get(PW_CLASS);
     ASSERT_TRUE(cclass);
-    EXPECT_EQ("Class=foobar", cclass->toText());
+    EXPECT_EQ("Class='foobar'", cclass->toText());
 }
 
 /// Verify that buildAcct on IPv4/DUID can get the Class from host cache.
@@ -1235,7 +1235,7 @@ void AccountingTest::testBuildAcctLease4ClassDuid() {
     ASSERT_LE(6, attrs->size());
     ConstAttributePtr cclass = attrs->get(PW_CLASS);
     ASSERT_TRUE(cclass);
-    EXPECT_EQ("Class=foobar", cclass->toText());
+    EXPECT_EQ("Class='foobar'", cclass->toText());
 }
 
 /// Verify that buildAcct on IPv4/Flex can get the Class from host cache.
@@ -1268,7 +1268,7 @@ void AccountingTest::testBuildAcctLease4ClassFlex() {
     ASSERT_LE(6, attrs->size());
     ConstAttributePtr cclass = attrs->get(PW_CLASS);
     ASSERT_TRUE(cclass);
-    EXPECT_EQ("Class=foobar", cclass->toText());
+    EXPECT_EQ("Class='foobar'", cclass->toText());
 }
 
 /// Verify that buildAcct on IPv6 lease works.
@@ -1292,10 +1292,10 @@ void AccountingTest::testBuildAcctLease6() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
+    EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
     ConstAttributePtr calling_station_id = attrs->get(PW_CALLING_STATION_ID);
     ASSERT_TRUE(calling_station_id);
-    EXPECT_EQ("Calling-Station-Id=20:e5:2a:b8:15:14",
+    EXPECT_EQ("Calling-Station-Id='20:e5:2a:b8:15:14'",
               calling_station_id->toText());
     ConstAttributePtr framed_ip_address = attrs->get(PW_FRAMED_IPV6_ADDRESS);
     ASSERT_TRUE(framed_ip_address);
@@ -1335,10 +1335,10 @@ void AccountingTest::testBuildAcctLease6prefix() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
+    EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
     ConstAttributePtr calling_station_id = attrs->get(PW_CALLING_STATION_ID);
     ASSERT_TRUE(calling_station_id);
-    EXPECT_EQ("Calling-Station-Id=20:e5:2a:b8:15:14",
+    EXPECT_EQ("Calling-Station-Id='20:e5:2a:b8:15:14'",
               calling_station_id->toText());
     ConstAttributePtr delegated_prefix = attrs->get(PW_DELEGATED_IPV6_PREFIX);
     ASSERT_TRUE(delegated_prefix);
@@ -1376,7 +1376,7 @@ void AccountingTest::testBuildAcctLease6canon() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr calling_station_id = attrs->get(PW_CALLING_STATION_ID);
     ASSERT_TRUE(calling_station_id);
-    EXPECT_EQ("Calling-Station-Id=20-e5-2a-b8-15-14",
+    EXPECT_EQ("Calling-Station-Id='20-e5-2a-b8-15-14'",
               calling_station_id->toText());
     EXPECT_EQ(0, attrs->count(PW_CLASS));
 }
@@ -1400,7 +1400,7 @@ void AccountingTest::testBuildAcctLease6noPop0() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
+    EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
 }
 
 /// Verify that buildAcct on IPv6 lease works with not printable duid.
@@ -1422,7 +1422,7 @@ void AccountingTest::testBuildAcctLease6notPrintable() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
+    EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
     EXPECT_EQ(0, attrs->count(PW_CLASS));
 }
 
@@ -1445,7 +1445,7 @@ void AccountingTest::testBuildAcctLease6Pop0() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
+    EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
     EXPECT_EQ(0, attrs->count(PW_CLASS));
 }
 
@@ -1468,7 +1468,7 @@ void AccountingTest::testBuildAcctLease6Printable() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=Foobar", user_name->toText());
+    EXPECT_EQ("User-Name='Foobar'", user_name->toText());
     EXPECT_EQ(0, attrs->count(PW_CLASS));
 }
 
@@ -1491,7 +1491,7 @@ void AccountingTest::testBuildAcctLease6Pop0Printable() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=Foobar", user_name->toText());
+    EXPECT_EQ("User-Name='Foobar'", user_name->toText());
     EXPECT_EQ(0, attrs->count(PW_CLASS));
 }
 
@@ -1701,7 +1701,7 @@ void AccountingTest::testBuildAcctLease6ClassDUID() {
     ASSERT_LE(6, attrs->size());
     ConstAttributePtr cclass = attrs->get(PW_CLASS);
     ASSERT_TRUE(cclass);
-    EXPECT_EQ("Class=foobar", cclass->toText());
+    EXPECT_EQ("Class='foobar'", cclass->toText());
 }
 
 /// Verify that buildAcct on IPv6/HwAddr can get the Class from host cache.
@@ -1736,7 +1736,7 @@ void AccountingTest::testBuildAcctLease6ClassHwAddr() {
     ASSERT_LE(6, attrs->size());
     ConstAttributePtr cclass = attrs->get(PW_CLASS);
     ASSERT_TRUE(cclass);
-    EXPECT_EQ("Class=foobar", cclass->toText());
+    EXPECT_EQ("Class='foobar'", cclass->toText());
 }
 
 /// Verify that buildAcct on IPv6/Flex can get the Class from host cache.
@@ -1770,7 +1770,7 @@ void AccountingTest::testBuildAcctLease6ClassFlex() {
     ASSERT_LE(6, attrs->size());
     ConstAttributePtr cclass = attrs->get(PW_CLASS);
     ASSERT_TRUE(cclass);
-    EXPECT_EQ("Class=foobar", cclass->toText());
+    EXPECT_EQ("Class='foobar'", cclass->toText());
 }
 
 /// Verify that buildAcct4 works.
@@ -1794,10 +1794,10 @@ void AccountingTest::testBuildAcct4() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
+    EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
     ConstAttributePtr calling_station_id = attrs->get(PW_CALLING_STATION_ID);
     ASSERT_TRUE(calling_station_id);
-    EXPECT_EQ("Calling-Station-Id=20:e5:2a:b8:15:14",
+    EXPECT_EQ("Calling-Station-Id='20:e5:2a:b8:15:14'",
               calling_station_id->toText());
     ConstAttributePtr framed_ip_address = attrs->get(PW_FRAMED_IP_ADDRESS);
     ASSERT_TRUE(framed_ip_address);
@@ -1872,7 +1872,7 @@ void AccountingTest::testBuildAcct4canon() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr calling_station_id = attrs->get(PW_CALLING_STATION_ID);
     ASSERT_TRUE(calling_station_id);
-    EXPECT_EQ("Calling-Station-Id=20-e5-2a-b8-15-14",
+    EXPECT_EQ("Calling-Station-Id='20-e5-2a-b8-15-14'",
               calling_station_id->toText());
     ConstAttributePtr framed_ip_address = attrs->get(PW_FRAMED_IP_ADDRESS);
     EXPECT_EQ("Framed-IP-Address=192.0.2.1", framed_ip_address->toText());
@@ -1897,7 +1897,7 @@ void AccountingTest::testBuildAcct4noPop0() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
+    EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
     EXPECT_EQ(0, attrs->count(PW_CLASS));
 }
 
@@ -1919,7 +1919,7 @@ void AccountingTest::testBuildAcct4notPrintable() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
+    EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
     EXPECT_EQ(0, attrs->count(PW_CLASS));
 }
 
@@ -1941,7 +1941,7 @@ void AccountingTest::testBuildAcct4Pop0() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
+    EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
     EXPECT_EQ(0, attrs->count(PW_CLASS));
 }
 
@@ -1963,7 +1963,7 @@ void AccountingTest::testBuildAcct4Printable() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=Foobar", user_name->toText());
+    EXPECT_EQ("User-Name='Foobar'", user_name->toText());
     EXPECT_EQ(0, attrs->count(PW_CLASS));
 }
 
@@ -1985,7 +1985,7 @@ void AccountingTest::testBuildAcct4Pop0Printable() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=Foobar", user_name->toText());
+    EXPECT_EQ("User-Name='Foobar'", user_name->toText());
     EXPECT_EQ(0, attrs->count(PW_CLASS));
 }
 
@@ -2029,7 +2029,7 @@ void AccountingTest::testBuildAcct4noClientId() {
     ASSERT_LE(4, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=20:e5:2a:b8:15:14", user_name->toText());
+    EXPECT_EQ("User-Name='20:e5:2a:b8:15:14'", user_name->toText());
     EXPECT_EQ(0, attrs->count(PW_CLASS));
 }
 
@@ -2050,7 +2050,7 @@ void AccountingTest::testBuildAcct4noClientIdcanon() {
     ASSERT_LE(4, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=20-e5-2a-b8-15-14", user_name->toText());
+    EXPECT_EQ("User-Name='20-e5-2a-b8-15-14'", user_name->toText());
     EXPECT_EQ(0, attrs->count(PW_CLASS));
 }
 
@@ -2132,7 +2132,7 @@ void AccountingTest::testBuildAcct4ClassClientID() {
     ASSERT_LE(6, attrs->size());
     ConstAttributePtr cclass = attrs->get(PW_CLASS);
     ASSERT_TRUE(cclass);
-    EXPECT_EQ("Class=foobar", cclass->toText());
+    EXPECT_EQ("Class='foobar'", cclass->toText());
 }
 
 /// Verify that buildAcct4 on HwAddr can get the Class from host cache.
@@ -2167,7 +2167,7 @@ void AccountingTest::testBuildAcct4ClassHwAddr() {
     ASSERT_LE(6, attrs->size());
     ConstAttributePtr cclass = attrs->get(PW_CLASS);
     ASSERT_TRUE(cclass);
-    EXPECT_EQ("Class=foobar", cclass->toText());
+    EXPECT_EQ("Class='foobar'", cclass->toText());
 }
 
 /// Verify that buildAcct4 on Flex can get the Class from host cache.
@@ -2202,7 +2202,7 @@ void AccountingTest::testBuildAcct4ClassFlex() {
     ASSERT_LE(6, attrs->size());
     ConstAttributePtr cclass = attrs->get(PW_CLASS);
     ASSERT_TRUE(cclass);
-    EXPECT_EQ("Class=foobar", cclass->toText());
+    EXPECT_EQ("Class='foobar'", cclass->toText());
 }
 
 /// Verify that buildAcct6 works.
@@ -2226,10 +2226,10 @@ void AccountingTest::testBuildAcct6() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
+    EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
     ConstAttributePtr calling_station_id = attrs->get(PW_CALLING_STATION_ID);
     ASSERT_TRUE(calling_station_id);
-    EXPECT_EQ("Calling-Station-Id=20:e5:2a:b8:15:14",
+    EXPECT_EQ("Calling-Station-Id='20:e5:2a:b8:15:14'",
               calling_station_id->toText());
     ConstAttributePtr framed_ip_address = attrs->get(PW_FRAMED_IPV6_ADDRESS);
     ASSERT_TRUE(framed_ip_address);
@@ -2270,10 +2270,10 @@ void AccountingTest::testBuildAcct6prefix() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
+    EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
     ConstAttributePtr calling_station_id = attrs->get(PW_CALLING_STATION_ID);
     ASSERT_TRUE(calling_station_id);
-    EXPECT_EQ("Calling-Station-Id=20:e5:2a:b8:15:14",
+    EXPECT_EQ("Calling-Station-Id='20:e5:2a:b8:15:14'",
               calling_station_id->toText());
     ConstAttributePtr delegated_prefix = attrs->get(PW_DELEGATED_IPV6_PREFIX);
     ASSERT_TRUE(delegated_prefix);
@@ -2464,7 +2464,7 @@ void AccountingTest::testBuildAcct6canon() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr calling_station_id = attrs->get(PW_CALLING_STATION_ID);
     ASSERT_TRUE(calling_station_id);
-    EXPECT_EQ("Calling-Station-Id=20-e5-2a-b8-15-14",
+    EXPECT_EQ("Calling-Station-Id='20-e5-2a-b8-15-14'",
               calling_station_id->toText());
     ConstAttributePtr framed_ip_address = attrs->get(PW_FRAMED_IPV6_ADDRESS);
     EXPECT_EQ("Framed-IPv6-Address=2001:db8::1235",
@@ -2491,7 +2491,7 @@ void AccountingTest::testBuildAcct6noPop0() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
+    EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
     EXPECT_EQ(0, attrs->count(PW_CLASS));
 }
 
@@ -2514,7 +2514,7 @@ void AccountingTest::testBuildAcct6notPrintable() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
+    EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
     EXPECT_EQ(0, attrs->count(PW_CLASS));
 }
 
@@ -2537,7 +2537,7 @@ void AccountingTest::testBuildAcct6Pop0() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
+    EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
     EXPECT_EQ(0, attrs->count(PW_CLASS));
 }
 
@@ -2560,7 +2560,7 @@ void AccountingTest::testBuildAcct6Printable() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=Foobar", user_name->toText());
+    EXPECT_EQ("User-Name='Foobar'", user_name->toText());
     EXPECT_EQ(0, attrs->count(PW_CLASS));
 }
 
@@ -2583,7 +2583,7 @@ void AccountingTest::testBuildAcct6Pop0Printable() {
     ASSERT_LE(5, attrs->size());
     ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
     ASSERT_TRUE(user_name);
-    EXPECT_EQ("User-Name=Foobar", user_name->toText());
+    EXPECT_EQ("User-Name='Foobar'", user_name->toText());
     EXPECT_EQ(0, attrs->count(PW_CLASS));
 }
 
@@ -2690,7 +2690,7 @@ void AccountingTest::testBuildAcct6ClassDUID() {
     ASSERT_LE(6, attrs->size());
     ConstAttributePtr cclass = attrs->get(PW_CLASS);
     ASSERT_TRUE(cclass);
-    EXPECT_EQ("Class=foobar", cclass->toText());
+    EXPECT_EQ("Class='foobar'", cclass->toText());
 }
 
 /// Verify that buildAcct6 on HwAddr can get the Class from host cache.
@@ -2724,7 +2724,7 @@ void AccountingTest::testBuildAcct6ClassHwAddr() {
     ASSERT_LE(6, attrs->size());
     ConstAttributePtr cclass = attrs->get(PW_CLASS);
     ASSERT_TRUE(cclass);
-    EXPECT_EQ("Class=foobar", cclass->toText());
+    EXPECT_EQ("Class='foobar'", cclass->toText());
 }
 
 /// Verify that buildAcct6 on Flex can get the Class from host cache.
@@ -2758,7 +2758,7 @@ void AccountingTest::testBuildAcct6ClassFlex() {
     ASSERT_LE(6, attrs->size());
     ConstAttributePtr cclass = attrs->get(PW_CLASS);
     ASSERT_TRUE(cclass);
-    EXPECT_EQ("Class=foobar", cclass->toText());
+    EXPECT_EQ("Class='foobar'", cclass->toText());
 }
 
 /// Verify that lease4_select hook returns for fake allocations.
index 927e391af467a84009e7e3f6740f655573e6def3..dc3e0c45f4118a992bbd6d2da5afefa278821cfc 100644 (file)
@@ -35,6 +35,9 @@ AttributeTest::compare(ConstAttributePtr first, ConstAttributePtr second) {
     } else if (first->getValueType() == PW_TYPE_IPV6PREFIX) {
         return ((first->toIpv6Prefix() == second->toIpv6Prefix()) &&
                 (first->toIpv6PrefixLen() == second->toIpv6PrefixLen()));
+    } else if (first->getValueType() == PW_TYPE_VSA) {
+        return ((first->toVendorId() == second->toVendorId()) &&
+                (first->toVsaData() == second->toVsaData()));
     }
     // Should not happen...
     return (false);
index 1bc3e32c900727cbc9d6fcb44d23684508ce4e38..e7f51066dd845d24b6e5dabf3d742c37cd2b0c5f 100644 (file)
@@ -28,22 +28,6 @@ using namespace isc::test;
 
 namespace {
 
-// Verify error cases for generic factory fromText.
-TEST_F(AttributeTest, fromText) {
-    EXPECT_THROW_MSG(Attribute::fromText(""), BadValue,
-                     "empty text attribute");
-    EXPECT_THROW_MSG(Attribute::fromText(" "), BadValue,
-                     "blank text attribute ' '");
-    EXPECT_THROW_MSG(Attribute::fromText("foo-bar"), BadValue,
-                     "can't find '=' in text attribute 'foo-bar'");
-    EXPECT_THROW_MSG(Attribute::fromText("=bar"), BadValue,
-                     "empty attribute name in '=bar'");
-    EXPECT_THROW_MSG(Attribute::fromText("foo="), BadValue,
-                     "empty attribute value in 'foo='");
-    EXPECT_THROW_MSG(Attribute::fromText("Foo-Bar=1"), NotFound,
-                     "can't find attribute definition for 'Foo-Bar'");
-}
-
 // Verify error cases for factory fromText with definition.
 TEST_F(AttributeTest, defFromText) {
     AttrDefPtr def;
@@ -107,18 +91,13 @@ TEST_F(AttributeTest, attrString) {
     string to_string;
     EXPECT_NO_THROW_LOG(to_string = attr->toString());
     EXPECT_EQ("foobar", to_string);
-    EXPECT_EQ("User-Name=foobar", attr->toText());
+    EXPECT_EQ("User-Name='foobar'", attr->toText());
     vector<uint8_t> binary = { 1, 8, 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72 };
     EXPECT_EQ(binary, attr->toBytes());
     string expected = "{ \"type\": 1, \"name\": \"User-Name\", ";
     expected += " \"data\": \"foobar\" }";
     runToElementTest<Attribute>(expected, *attr);
 
-    AttributePtr from_text = Attribute::fromText("User-Name=foobar");
-    ASSERT_TRUE(from_text);
-    EXPECT_TRUE(compare(from_text, attr))
-        << from_text->toText() << " != " << attr->toText();
-
     AttributePtr from_bytes = Attribute::fromBytes(binary);
     ASSERT_TRUE(from_bytes);
     EXPECT_TRUE(compare(from_bytes, attr))
@@ -177,6 +156,8 @@ TEST_F(AttributeTest, attrString) {
                      "the attribute value type must be ipv6prefix, not string");
     EXPECT_THROW_MSG(attr->toVendorId(), TypeError,
                      "the attribute value type must be vsa, not string");
+    EXPECT_THROW_MSG(attr->toVsaData(), TypeError,
+                     "the attribute value type must be vsa, not string");
 }
 
 // Verifies raw string attribute.
@@ -190,18 +171,13 @@ TEST_F(AttributeTest, rawAttrString) {
     string to_string;
     EXPECT_NO_THROW_LOG(to_string = attr->toString());
     EXPECT_EQ("\x01\x02\x03", to_string);
-    EXPECT_EQ("User-Name=\x01\x02\x03", attr->toText());
+    EXPECT_EQ("User-Name=0x010203", attr->toText());
     vector<uint8_t> binary = { 1, 5, 1, 2, 3 };
     EXPECT_EQ(binary, attr->toBytes());
     string expected = "{ \"type\": 1, \"name\": \"User-Name\", ";
     expected += " \"raw\": \"010203\" }";
     runToElementTest<Attribute>(expected, *attr);
 
-    AttributePtr from_text = Attribute::fromText("User-Name=\x01\x02\x03");
-    ASSERT_TRUE(from_text);
-    EXPECT_TRUE(compare(from_text, attr))
-        << from_text->toText() << " != " << attr->toText();
-
     AttributePtr from_bytes = Attribute::fromBytes(binary);
     ASSERT_TRUE(from_bytes);
     EXPECT_TRUE(compare(from_bytes, attr))
@@ -232,19 +208,6 @@ TEST_F(AttributeTest, attrInt) {
     expected += " \"data\": \"15\" }";
     runToElementTest<Attribute>(expected, *attr);
 
-    AttributePtr from_text = Attribute::fromText("NAS-Port-Type=Ethernet");
-    ASSERT_TRUE(from_text);
-    EXPECT_TRUE(compare(from_text, attr))
-        << from_text->toText() << " != " << attr->toText();
-
-    AttributePtr attr2;
-    ASSERT_NO_THROW(attr2 = Attribute::fromInt(61, 1155));
-    ASSERT_TRUE(attr2);
-    AttributePtr from_text2 = Attribute::fromText("NAS-Port-Type=1155");
-    ASSERT_TRUE(from_text2);
-    EXPECT_TRUE(compare(from_text2, attr2))
-        << from_text2->toText() << " != " << attr2->toText();
-
     AttributePtr from_bytes = Attribute::fromBytes(binary);
     ASSERT_TRUE(from_bytes);
     EXPECT_TRUE(compare(from_bytes, attr))
@@ -270,9 +233,9 @@ TEST_F(AttributeTest, attrInt) {
         << def_bytes->toText() << " != " << attr->toText();
 
     EXPECT_THROW_MSG(attr->toString(), TypeError,
-                     "the attribute value type must be string or vsa, not integer");
+                     "the attribute value type must be string, not integer");
     EXPECT_THROW_MSG(attr->toBinary(), TypeError,
-                     "the attribute value type must be string or vsa, not integer");
+                     "the attribute value type must be string, not integer");
     EXPECT_THROW_MSG(attr->toIpAddr(), TypeError,
                      "the attribute value type must be ipaddr, not integer");
     EXPECT_THROW_MSG(attr->toIpv6Addr(), TypeError,
@@ -284,6 +247,8 @@ TEST_F(AttributeTest, attrInt) {
 );
     EXPECT_THROW_MSG(attr->toVendorId(), TypeError,
                      "the attribute value type must be vsa, not integer");
+    EXPECT_THROW_MSG(attr->toVsaData(), TypeError,
+                     "the attribute value type must be vsa, not integer");
 }
 
 // Verifies IP address attribute.
@@ -310,11 +275,6 @@ TEST_F(AttributeTest, attrIpAddr) {
     expected += " \"data\": \"192.0.2.1\" }";
     runToElementTest<Attribute>(expected, *attr);
 
-    AttributePtr from_text = Attribute::fromText("Framed-IP-Address=192.0.2.1");
-    ASSERT_TRUE(from_text);
-    EXPECT_TRUE(compare(from_text, attr))
-        << from_text->toText() << " != " << attr->toText();
-
     EXPECT_THROW_MSG(Attribute::fromIpAddr(8, IOAddress("2001:db8::1235")),
                      BadValue, "not v4 address 2001:db8::1235");
 
@@ -343,9 +303,9 @@ TEST_F(AttributeTest, attrIpAddr) {
         << def_bytes->toText() << " != " << attr->toText();
 
     EXPECT_THROW_MSG(attr->toString(), TypeError,
-                     "the attribute value type must be string or vsa, not ipaddr");
+                     "the attribute value type must be string, not ipaddr");
     EXPECT_THROW_MSG(attr->toBinary(), TypeError,
-                     "the attribute value type must be string or vsa, not ipaddr");
+                     "the attribute value type must be string, not ipaddr");
     EXPECT_THROW_MSG(attr->toInt(), TypeError,
                      "the attribute value type must be integer, not ipaddr");
     EXPECT_THROW_MSG(attr->toIpv6Addr(), TypeError,
@@ -356,6 +316,8 @@ TEST_F(AttributeTest, attrIpAddr) {
                      "the attribute value type must be ipv6prefix, not ipaddr");
     EXPECT_THROW_MSG(attr->toVendorId(), TypeError,
                      "the attribute value type must be vsa, not ipaddr");
+    EXPECT_THROW_MSG(attr->toVsaData(), TypeError,
+                     "the attribute value type must be vsa, not ipaddr");
 }
 
 // Verifies IPv6 address attribute.
@@ -384,19 +346,13 @@ TEST_F(AttributeTest, attrIpv6Addr) {
     expected += " \"data\": \"2001:db8::1235\" }";
     runToElementTest<Attribute>(expected, *attr);
 
-    AttributePtr from_text =
-        Attribute::fromText("Framed-IPv6-Address=2001:db8::1235");
-    ASSERT_TRUE(from_text);
-    EXPECT_TRUE(compare(from_text, attr))
-        << from_text->toText() << " != " << attr->toText();
-
     EXPECT_THROW_MSG(Attribute::fromIpv6Addr(168, IOAddress("192.0.2.1")),
                      BadValue, "not v6 address 192.0.2.1");
 
     AttributePtr def_text = Attribute::fromText(def, "2001:db8::1235");
-    ASSERT_TRUE(from_text);
-    EXPECT_TRUE(compare(from_text, attr))
-        << from_text->toText() << " != " << attr->toText();
+    ASSERT_TRUE(def_text);
+    EXPECT_TRUE(compare(def_text, attr))
+        << def_text->toText() << " != " << attr->toText();
 
     AttributePtr from_bytes = Attribute::fromBytes(binary);
     ASSERT_TRUE(from_bytes);
@@ -422,9 +378,9 @@ TEST_F(AttributeTest, attrIpv6Addr) {
         << def_bytes->toText() << " != " << attr->toText();
 
     EXPECT_THROW_MSG(attr->toString(), TypeError,
-                     "the attribute value type must be string or vsa, not ipv6addr");
+                     "the attribute value type must be string, not ipv6addr");
     EXPECT_THROW_MSG(attr->toBinary(), TypeError,
-                     "the attribute value type must be string or vsa, not ipv6addr");
+                     "the attribute value type must be string, not ipv6addr");
     EXPECT_THROW_MSG(attr->toInt(), TypeError,
                      "the attribute value type must be integer, not ipv6addr");
     EXPECT_THROW_MSG(attr->toIpAddr(), TypeError,
@@ -435,6 +391,8 @@ TEST_F(AttributeTest, attrIpv6Addr) {
                      "the attribute value type must be ipv6prefix, not ipv6addr");
     EXPECT_THROW_MSG(attr->toVendorId(), TypeError,
                      "the attribute value type must be vsa, not ipv6addr");
+    EXPECT_THROW_MSG(attr->toVsaData(), TypeError,
+                     "the attribute value type must be vsa, not ipv6addr");
 }
 
 // Verifies IPv6 prefix attribute.
@@ -466,28 +424,11 @@ TEST_F(AttributeTest, attrIpv6Prefix) {
     expected += " \"data\": \"2001:db8::1235/128\" }";
     runToElementTest<Attribute>(expected, *attr);
 
-    AttributePtr from_text;
-    EXPECT_NO_THROW_LOG(from_text = Attribute::fromText("Delegated-IPv6-Prefix=2001:db8::1235/128"));
-    ASSERT_TRUE(from_text);
-    EXPECT_TRUE(compare(from_text, attr))
-        << from_text->toText() << " != " << attr->toText();
-
-    EXPECT_THROW_MSG(Attribute::fromText("Delegated-IPv6-Prefix=192.0.2.1/32"),
-                     BadValue, "not v6 address 192.0.2.1");
-
     AttributePtr def_text;
     EXPECT_NO_THROW_LOG(def_text = Attribute::fromText(def, "2001:db8::1235/128"));
-    ASSERT_TRUE(from_text);
-    EXPECT_TRUE(compare(from_text, attr))
-        << from_text->toText() << " != " << attr->toText();
-
-    string long_prefix = "Delegated-IPv6-Prefix=2001:db8::1235/300";
-    EXPECT_THROW_MSG(Attribute::fromText(long_prefix), BadValue,
-                     "not 8 bit prefix length 2001:db8::1235/300");
-
-    long_prefix = "Delegated-IPv6-Prefix=2001:db8::1235/129";
-    EXPECT_THROW_MSG(Attribute::fromText(long_prefix), BadValue,
-                     "too long prefix 129");
+    ASSERT_TRUE(def_text);
+    EXPECT_TRUE(compare(def_text, attr))
+        << def_text->toText() << " != " << attr->toText();
 
     AttributePtr from_bytes;
     EXPECT_NO_THROW_LOG(from_bytes = Attribute::fromBytes(binary));
@@ -533,9 +474,9 @@ TEST_F(AttributeTest, attrIpv6Prefix) {
         << def_bytes->toText() << " != " << attr->toText();
 
     EXPECT_THROW_MSG(attr->toString(), TypeError,
-                     "the attribute value type must be string or vsa, not ipv6prefix");
+                     "the attribute value type must be string, not ipv6prefix");
     EXPECT_THROW_MSG(attr->toBinary(), TypeError,
-                     "the attribute value type must be string or vsa, not ipv6prefix");
+                     "the attribute value type must be string, not ipv6prefix");
     EXPECT_THROW_MSG(attr->toInt(), TypeError,
                      "the attribute value type must be integer, not ipv6prefix");
     EXPECT_THROW_MSG(attr->toIpAddr(), TypeError,
@@ -544,6 +485,109 @@ TEST_F(AttributeTest, attrIpv6Prefix) {
                      "the attribute value type must be ipv6addr, not ipv6prefix");
     EXPECT_THROW_MSG(attr->toVendorId(), TypeError,
                      "the attribute value type must be vsa, not ipv6prefix");
+    EXPECT_THROW_MSG(attr->toVsaData(), TypeError,
+                     "the attribute value type must be vsa, not ipv6prefix");
+}
+
+// Verifies vsa attribute.
+TEST_F(AttributeTest, attrVsa) {
+    // Using Vector-Specific (26) *only* vsa attribute.
+    AttrDefPtr def = AttrDefs::instance().getByType(PW_VENDOR_SPECIFIC);
+    ASSERT_TRUE(def);
+    EXPECT_EQ(26, def->type_);
+    EXPECT_EQ(PW_TYPE_VSA, def->value_type_);
+
+    AttributePtr attr;
+    ASSERT_NO_THROW(attr = Attribute::fromVsa(PW_VENDOR_SPECIFIC,
+                                              1234, "foobar"));
+    ASSERT_TRUE(attr);
+
+    EXPECT_EQ(26, attr->getType());
+    EXPECT_EQ(PW_TYPE_VSA, attr->getValueType());
+    uint32_t vendor = 0;
+    ASSERT_NO_THROW(vendor = attr->toVendorId());
+    EXPECT_EQ(1234, vendor);
+    EXPECT_EQ("Vendor-Specific=[1234]0x666F6F626172", attr->toText());
+    vector<uint8_t> binary = { 26, 12, 0, 0, 0x04, 0xd2,
+                               0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72 };
+    EXPECT_EQ(binary, attr->toBytes());
+    string expected = "{ \"type\": 26, \"name\": \"Vendor-Specific\", ";
+    expected += " \"vendor\": \"1234\", \"vsa-raw\": \"666F6F626172\" }";
+    runToElementTest<Attribute>(expected, *attr);
+
+    AttributePtr from_bytes = Attribute::fromBytes(binary);
+    ASSERT_TRUE(from_bytes);
+    EXPECT_TRUE(compare(from_bytes, attr))
+        << from_bytes->toText() << " != " << attr->toText();
+
+    vector<uint8_t> value = { 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72 };
+    AttributePtr def_value = Attribute::fromVsa(PW_VENDOR_SPECIFIC,
+                                                1234, value);
+    ASSERT_TRUE(def_value);
+    EXPECT_TRUE(compare(def_value, attr))
+        << def_value->toText() << " != " << attr->toText();
+
+    vector<uint8_t> bytes = { 0, 0, 0x04, 0xd2,
+                              0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72 };
+    AttributePtr def_bytes = Attribute::fromBytes(def, bytes);
+    ASSERT_TRUE(def_bytes);
+    EXPECT_TRUE(compare(def_bytes, attr))
+        << def_bytes->toText() << " != " << attr->toText();
+
+    EXPECT_THROW_MSG(Attribute::fromVsa(PW_VENDOR_SPECIFIC, 1234, ""),
+                     BadValue, "value is empty");
+
+    vector<uint8_t> empty;
+    EXPECT_THROW_MSG(Attribute::fromVsa(PW_VENDOR_SPECIFIC, 1234, empty),
+                     BadValue, "value is empty");
+
+    vector<uint8_t> small = { 26, 6, 0, 0, 0x4, 0xd2 };
+    EXPECT_THROW_MSG(Attribute::fromBytes(small), BadValue,
+                     "value is too small 4 < 5");
+
+    vector<uint8_t> small2 = { 0, 0, 0x4, 0xd2 };
+    EXPECT_THROW_MSG(Attribute::fromBytes(def, small2), BadValue,
+                     "value is too small 4 < 5");
+
+    string big_text(MAX_VSA_DATA_LEN, 'x');
+    EXPECT_NO_THROW_LOG(Attribute::fromVsa(PW_VENDOR_SPECIFIC,
+                                           1234, big_text));
+
+    vector<uint8_t> big_binary(MAX_VSA_DATA_LEN, 0x78);
+    EXPECT_NO_THROW_LOG(Attribute::fromVsa(PW_VENDOR_SPECIFIC,
+                                           1234, big_binary));
+
+    vector<uint8_t> big_value(MAX_STRING_LEN, 0x87);
+    EXPECT_NO_THROW_LOG(Attribute::fromBytes(def, big_value));
+
+    string too_big_text(MAX_VSA_DATA_LEN + 1, 'x');
+    EXPECT_THROW_MSG(Attribute::fromVsa(PW_VENDOR_SPECIFIC, 1234,
+                                        too_big_text), BadValue,
+                     "value is too large 250 > 249");
+
+    vector<uint8_t> too_big_binary(MAX_VSA_DATA_LEN + 1, 0x87);
+    EXPECT_THROW_MSG(Attribute::fromVsa(PW_VENDOR_SPECIFIC, 1234,
+                                        too_big_binary), BadValue,
+                     "value is too large 250 > 249");
+
+    vector<uint8_t> too_bigvalue(MAX_STRING_LEN + 1, 0x87);
+    EXPECT_THROW_MSG(Attribute::fromBytes(def, too_bigvalue), BadValue,
+                     "value is too large 254 > 253");
+
+    EXPECT_THROW_MSG(attr->toString(), TypeError,
+                     "the attribute value type must be string, not vsa");
+    EXPECT_THROW_MSG(attr->toBinary(), TypeError,
+                     "the attribute value type must be string, not vsa");
+    EXPECT_THROW_MSG(attr->toInt(), TypeError,
+                     "the attribute value type must be integer, not vsa");
+    EXPECT_THROW_MSG(attr->toIpAddr(), TypeError,
+                     "the attribute value type must be ipaddr, not vsa");
+    EXPECT_THROW_MSG(attr->toIpv6Addr(), TypeError,
+                     "the attribute value type must be ipv6addr, not vsa");
+    EXPECT_THROW_MSG(attr->toIpv6Prefix(), TypeError,
+                     "the attribute value type must be ipv6prefix, not vsa");
+    EXPECT_THROW_MSG(attr->toIpv6PrefixLen(), TypeError,
+                     "the attribute value type must be ipv6prefix, not vsa");
 }
 
 // Verifies basic methods for attribute collection.
@@ -604,8 +648,8 @@ TEST_F(AttributeTest, attributesAddDel) {
     attr.reset();
 
     // toText.
-    string expected = "User-Name=foobar,\nUser-Name=foo,\n";
-    expected += "Service-Type=20,\nUser-Name=bar";
+    string expected = "User-Name='foobar',\nUser-Name='foo',\n";
+    expected += "Service-Type=20,\nUser-Name='bar'";
     string got;
     ASSERT_NO_THROW(got = attrs.toText());
     EXPECT_EQ(expected, got) << expected << "\n" << got << "\n";
index f1caf080d8a43b276480e5fa7250888b7a4ca44e..d060aa21bc8063c31eaa29c6941512c57be7a2c6 100644 (file)
@@ -792,7 +792,7 @@ TEST_F(ConfigTest, attribute) {
     ASSERT_EQ(1, attrs.size());
     const ConstAttributePtr& first = *attrs.cbegin();
     ASSERT_TRUE(first);
-    EXPECT_EQ("User-Name=foobar", first->toText());
+    EXPECT_EQ("User-Name='foobar'", first->toText());
 
     // Another way to check.
     string expected = "[ { "
@@ -801,6 +801,15 @@ TEST_F(ConfigTest, attribute) {
         " \"data\": \"foobar\" } ]";
     runToElementTest<CfgAttributes>(expected, srv->attributes_);
 
+    // Vendor-Specific (26) does not support textual data.
+    srv->attributes_.clear();
+    attr = Element::createMap();
+    attr->set("data", Element::create("foobar"));
+    attr->set("type", Element::create(26));
+    expected = "can't create Vendor-Specific attribute from [foobar]: ";
+    expected += "Can't decode vsa from text");
+    EXPECT_THROW_MSG(parser.parse(srv, attr), ConfigError, expected);
+
     // One of expr, data, raw
     srv->attributes_.clear();
     attr = Element::createMap();
@@ -845,9 +854,7 @@ TEST_F(ConfigTest, attribute) {
     EXPECT_EQ("", srv->attributes_.getTest(1));
     const ConstAttributePtr& firstr = srv->attributes_.get(1);
     ASSERT_TRUE(firstr);
-    expected = "User-Name=f\x01\x02";
-    expected += "bar";
-    EXPECT_EQ(expected, firstr->toText());
+    EXPECT_EQ("User-Name=0x660102626172", firstr->toText());
     expected = "[ { "
         " \"name\": \"User-Name\", "
         " \"type\": 1, "
index a0589478d300d62fe819a570d6baf6b1253300d7..9cbacbe61efb4cd994bb3d48092a502036ab50f5 100644 (file)
@@ -140,6 +140,8 @@ TEST_F(DictionaryTest, parseLine) {
                      "expected 3 tokens, got 2 at line 1");
     EXPECT_THROW_MSG(parseLine("VENDOR my-vendor 44 17"), BadValue,
                      "expected 3 tokens, got 4 at line 1");
+    EXPECT_THROW_MSG(parseLine("VENDOR my-vendor 0"), BadValue,
+                     "0 is reserved at line 1");
 
     EXPECT_THROW_MSG(parseLine("BEGIN-VENDOR my-vendor"), BadValue,
                      "unknown dictionary entry 'BEGIN-VENDOR' at line 1");
index 05aafd88cb148ee34dda0566eb1c850380f53ac7..981a3b6dc5c1f49f918e40655fadbda4014dda78 100644 (file)
@@ -743,7 +743,7 @@ TEST_F(RequestTest, accept) {
     ASSERT_EQ(1, received_attributes_->count(1));
     const ConstAttributePtr& attr = received_attributes_->get(1);
     ASSERT_TRUE(attr);
-    EXPECT_EQ("User-Name=user", attr->toText());
+    EXPECT_EQ("User-Name='user'", attr->toText());
 }
 
 /// Verify what happens with Accounting-Response response.
@@ -938,7 +938,7 @@ TEST_F(RequestTest, badAccept) {
     ASSERT_EQ(1, received_attributes_->count(1));
     const ConstAttributePtr& attr = received_attributes_->get(1);
     ASSERT_TRUE(attr);
-    EXPECT_EQ("User-Name=user", attr->toText());
+    EXPECT_EQ("User-Name='user'", attr->toText());
 }
 
 /// Verify what happens with bad Accounting-Response response.
@@ -1326,7 +1326,7 @@ TEST_F(RequestTest, reject) {
     ASSERT_EQ(1, received_attributes_->count(1));
     const ConstAttributePtr& attr = received_attributes_->get(1);
     ASSERT_TRUE(attr);
-    EXPECT_EQ("User-Name=user", attr->toText());
+    EXPECT_EQ("User-Name='user'", attr->toText());
 }
 
 /// Verify what happens with a backup authentication server.
@@ -1427,7 +1427,7 @@ TEST_F(RequestTest, accept2) {
     ASSERT_EQ(1, received_attributes_->count(1));
     const ConstAttributePtr& attr = received_attributes_->get(1);
     ASSERT_TRUE(attr);
-    EXPECT_EQ("User-Name=user", attr->toText());
+    EXPECT_EQ("User-Name='user'", attr->toText());
 }
 
 /// Verify what happens with a backup accounting server.