summaryrefslogtreecommitdiff
path: root/indra/llcommon/llsdserialize.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcommon/llsdserialize.cpp')
-rw-r--r--indra/llcommon/llsdserialize.cpp761
1 files changed, 543 insertions, 218 deletions
diff --git a/indra/llcommon/llsdserialize.cpp b/indra/llcommon/llsdserialize.cpp
index 5ab94715c5..02152cfe79 100644
--- a/indra/llcommon/llsdserialize.cpp
+++ b/indra/llcommon/llsdserialize.cpp
@@ -45,26 +45,18 @@
#include "lldate.h"
#include "llsd.h"
+#include "llstring.h"
#include "lluri.h"
// File constants
static const int MAX_HDR_LEN = 20;
static const char LEGACY_NON_HEADER[] = "<llsd>";
+const std::string LLSD_BINARY_HEADER("LLSD/Binary");
+const std::string LLSD_XML_HEADER("LLSD/XML");
-//static
-const char* LLSDSerialize::LLSDBinaryHeader = "LLSD/Binary";
-
-//static
-const char* LLSDSerialize::LLSDXMLHeader = "LLSD/XML";
-
-// virtual
-LLSDParser::~LLSDParser()
-{ }
-
-// virtual
-LLSDNotationParser::~LLSDNotationParser()
-{ }
-
+/**
+ * LLSDSerialize
+ */
// static
void LLSDSerialize::serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize type, U32 options)
@@ -74,12 +66,12 @@ void LLSDSerialize::serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize
switch (type)
{
case LLSD_BINARY:
- str << "<? " << LLSDBinaryHeader << " ?>\n";
+ str << "<? " << LLSD_BINARY_HEADER << " ?>\n";
f = new LLSDBinaryFormatter;
break;
case LLSD_XML:
- str << "<? " << LLSDXMLHeader << " ?>\n";
+ str << "<? " << LLSD_XML_HEADER << " ?>\n";
f = new LLSDXMLFormatter;
break;
@@ -94,7 +86,7 @@ void LLSDSerialize::serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize
}
// static
-bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str)
+bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str, S32 max_bytes)
{
LLPointer<LLSDParser> p = NULL;
char hdr_buf[MAX_HDR_LEN + 1] = ""; /* Flawfinder: ignore */
@@ -102,8 +94,8 @@ bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str)
int inbuf = 0;
bool legacy_no_header = false;
bool fail_if_not_legacy = false;
- std::string header = "";
-
+ std::string header;
+
/*
* Get the first line before anything.
*/
@@ -155,15 +147,15 @@ bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str)
*/
if (legacy_no_header)
{
- LLSDXMLParser *x = new LLSDXMLParser;
+ LLSDXMLParser* x = new LLSDXMLParser;
x->parsePart(hdr_buf, inbuf);
p = x;
}
- else if (header == LLSDBinaryHeader)
+ else if (header == LLSD_BINARY_HEADER)
{
p = new LLSDBinaryParser;
}
- else if (header == LLSDXMLHeader)
+ else if (header == LLSD_XML_HEADER)
{
p = new LLSDXMLParser;
}
@@ -174,7 +166,7 @@ bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str)
if (p.notNull())
{
- p->parse(str, sd);
+ p->parse(str, sd, max_bytes);
return true;
}
@@ -230,11 +222,69 @@ F64 ll_ntohd(F64 netdouble)
/**
* Local functions.
*/
-bool deserialize_string(std::istream& str, std::string& value);
-bool deserialize_string_delim(std::istream& str, std::string& value, char d);
-bool deserialize_string_raw(std::istream& str, std::string& value);
+/**
+ * @brief Figure out what kind of string it is (raw or delimited) and handoff.
+ *
+ * @param istr The stream to read from.
+ * @param value [out] The string which was found.
+ * @param max_bytes The maximum possible length of the string.
+ * @return Returns number of bytes read off of the stream. Returns
+ * PARSE_FAILURE (-1) on failure.
+ */
+int deserialize_string(std::istream& istr, std::string& value, S32 max_bytes);
+
+/**
+ * @brief Parse a delimited string.
+ *
+ * @param istr The stream to read from, with the delimiter already popped.
+ * @param value [out] The string which was found.
+ * @param d The delimiter to use.
+ * @return Returns number of bytes read off of the stream. Returns
+ * PARSE_FAILURE (-1) on failure.
+ */
+int deserialize_string_delim(std::istream& istr, std::string& value, char d);
+
+/**
+ * @brief Read a raw string off the stream.
+ *
+ * @param istr The stream to read from, with the (len) parameter
+ * leading the stream.
+ * @param value [out] The string which was found.
+ * @param d The delimiter to use.
+ * @param max_bytes The maximum possible length of the string.
+ * @return Returns number of bytes read off of the stream. Returns
+ * PARSE_FAILURE (-1) on failure.
+ */
+int deserialize_string_raw(
+ std::istream& istr,
+ std::string& value,
+ S32 max_bytes);
+
+/**
+ * @brief helper method for dealing with the different notation boolean format.
+ *
+ * @param istr The stream to read from with the leading character stripped.
+ * @param data [out] the result of the parse.
+ * @param compare The string to compare the boolean against
+ * @param vale The value to assign to data if the parse succeeds.
+ * @return Returns number of bytes read off of the stream. Returns
+ * PARSE_FAILURE (-1) on failure.
+ */
+int deserialize_boolean(
+ std::istream& istr,
+ LLSD& data,
+ const std::string& compare,
+ bool value);
+
+/**
+ * @brief Do notation escaping of a string to an ostream.
+ *
+ * @param value The string to escape and serialize
+ * @param str The stream to serialize to.
+ */
void serialize_string(const std::string& value, std::ostream& str);
+
/**
* Local constants.
*/
@@ -244,20 +294,96 @@ static const std::string NOTATION_FALSE_SERIAL("false");
static const char BINARY_TRUE_SERIAL = '1';
static const char BINARY_FALSE_SERIAL = '0';
-static const S32 NOTATION_PARSE_FAILURE = -1;
/**
* LLSDParser
*/
-LLSDParser::LLSDParser()
+LLSDParser::LLSDParser() : mCheckLimits(true), mMaxBytesLeft(0)
+{
+}
+
+// virtual
+LLSDParser::~LLSDParser()
+{ }
+
+S32 LLSDParser::parse(std::istream& istr, LLSD& data, S32 max_bytes)
+{
+ mCheckLimits = (LLSDSerialize::SIZE_UNLIMITED == max_bytes) ? false : true;
+ mMaxBytesLeft = max_bytes;
+ return doParse(istr, data);
+}
+
+
+int LLSDParser::get(std::istream& istr) const
+{
+ if(mCheckLimits) --mMaxBytesLeft;
+ return istr.get();
+}
+
+std::istream& LLSDParser::get(
+ std::istream& istr,
+ char* s,
+ std::streamsize n,
+ char delim) const
+{
+ istr.get(s, n, delim);
+ if(mCheckLimits) mMaxBytesLeft -= istr.gcount();
+ return istr;
+}
+
+std::istream& LLSDParser::get(
+ std::istream& istr,
+ std::streambuf& sb,
+ char delim) const
+{
+ istr.get(sb, delim);
+ if(mCheckLimits) mMaxBytesLeft -= istr.gcount();
+ return istr;
+}
+
+std::istream& LLSDParser::ignore(std::istream& istr) const
{
+ istr.ignore();
+ if(mCheckLimits) --mMaxBytesLeft;
+ return istr;
}
+std::istream& LLSDParser::putback(std::istream& istr, char c) const
+{
+ istr.putback(c);
+ if(mCheckLimits) ++mMaxBytesLeft;
+ return istr;
+}
+
+std::istream& LLSDParser::read(
+ std::istream& istr,
+ char* s,
+ std::streamsize n) const
+{
+ istr.read(s, n);
+ if(mCheckLimits) mMaxBytesLeft -= istr.gcount();
+ return istr;
+}
+
+void LLSDParser::account(S32 bytes) const
+{
+ if(mCheckLimits) mMaxBytesLeft -= bytes;
+}
+
+
/**
* LLSDNotationParser
*/
+LLSDNotationParser::LLSDNotationParser()
+{
+}
+
+// virtual
+LLSDNotationParser::~LLSDNotationParser()
+{ }
+
// virtual
-S32 LLSDNotationParser::parse(std::istream& istr, LLSD& data) const
+S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data) const
{
// map: { string:object, string:object }
// array: [ object, object, object ]
@@ -275,7 +401,7 @@ S32 LLSDNotationParser::parse(std::istream& istr, LLSD& data) const
while(isspace(c))
{
// pop the whitespace.
- c = istr.get();
+ c = get(istr);
c = istr.peek();
continue;
}
@@ -287,107 +413,142 @@ S32 LLSDNotationParser::parse(std::istream& istr, LLSD& data) const
switch(c)
{
case '{':
- parse_count += parseMap(istr, data);
- if(istr.fail())
+ {
+ S32 child_count = parseMap(istr, data);
+ if((child_count == PARSE_FAILURE) || data.isUndefined())
{
- llinfos << "STREAM FAILURE reading map." << llendl;
+ parse_count = PARSE_FAILURE;
}
- if(data.isUndefined())
+ else
{
- parse_count = NOTATION_PARSE_FAILURE;
+ parse_count += child_count;
+ }
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading map." << llendl;
+ parse_count = PARSE_FAILURE;
}
break;
+ }
case '[':
- parse_count += parseArray(istr, data);
- if(istr.fail())
+ {
+ S32 child_count = parseArray(istr, data);
+ if((child_count == PARSE_FAILURE) || data.isUndefined())
{
- llinfos << "STREAM FAILURE reading array." << llendl;
+ parse_count = PARSE_FAILURE;
}
- if(data.isUndefined())
+ else
{
- parse_count = NOTATION_PARSE_FAILURE;
+ parse_count += child_count;
+ }
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading array." << llendl;
+ parse_count = PARSE_FAILURE;
}
break;
+ }
case '!':
- c = istr.get();
+ c = get(istr);
data.clear();
break;
case '0':
- c = istr.get();
+ c = get(istr);
data = false;
break;
case 'F':
case 'f':
- do
+ ignore(istr);
+ c = istr.peek();
+ if(isalpha(c))
{
- istr.ignore();
- c = istr.peek();
- } while (isalpha(c));
- data = false;
+ int cnt = deserialize_boolean(
+ istr,
+ data,
+ NOTATION_FALSE_SERIAL,
+ false);
+ if(PARSE_FAILURE == cnt) parse_count = cnt;
+ else account(cnt);
+ }
+ else
+ {
+ data = false;
+ }
if(istr.fail())
{
llinfos << "STREAM FAILURE reading boolean." << llendl;
+ parse_count = PARSE_FAILURE;
}
break;
case '1':
- c = istr.get();
+ c = get(istr);
data = true;
break;
case 'T':
case 't':
- do
+ ignore(istr);
+ c = istr.peek();
+ if(isalpha(c))
{
- istr.ignore();
- c = istr.peek();
- } while (isalpha(c));
- data = true;
+ int cnt = deserialize_boolean(istr,data,NOTATION_TRUE_SERIAL,true);
+ if(PARSE_FAILURE == cnt) parse_count = cnt;
+ else account(cnt);
+ }
+ else
+ {
+ data = true;
+ }
if(istr.fail())
{
llinfos << "STREAM FAILURE reading boolean." << llendl;
+ parse_count = PARSE_FAILURE;
}
break;
case 'i':
{
- c = istr.get();
+ c = get(istr);
S32 integer = 0;
istr >> integer;
data = integer;
if(istr.fail())
{
llinfos << "STREAM FAILURE reading integer." << llendl;
+ parse_count = PARSE_FAILURE;
}
break;
}
case 'r':
{
- c = istr.get();
+ c = get(istr);
F64 real = 0.0;
istr >> real;
data = real;
if(istr.fail())
{
llinfos << "STREAM FAILURE reading real." << llendl;
+ parse_count = PARSE_FAILURE;
}
break;
}
case 'u':
{
- c = istr.get();
+ c = get(istr);
LLUUID id;
istr >> id;
data = id;
if(istr.fail())
{
llinfos << "STREAM FAILURE reading uuid." << llendl;
+ parse_count = PARSE_FAILURE;
}
break;
}
@@ -395,126 +556,144 @@ S32 LLSDNotationParser::parse(std::istream& istr, LLSD& data) const
case '\"':
case '\'':
case 's':
- parseString(istr, data);
- if(istr.fail())
+ if(!parseString(istr, data))
{
- llinfos << "STREAM FAILURE reading string." << llendl;
+ parse_count = PARSE_FAILURE;
}
- if(data.isUndefined())
+ if(istr.fail())
{
- parse_count = NOTATION_PARSE_FAILURE;
+ llinfos << "STREAM FAILURE reading string." << llendl;
+ parse_count = PARSE_FAILURE;
}
break;
case 'l':
{
- c = istr.get(); // pop the 'l'
- c = istr.get(); // pop the delimiter
+ c = get(istr); // pop the 'l'
+ c = get(istr); // pop the delimiter
std::string str;
- deserialize_string_delim(istr, str, c);
- data = LLURI(str);
+ int cnt = deserialize_string_delim(istr, str, c);
+ if(PARSE_FAILURE == cnt)
+ {
+ parse_count = PARSE_FAILURE;
+ }
+ else
+ {
+ data = LLURI(str);
+ account(cnt);
+ }
if(istr.fail())
{
llinfos << "STREAM FAILURE reading link." << llendl;
+ parse_count = PARSE_FAILURE;
}
break;
}
case 'd':
{
- c = istr.get(); // pop the 'd'
- c = istr.get(); // pop the delimiter
+ c = get(istr); // pop the 'd'
+ c = get(istr); // pop the delimiter
std::string str;
- deserialize_string_delim(istr, str, c);
- data = LLDate(str);
+ int cnt = deserialize_string_delim(istr, str, c);
+ if(PARSE_FAILURE == cnt)
+ {
+ parse_count = PARSE_FAILURE;
+ }
+ else
+ {
+ data = LLDate(str);
+ account(cnt);
+ }
if(istr.fail())
{
llinfos << "STREAM FAILURE reading date." << llendl;
+ parse_count = PARSE_FAILURE;
}
break;
}
case 'b':
- parseBinary(istr, data);
- if(istr.fail())
+ if(!parseBinary(istr, data))
{
- llinfos << "STREAM FAILURE reading data." << llendl;
+ parse_count = PARSE_FAILURE;
}
- if(data.isUndefined())
+ if(istr.fail())
{
- parse_count = NOTATION_PARSE_FAILURE;
+ llinfos << "STREAM FAILURE reading data." << llendl;
+ parse_count = PARSE_FAILURE;
}
break;
default:
- data.clear();
- parse_count = NOTATION_PARSE_FAILURE;
+ parse_count = PARSE_FAILURE;
llinfos << "Unrecognized character while parsing: int(" << (int)c
- << ")" << llendl;
+ << ")" << llendl;
break;
}
+ if(PARSE_FAILURE == parse_count)
+ {
+ data.clear();
+ }
return parse_count;
}
-// static
-LLSD LLSDNotationParser::parse(std::istream& istr)
-{
- LLSDNotationParser parser;
- LLSD rv;
- S32 count = parser.parse(istr, rv);
- lldebugs << "LLSDNotationParser::parse parsed " << count << " objects."
- << llendl;
- return rv;
-}
-
S32 LLSDNotationParser::parseMap(std::istream& istr, LLSD& map) const
{
// map: { string:object, string:object }
map = LLSD::emptyMap();
S32 parse_count = 0;
- char c = istr.get();
+ char c = get(istr);
if(c == '{')
{
// eat commas, white
bool found_name = false;
std::string name;
- c = istr.get();
+ c = get(istr);
while(c != '}' && istr.good())
{
if(!found_name)
{
if((c == '\"') || (c == '\'') || (c == 's'))
{
- istr.putback(c);
+ putback(istr, c);
found_name = true;
- deserialize_string(istr, name);
+ int count = deserialize_string(istr, name, mMaxBytesLeft);
+ if(PARSE_FAILURE == count) return PARSE_FAILURE;
+ account(count);
}
- c = istr.get();
+ c = get(istr);
}
else
{
if(isspace(c) || (c == ':'))
{
- c = istr.get();
+ c = get(istr);
continue;
}
- istr.putback(c);
+ putback(istr, c);
LLSD child;
- S32 count = parse(istr, child);
+ S32 count = doParse(istr, child);
if(count > 0)
{
+ // There must be a value for every key, thus
+ // child_count must be greater than 0.
parse_count += count;
map.insert(name, child);
}
else
{
- map.clear();
- return NOTATION_PARSE_FAILURE;
+ return PARSE_FAILURE;
}
found_name = false;
- c = istr.get();
+ c = get(istr);
}
}
+ if(c != '}')
+ {
+ map.clear();
+ return PARSE_FAILURE;
+ }
}
return parse_count;
}
@@ -524,52 +703,51 @@ S32 LLSDNotationParser::parseArray(std::istream& istr, LLSD& array) const
// array: [ object, object, object ]
array = LLSD::emptyArray();
S32 parse_count = 0;
- char c = istr.get();
+ char c = get(istr);
if(c == '[')
{
// eat commas, white
- c = istr.get();
+ c = get(istr);
while((c != ']') && istr.good())
{
LLSD child;
if(isspace(c) || (c == ','))
{
- c = istr.get();
+ c = get(istr);
continue;
}
- istr.putback(c);
- S32 count = parse(istr, child);
- if(count > 0)
+ putback(istr, c);
+ S32 count = doParse(istr, child);
+ if(PARSE_FAILURE == count)
{
- parse_count += count;
- array.append(child);
+ return PARSE_FAILURE;
}
else
{
- array.clear();
- return NOTATION_PARSE_FAILURE;
+ parse_count += count;
+ array.append(child);
}
- c = istr.get();
+ c = get(istr);
+ }
+ if(c != ']')
+ {
+ return PARSE_FAILURE;
}
}
return parse_count;
}
-void LLSDNotationParser::parseString(std::istream& istr, LLSD& data) const
+bool LLSDNotationParser::parseString(std::istream& istr, LLSD& data) const
{
std::string value;
- if(deserialize_string(istr, value))
- {
- data = value;
- }
- else
- {
- // failed to parse.
- data.clear();
- }
+ int count = deserialize_string(istr, value, mMaxBytesLeft);
+ if(PARSE_FAILURE == count) return false;
+ account(count);
+ data = value;
+ return true;
}
-void LLSDNotationParser::parseBinary(std::istream& istr, LLSD& data) const
+bool LLSDNotationParser::parseBinary(std::istream& istr, LLSD& data) const
{
// binary: b##"ff3120ab1"
// or: b(len)"..."
@@ -582,40 +760,44 @@ void LLSDNotationParser::parseBinary(std::istream& istr, LLSD& data) const
// need to read the base out.
char buf[BINARY_BUFFER_SIZE]; /* Flawfinder: ignore */
- istr.get(buf, STREAM_GET_COUNT, '"');
- char c = istr.get();
- if((c == '"') && (0 == strncmp("b(", buf, 2)))
+ get(istr, buf, STREAM_GET_COUNT, '"');
+ char c = get(istr);
+ if(c != '"') return false;
+ if(0 == strncmp("b(", buf, 2))
{
// We probably have a valid raw binary stream. determine
// the size, and read it.
- // *FIX: Should we set a maximum size?
S32 len = strtol(buf + 2, NULL, 0);
+ if(len > mMaxBytesLeft) return false;
std::vector<U8> value;
if(len)
{
value.resize(len);
- fullread(istr, (char *)&value[0], len);
+ account(fullread(istr, (char *)&value[0], len));
}
- c = istr.get(); // strip off the trailing double-quote
+ c = get(istr); // strip off the trailing double-quote
data = value;
}
- else if((c == '"') && (0 == strncmp("b64", buf, 3)))
+ else if(0 == strncmp("b64", buf, 3))
{
// *FIX: A bit inefficient, but works for now. To make the
// format better, I would need to add a hint into the
// serialization format that indicated how long it was.
std::stringstream coded_stream;
- istr.get(*(coded_stream.rdbuf()), '\"');
- c = istr.get();
+ get(istr, *(coded_stream.rdbuf()), '\"');
+ c = get(istr);
std::string encoded(coded_stream.str());
S32 len = apr_base64_decode_len(encoded.c_str());
std::vector<U8> value;
- value.resize(len);
- len = apr_base64_decode_binary(&value[0], encoded.c_str());
- value.resize(len);
+ if(len)
+ {
+ value.resize(len);
+ len = apr_base64_decode_binary(&value[0], encoded.c_str());
+ value.resize(len);
+ }
data = value;
}
- else if((c == '"') && (0 == strncmp("b16", buf, 3)))
+ else if(0 == strncmp("b16", buf, 3))
{
// yay, base 16. We pop the next character which is either a
// double quote or base 16 data. If it's a double quote, we're
@@ -626,14 +808,14 @@ void LLSDNotationParser::parseBinary(std::istream& istr, LLSD& data) const
U8 byte_buffer[BINARY_BUFFER_SIZE];
U8* write;
std::vector<U8> value;
- c = istr.get();
+ c = get(istr);
while(c != '"')
{
- istr.putback(c);
+ putback(istr, c);
read = buf;
write = byte_buffer;
- istr.get(buf, STREAM_GET_COUNT, '"');
- c = istr.get();
+ get(istr, buf, STREAM_GET_COUNT, '"');
+ c = get(istr);
while(*read != '\0') /*Flawfinder: ignore*/
{
byte = hex_as_nybble(*read++);
@@ -648,8 +830,9 @@ void LLSDNotationParser::parseBinary(std::istream& istr, LLSD& data) const
}
else
{
- data.clear();
+ return false;
}
+ return true;
}
@@ -666,7 +849,7 @@ LLSDBinaryParser::~LLSDBinaryParser()
}
// virtual
-S32 LLSDBinaryParser::parse(std::istream& istr, LLSD& data) const
+S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data) const
{
/**
* Undefined: '!'<br>
@@ -685,7 +868,7 @@ S32 LLSDBinaryParser::parse(std::istream& istr, LLSD& data) const
* notation format.
*/
char c;
- c = istr.get();
+ c = get(istr);
if(!istr.good())
{
return 0;
@@ -694,20 +877,42 @@ S32 LLSDBinaryParser::parse(std::istream& istr, LLSD& data) const
switch(c)
{
case '{':
- parse_count += parseMap(istr, data);
+ {
+ S32 child_count = parseMap(istr, data);
+ if((child_count == PARSE_FAILURE) || data.isUndefined())
+ {
+ parse_count = PARSE_FAILURE;
+ }
+ else
+ {
+ parse_count += child_count;
+ }
if(istr.fail())
{
llinfos << "STREAM FAILURE reading binary map." << llendl;
+ parse_count = PARSE_FAILURE;
}
break;
+ }
case '[':
- parse_count += parseArray(istr, data);
+ {
+ S32 child_count = parseArray(istr, data);
+ if((child_count == PARSE_FAILURE) || data.isUndefined())
+ {
+ parse_count = PARSE_FAILURE;
+ }
+ else
+ {
+ parse_count += child_count;
+ }
if(istr.fail())
{
llinfos << "STREAM FAILURE reading binary array." << llendl;
+ parse_count = PARSE_FAILURE;
}
break;
+ }
case '!':
data.clear();
@@ -724,7 +929,7 @@ S32 LLSDBinaryParser::parse(std::istream& istr, LLSD& data) const
case 'i':
{
U32 value_nbo = 0;
- istr.read((char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/
+ read(istr, (char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/
data = (S32)ntohl(value_nbo);
if(istr.fail())
{
@@ -736,7 +941,7 @@ S32 LLSDBinaryParser::parse(std::istream& istr, LLSD& data) const
case 'r':
{
F64 real_nbo = 0.0;
- istr.read((char*)&real_nbo, sizeof(F64)); /*Flawfinder: ignore*/
+ read(istr, (char*)&real_nbo, sizeof(F64)); /*Flawfinder: ignore*/
data = ll_ntohd(real_nbo);
if(istr.fail())
{
@@ -748,7 +953,7 @@ S32 LLSDBinaryParser::parse(std::istream& istr, LLSD& data) const
case 'u':
{
LLUUID id;
- istr.read((char*)(&id.mData), UUID_BYTES); /*Flawfinder: ignore*/
+ read(istr, (char*)(&id.mData), UUID_BYTES); /*Flawfinder: ignore*/
data = id;
if(istr.fail())
{
@@ -761,19 +966,40 @@ S32 LLSDBinaryParser::parse(std::istream& istr, LLSD& data) const
case '"':
{
std::string value;
- deserialize_string_delim(istr, value, c);
- data = value;
+ int cnt = deserialize_string_delim(istr, value, c);
+ if(PARSE_FAILURE == cnt)
+ {
+ parse_count = PARSE_FAILURE;
+ }
+ else
+ {
+ data = value;
+ account(cnt);
+ }
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary (notation-style) string."
+ << llendl;
+ parse_count = PARSE_FAILURE;
+ }
break;
}
case 's':
{
std::string value;
- parseString(istr, value);
- data = value;
+ if(parseString(istr, value))
+ {
+ data = value;
+ }
+ else
+ {
+ parse_count = PARSE_FAILURE;
+ }
if(istr.fail())
{
llinfos << "STREAM FAILURE reading binary string." << llendl;
+ parse_count = PARSE_FAILURE;
}
break;
}
@@ -781,11 +1007,18 @@ S32 LLSDBinaryParser::parse(std::istream& istr, LLSD& data) const
case 'l':
{
std::string value;
- parseString(istr, value);
- data = LLURI(value);
+ if(parseString(istr, value))
+ {
+ data = LLURI(value);
+ }
+ else
+ {
+ parse_count = PARSE_FAILURE;
+ }
if(istr.fail())
{
llinfos << "STREAM FAILURE reading binary link." << llendl;
+ parse_count = PARSE_FAILURE;
}
break;
}
@@ -793,11 +1026,12 @@ S32 LLSDBinaryParser::parse(std::istream& istr, LLSD& data) const
case 'd':
{
F64 real = 0.0;
- istr.read((char*)&real, sizeof(F64)); /*Flawfinder: ignore*/
+ read(istr, (char*)&real, sizeof(F64)); /*Flawfinder: ignore*/
data = LLDate(real);
if(istr.fail())
{
llinfos << "STREAM FAILURE reading binary date." << llendl;
+ parse_count = PARSE_FAILURE;
}
break;
}
@@ -806,75 +1040,94 @@ S32 LLSDBinaryParser::parse(std::istream& istr, LLSD& data) const
{
// We probably have a valid raw binary stream. determine
// the size, and read it.
- // *FIX: Should we set a maximum size?
U32 size_nbo = 0;
- istr.read((char*)&size_nbo, sizeof(U32)); /*Flawfinder: ignore*/
+ read(istr, (char*)&size_nbo, sizeof(U32)); /*Flawfinder: ignore*/
S32 size = (S32)ntohl(size_nbo);
- std::vector<U8> value;
- if(size)
+ if(size > mMaxBytesLeft)
{
- value.resize(size);
- istr.read((char*)&value[0], size); /*Flawfinder: ignore*/
+ parse_count = PARSE_FAILURE;
+ }
+ else
+ {
+ std::vector<U8> value;
+ if(size > 0)
+ {
+ value.resize(size);
+ account(fullread(istr, (char*)&value[0], size));
+ }
+ data = value;
}
- data = value;
if(istr.fail())
{
llinfos << "STREAM FAILURE reading binary." << llendl;
+ parse_count = PARSE_FAILURE;
}
break;
}
default:
- --parse_count;
+ parse_count = PARSE_FAILURE;
llinfos << "Unrecognized character while parsing: int(" << (int)c
- << ")" << llendl;
+ << ")" << llendl;
break;
}
+ if(PARSE_FAILURE == parse_count)
+ {
+ data.clear();
+ }
return parse_count;
}
-// static
-LLSD LLSDBinaryParser::parse(std::istream& istr)
-{
- LLSDBinaryParser parser;
- LLSD rv;
- S32 count = parser.parse(istr, rv);
- lldebugs << "LLSDBinaryParser::parse parsed " << count << " objects."
- << llendl;
- return rv;
-}
-
S32 LLSDBinaryParser::parseMap(std::istream& istr, LLSD& map) const
{
map = LLSD::emptyMap();
U32 value_nbo = 0;
- istr.read((char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/
+ read(istr, (char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/
S32 size = (S32)ntohl(value_nbo);
S32 parse_count = 0;
S32 count = 0;
- char c = istr.get();
+ char c = get(istr);
while(c != '}' && (count < size) && istr.good())
{
std::string name;
switch(c)
{
case 'k':
- parseString(istr, name);
+ if(!parseString(istr, name))
+ {
+ return PARSE_FAILURE;
+ }
break;
case '\'':
case '"':
- deserialize_string_delim(istr, name, c);
+ {
+ int cnt = deserialize_string_delim(istr, name, c);
+ if(PARSE_FAILURE == cnt) return PARSE_FAILURE;
+ account(cnt);
break;
}
+ }
LLSD child;
- S32 child_count = parse(istr, child);
- if(child_count)
+ S32 child_count = doParse(istr, child);
+ if(child_count > 0)
{
+ // There must be a value for every key, thus child_count
+ // must be greater than 0.
parse_count += child_count;
map.insert(name, child);
}
+ else
+ {
+ return PARSE_FAILURE;
+ }
++count;
- c = istr.get();
+ c = get(istr);
+ }
+ if((c != '}') || (count < size))
+ {
+ // Make sure it is correctly terminated and we parsed as many
+ // as were said to be there.
+ return PARSE_FAILURE;
}
return parse_count;
}
@@ -883,7 +1136,7 @@ S32 LLSDBinaryParser::parseArray(std::istream& istr, LLSD& array) const
{
array = LLSD::emptyArray();
U32 value_nbo = 0;
- istr.read((char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/
+ read(istr, (char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/
S32 size = (S32)ntohl(value_nbo);
// *FIX: This would be a good place to reserve some space in the
@@ -895,7 +1148,11 @@ S32 LLSDBinaryParser::parseArray(std::istream& istr, LLSD& array) const
while((c != ']') && (count < size) && istr.good())
{
LLSD child;
- S32 child_count = parse(istr, child);
+ S32 child_count = doParse(istr, child);
+ if(PARSE_FAILURE == child_count)
+ {
+ return PARSE_FAILURE;
+ }
if(child_count)
{
parse_count += child_count;
@@ -904,22 +1161,33 @@ S32 LLSDBinaryParser::parseArray(std::istream& istr, LLSD& array) const
++count;
c = istr.peek();
}
- c = istr.get();
+ c = get(istr);
+ if((c != ']') || (count < size))
+ {
+ // Make sure it is correctly terminated and we parsed as many
+ // as were said to be there.
+ return PARSE_FAILURE;
+ }
return parse_count;
}
-void LLSDBinaryParser::parseString(
+bool LLSDBinaryParser::parseString(
std::istream& istr,
std::string& value) const
{
// *FIX: This is memory inefficient.
U32 value_nbo = 0;
- istr.read((char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/
+ read(istr, (char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/
S32 size = (S32)ntohl(value_nbo);
+ if(size > mMaxBytesLeft) return false;
std::vector<char> buf;
- buf.resize(size);
- istr.read(&buf[0], size); /*Flawfinder: ignore*/
- value.assign(buf.begin(), buf.end());
+ if(size)
+ {
+ buf.resize(size);
+ account(fullread(istr, &buf[0], size));
+ value.assign(buf.begin(), buf.end());
+ }
+ return true;
}
@@ -1217,33 +1485,38 @@ void LLSDBinaryFormatter::formatString(
/**
* local functions
*/
-bool deserialize_string(std::istream& str, std::string& value)
+int deserialize_string(std::istream& istr, std::string& value, S32 max_bytes)
{
- char c = str.get();
- if (str.fail())
+ char c = istr.get();
+ if(istr.fail())
{
- // No data in stream, bail out
- return false;
+ // No data in stream, bail out but mention the character we
+ // grabbed.
+ return LLSDParser::PARSE_FAILURE;
}
- bool rv = false;
+ int rv = LLSDParser::PARSE_FAILURE;
switch(c)
{
case '\'':
case '"':
- rv = deserialize_string_delim(str, value, c);
+ rv = deserialize_string_delim(istr, value, c);
break;
case 's':
- rv = deserialize_string_raw(str, value);
+ // technically, less than max_bytes, but this is just meant to
+ // catch egregious protocol errors. parse errors will be
+ // caught in the case of incorrect counts.
+ rv = deserialize_string_raw(istr, value, max_bytes);
break;
default:
break;
}
- return rv;
+ if(LLSDParser::PARSE_FAILURE == rv) return rv;
+ return rv + 1; // account for the character grabbed at the top.
}
-bool deserialize_string_delim(
- std::istream& str,
+int deserialize_string_delim(
+ std::istream& istr,
std::string& value,
char delim)
{
@@ -1252,16 +1525,18 @@ bool deserialize_string_delim(
bool found_hex = false;
bool found_digit = false;
U8 byte = 0;
-
+ int count = 0;
+
while (true)
{
- char next_char = str.get();
-
- if(str.fail())
+ char next_char = istr.get();
+ ++count;
+
+ if(istr.fail())
{
// If our stream is empty, break out
value = write_buffer.str();
- return false;
+ return LLSDParser::PARSE_FAILURE;
}
if(found_escape)
@@ -1338,35 +1613,48 @@ bool deserialize_string_delim(
}
value = write_buffer.str();
- return true;
+ return count;
}
-bool deserialize_string_raw(std::istream& str, std::string& value)
+int deserialize_string_raw(
+ std::istream& istr,
+ std::string& value,
+ S32 max_bytes)
{
- bool ok = false;
+ int count = 0;
const S32 BUF_LEN = 20;
char buf[BUF_LEN]; /* Flawfinder: ignore */
- str.get(buf, BUF_LEN - 1, ')');
- char c = str.get();
- c = str.get();
+ istr.get(buf, BUF_LEN - 1, ')');
+ count += istr.gcount();
+ char c = istr.get();
+ c = istr.get();
+ count += 2;
if(((c == '"') || (c == '\'')) && (buf[0] == '('))
{
// We probably have a valid raw string. determine
// the size, and read it.
- // *FIX: Should we set a maximum size?
// *FIX: This is memory inefficient.
S32 len = strtol(buf + 1, NULL, 0);
+ if(len > max_bytes) return LLSDParser::PARSE_FAILURE;
std::vector<char> buf;
- buf.resize(len);
- str.read(&buf[0], len); /*Flawfinder: ignore*/
- value.assign(buf.begin(), buf.end());
- c = str.get();
- if((c == '"') || (c == '\''))
+ if(len)
+ {
+ buf.resize(len);
+ count += fullread(istr, (char *)&buf[0], len);
+ value.assign(buf.begin(), buf.end());
+ }
+ c = istr.get();
+ ++count;
+ if(!((c == '"') || (c == '\'')))
{
- ok = true;
+ return LLSDParser::PARSE_FAILURE;
}
}
- return ok;
+ else
+ {
+ return LLSDParser::PARSE_FAILURE;
+ }
+ return count;
}
static const char* NOTATION_STRING_CHARACTERS[256] =
@@ -1641,6 +1929,43 @@ void serialize_string(const std::string& value, std::ostream& str)
}
}
+int deserialize_boolean(
+ std::istream& istr,
+ LLSD& data,
+ const std::string& compare,
+ bool value)
+{
+ //
+ // this method is a little goofy, because it gets the stream at
+ // the point where the t or f has already been
+ // consumed. Basically, parse for a patch to the string passed in
+ // starting at index 1. If it's a match:
+ // * assign data to value
+ // * return the number of bytes read
+ // otherwise:
+ // * set data to LLSD::null
+ // * return LLSDParser::PARSE_FAILURE (-1)
+ //
+ int bytes_read = 0;
+ std::string::size_type ii = 0;
+ char c = istr.peek();
+ while((++ii < compare.size())
+ && (tolower(c) == (int)compare[ii])
+ && istr.good())
+ {
+ istr.ignore();
+ ++bytes_read;
+ c = istr.peek();
+ }
+ if(compare.size() != ii)
+ {
+ data.clear();
+ return LLSDParser::PARSE_FAILURE;
+ }
+ data = value;
+ return bytes_read;
+}
+
std::ostream& operator<<(std::ostream& s, const LLSD& llsd)
{
s << LLSDNotationStreamer(llsd);