diff options
Diffstat (limited to 'indra/llcommon/llsdserialize.cpp')
-rw-r--r-- | indra/llcommon/llsdserialize.cpp | 265 |
1 files changed, 157 insertions, 108 deletions
diff --git a/indra/llcommon/llsdserialize.cpp b/indra/llcommon/llsdserialize.cpp index a510b73096..e7482b601d 100644 --- a/indra/llcommon/llsdserialize.cpp +++ b/indra/llcommon/llsdserialize.cpp @@ -48,12 +48,13 @@ #endif #include "lldate.h" +#include "llmemorystream.h" #include "llsd.h" #include "llstring.h" #include "lluri.h" // File constants -static const int MAX_HDR_LEN = 20; +static const size_t MAX_HDR_LEN = 20; static const S32 UNZIP_LLSD_MAX_DEPTH = 96; static const char LEGACY_NON_HEADER[] = "<llsd>"; const std::string LLSD_BINARY_HEADER("LLSD/Binary"); @@ -64,6 +65,23 @@ const std::string LLSD_NOTATION_HEADER("llsd/notation"); #define windowBits 15 #define ENABLE_ZLIB_GZIP 32 +// If we published this in llsdserialize.h, we could use it in the +// implementation of LLSDOStreamer's operator<<(). +template <class Formatter> +void format_using(const LLSD& data, std::ostream& ostr, + LLSDFormatter::EFormatterOptions options=LLSDFormatter::OPTIONS_PRETTY_BINARY) +{ + LLPointer<Formatter> f{ new Formatter }; + f->format(data, ostr, options); +} + +template <class Parser> +S32 parse_using(std::istream& istr, LLSD& data, size_t max_bytes, S32 max_depth=-1) +{ + LLPointer<Parser> p{ new Parser }; + return p->parse(istr, data, max_bytes, max_depth); +} + /** * LLSDSerialize */ @@ -86,10 +104,10 @@ void LLSDSerialize::serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize f = new LLSDXMLFormatter; break; - case LLSD_NOTATION: - str << "<? " << LLSD_NOTATION_HEADER << " ?>\n"; - f = new LLSDNotationFormatter; - break; + case LLSD_NOTATION: + str << "<? " << LLSD_NOTATION_HEADER << " ?>\n"; + f = new LLSDNotationFormatter; + break; default: LL_WARNS() << "serialize request for unknown ELLSD_Serialize" << LL_ENDL; @@ -102,20 +120,33 @@ void LLSDSerialize::serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize } // static -bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str, S32 max_bytes) +bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str, llssize max_bytes) { - LLPointer<LLSDParser> p = NULL; char hdr_buf[MAX_HDR_LEN + 1] = ""; /* Flawfinder: ignore */ - int i; - int inbuf = 0; - bool legacy_no_header = false; bool fail_if_not_legacy = false; - std::string header; /* - * Get the first line before anything. + * Get the first line before anything. Don't read more than max_bytes: + * this get() overload reads no more than (count-1) bytes into the + * specified buffer. In the usual case when max_bytes exceeds + * sizeof(hdr_buf), get() will read no more than sizeof(hdr_buf)-2. */ - str.get(hdr_buf, MAX_HDR_LEN, '\n'); + str.get(hdr_buf, llmin(max_bytes+1, sizeof(hdr_buf)-1), '\n'); + auto inbuf = str.gcount(); + // https://en.cppreference.com/w/cpp/io/basic_istream/get + // When the get() above sees the specified delimiter '\n', it stops there + // without pulling it from the stream. If it turns out that the stream + // does NOT contain a header, and the content includes meaningful '\n', + // it's important to pull that into hdr_buf too. + if (inbuf < max_bytes && str.get(hdr_buf[inbuf])) + { + // got the delimiting '\n' + ++inbuf; + // None of the following requires that hdr_buf contain a final '\0' + // byte. We could store one if needed, since even the incremented + // inbuf won't exceed sizeof(hdr_buf)-1, but there's no need. + } + std::string header{ hdr_buf, static_cast<std::string::size_type>(inbuf) }; if (str.fail()) { str.clear(); @@ -123,79 +154,97 @@ bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str, S32 max_bytes) } if (!strncasecmp(LEGACY_NON_HEADER, hdr_buf, strlen(LEGACY_NON_HEADER))) /* Flawfinder: ignore */ + { // Create a LLSD XML parser, and parse the first chunk read above. + LLSDXMLParser x; + x.parsePart(hdr_buf, inbuf); // Parse the first part that was already read + auto parsed = x.parse(str, sd, max_bytes - inbuf); // Parse the rest of it + // Formally we should probably check (parsed != PARSE_FAILURE && + // parsed > 0), but since PARSE_FAILURE is -1, this suffices. + return (parsed > 0); + } + + if (fail_if_not_legacy) { - legacy_no_header = true; - inbuf = (int)str.gcount(); + LL_WARNS() << "deserialize LLSD parse failure" << LL_ENDL; + return false; } - else + + /* + * Remove the newline chars + */ + std::string::size_type lastchar = header.find_last_not_of("\r\n"); + if (lastchar != std::string::npos) { - if (fail_if_not_legacy) - goto fail; - /* - * Remove the newline chars - */ - for (i = 0; i < MAX_HDR_LEN; i++) - { - if (hdr_buf[i] == 0 || hdr_buf[i] == '\r' || - hdr_buf[i] == '\n') - { - hdr_buf[i] = 0; - break; - } - } - header = hdr_buf; + // It's important that find_last_not_of() returns size_type, which is + // why lastchar explicitly declares the type above. erase(size_type) + // erases from that offset to the end of the string, whereas + // erase(iterator) erases only a single character. + header.erase(lastchar+1); + } - std::string::size_type start = std::string::npos; - std::string::size_type end = std::string::npos; - start = header.find_first_not_of("<? "); - if (start != std::string::npos) + // trim off the <? ... ?> header syntax + auto start = header.find_first_not_of("<? "); + if (start != std::string::npos) + { + auto end = header.find_first_of(" ?", start); + if (end != std::string::npos) { - end = header.find_first_of(" ?", start); + header = header.substr(start, end - start); + ws(str); } - if ((start == std::string::npos) || (end == std::string::npos)) - goto fail; - - header = header.substr(start, end - start); - ws(str); } /* * Create the parser as appropriate */ - if (legacy_no_header) - { // Create a LLSD XML parser, and parse the first chunk read above - LLSDXMLParser* x = new LLSDXMLParser(); - x->parsePart(hdr_buf, inbuf); // Parse the first part that was already read - x->parseLines(str, sd); // Parse the rest of it - delete x; - return true; - } - - if (header == LLSD_BINARY_HEADER) + if (0 == LLStringUtil::compareInsensitive(header, LLSD_BINARY_HEADER)) { - p = new LLSDBinaryParser; + return (parse_using<LLSDBinaryParser>(str, sd, max_bytes-inbuf) > 0); } - else if (header == LLSD_XML_HEADER) + else if (0 == LLStringUtil::compareInsensitive(header, LLSD_XML_HEADER)) { - p = new LLSDXMLParser; + return (parse_using<LLSDXMLParser>(str, sd, max_bytes-inbuf) > 0); } - else if (header == LLSD_NOTATION_HEADER) + else if (0 == LLStringUtil::compareInsensitive(header, LLSD_NOTATION_HEADER)) { - p = new LLSDNotationParser; + return (parse_using<LLSDNotationParser>(str, sd, max_bytes-inbuf) > 0); } - else + else // no header we recognize { - LL_WARNS() << "deserialize request for unknown ELLSD_Serialize" << LL_ENDL; - } - - if (p.notNull()) - { - p->parse(str, sd, max_bytes); - return true; + LLPointer<LLSDParser> p; + if (inbuf && hdr_buf[0] == '<') + { + // looks like XML + LL_DEBUGS() << "deserialize request with no header, assuming XML" << LL_ENDL; + p = new LLSDXMLParser; + } + else + { + // assume notation + LL_DEBUGS() << "deserialize request with no header, assuming notation" << LL_ENDL; + p = new LLSDNotationParser; + } + // Since we've already read 'inbuf' bytes into 'hdr_buf', prepend that + // data to whatever remains in 'str'. + LLMemoryStreamBuf already(reinterpret_cast<const U8*>(hdr_buf), inbuf); + cat_streambuf prebuff(&already, str.rdbuf()); + std::istream prepend(&prebuff); +#if 1 + return (p->parse(prepend, sd, max_bytes) > 0); +#else + // debugging the reconstituted 'prepend' stream + // allocate a buffer that we hope is big enough for the whole thing + std::vector<char> wholemsg((max_bytes == size_t(SIZE_UNLIMITED))? 1024 : max_bytes); + prepend.read(wholemsg.data(), std::min(max_bytes, wholemsg.size())); + LLMemoryStream replay(reinterpret_cast<const U8*>(wholemsg.data()), prepend.gcount()); + auto success{ p->parse(replay, sd, prepend.gcount()) > 0 }; + { + LL_DEBUGS() << (success? "parsed: $$" : "failed: '") + << std::string(wholemsg.data(), llmin(prepend.gcount(), 100)) << "$$" + << LL_ENDL; + } + return success; +#endif } - -fail: - LL_WARNS() << "deserialize LLSD parse failure" << LL_ENDL; - return false; } /** @@ -255,7 +304,7 @@ F64 ll_ntohd(F64 netdouble) * @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); +llssize deserialize_string(std::istream& istr, std::string& value, llssize max_bytes); /** * @brief Parse a delimited string. @@ -266,7 +315,7 @@ int deserialize_string(std::istream& istr, std::string& value, S32 max_bytes); * @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); +llssize deserialize_string_delim(std::istream& istr, std::string& value, char d); /** * @brief Read a raw string off the stream. @@ -280,10 +329,10 @@ int deserialize_string_delim(std::istream& istr, std::string& value, char d); * @return Returns number of bytes read off of the stream. Returns * PARSE_FAILURE (-1) on failure. */ -int deserialize_string_raw( +llssize deserialize_string_raw( std::istream& istr, std::string& value, - S32 max_bytes); + llssize max_bytes); /** * @brief helper method for dealing with the different notation boolean format. @@ -295,7 +344,7 @@ int deserialize_string_raw( * @return Returns number of bytes read off of the stream. Returns * PARSE_FAILURE (-1) on failure. */ -int deserialize_boolean( +llssize deserialize_boolean( std::istream& istr, LLSD& data, const std::string& compare, @@ -332,7 +381,7 @@ LLSDParser::LLSDParser() LLSDParser::~LLSDParser() { } -S32 LLSDParser::parse(std::istream& istr, LLSD& data, S32 max_bytes, S32 max_depth) +S32 LLSDParser::parse(std::istream& istr, LLSD& data, llssize max_bytes, S32 max_depth) { mCheckLimits = (LLSDSerialize::SIZE_UNLIMITED == max_bytes) ? false : true; mMaxBytesLeft = max_bytes; @@ -362,7 +411,7 @@ std::istream& LLSDParser::get( char delim) const { istr.get(s, n, delim); - if(mCheckLimits) mMaxBytesLeft -= (int)istr.gcount(); + if(mCheckLimits) mMaxBytesLeft -= istr.gcount(); return istr; } @@ -372,7 +421,7 @@ std::istream& LLSDParser::get( char delim) const { istr.get(sb, delim); - if(mCheckLimits) mMaxBytesLeft -= (int)istr.gcount(); + if(mCheckLimits) mMaxBytesLeft -= istr.gcount(); return istr; } @@ -396,11 +445,11 @@ std::istream& LLSDParser::read( std::streamsize n) const { istr.read(s, n); - if(mCheckLimits) mMaxBytesLeft -= (int)istr.gcount(); + if(mCheckLimits) mMaxBytesLeft -= istr.gcount(); return istr; } -void LLSDParser::account(S32 bytes) const +void LLSDParser::account(llssize bytes) const { if(mCheckLimits) mMaxBytesLeft -= bytes; } @@ -505,7 +554,7 @@ S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) c c = istr.peek(); if(isalpha(c)) { - int cnt = deserialize_boolean( + auto cnt = deserialize_boolean( istr, data, NOTATION_FALSE_SERIAL, @@ -535,7 +584,7 @@ S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) c c = istr.peek(); if(isalpha(c)) { - int cnt = deserialize_boolean(istr,data,NOTATION_TRUE_SERIAL,true); + auto cnt = deserialize_boolean(istr,data,NOTATION_TRUE_SERIAL,true); if(PARSE_FAILURE == cnt) parse_count = cnt; else account(cnt); } @@ -611,7 +660,7 @@ S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) c c = get(istr); // pop the 'l' c = get(istr); // pop the delimiter std::string str; - int cnt = deserialize_string_delim(istr, str, c); + auto cnt = deserialize_string_delim(istr, str, c); if(PARSE_FAILURE == cnt) { parse_count = PARSE_FAILURE; @@ -634,7 +683,7 @@ S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) c c = get(istr); // pop the 'd' c = get(istr); // pop the delimiter std::string str; - int cnt = deserialize_string_delim(istr, str, c); + auto cnt = deserialize_string_delim(istr, str, c); if(PARSE_FAILURE == cnt) { parse_count = PARSE_FAILURE; @@ -666,7 +715,7 @@ S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) c default: parse_count = PARSE_FAILURE; - LL_INFOS() << "Unrecognized character while parsing: int(" << (int)c + LL_INFOS() << "Unrecognized character while parsing: int(" << int(c) << ")" << LL_ENDL; break; } @@ -697,7 +746,7 @@ S32 LLSDNotationParser::parseMap(std::istream& istr, LLSD& map, S32 max_depth) c { putback(istr, c); found_name = true; - int count = deserialize_string(istr, name, mMaxBytesLeft); + auto count = deserialize_string(istr, name, mMaxBytesLeft); if(PARSE_FAILURE == count) return PARSE_FAILURE; account(count); } @@ -779,7 +828,7 @@ S32 LLSDNotationParser::parseArray(std::istream& istr, LLSD& array, S32 max_dept bool LLSDNotationParser::parseString(std::istream& istr, LLSD& data) const { std::string value; - int count = deserialize_string(istr, value, mMaxBytesLeft); + auto count = deserialize_string(istr, value, mMaxBytesLeft); if(PARSE_FAILURE == count) return false; account(count); data = value; @@ -806,13 +855,13 @@ bool LLSDNotationParser::parseBinary(std::istream& istr, LLSD& data) const { // We probably have a valid raw binary stream. determine // the size, and read it. - S32 len = strtol(buf + 2, NULL, 0); + auto len = strtol(buf + 2, NULL, 0); if(mCheckLimits && (len > mMaxBytesLeft)) return false; std::vector<U8> value; if(len) { value.resize(len); - account((int)fullread(istr, (char *)&value[0], len)); + account(fullread(istr, (char *)&value[0], len)); } c = get(istr); // strip off the trailing double-quote data = value; @@ -1009,7 +1058,7 @@ S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) con case '"': { std::string value; - int cnt = deserialize_string_delim(istr, value, c); + auto cnt = deserialize_string_delim(istr, value, c); if(PARSE_FAILURE == cnt) { parse_count = PARSE_FAILURE; @@ -1096,7 +1145,7 @@ S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) con if(size > 0) { value.resize(size); - account((int)fullread(istr, (char*)&value[0], size)); + account(fullread(istr, (char*)&value[0], size)); } data = value; } @@ -1110,7 +1159,7 @@ S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) con default: parse_count = PARSE_FAILURE; - LL_INFOS() << "Unrecognized character while parsing: int(" << (int)c + LL_INFOS() << "Unrecognized character while parsing: int(" << int(c) << ")" << LL_ENDL; break; } @@ -1144,7 +1193,7 @@ S32 LLSDBinaryParser::parseMap(std::istream& istr, LLSD& map, S32 max_depth) con case '\'': case '"': { - int cnt = deserialize_string_delim(istr, name, c); + auto cnt = deserialize_string_delim(istr, name, c); if(PARSE_FAILURE == cnt) return PARSE_FAILURE; account(cnt); break; @@ -1228,7 +1277,7 @@ bool LLSDBinaryParser::parseString( if(size) { buf.resize(size); - account((int)fullread(istr, &buf[0], size)); + account(fullread(istr, &buf[0], size)); value.assign(buf.begin(), buf.end()); } return true; @@ -1432,7 +1481,7 @@ S32 LLSDNotationFormatter::format_impl(const LLSD& data, std::ostream& ostr, ostr << std::uppercase; auto oldfill(ostr.fill('0')); auto oldwidth(ostr.width()); - for (int i = 0; i < buffer.size(); i++) + for (size_t i = 0; i < buffer.size(); i++) { // have to restate setw() before every conversion ostr << std::setw(2) << (int) buffer[i]; @@ -1595,7 +1644,7 @@ void LLSDBinaryFormatter::formatString( /** * local functions */ -int deserialize_string(std::istream& istr, std::string& value, S32 max_bytes) +llssize deserialize_string(std::istream& istr, std::string& value, llssize max_bytes) { int c = istr.get(); if(istr.fail()) @@ -1605,7 +1654,7 @@ int deserialize_string(std::istream& istr, std::string& value, S32 max_bytes) return LLSDParser::PARSE_FAILURE; } - int rv = LLSDParser::PARSE_FAILURE; + llssize rv = LLSDParser::PARSE_FAILURE; switch(c) { case '\'': @@ -1625,7 +1674,7 @@ int deserialize_string(std::istream& istr, std::string& value, S32 max_bytes) return rv + 1; // account for the character grabbed at the top. } -int deserialize_string_delim( +llssize deserialize_string_delim( std::istream& istr, std::string& value, char delim) @@ -1635,7 +1684,7 @@ int deserialize_string_delim( bool found_hex = false; bool found_digit = false; U8 byte = 0; - int count = 0; + llssize count = 0; while (true) { @@ -1650,7 +1699,7 @@ int deserialize_string_delim( } char next_char = (char)next_byte; // Now that we know it's not EOF - + if(found_escape) { // next character(s) is a special sequence. @@ -1728,16 +1777,16 @@ int deserialize_string_delim( return count; } -int deserialize_string_raw( +llssize deserialize_string_raw( std::istream& istr, std::string& value, - S32 max_bytes) + llssize max_bytes) { - int count = 0; + llssize count = 0; const S32 BUF_LEN = 20; char buf[BUF_LEN]; /* Flawfinder: ignore */ istr.get(buf, BUF_LEN - 1, ')'); - count += (int)istr.gcount(); + count += istr.gcount(); int c = istr.get(); c = istr.get(); count += 2; @@ -1746,13 +1795,13 @@ int deserialize_string_raw( // We probably have a valid raw string. determine // the size, and read it. // *FIX: This is memory inefficient. - S32 len = strtol(buf + 1, NULL, 0); + auto len = strtol(buf + 1, NULL, 0); if((max_bytes>0)&&(len>max_bytes)) return LLSDParser::PARSE_FAILURE; std::vector<char> buf; if(len) { buf.resize(len); - count += (int)fullread(istr, (char *)&buf[0], len); + count += fullread(istr, (char *)&buf[0], len); value.assign(buf.begin(), buf.end()); } c = istr.get(); @@ -2041,7 +2090,7 @@ void serialize_string(const std::string& value, std::ostream& str) } } -int deserialize_boolean( +llssize deserialize_boolean( std::istream& istr, LLSD& data, const std::string& compare, @@ -2058,7 +2107,7 @@ int deserialize_boolean( // * set data to LLSD::null // * return LLSDParser::PARSE_FAILURE (-1) // - int bytes_read = 0; + llssize bytes_read = 0; std::string::size_type ii = 0; char c = istr.peek(); while((++ii < compare.size()) @@ -2113,7 +2162,7 @@ std::string zip_llsd(LLSD& data) U8 out[CHUNK]; - strm.avail_in = source.size(); + strm.avail_in = narrow(source.size()); strm.next_in = (U8*) source.data(); U8* output = NULL; @@ -2287,7 +2336,7 @@ LLUZipHelper::EZipRresult LLUZipHelper::unzip_llsd(LLSD& data, const U8* in, S32 //This unzip function will only work with a gzip header and trailer - while the contents //of the actual compressed data is the same for either format (gzip vs zlib ), the headers //and trailers are different for the formats. -U8* unzip_llsdNavMesh( bool& valid, unsigned int& outsize, std::istream& is, S32 size ) +U8* unzip_llsdNavMesh( bool& valid, size_t& outsize, std::istream& is, S32 size ) { if (size == 0) { |