/** * @file message.cpp * @brief LLMessageSystem class implementation * * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc. * $License$ */ #include "linden_common.h" #include "message.h" // system library includes #if !LL_WINDOWS // following header files required for inet_addr() #include #include #include #include #endif #include #include #include #include #include #include #include #include "llapr.h" #include "apr-1/apr_portable.h" #include "apr-1/apr_network_io.h" #include "apr-1/apr_poll.h" // linden library headers #include "indra_constants.h" #include "lldir.h" #include "llerror.h" #include "llfasttimer.h" #include "llmd5.h" #include "llsd.h" #include "lltransfermanager.h" #include "lluuid.h" #include "llxfermanager.h" #include "timing.h" #include "llquaternion.h" #include "u64.h" #include "v3dmath.h" #include "v3math.h" #include "v4math.h" #include "lltransfertargetvfile.h" // Constants //const char* MESSAGE_LOG_FILENAME = "message.log"; static const F32 CIRCUIT_DUMP_TIMEOUT = 30.f; static const S32 TRUST_TIME_WINDOW = 3; class LLMsgVarData { public: LLMsgVarData() : mName(NULL), mSize(-1), mDataSize(-1), mData(NULL), mType(MVT_U8) { } LLMsgVarData(const char *name, EMsgVariableType type) : mSize(-1), mDataSize(-1), mData(NULL), mType(type) { mName = (char *)name; } ~LLMsgVarData() { // copy constructor just copies the mData pointer, so only delete mData explicitly } void deleteData() { delete[] mData; mData = NULL; } void addData(const void *indata, S32 size, EMsgVariableType type, S32 data_size = -1); char *getName() const { return mName; } S32 getSize() const { return mSize; } void *getData() { return (void*)mData; } S32 getDataSize() const { return mDataSize; } EMsgVariableType getType() const { return mType; } protected: char *mName; S32 mSize; S32 mDataSize; U8 *mData; EMsgVariableType mType; }; class LLMsgBlkData { public: LLMsgBlkData(const char *name, S32 blocknum) : mOffset(-1), mBlockNumber(blocknum), mTotalSize(-1) { mName = (char *)name; } ~LLMsgBlkData() { for (msg_var_data_map_t::iterator iter = mMemberVarData.begin(); iter != mMemberVarData.end(); iter++) { iter->deleteData(); } } void addVariable(const char *name, EMsgVariableType type) { LLMsgVarData tmp(name,type); mMemberVarData[name] = tmp; } void addData(char *name, const void *data, S32 size, EMsgVariableType type, S32 data_size = -1) { LLMsgVarData* temp = &mMemberVarData[name]; // creates a new entry if one doesn't exist temp->addData(data, size, type, data_size); } S32 mOffset; S32 mBlockNumber; typedef LLDynamicArrayIndexed msg_var_data_map_t; msg_var_data_map_t mMemberVarData; char *mName; S32 mTotalSize; }; class LLMsgData { public: LLMsgData(const char *name) : mTotalSize(-1) { mName = (char *)name; } ~LLMsgData() { for_each(mMemberBlocks.begin(), mMemberBlocks.end(), DeletePairedPointer()); } void addBlock(LLMsgBlkData *blockp) { mMemberBlocks[blockp->mName] = blockp; } void addDataFast(char *blockname, char *varname, const void *data, S32 size, EMsgVariableType type, S32 data_size = -1); public: S32 mOffset; typedef std::map msg_blk_data_map_t; msg_blk_data_map_t mMemberBlocks; char *mName; S32 mTotalSize; }; inline void LLMsgVarData::addData(const void *data, S32 size, EMsgVariableType type, S32 data_size) { mSize = size; mDataSize = data_size; if ( (type != MVT_VARIABLE) && (type != MVT_FIXED) && (mType != MVT_VARIABLE) && (mType != MVT_FIXED)) { if (mType != type) { llwarns << "Type mismatch in addData for " << mName << " message: " << gMessageSystem->getCurrentSMessageName() << " block: " << gMessageSystem->getCurrentSBlockName() << llendl; } } if(size) { delete mData; // Delete it if it already exists mData = new U8[size]; htonmemcpy(mData, data, mType, size); } } inline void LLMsgData::addDataFast(char *blockname, char *varname, const void *data, S32 size, EMsgVariableType type, S32 data_size) { // remember that if the blocknumber is > 0 then the number is appended to the name char *namep = (char *)blockname; LLMsgBlkData* block_data = mMemberBlocks[namep]; if (block_data->mBlockNumber) { namep += block_data->mBlockNumber; block_data->addData(varname, data, size, type, data_size); } else { block_data->addData(varname, data, size, type, data_size); } } // LLMessage* classes store the template of messages class LLMessageVariable { public: LLMessageVariable() : mName(NULL), mType(MVT_NULL), mSize(-1) { } LLMessageVariable(char *name) : mType(MVT_NULL), mSize(-1) { mName = name; } LLMessageVariable(char *name, const EMsgVariableType type, const S32 size) : mType(type), mSize(size) { mName = gMessageStringTable.getString(name); } ~LLMessageVariable() {} friend std::ostream& operator<<(std::ostream& s, LLMessageVariable &msg); EMsgVariableType getType() const { return mType; } S32 getSize() const { return mSize; } char *getName() const { return mName; } protected: char *mName; EMsgVariableType mType; S32 mSize; }; typedef enum e_message_block_type { MBT_NULL, MBT_SINGLE, MBT_MULTIPLE, MBT_VARIABLE, MBT_EOF } EMsgBlockType; class LLMessageBlock { public: LLMessageBlock(char *name, EMsgBlockType type, S32 number = 1) : mType(type), mNumber(number), mTotalSize(0) { mName = gMessageStringTable.getString(name); } ~LLMessageBlock() { for_each(mMemberVariables.begin(), mMemberVariables.end(), DeletePairedPointer()); } void addVariable(char *name, const EMsgVariableType type, const S32 size) { LLMessageVariable** varp = &mMemberVariables[name]; if (*varp != NULL) { llerrs << name << " has already been used as a variable name!" << llendl; } *varp = new LLMessageVariable(name, type, size); if (((*varp)->getType() != MVT_VARIABLE) &&(mTotalSize != -1)) { mTotalSize += (*varp)->getSize(); } else { mTotalSize = -1; } } EMsgVariableType getVariableType(char *name) { return (mMemberVariables[name])->getType(); } S32 getVariableSize(char *name) { return (mMemberVariables[name])->getSize(); } friend std::ostream& operator<<(std::ostream& s, LLMessageBlock &msg); typedef std::map message_variable_map_t; message_variable_map_t mMemberVariables; char *mName; EMsgBlockType mType; S32 mNumber; S32 mTotalSize; }; enum EMsgFrequency { MFT_NULL = 0, // value is size of message number in bytes MFT_HIGH = 1, MFT_MEDIUM = 2, MFT_LOW = 4 }; typedef enum e_message_trust { MT_TRUST, MT_NOTRUST } EMsgTrust; enum EMsgEncoding { ME_UNENCODED, ME_ZEROCODED }; class LLMessageTemplate { public: LLMessageTemplate(const char *name, U32 message_number, EMsgFrequency freq) : //mMemberBlocks(), mName(NULL), mFrequency(freq), mTrust(MT_NOTRUST), mEncoding(ME_ZEROCODED), mMessageNumber(message_number), mTotalSize(0), mReceiveCount(0), mReceiveBytes(0), mReceiveInvalid(0), mDecodeTimeThisFrame(0.f), mTotalDecoded(0), mTotalDecodeTime(0.f), mMaxDecodeTimePerMsg(0.f), mBanFromTrusted(false), mBanFromUntrusted(false), mHandlerFunc(NULL), mUserData(NULL) { mName = gMessageStringTable.getString(name); } ~LLMessageTemplate() { for_each(mMemberBlocks.begin(), mMemberBlocks.end(), DeletePairedPointer()); } void addBlock(LLMessageBlock *blockp) { LLMessageBlock** member_blockp = &mMemberBlocks[blockp->mName]; if (*member_blockp != NULL) { llerrs << "Block " << blockp->mName << "has already been used as a block name!" << llendl; } *member_blockp = blockp; if ( (mTotalSize != -1) &&(blockp->mTotalSize != -1) &&( (blockp->mType == MBT_SINGLE) ||(blockp->mType == MBT_MULTIPLE))) { mTotalSize += blockp->mNumber*blockp->mTotalSize; } else { mTotalSize = -1; } } LLMessageBlock *getBlock(char *name) { return mMemberBlocks[name]; } // Trusted messages can only be recieved on trusted circuits. void setTrust(EMsgTrust t) { mTrust = t; } EMsgTrust getTrust(void) { return mTrust; } // controls for how the message should be encoded void setEncoding(EMsgEncoding e) { mEncoding = e; } EMsgEncoding getEncoding() { return mEncoding; } void setHandlerFunc(void (*handler_func)(LLMessageSystem *msgsystem, void **user_data), void **user_data) { mHandlerFunc = handler_func; mUserData = user_data; } BOOL callHandlerFunc(LLMessageSystem *msgsystem) { if (mHandlerFunc) { mHandlerFunc(msgsystem, mUserData); return TRUE; } return FALSE; } bool isBanned(bool trustedSource) { return trustedSource ? mBanFromTrusted : mBanFromUntrusted; } friend std::ostream& operator<<(std::ostream& s, LLMessageTemplate &msg); public: typedef std::map message_block_map_t; message_block_map_t mMemberBlocks; char *mName; EMsgFrequency mFrequency; EMsgTrust mTrust; EMsgEncoding mEncoding; U32 mMessageNumber; S32 mTotalSize; U32 mReceiveCount; // how many of this template have been received since last reset U32 mReceiveBytes; // How many bytes received U32 mReceiveInvalid; // How many "invalid" packets F32 mDecodeTimeThisFrame; // Total seconds spent decoding this frame U32 mTotalDecoded; // Total messages successfully decoded F32 mTotalDecodeTime; // Total time successfully decoding messages F32 mMaxDecodeTimePerMsg; bool mBanFromTrusted; bool mBanFromUntrusted; private: // message handler function (this is set by each application) void (*mHandlerFunc)(LLMessageSystem *msgsystem, void **user_data); void **mUserData; }; // static BOOL LLMessageSystem::mTimeDecodes = FALSE; // static, 50ms per message decode F32 LLMessageSystem::mTimeDecodesSpamThreshold = 0.05f; // *NOTE: This needs to be moved into a seperate file so that it never gets // included in the viewer. 30 Sep 2002 mark // *NOTE: I don't think it's important that the messgage system tracks // this since it must get set externally. 2004.08.25 Phoenix. static std::string g_shared_secret; std::string get_shared_secret(); class LLMessagePollInfo { public: apr_socket_t *mAPRSocketp; apr_pollfd_t mPollFD; }; // LLMessageVariable functions and friends std::ostream& operator<<(std::ostream& s, LLMessageVariable &msg) { s << "\t\t" << msg.mName << " ("; switch (msg.mType) { case MVT_FIXED: s << "Fixed, " << msg.mSize << " bytes total)\n"; break; case MVT_VARIABLE: s << "Variable, " << msg.mSize << " bytes of size info)\n"; break; default: s << "Unknown\n"; break; } return s; } // LLMessageBlock functions and friends std::ostream& operator<<(std::ostream& s, LLMessageBlock &msg) { s << "\t" << msg.mName << " ("; switch (msg.mType) { case MBT_SINGLE: s << "Fixed"; break; case MBT_MULTIPLE: s << "Multiple - " << msg.mNumber << " copies"; break; case MBT_VARIABLE: s << "Variable"; break; default: s << "Unknown"; break; } if (msg.mTotalSize != -1) { s << ", " << msg.mTotalSize << " bytes each, " << msg.mNumber*msg.mTotalSize << " bytes total)\n"; } else { s << ")\n"; } for (LLMessageBlock::message_variable_map_t::iterator iter = msg.mMemberVariables.begin(); iter != msg.mMemberVariables.end(); iter++) { LLMessageVariable& ci = *(iter->second); s << ci; } return s; } // LLMessageTemplate functions and friends std::ostream& operator<<(std::ostream& s, LLMessageTemplate &msg) { switch (msg.mFrequency) { case MFT_HIGH: s << "========================================\n" << "Message #" << msg.mMessageNumber << "\n" << msg.mName << " ("; s << "High"; break; case MFT_MEDIUM: s << "========================================\n" << "Message #"; s << (msg.mMessageNumber & 0xFF) << "\n" << msg.mName << " ("; s << "Medium"; break; case MFT_LOW: s << "========================================\n" << "Message #"; s << (msg.mMessageNumber & 0xFFFF) << "\n" << msg.mName << " ("; s << "Low"; break; default: s << "Unknown"; break; } if (msg.mTotalSize != -1) { s << ", " << msg.mTotalSize << " bytes total)\n"; } else { s << ")\n"; } for (LLMessageTemplate::message_block_map_t::iterator iter = msg.mMemberBlocks.begin(); iter != msg.mMemberBlocks.end(); iter++) { LLMessageBlock* ci = iter->second; s << *ci; } return s; } // LLMessageList functions and friends // Lets support a small subset of regular expressions here // Syntax is a string made up of: // a - checks against alphanumeric ([A-Za-z0-9]) // c - checks against character ([A-Za-z]) // f - checks against first variable character ([A-Za-z_]) // v - checks against variable ([A-Za-z0-9_]) // s - checks against sign of integer ([-0-9]) // d - checks against integer digit ([0-9]) // * - repeat last check // checks 'a' BOOL b_return_alphanumeric_ok(char c) { if ( ( (c < 'A') ||(c > 'Z')) &&( (c < 'a') ||(c > 'z')) &&( (c < '0') ||(c > '9'))) { return FALSE; } return TRUE; } // checks 'c' BOOL b_return_character_ok(char c) { if ( ( (c < 'A') ||(c > 'Z')) &&( (c < 'a') ||(c > 'z'))) { return FALSE; } return TRUE; } // checks 'f' BOOL b_return_first_variable_ok(char c) { if ( ( (c < 'A') ||(c > 'Z')) &&( (c < 'a') ||(c > 'z')) &&(c != '_')) { return FALSE; } return TRUE; } // checks 'v' BOOL b_return_variable_ok(char c) { if ( ( (c < 'A') ||(c > 'Z')) &&( (c < 'a') ||(c > 'z')) &&( (c < '0') ||(c > '9')) &&(c != '_')) { return FALSE; } return TRUE; } // checks 's' BOOL b_return_signed_integer_ok(char c) { if ( ( (c < '0') ||(c > '9')) &&(c != '-')) { return FALSE; } return TRUE; } // checks 'd' BOOL b_return_integer_ok(char c) { if ( (c < '0') ||(c > '9')) { return FALSE; } return TRUE; } BOOL (*gParseCheckCharacters[])(char c) = { b_return_alphanumeric_ok, b_return_character_ok, b_return_first_variable_ok, b_return_variable_ok, b_return_signed_integer_ok, b_return_integer_ok }; S32 get_checker_number(char checker) { switch(checker) { case 'a': return 0; case 'c': return 1; case 'f': return 2; case 'v': return 3; case 's': return 4; case 'd': return 5; case '*': return 9999; default: return -1; } } // check token based on passed simplified regular expression BOOL b_check_token(char *token, char *regexp) { S32 tptr, rptr = 0; S32 current_checker, next_checker = 0; current_checker = get_checker_number(regexp[rptr++]); if (current_checker == -1) { llerrs << "Invalid regular expression value!" << llendl; return FALSE; } if (current_checker == 9999) { llerrs << "Regular expression can't start with *!" << llendl; return FALSE; } for (tptr = 0; token[tptr]; tptr++) { if (current_checker == -1) { llerrs << "Input exceeds regular expression!\nDid you forget a *?" << llendl; return FALSE; } if (!gParseCheckCharacters[current_checker](token[tptr])) { return FALSE; } if (next_checker != 9999) { next_checker = get_checker_number(regexp[rptr++]); if (next_checker != 9999) { current_checker = next_checker; } } } return TRUE; } // C variable can be made up of upper or lower case letters, underscores, or numbers, but can't start with a number BOOL b_variable_ok(char *token) { if (!b_check_token(token, "fv*")) { llerrs << "Token '" << token << "' isn't a variable!" << llendl; return FALSE; } return TRUE; } // An integer is made up of the digits 0-9 and may be preceded by a '-' BOOL b_integer_ok(char *token) { if (!b_check_token(token, "sd*")) { llerrs << "Token isn't an integer!" << llendl; return FALSE; } return TRUE; } // An integer is made up of the digits 0-9 BOOL b_positive_integer_ok(char *token) { if (!b_check_token(token, "d*")) { llerrs << "Token isn't an integer!" << llendl; return FALSE; } return TRUE; } void LLMessageSystem::init() { // initialize member variables mVerboseLog = FALSE; mbError = FALSE; mErrorCode = 0; mIncomingCompressedSize = 0; mSendReliable = FALSE; mbSBuilt = FALSE; mbSClear = TRUE; mUnackedListDepth = 0; mUnackedListSize = 0; mDSMaxListDepth = 0; mCurrentRMessageData = NULL; mCurrentRMessageTemplate = NULL; mCurrentSMessageData = NULL; mCurrentSMessageTemplate = NULL; mCurrentSMessageName = NULL; mCurrentRecvPacketID = 0; mNumberHighFreqMessages = 0; mNumberMediumFreqMessages = 0; mNumberLowFreqMessages = 0; mPacketsIn = mPacketsOut = 0; mBytesIn = mBytesOut = 0; mCompressedPacketsIn = mCompressedPacketsOut = 0; mReliablePacketsIn = mReliablePacketsOut = 0; mCompressedBytesIn = 0; mCompressedBytesOut = 0; mUncompressedBytesIn = 0; mUncompressedBytesOut = 0; mTotalBytesIn = 0; mTotalBytesOut = 0; mDroppedPackets = 0; // total dropped packets in mResentPackets = 0; // total resent packets out mFailedResendPackets = 0; // total resend failure packets out mOffCircuitPackets = 0; // total # of off-circuit packets rejected mInvalidOnCircuitPackets = 0; // total # of on-circuit packets rejected mOurCircuitCode = 0; mMessageFileChecksum = 0; mMessageFileVersionNumber = 0.f; } LLMessageSystem::LLMessageSystem() { init(); mSystemVersionMajor = 0; mSystemVersionMinor = 0; mSystemVersionPatch = 0; mSystemVersionServer = 0; mVersionFlags = 0x0; // default to not accepting packets from not alive circuits mbProtected = TRUE; mSendPacketFailureCount = 0; mCircuitPrintFreq = 0.f; // seconds // initialize various bits of net info mSocket = 0; mPort = 0; mPollInfop = NULL; mResendDumpTime = 0; mMessageCountTime = 0; mCircuitPrintTime = 0; mCurrentMessageTimeSeconds = 0; // Constants for dumping output based on message processing time/count mNumMessageCounts = 0; mMaxMessageCounts = 0; // >= 0 means dump warnings mMaxMessageTime = 0.f; mTrueReceiveSize = 0; // Error if checking this state, subclass methods which aren't implemented are delegated // to properly constructed message system. mbError = TRUE; } // Read file and build message templates LLMessageSystem::LLMessageSystem(const char *filename, U32 port, S32 version_major, S32 version_minor, S32 version_patch) { init(); mSystemVersionMajor = version_major; mSystemVersionMinor = version_minor; mSystemVersionPatch = version_patch; mSystemVersionServer = 0; mVersionFlags = 0x0; // default to not accepting packets from not alive circuits mbProtected = TRUE; mSendPacketFailureCount = 0; mCircuitPrintFreq = 60.f; // seconds loadTemplateFile(filename); // initialize various bits of net info mSocket = 0; mPort = port; S32 error = start_net(mSocket, mPort); if (error != 0) { mbError = TRUE; mErrorCode = error; } //llinfos << << "*** port: " << mPort << llendl; // // Create the data structure that we can poll on // if (!gAPRPoolp) { llerrs << "No APR pool before message system initialization!" << llendl; ll_init_apr(); } apr_socket_t *aprSocketp = NULL; apr_os_sock_put(&aprSocketp, (apr_os_sock_t*)&mSocket, gAPRPoolp); mPollInfop = new LLMessagePollInfo; mPollInfop->mAPRSocketp = aprSocketp; mPollInfop->mPollFD.p = gAPRPoolp; mPollInfop->mPollFD.desc_type = APR_POLL_SOCKET; mPollInfop->mPollFD.reqevents = APR_POLLIN; mPollInfop->mPollFD.rtnevents = 0; mPollInfop->mPollFD.desc.s = aprSocketp; mPollInfop->mPollFD.client_data = NULL; F64 mt_sec = getMessageTimeSeconds(); mResendDumpTime = mt_sec; mMessageCountTime = mt_sec; mCircuitPrintTime = mt_sec; mCurrentMessageTimeSeconds = mt_sec; // Constants for dumping output based on message processing time/count mNumMessageCounts = 0; mMaxMessageCounts = 200; // >= 0 means dump warnings mMaxMessageTime = 1.f; mTrueReceiveSize = 0; } // Read file and build message templates void LLMessageSystem::loadTemplateFile(const char* filename) { if(!filename) { llerrs << "No template filename specified" << llendl; } char token[MAX_MESSAGE_INTERNAL_NAME_SIZE]; /* Flawfinder: ignore */ // state variables BOOL b_template_start = TRUE; BOOL b_template_end = FALSE; BOOL b_template = FALSE; BOOL b_block_start = FALSE; BOOL b_block_end = FALSE; BOOL b_block = FALSE; BOOL b_variable_start = FALSE; BOOL b_variable_end = FALSE; BOOL b_variable = FALSE; //BOOL b_in_comment_block = FALSE; // not yet used // working temp variables LLMessageTemplate *templatep = NULL; char template_name[MAX_MESSAGE_INTERNAL_NAME_SIZE]; /* Flawfinder: ignore */ LLMessageBlock *blockp = NULL; char block_name[MAX_MESSAGE_INTERNAL_NAME_SIZE]; /* Flawfinder: ignore */ LLMessageVariable var; char var_name[MAX_MESSAGE_INTERNAL_NAME_SIZE]; /* Flawfinder: ignore */ char formatString[MAX_MESSAGE_INTERNAL_NAME_SIZE]; FILE* messagefilep = NULL; mMessageFileChecksum = 0; mMessageFileVersionNumber = 0.f; S32 checksum_offset = 0; char* checkp = NULL; snprintf(formatString, sizeof(formatString), "%%%ds", MAX_MESSAGE_INTERNAL_NAME_SIZE); messagefilep = LLFile::fopen(filename, "r"); if (messagefilep) { // mName = gMessageStringTable.getString(filename); fseek(messagefilep, 0L, SEEK_SET ); while(fscanf(messagefilep, formatString, token) != EOF) { // skip comments if (token[0] == '/') { // skip to end of line while (token[0] != 10) fscanf(messagefilep, "%c", token); continue; } checkp = token; while (*checkp) { mMessageFileChecksum += ((U32)*checkp++) << checksum_offset; checksum_offset = (checksum_offset + 8) % 32; } // what are we looking for if (!strcmp(token, "{")) { // is that a legit option? if (b_template_start) { // yup! b_template_start = FALSE; // remember that it could be only a signal message, so name is all that it contains b_template_end = TRUE; // start working on it! b_template = TRUE; } else if (b_block_start) { // yup! b_block_start = FALSE; b_template_end = FALSE; // start working on it! b_block = TRUE; } else if (b_variable_start) { // yup! b_variable_start = FALSE; b_block_end = FALSE; // start working on it! b_variable = TRUE; } else { llerrs << "Detcted unexpected token '" << token << "' while parsing template." << llendl; mbError = TRUE; fclose(messagefilep); return; } } if (!strcmp(token, "}")) { // is that a legit option? if (b_template_end) { // yup! b_template_end = FALSE; b_template = FALSE; b_block_start = FALSE; // add data! // we've gotten a complete variable! hooray! // add it! addTemplate(templatep); //llinfos << "Read template: "templatep->mNametemp_str // << llendl; // look for next one! b_template_start = TRUE; } else if (b_block_end) { // yup! b_block_end = FALSE; b_variable_start = FALSE; // add data! // we've gotten a complete variable! hooray! // add it to template templatep->addBlock(blockp); // start working on it! b_template_end = TRUE; b_block_start = TRUE; } else if (b_variable_end) { // yup! b_variable_end = FALSE; // add data! // we've gotten a complete variable! hooray! // add it to block blockp->addVariable(var.getName(), var.getType(), var.getSize()); // start working on it! b_variable_start = TRUE; b_block_end = TRUE; } else { llerrs << "Detcted unexpected token '" << token << "' while parsing template." << llendl; mbError = TRUE; fclose(messagefilep); return; } } // now, are we looking to start a template? if (b_template) { b_template = FALSE; // name first if (fscanf(messagefilep, formatString, template_name) == EOF) { // oops, file ended llerrs << "Expected message template name, but file ended" << llendl; mbError = TRUE; fclose(messagefilep); return; } // debugging to help figure out busted templates //llinfos << template_name << llendl; // is name a legit C variable name if (!b_variable_ok(template_name)) { // nope! llerrs << "Not legal message template name: " << template_name << llendl; mbError = TRUE; fclose(messagefilep); return; } checkp = template_name; while (*checkp) { mMessageFileChecksum += ((U32)*checkp++) << checksum_offset; checksum_offset = (checksum_offset + 8) % 32; } // ok, now get Frequency ("High", "Medium", or "Low") if (fscanf(messagefilep, formatString, token) == EOF) { // oops, file ended llerrs << "Expected message template frequency, found EOF." << llendl; mbError = TRUE; fclose(messagefilep); return; } checkp = token; while (*checkp) { mMessageFileChecksum += ((U32)*checkp++) << checksum_offset; checksum_offset = (checksum_offset + 8) % 32; } // which one is it? if (!strcmp(token, "High")) { if (++mNumberHighFreqMessages == 255) { // oops, too many High Frequency messages!! llerrs << "Message " << template_name << " exceeded 254 High frequency messages!" << llendl; mbError = TRUE; fclose(messagefilep); return; } // ok, we can create a template! // message number is just mNumberHighFreqMessages templatep = new LLMessageTemplate(template_name, mNumberHighFreqMessages, MFT_HIGH); //lldebugs << "Template " << template_name << " # " // << std::hex << mNumberHighFreqMessages // << std::dec << " high" // << llendl; } else if (!strcmp(token, "Medium")) { if (++mNumberMediumFreqMessages == 255) { // oops, too many Medium Frequency messages!! llerrs << "Message " << template_name << " exceeded 254 Medium frequency messages!" << llendl; mbError = TRUE; fclose(messagefilep); return; } // ok, we can create a template! // message number is ((255 << 8) | mNumberMediumFreqMessages) templatep = new LLMessageTemplate(template_name, (255 << 8) | mNumberMediumFreqMessages, MFT_MEDIUM); //lldebugs << "Template " << template_name << " # " // << std::hex << mNumberMediumFreqMessages // << std::dec << " medium" // << llendl; } else if (!strcmp(token, "Low")) { if (++mNumberLowFreqMessages == 65535) { // oops, too many High Frequency messages!! llerrs << "Message " << template_name << " exceeded 65534 Low frequency messages!" << llendl; mbError = TRUE; fclose(messagefilep); return; } // ok, we can create a template! // message number is ((255 << 24) | (255 << 16) | mNumberLowFreqMessages) templatep = new LLMessageTemplate(template_name, (255 << 24) | (255 << 16) | mNumberLowFreqMessages, MFT_LOW); //lldebugs << "Template " << template_name << " # " // << std::hex << mNumberLowFreqMessages // << std::dec << " low" // << llendl; } else if (!strcmp(token, "Fixed")) { U32 message_num = 0; if (fscanf(messagefilep, formatString, token) == EOF) { // oops, file ended llerrs << "Expected message template number (fixed)," << " found EOF." << llendl; mbError = TRUE; fclose(messagefilep); return; } checkp = token; while (*checkp) { mMessageFileChecksum += ((U32)*checkp++) << checksum_offset; checksum_offset = (checksum_offset + 8) % 32; } message_num = strtoul(token,NULL,0); // ok, we can create a template! // message number is ((255 << 24) | (255 << 16) | mNumberLowFreqMessages) templatep = new LLMessageTemplate(template_name, message_num, MFT_LOW); } else { // oops, bad frequency line llerrs << "Bad frequency! " << token << " isn't High, Medium, or Low" << llendl mbError = TRUE; fclose(messagefilep); return; } // Now get trust ("Trusted", "NotTrusted") if (fscanf(messagefilep, formatString, token) == EOF) { // File ended llerrs << "Expected message template " "trust, but file ended." << llendl; mbError = TRUE; fclose(messagefilep); return; } checkp = token; while (*checkp) { mMessageFileChecksum += ((U32) *checkp++) << checksum_offset; checksum_offset = (checksum_offset + 8) % 32; } if (strcmp(token, "Trusted") == 0) { templatep->setTrust(MT_TRUST); } else if (strcmp(token, "NotTrusted") == 0) { templatep->setTrust(MT_NOTRUST); } else { // bad trust token llerrs << "bad trust: " << token << " isn't Trusted or NotTrusted" << llendl; mbError = TRUE; fclose(messagefilep); return; } // get encoding if (fscanf(messagefilep, formatString, token) == EOF) { // File ended llerrs << "Expected message encoding, but file ended." << llendl; mbError = TRUE; fclose(messagefilep); return; } checkp = token; while(*checkp) { mMessageFileChecksum += ((U32) *checkp++) << checksum_offset; checksum_offset = (checksum_offset + 8) % 32; } if(0 == strcmp(token, "Unencoded")) { templatep->setEncoding(ME_UNENCODED); } else if(0 == strcmp(token, "Zerocoded")) { templatep->setEncoding(ME_ZEROCODED); } else { // bad trust token llerrs << "bad encoding: " << token << " isn't Unencoded or Zerocoded" << llendl; mbError = TRUE; fclose(messagefilep); return; } // ok, now we need to look for a block b_block_start = TRUE; continue; } // now, are we looking to start a template? if (b_block) { b_block = FALSE; // ok, need to pull header info // name first if (fscanf(messagefilep, formatString, block_name) == EOF) { // oops, file ended llerrs << "Expected block name, but file ended" << llendl; mbError = TRUE; fclose(messagefilep); return; } checkp = block_name; while (*checkp) { mMessageFileChecksum += ((U32)*checkp++) << checksum_offset; checksum_offset = (checksum_offset + 8) % 32; } // is name a legit C variable name if (!b_variable_ok(block_name)) { // nope! llerrs << block_name << "is not a legal block name" << llendl; mbError = TRUE; fclose(messagefilep); return; } // now, block type ("Single", "Multiple", or "Variable") if (fscanf(messagefilep, formatString, token) == EOF) { // oops, file ended llerrs << "Expected block type, but file ended." << llendl; mbError = TRUE; fclose(messagefilep); return; } checkp = token; while (*checkp) { mMessageFileChecksum += ((U32)*checkp++) << checksum_offset; checksum_offset = (checksum_offset + 8) % 32; } // which one is it? if (!strcmp(token, "Single")) { // ok, we can create a block blockp = new LLMessageBlock(block_name, MBT_SINGLE); } else if (!strcmp(token, "Multiple")) { // need to get the number of repeats if (fscanf(messagefilep, formatString, token) == EOF) { // oops, file ended llerrs << "Expected block multiple count," " but file ended." << llendl; mbError = TRUE; fclose(messagefilep); return; } checkp = token; while (*checkp) { mMessageFileChecksum += ((U32)*checkp++) << checksum_offset; checksum_offset = (checksum_offset + 8) % 32; } // is it a legal integer if (!b_positive_integer_ok(token)) { // nope! llerrs << token << "is not a legal integer for" " block multiple count" << llendl; mbError = TRUE; fclose(messagefilep); return; } // ok, we can create a block blockp = new LLMessageBlock(block_name, MBT_MULTIPLE, atoi(token)); } else if (!strcmp(token, "Variable")) { // ok, we can create a block blockp = new LLMessageBlock(block_name, MBT_VARIABLE); } else { // oops, bad block type llerrs << "Bad block type! " << token << " isn't Single, Multiple, or Variable" << llendl; mbError = TRUE; fclose(messagefilep); return; } // ok, now we need to look for a variable b_variable_start = TRUE; continue; } // now, are we looking to start a template? if (b_variable) { b_variable = FALSE; // ok, need to pull header info // name first if (fscanf(messagefilep, formatString, var_name) == EOF) { // oops, file ended llerrs << "Expected variable name, but file ended." << llendl; mbError = TRUE; fclose(messagefilep); return; } checkp = var_name; while (*checkp) { mMessageFileChecksum += ((U32)*checkp++) << checksum_offset; checksum_offset = (checksum_offset + 8) % 32; } // is name a legit C variable name if (!b_variable_ok(var_name)) { // nope! llerrs << var_name << " is not a legal variable name" << llendl; mbError = TRUE; fclose(messagefilep); return; } // now, variable type ("Fixed" or "Variable") if (fscanf(messagefilep, formatString, token) == EOF) { // oops, file ended llerrs << "Expected variable type, but file ended" << llendl; mbError = TRUE; fclose(messagefilep); return; } checkp = token; while (*checkp) { mMessageFileChecksum += ((U32)*checkp++) << checksum_offset; checksum_offset = (checksum_offset + 8) % 32; } // which one is it? if (!strcmp(token, "U8")) { var = LLMessageVariable(var_name, MVT_U8, 1); } else if (!strcmp(token, "U16")) { var = LLMessageVariable(var_name, MVT_U16, 2); } else if (!strcmp(token, "U32")) { var = LLMessageVariable(var_name, MVT_U32, 4); } else if (!strcmp(token, "U64")) { var = LLMessageVariable(var_name, MVT_U64, 8); } else if (!strcmp(token, "S8")) { var = LLMessageVariable(var_name, MVT_S8, 1); } else if (!strcmp(token, "S16")) { var = LLMessageVariable(var_name, MVT_S16, 2); } else if (!strcmp(token, "S32")) { var = LLMessageVariable(var_name, MVT_S32, 4); } else if (!strcmp(token, "S64")) { var = LLMessageVariable(var_name, MVT_S64, 8); } else if (!strcmp(token, "F32")) { var = LLMessageVariable(var_name, MVT_F32, 4); } else if (!strcmp(token, "F64")) { var = LLMessageVariable(var_name, MVT_F64, 8); } else if (!strcmp(token, "LLVector3")) { var = LLMessageVariable(var_name, MVT_LLVector3, 12); } else if (!strcmp(token, "LLVector3d")) { var = LLMessageVariable(var_name, MVT_LLVector3d, 24); } else if (!strcmp(token, "LLVector4")) { var = LLMessageVariable(var_name, MVT_LLVector4, 16); } else if (!strcmp(token, "LLQuaternion")) { var = LLMessageVariable(var_name, MVT_LLQuaternion, 12); } else if (!strcmp(token, "LLUUID")) { var = LLMessageVariable(var_name, MVT_LLUUID, 16); } else if (!strcmp(token, "BOOL")) { var = LLMessageVariable(var_name, MVT_BOOL, 1); } else if (!strcmp(token, "IPADDR")) { var = LLMessageVariable(var_name, MVT_IP_ADDR, 4); } else if (!strcmp(token, "IPPORT")) { var = LLMessageVariable(var_name, MVT_IP_PORT, 2); } else if (!strcmp(token, "Fixed")) { // need to get the variable size if (fscanf(messagefilep, formatString, token) == EOF) { // oops, file ended llerrs << "Expected variable size, but file ended" << llendl; mbError = TRUE; fclose(messagefilep); return; } checkp = token; while (*checkp) { mMessageFileChecksum += ((U32)*checkp++) << checksum_offset; checksum_offset = (checksum_offset + 8) % 32; } // is it a legal integer if (!b_positive_integer_ok(token)) { // nope! llerrs << token << " is not a legal integer for" " variable size" << llendl; mbError = TRUE; fclose(messagefilep); return; } // ok, we can create a block var = LLMessageVariable(var_name, MVT_FIXED, atoi(token)); } else if (!strcmp(token, "Variable")) { // need to get the variable size if (fscanf(messagefilep, formatString, token) == EOF) { // oops, file ended llerrs << "Expected variable size, but file ended" << llendl; mbError = TRUE; fclose(messagefilep); return; } checkp = token; while (*checkp) { mMessageFileChecksum += ((U32)*checkp++) << checksum_offset; checksum_offset = (checksum_offset + 8) % 32; } // is it a legal integer if (!b_positive_integer_ok(token)) { // nope! llerrs << token << "is not a legal integer" " for variable size" << llendl; mbError = TRUE; fclose(messagefilep); return; } // ok, we can create a block var = LLMessageVariable(var_name, MVT_VARIABLE, atoi(token)); } else { // oops, bad variable type llerrs << "Bad variable type! " << token << " isn't Fixed or Variable" << llendl; mbError = TRUE; fclose(messagefilep); return; } // we got us a variable! b_variable_end = TRUE; continue; } // do we have a version number stuck in the file? if (!strcmp(token, "version")) { // version number if (fscanf(messagefilep, formatString, token) == EOF) { // oops, file ended llerrs << "Expected version number, but file ended" << llendl; mbError = TRUE; fclose(messagefilep); return; } checkp = token; while (*checkp) { mMessageFileChecksum += ((U32)*checkp++) << checksum_offset; checksum_offset = (checksum_offset + 8) % 32; } mMessageFileVersionNumber = (F32)atof(token); // llinfos << "### Message template version " << mMessageFileVersionNumber << " ###" << llendl; continue; } } llinfos << "Message template checksum = " << std::hex << mMessageFileChecksum << std::dec << llendl; } else { llwarns << "Failed to open template: " << filename << llendl; mbError = TRUE; return; } fclose(messagefilep); } LLMessageSystem::~LLMessageSystem() { mMessageTemplates.clear(); // don't delete templates. for_each(mMessageNumbers.begin(), mMessageNumbers.end(), DeletePairedPointer()); mMessageNumbers.clear(); if (!mbError) { end_net(); } delete mCurrentRMessageData; mCurrentRMessageData = NULL; delete mCurrentSMessageData; mCurrentSMessageData = NULL; delete mPollInfop; mPollInfop = NULL; } void LLMessageSystem::clearReceiveState() { mReceiveSize = -1; mCurrentRecvPacketID = 0; mCurrentRMessageTemplate = NULL; delete mCurrentRMessageData; mCurrentRMessageData = NULL; mIncomingCompressedSize = 0; mLastSender.invalidate(); } BOOL LLMessageSystem::poll(F32 seconds) { S32 num_socks; apr_status_t status; status = apr_poll(&(mPollInfop->mPollFD), 1, &num_socks,(U64)(seconds*1000000.f)); if (status != APR_TIMEUP) { ll_apr_warn_status(status); } if (num_socks) { return TRUE; } else { return FALSE; } } // Returns TRUE if a valid, on-circuit message has been received. BOOL LLMessageSystem::checkMessages( S64 frame_count ) { BOOL valid_packet = FALSE; LLTransferTargetVFile::updateQueue(); if (!mNumMessageCounts) { // This is the first message being handled after a resetReceiveCounts, we must be starting // the message processing loop. Reset the timers. mCurrentMessageTimeSeconds = totalTime() * SEC_PER_USEC; mMessageCountTime = getMessageTimeSeconds(); } // loop until either no packets or a valid packet // i.e., burn through packets from unregistered circuits do { clearReceiveState(); BOOL recv_reliable = FALSE; BOOL recv_resent = FALSE; S32 acks = 0; S32 true_rcv_size = 0; U8* buffer = mTrueReceiveBuffer; mTrueReceiveSize = mPacketRing.receivePacket(mSocket, (char *)mTrueReceiveBuffer); // If you want to dump all received packets into SecondLife.log, uncomment this //dumpPacketToLog(); mReceiveSize = mTrueReceiveSize; mLastSender = mPacketRing.getLastSender(); if (mReceiveSize < (S32) LL_MINIMUM_VALID_PACKET_SIZE) { // A receive size of zero is OK, that means that there are no more packets available. // Ones that are non-zero but below the minimum packet size are worrisome. if (mReceiveSize > 0) { llwarns << "Invalid (too short) packet discarded " << mReceiveSize << llendl; callExceptionFunc(MX_PACKET_TOO_SHORT); } // no data in packet receive buffer valid_packet = FALSE; } else { LLHost host; LLCircuitData *cdp; // note if packet acks are appended. if(buffer[0] & LL_ACK_FLAG) { acks += buffer[--mReceiveSize]; true_rcv_size = mReceiveSize; mReceiveSize -= acks * sizeof(TPACKETID); } // process the message as normal mIncomingCompressedSize = zeroCodeExpand(&buffer,&mReceiveSize); mCurrentRecvPacketID = buffer[1] + ((buffer[0] & 0x0f ) * 256); if (sizeof(TPACKETID) == 4) { mCurrentRecvPacketID *= 256; mCurrentRecvPacketID += buffer[2]; mCurrentRecvPacketID *= 256; mCurrentRecvPacketID += buffer[3]; } host = getSender(); //llinfos << host << ":" << mCurrentRecvPacketID << llendl; // For testing the weird case we're having in the office where the first few packets // on a connection get dropped //if ((mCurrentRecvPacketID < 8) && !(buffer[0] & LL_RESENT_FLAG)) //{ // llinfos << "Evil! Dropping " << mCurrentRecvPacketID << " from " << host << " for fun!" << llendl; // continue; //} cdp = mCircuitInfo.findCircuit(host); if (!cdp) { // This packet comes from a circuit we don't know about. // Are we rejecting off-circuit packets? if (mbProtected) { // cdp is already NULL, so we don't need to unset it. } else { // nope, open the new circuit cdp = mCircuitInfo.addCircuitData(host, mCurrentRecvPacketID); // I added this - I think it's correct - DJS // reset packet in ID cdp->setPacketInID(mCurrentRecvPacketID); // And claim the packet is on the circuit we just added. } } else { // this is an old circuit. . . is it still alive? if (!cdp->isAlive()) { // nope. don't accept if we're protected if (mbProtected) { // don't accept packets from unexpected sources cdp = NULL; } else { // wake up the circuit cdp->setAlive(TRUE); // reset packet in ID cdp->setPacketInID(mCurrentRecvPacketID); } } } // At this point, cdp is now a pointer to the circuit that // this message came in on if it's valid, and NULL if the // circuit was bogus. if(cdp && (acks > 0) && ((S32)(acks * sizeof(TPACKETID)) < (true_rcv_size))) { TPACKETID packet_id; U32 mem_id=0; for(S32 i = 0; i < acks; ++i) { true_rcv_size -= sizeof(TPACKETID); memcpy(&mem_id, &mTrueReceiveBuffer[true_rcv_size], /* Flawfinder: ignore*/ sizeof(TPACKETID)); packet_id = ntohl(mem_id); //llinfos << "got ack: " << packet_id << llendl; cdp->ackReliablePacket(packet_id); } if (!cdp->getUnackedPacketCount()) { // Remove this circuit from the list of circuits with unacked packets mCircuitInfo.mUnackedCircuitMap.erase(cdp->mHost); } } if (buffer[0] & LL_RELIABLE_FLAG) { recv_reliable = TRUE; } if (buffer[0] & LL_RESENT_FLAG) { recv_resent = TRUE; if (cdp && cdp->isDuplicateResend(mCurrentRecvPacketID)) { // We need to ACK here to suppress // further resends of packets we've // already seen. if (recv_reliable) { //mAckList.addData(new LLPacketAck(host, mCurrentRecvPacketID)); // *************************************** // TESTING CODE //if(mCircuitInfo.mCurrentCircuit->mHost != host) //{ // llwarns << "DISCARDED PACKET HOST MISMATCH! HOST: " // << host << " CIRCUIT: " // << mCircuitInfo.mCurrentCircuit->mHost // << llendl; //} // *************************************** //mCircuitInfo.mCurrentCircuit->mAcks.put(mCurrentRecvPacketID); cdp->collectRAck(mCurrentRecvPacketID); } //llinfos << "Discarding duplicate resend from " << host << llendl; if(mVerboseLog) { std::ostringstream str; str << "MSG: <- " << host; char buffer[MAX_STRING]; /* Flawfinder: ignore*/ snprintf(buffer, MAX_STRING, "\t%6d\t%6d\t%6d ", mReceiveSize, (mIncomingCompressedSize ? mIncomingCompressedSize : mReceiveSize), mCurrentRecvPacketID);/* Flawfinder: ignore*/ str << buffer << "(unknown)" << (recv_reliable ? " reliable" : "") << " resent " << ((acks > 0) ? "acks" : "") << " DISCARD DUPLICATE"; llinfos << str.str() << llendl; } mPacketsIn++; valid_packet = FALSE; continue; } } // UseCircuitCode can be a valid, off-circuit packet. // But we don't want to acknowledge UseCircuitCode until the circuit is // available, which is why the acknowledgement test is done above. JC valid_packet = decodeTemplate( buffer, mReceiveSize, &mCurrentRMessageTemplate ); if( valid_packet ) { mCurrentRMessageTemplate->mReceiveCount++; lldebugst(LLERR_MESSAGE) << "MessageRecvd:" << mCurrentRMessageTemplate->mName << " from " << host << llendl; } // UseCircuitCode is allowed in even from an invalid circuit, so that // we can toss circuits around. if (valid_packet && !cdp && (mCurrentRMessageTemplate->mName != _PREHASH_UseCircuitCode) ) { logMsgFromInvalidCircuit( host, recv_reliable ); clearReceiveState(); valid_packet = FALSE; } if (valid_packet && cdp && !cdp->getTrusted() && (mCurrentRMessageTemplate->getTrust() == MT_TRUST) ) { logTrustedMsgFromUntrustedCircuit( host ); clearReceiveState(); sendDenyTrustedCircuit(host); valid_packet = FALSE; } if (valid_packet && mCurrentRMessageTemplate->isBanned(cdp && cdp->getTrusted())) { llwarns << "LLMessageSystem::checkMessages " << "received banned message " << mCurrentRMessageTemplate->mName << " from " << ((cdp && cdp->getTrusted()) ? "trusted " : "untrusted ") << host << llendl; clearReceiveState(); valid_packet = FALSE; } if( valid_packet ) { logValidMsg(cdp, host, recv_reliable, recv_resent, (BOOL)(acks>0) ); valid_packet = decodeData( buffer, host ); } // It's possible that the circuit went away, because ANY message can disable the circuit // (for example, UseCircuit, CloseCircuit, DisableSimulator). Find it again. cdp = mCircuitInfo.findCircuit(host); if (valid_packet) { /* Code for dumping the complete contents of a message. Keep for future use in optimizing messages. if( 1 ) { static char* object_update = gMessageStringTable.getString("ObjectUpdate"); if(object_update == mCurrentRMessageTemplate->mName ) { llinfos << "ObjectUpdate:" << llendl; U32 i; llinfos << " Zero Encoded: " << zero_unexpanded_size << llendl; for( i = 0; imMemberBlocks.begin(), end = mCurrentRMessageTemplate->mMemberBlocks.end(); iter != end; iter++) { LLMessageBlock* block = iter->second; const char* block_name = block->mName; for (LLMsgBlkData::msg_var_data_map_t::iterator iter = block->mMemberVariables.begin(), end = block->mMemberVariables.end(); iter != end; iter++) { const char* var_name = iter->first; if( getNumberOfBlocksFast( block_name ) < 1 ) { llinfos << var_name << " has no blocks" << llendl; } for( S32 blocknum = 0; blocknum < getNumberOfBlocksFast( block_name ); blocknum++ ) { char *bnamep = (char *)block_name + blocknum; // this works because it's just a hash. The bnamep is never derefference char *vnamep = (char *)var_name; LLMsgBlkData *msg_block_data = mCurrentRMessageData->mMemberBlocks[bnamep]; char errmsg[1024]; if (!msg_block_data) { sprintf(errmsg, "Block %s #%d not in message %s", block_name, blocknum, mCurrentRMessageData->mName); llerrs << errmsg << llendl; } LLMsgVarData vardata = msg_block_data->mMemberVarData[vnamep]; if (!vardata.getName()) { sprintf(errmsg, "Variable %s not in message %s block %s", vnamep, mCurrentRMessageData->mName, bnamep); llerrs << errmsg << llendl; } const S32 vardata_size = vardata.getSize(); if( vardata_size ) { for( i = 0; i < vardata_size; i++ ) { byte_count++; llinfos << block_name << " " << var_name << " [" << blocknum << "][" << i << "]= " << (U32)(((U8*)vardata.getData())[i]) << llendl; } } else { llinfos << block_name << " " << var_name << " [" << blocknum << "] 0 bytes" << llendl; } } } } llinfos << "Byte count =" << byte_count << llendl; } } */ mPacketsIn++; mBytesIn += mTrueReceiveSize; // ACK here for valid packets that we've seen // for the first time. if (cdp && recv_reliable) { // Add to the recently received list for duplicate suppression cdp->mRecentlyReceivedReliablePackets[mCurrentRecvPacketID] = getMessageTimeUsecs(); // Put it onto the list of packets to be acked cdp->collectRAck(mCurrentRecvPacketID); mReliablePacketsIn++; } } else { if (mbProtected && (!cdp)) { llwarns << "Packet " << (mCurrentRMessageTemplate ? mCurrentRMessageTemplate->mName : "") << " from invalid circuit " << host << llendl; mOffCircuitPackets++; } else { mInvalidOnCircuitPackets++; } } // Code for dumping the complete contents of a message // delete [] zero_unexpanded_buffer; } } while (!valid_packet && mReceiveSize > 0); F64 mt_sec = getMessageTimeSeconds(); // Check to see if we need to print debug info if ((mt_sec - mCircuitPrintTime) > mCircuitPrintFreq) { dumpCircuitInfo(); mCircuitPrintTime = mt_sec; } if( !valid_packet ) { clearReceiveState(); } return valid_packet; } S32 LLMessageSystem::getReceiveBytes() const { if (getReceiveCompressedSize()) { return getReceiveCompressedSize() * 8; } else { return getReceiveSize() * 8; } } void LLMessageSystem::processAcks() { F64 mt_sec = getMessageTimeSeconds(); { gTransferManager.updateTransfers(); if (gXferManager) { gXferManager->retransmitUnackedPackets(); } if (gAssetStorage) { gAssetStorage->checkForTimeouts(); } } BOOL dump = FALSE; { // Check the status of circuits mCircuitInfo.updateWatchDogTimers(this); //resend any necessary packets mCircuitInfo.resendUnackedPackets(mUnackedListDepth, mUnackedListSize); //cycle through ack list for each host we need to send acks to mCircuitInfo.sendAcks(); if (!mDenyTrustedCircuitSet.empty()) { llinfos << "Sending queued DenyTrustedCircuit messages." << llendl; for (host_set_t::iterator hostit = mDenyTrustedCircuitSet.begin(); hostit != mDenyTrustedCircuitSet.end(); ++hostit) { reallySendDenyTrustedCircuit(*hostit); } mDenyTrustedCircuitSet.clear(); } if (mMaxMessageCounts >= 0) { if (mNumMessageCounts >= mMaxMessageCounts) { dump = TRUE; } } if (mMaxMessageTime >= 0.f) { // This is one of the only places where we're required to get REAL message system time. mReceiveTime = (F32)(getMessageTimeSeconds(TRUE) - mMessageCountTime); if (mReceiveTime > mMaxMessageTime) { dump = TRUE; } } } if (dump) { dumpReceiveCounts(); } resetReceiveCounts(); if ((mt_sec - mResendDumpTime) > CIRCUIT_DUMP_TIMEOUT) { mResendDumpTime = mt_sec; mCircuitInfo.dumpResends(); } } void LLMessageSystem::newMessageFast(const char *name) { mbSBuilt = FALSE; mbSClear = FALSE; mCurrentSendTotal = 0; mSendReliable = FALSE; char *namep = (char *)name; if (mMessageTemplates.count(namep) > 0) { mCurrentSMessageTemplate = mMessageTemplates[namep]; if (mCurrentSMessageData) { delete mCurrentSMessageData; } mCurrentSMessageData = new LLMsgData(namep); mCurrentSMessageName = namep; mCurrentSDataBlock = NULL; mCurrentSBlockName = NULL; // add at one of each block LLMessageTemplate* msg_template = mMessageTemplates[namep]; for (LLMessageTemplate::message_block_map_t::iterator iter = msg_template->mMemberBlocks.begin(); iter != msg_template->mMemberBlocks.end(); iter++) { LLMessageBlock* ci = iter->second; LLMsgBlkData *tblockp; tblockp = new LLMsgBlkData(ci->mName, 0); mCurrentSMessageData->addBlock(tblockp); } } else { llerrs << "newMessage - Message " << name << " not registered" << llendl; } } void LLMessageSystem::copyMessageRtoS() { if (!mCurrentRMessageTemplate) { return; } newMessageFast(mCurrentRMessageTemplate->mName); // copy the blocks // counting variables used to encode multiple block info S32 block_count = 0; char *block_name = NULL; // loop through msg blocks to loop through variables, totalling up size data and filling the new (send) message LLMsgData::msg_blk_data_map_t::iterator iter = mCurrentRMessageData->mMemberBlocks.begin(); LLMsgData::msg_blk_data_map_t::iterator end = mCurrentRMessageData->mMemberBlocks.end(); for(; iter != end; ++iter) { LLMsgBlkData* mbci = iter->second; if(!mbci) continue; // do we need to encode a block code? if (block_count == 0) { block_count = mbci->mBlockNumber; block_name = (char *)mbci->mName; } // counting down mutliple blocks block_count--; nextBlockFast(block_name); // now loop through the variables LLMsgBlkData::msg_var_data_map_t::iterator dit = mbci->mMemberVarData.begin(); LLMsgBlkData::msg_var_data_map_t::iterator dend = mbci->mMemberVarData.end(); for(; dit != dend; ++dit) { LLMsgVarData& mvci = *dit; addDataFast(mvci.getName(), mvci.getData(), mvci.getType(), mvci.getSize()); } } } void LLMessageSystem::clearMessage() { mbSBuilt = FALSE; mbSClear = TRUE; mCurrentSendTotal = 0; mSendReliable = FALSE; mCurrentSMessageTemplate = NULL; delete mCurrentSMessageData; mCurrentSMessageData = NULL; mCurrentSMessageName = NULL; mCurrentSDataBlock = NULL; mCurrentSBlockName = NULL; } // set block to add data to within current message void LLMessageSystem::nextBlockFast(const char *blockname) { char *bnamep = (char *)blockname; if (!mCurrentSMessageTemplate) { llerrs << "newMessage not called prior to setBlock" << llendl; return; } // now, does this block exist? LLMessageTemplate::message_block_map_t::iterator temp_iter = mCurrentSMessageTemplate->mMemberBlocks.find(bnamep); if (temp_iter == mCurrentSMessageTemplate->mMemberBlocks.end()) { llerrs << "LLMessageSystem::nextBlockFast " << bnamep << " not a block in " << mCurrentSMessageTemplate->mName << llendl; return; } LLMessageBlock* template_data = temp_iter->second; // ok, have we already set this block? LLMsgBlkData* block_data = mCurrentSMessageData->mMemberBlocks[bnamep]; if (block_data->mBlockNumber == 0) { // nope! set this as the current block block_data->mBlockNumber = 1; mCurrentSDataBlock = block_data; mCurrentSBlockName = bnamep; // add placeholders for each of the variables for (LLMessageBlock::message_variable_map_t::iterator iter = template_data->mMemberVariables.begin(); iter != template_data->mMemberVariables.end(); iter++) { LLMessageVariable& ci = *(iter->second); mCurrentSDataBlock->addVariable(ci.getName(), ci.getType()); } return; } else { // already have this block. . . // are we supposed to have a new one? // if the block is type MBT_SINGLE this is bad! if (template_data->mType == MBT_SINGLE) { llerrs << "LLMessageSystem::nextBlockFast called multiple times" << " for " << bnamep << " but is type MBT_SINGLE" << llendl; return; } // if the block is type MBT_MULTIPLE then we need a known number, make sure that we're not exceeding it if ( (template_data->mType == MBT_MULTIPLE) &&(mCurrentSDataBlock->mBlockNumber == template_data->mNumber)) { llerrs << "LLMessageSystem::nextBlockFast called " << mCurrentSDataBlock->mBlockNumber << " times for " << bnamep << " exceeding " << template_data->mNumber << " specified in type MBT_MULTIPLE." << llendl; return; } // ok, we can make a new one // modify the name to avoid name collision by adding number to end S32 count = block_data->mBlockNumber; // incrememt base name's count block_data->mBlockNumber++; if (block_data->mBlockNumber > MAX_BLOCKS) { llerrs << "Trying to pack too many blocks into MBT_VARIABLE type (limited to " << MAX_BLOCKS << ")" << llendl; } // create new name // Nota Bene: if things are working correctly, mCurrentMessageData->mMemberBlocks[blockname]->mBlockNumber == mCurrentDataBlock->mBlockNumber + 1 char *nbnamep = bnamep + count; mCurrentSDataBlock = new LLMsgBlkData(bnamep, count); mCurrentSDataBlock->mName = nbnamep; mCurrentSMessageData->mMemberBlocks[nbnamep] = mCurrentSDataBlock; // add placeholders for each of the variables for (LLMessageBlock::message_variable_map_t::iterator iter = template_data->mMemberVariables.begin(), end = template_data->mMemberVariables.end(); iter != end; iter++) { LLMessageVariable& ci = *(iter->second); mCurrentSDataBlock->addVariable(ci.getName(), ci.getType()); } return; } } // add data to variable in current block void LLMessageSystem::addDataFast(const char *varname, const void *data, EMsgVariableType type, S32 size) { char *vnamep = (char *)varname; // do we have a current message? if (!mCurrentSMessageTemplate) { llerrs << "newMessage not called prior to addData" << llendl; return; } // do we have a current block? if (!mCurrentSDataBlock) { llerrs << "setBlock not called prior to addData" << llendl; return; } // kewl, add the data if it exists LLMessageVariable* var_data = mCurrentSMessageTemplate->mMemberBlocks[mCurrentSBlockName]->mMemberVariables[vnamep]; if (!var_data || !var_data->getName()) { llerrs << vnamep << " not a variable in block " << mCurrentSBlockName << " of " << mCurrentSMessageTemplate->mName << llendl; return; } // ok, it seems ok. . . are we the correct size? if (var_data->getType() == MVT_VARIABLE) { // Variable 1 can only store 255 bytes, make sure our data is smaller if ((var_data->getSize() == 1) && (size > 255)) { llwarns << "Field " << varname << " is a Variable 1 but program " << "attempted to stuff more than 255 bytes in " << "(" << size << "). Clamping size and truncating data." << llendl; size = 255; char *truncate = (char *)data; truncate[255] = 0; } // no correct size for MVT_VARIABLE, instead we need to tell how many bytes the size will be encoded as mCurrentSDataBlock->addData(vnamep, data, size, type, var_data->getSize()); mCurrentSendTotal += size; } else { if (size != var_data->getSize()) { llerrs << varname << " is type MVT_FIXED but request size " << size << " doesn't match template size " << var_data->getSize() << llendl; return; } // alright, smash it in mCurrentSDataBlock->addData(vnamep, data, size, type); mCurrentSendTotal += size; } } // add data to variable in current block - fails if variable isn't MVT_FIXED void LLMessageSystem::addDataFast(const char *varname, const void *data, EMsgVariableType type) { char *vnamep = (char *)varname; // do we have a current message? if (!mCurrentSMessageTemplate) { llerrs << "newMessage not called prior to addData" << llendl; return; } // do we have a current block? if (!mCurrentSDataBlock) { llerrs << "setBlock not called prior to addData" << llendl; return; } // kewl, add the data if it exists LLMessageVariable* var_data = mCurrentSMessageTemplate->mMemberBlocks[mCurrentSBlockName]->mMemberVariables[vnamep]; if (!var_data->getName()) { llerrs << vnamep << " not a variable in block " << mCurrentSBlockName << " of " << mCurrentSMessageTemplate->mName << llendl; return; } // ok, it seems ok. . . are we MVT_VARIABLE? if (var_data->getType() == MVT_VARIABLE) { // nope llerrs << vnamep << " is type MVT_VARIABLE. Call using addData(name, data, size)" << llendl; return; } else { mCurrentSDataBlock->addData(vnamep, data, var_data->getSize(), type); mCurrentSendTotal += var_data->getSize(); } } BOOL LLMessageSystem::isSendFull(const char* blockname) { if(!blockname) { return (mCurrentSendTotal > MTUBYTES); } return isSendFullFast(gMessageStringTable.getString(blockname)); } BOOL LLMessageSystem::isSendFullFast(const char* blockname) { if(mCurrentSendTotal > MTUBYTES) { return TRUE; } if(!blockname) { return FALSE; } char* bnamep = (char*)blockname; S32 max; LLMessageBlock* template_data = mCurrentSMessageTemplate->mMemberBlocks[bnamep]; switch(template_data->mType) { case MBT_SINGLE: max = 1; break; case MBT_MULTIPLE: max = template_data->mNumber; break; case MBT_VARIABLE: default: max = MAX_BLOCKS; break; } if(mCurrentSMessageData->mMemberBlocks[bnamep]->mBlockNumber >= max) { return TRUE; } return FALSE; } // blow away the last block of a message, return FALSE if that leaves no blocks or there wasn't a block to remove BOOL LLMessageSystem::removeLastBlock() { if (mCurrentSBlockName) { if ( (mCurrentSMessageData) &&(mCurrentSMessageTemplate)) { if (mCurrentSMessageData->mMemberBlocks[mCurrentSBlockName]->mBlockNumber >= 1) { // At least one block for the current block name. // Store the current block name for future reference. char *block_name = mCurrentSBlockName; // Decrement the sent total by the size of the // data in the message block that we're currently building. LLMessageBlock* template_data = mCurrentSMessageTemplate->mMemberBlocks[mCurrentSBlockName]; for (LLMessageBlock::message_variable_map_t::iterator iter = template_data->mMemberVariables.begin(); iter != template_data->mMemberVariables.end(); iter++) { LLMessageVariable& ci = *(iter->second); mCurrentSendTotal -= ci.getSize(); } // Now we want to find the block that we're blowing away. // Get the number of blocks. LLMsgBlkData* block_data = mCurrentSMessageData->mMemberBlocks[block_name]; S32 num_blocks = block_data->mBlockNumber; // Use the same (suspect?) algorithm that's used to generate // the names in the nextBlock method to find it. char *block_getting_whacked = block_name + num_blocks - 1; LLMsgBlkData* whacked_data = mCurrentSMessageData->mMemberBlocks[block_getting_whacked]; delete whacked_data; mCurrentSMessageData->mMemberBlocks.erase(block_getting_whacked); if (num_blocks <= 1) { // we just blew away the last one, so return FALSE return FALSE; } else { // Decrement the counter. block_data->mBlockNumber--; return TRUE; } } } } return FALSE; } // make sure that all the desired data is in place and then copy the data into mSendBuffer void LLMessageSystem::buildMessage() { // basic algorithm is to loop through the various pieces, building // size and offset info if we encounter a -1 for mSize at any // point that variable wasn't given data // do we have a current message? if (!mCurrentSMessageTemplate) { llerrs << "newMessage not called prior to buildMessage" << llendl; return; } // zero out some useful values // leave room for circuit counter mSendSize = LL_PACKET_ID_SIZE; // encode message number and adjust total_offset if (mCurrentSMessageTemplate->mFrequency == MFT_HIGH) { // old, endian-dependant way // memcpy(&mSendBuffer[mSendSize], &mCurrentMessageTemplate->mMessageNumber, sizeof(U8)); // new, independant way mSendBuffer[mSendSize] = (U8)mCurrentSMessageTemplate->mMessageNumber; mSendSize += sizeof(U8); } else if (mCurrentSMessageTemplate->mFrequency == MFT_MEDIUM) { U8 temp = 255; memcpy(&mSendBuffer[mSendSize], &temp, sizeof(U8)); /*Flawfinder: ignore*/ mSendSize += sizeof(U8); // mask off unsightly bits temp = mCurrentSMessageTemplate->mMessageNumber & 255; memcpy(&mSendBuffer[mSendSize], &temp, sizeof(U8)); /*Flawfinder: ignore*/ mSendSize += sizeof(U8); } else if (mCurrentSMessageTemplate->mFrequency == MFT_LOW) { U8 temp = 255; U16 message_num; memcpy(&mSendBuffer[mSendSize], &temp, sizeof(U8)); /*Flawfinder: ignore*/ mSendSize += sizeof(U8); memcpy(&mSendBuffer[mSendSize], &temp, sizeof(U8)); /*Flawfinder: ignore*/ mSendSize += sizeof(U8); // mask off unsightly bits message_num = mCurrentSMessageTemplate->mMessageNumber & 0xFFFF; // convert to network byte order message_num = htons(message_num); memcpy(&mSendBuffer[mSendSize], &message_num, sizeof(U16)); /*Flawfinder: ignore*/ mSendSize += sizeof(U16); } else { llerrs << "unexpected message frequency in buildMessage" << llendl; return; } // counting variables used to encode multiple block info S32 block_count = 0; U8 temp_block_number; // loop through msg blocks to loop through variables, totalling up size data and copying into mSendBuffer for (LLMsgData::msg_blk_data_map_t::iterator iter = mCurrentSMessageData->mMemberBlocks.begin(), end = mCurrentSMessageData->mMemberBlocks.end(); iter != end; iter++) { LLMsgBlkData* mbci = iter->second; // do we need to encode a block code? if (block_count == 0) { block_count = mbci->mBlockNumber; LLMessageBlock* template_data = mCurrentSMessageTemplate->mMemberBlocks[mbci->mName]; // ok, if this is the first block of a repeating pack, set block_count and, if it's type MBT_VARIABLE encode a byte for how many there are if (template_data->mType == MBT_VARIABLE) { // remember that mBlockNumber is a S32 temp_block_number = (U8)mbci->mBlockNumber; if ((S32)(mSendSize + sizeof(U8)) < MAX_BUFFER_SIZE) { memcpy(&mSendBuffer[mSendSize], &temp_block_number, sizeof(U8)); mSendSize += sizeof(U8); } else { // Just reporting error is likely not enough. Need // to check how to abort or error out gracefully // from this function. XXXTBD llerrs << "buildMessage failed. Message excedding" " sendBuffersize." << llendl; } } else if (template_data->mType == MBT_MULTIPLE) { if (block_count != template_data->mNumber) { // nope! need to fill it in all the way! llerrs << "Block " << mbci->mName << " is type MBT_MULTIPLE but only has data for " << block_count << " out of its " << template_data->mNumber << " blocks" << llendl; } } } // counting down multiple blocks block_count--; // now loop through the variables for (LLMsgBlkData::msg_var_data_map_t::iterator iter = mbci->mMemberVarData.begin(); iter != mbci->mMemberVarData.end(); iter++) { LLMsgVarData& mvci = *iter; if (mvci.getSize() == -1) { // oops, this variable wasn't ever set! llerrs << "The variable " << mvci.getName() << " in block " << mbci->mName << " of message " << mCurrentSMessageData->mName << " wasn't set prior to buildMessage call" << llendl; } else { S32 data_size = mvci.getDataSize(); if(data_size > 0) { // The type is MVT_VARIABLE, which means that we // need to encode a size argument. Otherwise, // there is no need. S32 size = mvci.getSize(); U8 sizeb; U16 sizeh; switch(data_size) { case 1: sizeb = size; htonmemcpy(&mSendBuffer[mSendSize], &sizeb, MVT_U8, 1); break; case 2: sizeh = size; htonmemcpy(&mSendBuffer[mSendSize], &sizeh, MVT_U16, 2); break; case 4: htonmemcpy(&mSendBuffer[mSendSize], &size, MVT_S32, 4); break; default: llerrs << "Attempting to build variable field with unknown size of " << size << llendl; break; } mSendSize += mvci.getDataSize(); } // if there is any data to pack, pack it if((mvci.getData() != NULL) && mvci.getSize()) { if(mSendSize + mvci.getSize() < (S32)sizeof(mSendBuffer)) { memcpy( &mSendBuffer[mSendSize], mvci.getData(), mvci.getSize()); mSendSize += mvci.getSize(); } else { // Just reporting error is likely not // enough. Need to check how to abort or error // out gracefully from this function. XXXTBD llerrs << "LLMessageSystem::buildMessage failed. " << "Attempted to pack " << mSendSize + mvci.getSize() << " bytes into a buffer with size " << mSendBuffer << "." << llendl } } } } } mbSBuilt = TRUE; } S32 LLMessageSystem::sendReliable(const LLHost &host) { return sendReliable(host, LL_DEFAULT_RELIABLE_RETRIES, TRUE, LL_PING_BASED_TIMEOUT_DUMMY, NULL, NULL); } S32 LLMessageSystem::sendSemiReliable(const LLHost &host, void (*callback)(void **,S32), void ** callback_data) { F32 timeout; LLCircuitData *cdp = mCircuitInfo.findCircuit(host); if (cdp) { timeout = llmax(LL_MINIMUM_SEMIRELIABLE_TIMEOUT_SECONDS, LL_SEMIRELIABLE_TIMEOUT_FACTOR * cdp->getPingDelayAveraged()); } else { timeout = LL_SEMIRELIABLE_TIMEOUT_FACTOR * LL_AVERAGED_PING_MAX; } return sendReliable(host, 0, FALSE, timeout, callback, callback_data); } // send the message via a UDP packet S32 LLMessageSystem::sendReliable( const LLHost &host, S32 retries, BOOL ping_based_timeout, F32 timeout, void (*callback)(void **,S32), void ** callback_data) { if (ping_based_timeout) { LLCircuitData *cdp = mCircuitInfo.findCircuit(host); if (cdp) { timeout = llmax(LL_MINIMUM_RELIABLE_TIMEOUT_SECONDS, LL_RELIABLE_TIMEOUT_FACTOR * cdp->getPingDelayAveraged()); } else { timeout = llmax(LL_MINIMUM_RELIABLE_TIMEOUT_SECONDS, LL_RELIABLE_TIMEOUT_FACTOR * LL_AVERAGED_PING_MAX); } } mSendReliable = TRUE; mReliablePacketParams.set(host, retries, ping_based_timeout, timeout, callback, callback_data, mCurrentSMessageName); return sendMessage(host); } void LLMessageSystem::forwardMessage(const LLHost &host) { copyMessageRtoS(); sendMessage(host); } void LLMessageSystem::forwardReliable(const LLHost &host) { copyMessageRtoS(); sendReliable(host); } void LLMessageSystem::forwardReliable(const U32 circuit_code) { copyMessageRtoS(); sendReliable(findHost(circuit_code)); } S32 LLMessageSystem::flushSemiReliable(const LLHost &host, void (*callback)(void **,S32), void ** callback_data) { F32 timeout; LLCircuitData *cdp = mCircuitInfo.findCircuit(host); if (cdp) { timeout = llmax(LL_MINIMUM_SEMIRELIABLE_TIMEOUT_SECONDS, LL_SEMIRELIABLE_TIMEOUT_FACTOR * cdp->getPingDelayAveraged()); } else { timeout = LL_SEMIRELIABLE_TIMEOUT_FACTOR * LL_AVERAGED_PING_MAX; } S32 send_bytes = 0; if (mCurrentSendTotal) { mSendReliable = TRUE; // No need for ping-based retry as not going to retry mReliablePacketParams.set(host, 0, FALSE, timeout, callback, callback_data, mCurrentSMessageName); send_bytes = sendMessage(host); clearMessage(); } else { delete callback_data; } return send_bytes; } S32 LLMessageSystem::flushReliable(const LLHost &host) { S32 send_bytes = 0; if (mCurrentSendTotal) { send_bytes = sendReliable(host); } clearMessage(); return send_bytes; } // This can be called from signal handlers, // so should should not use llinfos. S32 LLMessageSystem::sendMessage(const LLHost &host) { if (!mbSBuilt) { buildMessage(); } mCurrentSendTotal = 0; if (!(host.isOk())) // if port and ip are zero, don't bother trying to send the message { return 0; } LLCircuitData *cdp = mCircuitInfo.findCircuit(host); if (!cdp) { // this is a new circuit! // are we protected? if (mbProtected) { // yup! don't send packets to an unknown circuit if(mVerboseLog) { llinfos << "MSG: -> " << host << "\tUNKNOWN CIRCUIT:\t" << mCurrentSMessageName << llendl; } llwarns << "sendMessage - Trying to send " << mCurrentSMessageName << " on unknown circuit " << host << llendl; return 0; } else { // nope, open the new circuit cdp = mCircuitInfo.addCircuitData(host, 0); } } else { // this is an old circuit. . . is it still alive? if (!cdp->isAlive()) { // nope. don't send to dead circuits if(mVerboseLog) { llinfos << "MSG: -> " << host << "\tDEAD CIRCUIT\t\t" << mCurrentSMessageName << llendl; } llwarns << "sendMessage - Trying to send message " << mCurrentSMessageName << " to dead circuit " << host << llendl; return 0; } } memset(mSendBuffer,0,LL_PACKET_ID_SIZE); // zero out the packet ID field // add the send id to the front of the message cdp->nextPacketOutID(); // Packet ID size is always 4 *((S32*)&mSendBuffer[0]) = htonl(cdp->getPacketOutID()); // Compress the message, which will usually reduce its size. U8 * buf_ptr = (U8 *)mSendBuffer; S32 buffer_length = mSendSize; if(ME_ZEROCODED == mCurrentSMessageTemplate->getEncoding()) { zeroCode(&buf_ptr, &buffer_length); } if (buffer_length > 1500) { if((mCurrentSMessageName != _PREHASH_ChildAgentUpdate) && (mCurrentSMessageName != _PREHASH_SendXferPacket)) { llwarns << "sendMessage - Trying to send " << ((buffer_length > 4000) ? "EXTRA " : "") << "BIG message " << mCurrentSMessageName << " - " << buffer_length << llendl; } } if (mSendReliable) { buf_ptr[0] |= LL_RELIABLE_FLAG; if (!cdp->getUnackedPacketCount()) { // We are adding the first packed onto the unacked packet list(s) // Add this circuit to the list of circuits with unacked packets mCircuitInfo.mUnackedCircuitMap[cdp->mHost] = cdp; } cdp->addReliablePacket(mSocket,buf_ptr,buffer_length, &mReliablePacketParams); mReliablePacketsOut++; } // tack packet acks onto the end of this message S32 space_left = (MTUBYTES - buffer_length) / sizeof(TPACKETID); // space left for packet ids S32 ack_count = (S32)cdp->mAcks.size(); BOOL is_ack_appended = FALSE; std::vector acks; if((space_left > 0) && (ack_count > 0) && (mCurrentSMessageName != _PREHASH_PacketAck)) { buf_ptr[0] |= LL_ACK_FLAG; S32 append_ack_count = llmin(space_left, ack_count); const S32 MAX_ACKS = 250; append_ack_count = llmin(append_ack_count, MAX_ACKS); std::vector::iterator iter = cdp->mAcks.begin(); std::vector::iterator last = cdp->mAcks.begin(); last += append_ack_count; TPACKETID packet_id; for( ; iter != last ; ++iter) { // grab the next packet id. packet_id = (*iter); if(mVerboseLog) { acks.push_back(packet_id); } // put it on the end of the buffer packet_id = htonl(packet_id); if((S32)(buffer_length + sizeof(TPACKETID)) < MAX_BUFFER_SIZE) { memcpy(&buf_ptr[buffer_length], &packet_id, sizeof(TPACKETID)); // Do the accounting buffer_length += sizeof(TPACKETID); } else { // Just reporting error is likely not enough. Need to // check how to abort or error out gracefully from // this function. XXXTBD // *NOTE: Actually hitting this error would indicate // the calculation above for space_left, ack_count, // append_acout_count is incorrect or that // MAX_BUFFER_SIZE has fallen below MTU which is bad // and probably programmer error. llerrs << "Buffer packing failed due to size.." << llendl; } } // clean up the source cdp->mAcks.erase(cdp->mAcks.begin(), last); // tack the count in the final byte U8 count = (U8)append_ack_count; buf_ptr[buffer_length++] = count; is_ack_appended = TRUE; } BOOL success; success = mPacketRing.sendPacket(mSocket, (char *)buf_ptr, buffer_length, host); if (!success) { mSendPacketFailureCount++; } else { // mCircuitInfo already points to the correct circuit data cdp->addBytesOut( buffer_length ); } if(mVerboseLog) { std::ostringstream str; str << "MSG: -> " << host; char buffer[MAX_STRING]; /* Flawfinder: ignore */ snprintf(buffer, MAX_STRING, "\t%6d\t%6d\t%6d ", mSendSize, buffer_length, cdp->getPacketOutID()); /* Flawfinder: ignore */ str << buffer << mCurrentSMessageTemplate->mName << (mSendReliable ? " reliable " : ""); if(is_ack_appended) { str << "\tACKS:\t"; std::ostream_iterator append(str, " "); std::copy(acks.begin(), acks.end(), append); } llinfos << str.str() << llendl; } lldebugst(LLERR_MESSAGE) << "MessageSent at: " << (S32)totalTime() << ", " << mCurrentSMessageTemplate->mName << " to " << host << llendl; // ok, clean up temp data delete mCurrentSMessageData; mCurrentSMessageData = NULL; mPacketsOut++; mBytesOut += buffer_length; return buffer_length; } // Returns template for the message contained in buffer BOOL LLMessageSystem::decodeTemplate( const U8* buffer, S32 buffer_size, // inputs LLMessageTemplate** msg_template ) // outputs { const U8* header = buffer + LL_PACKET_ID_SIZE; // is there a message ready to go? if (buffer_size <= 0) { llwarns << "No message waiting for decode!" << llendl; return(FALSE); } U32 num = 0; if (header[0] != 255) { // high frequency message num = header[0]; } else if ((buffer_size >= ((S32) LL_MINIMUM_VALID_PACKET_SIZE + 1)) && (header[1] != 255)) { // medium frequency message num = (255 << 8) | header[1]; } else if ((buffer_size >= ((S32) LL_MINIMUM_VALID_PACKET_SIZE + 3)) && (header[1] == 255)) { // low frequency message U16 message_id_U16 = 0; // I think this check busts the message system. // it appears that if there is a NULL in the message #, it won't copy it.... // what was the goal? //if(header[2]) memcpy(&message_id_U16, &header[2], 2); // dependant on endian-ness: // U32 temp = (255 << 24) | (255 << 16) | header[2]; // independant of endian-ness: message_id_U16 = ntohs(message_id_U16); num = 0xFFFF0000 | message_id_U16; } else // bogus packet received (too short) { llwarns << "Packet with unusable length received (too short): " << buffer_size << llendl; return(FALSE); } LLMessageTemplate* temp = get_ptr_in_map(mMessageNumbers,num); if (temp) { *msg_template = temp; } else { llwarns << "Message #" << std::hex << num << std::dec << " received but not registered!" << llendl; callExceptionFunc(MX_UNREGISTERED_MESSAGE); return(FALSE); } return(TRUE); } void LLMessageSystem::logMsgFromInvalidCircuit( const LLHost& host, BOOL recv_reliable ) { if(mVerboseLog) { std::ostringstream str; str << "MSG: <- " << host; char buffer[MAX_STRING]; /* Flawfinder: ignore */ snprintf(buffer, MAX_STRING, "\t%6d\t%6d\t%6d ", mReceiveSize, (mIncomingCompressedSize ? mIncomingCompressedSize: mReceiveSize), mCurrentRecvPacketID); /* Flawfinder: ignore */ str << buffer << mCurrentRMessageTemplate->mName << (recv_reliable ? " reliable" : "") << " REJECTED"; llinfos << str.str() << llendl; } // nope! // cout << "Rejecting unexpected message " << mCurrentMessageTemplate->mName << " from " << hex << ip << " , " << dec << port << endl; // Keep track of rejected messages as well if (mNumMessageCounts >= MAX_MESSAGE_COUNT_NUM) { llwarns << "Got more than " << MAX_MESSAGE_COUNT_NUM << " packets without clearing counts" << llendl; } else { mMessageCountList[mNumMessageCounts].mMessageNum = mCurrentRMessageTemplate->mMessageNumber; mMessageCountList[mNumMessageCounts].mMessageBytes = mReceiveSize; mMessageCountList[mNumMessageCounts].mInvalid = TRUE; mNumMessageCounts++; } } void LLMessageSystem::logTrustedMsgFromUntrustedCircuit( const LLHost& host ) { // RequestTrustedCircuit is how we establish trust, so don't spam // if it's received on a trusted circuit. JC if (strcmp(mCurrentRMessageTemplate->mName, "RequestTrustedCircuit")) { llwarns << "Received trusted message on untrusted circuit. " << "Will reply with deny. " << "Message: " << mCurrentRMessageTemplate->mName << " Host: " << host << llendl; } if (mNumMessageCounts >= MAX_MESSAGE_COUNT_NUM) { llwarns << "got more than " << MAX_MESSAGE_COUNT_NUM << " packets without clearing counts" << llendl; } else { mMessageCountList[mNumMessageCounts].mMessageNum = mCurrentRMessageTemplate->mMessageNumber; mMessageCountList[mNumMessageCounts].mMessageBytes = mReceiveSize; mMessageCountList[mNumMessageCounts].mInvalid = TRUE; mNumMessageCounts++; } } void LLMessageSystem::logValidMsg(LLCircuitData *cdp, const LLHost& host, BOOL recv_reliable, BOOL recv_resent, BOOL recv_acks ) { if (mNumMessageCounts >= MAX_MESSAGE_COUNT_NUM) { llwarns << "Got more than " << MAX_MESSAGE_COUNT_NUM << " packets without clearing counts" << llendl; } else { mMessageCountList[mNumMessageCounts].mMessageNum = mCurrentRMessageTemplate->mMessageNumber; mMessageCountList[mNumMessageCounts].mMessageBytes = mReceiveSize; mMessageCountList[mNumMessageCounts].mInvalid = FALSE; mNumMessageCounts++; } if (cdp) { // update circuit packet ID tracking (missing/out of order packets) cdp->checkPacketInID( mCurrentRecvPacketID, recv_resent ); cdp->addBytesIn( mTrueReceiveSize ); } if(mVerboseLog) { std::ostringstream str; str << "MSG: <- " << host; char buffer[MAX_STRING]; /* Flawfinder: ignore */ snprintf(buffer, MAX_STRING, "\t%6d\t%6d\t%6d ", mReceiveSize, (mIncomingCompressedSize ? mIncomingCompressedSize : mReceiveSize), mCurrentRecvPacketID); /* Flawfinder: ignore */ str << buffer << mCurrentRMessageTemplate->mName << (recv_reliable ? " reliable" : "") << (recv_resent ? " resent" : "") << (recv_acks ? " acks" : ""); llinfos << str.str() << llendl; } } void LLMessageSystem::logRanOffEndOfPacket( const LLHost& host ) { // we've run off the end of the packet! llwarns << "Ran off end of packet " << mCurrentRMessageTemplate->mName << " with id " << mCurrentRecvPacketID << " from " << host << llendl; if(mVerboseLog) { llinfos << "MSG: -> " << host << "\tREAD PAST END:\t" << mCurrentRecvPacketID << " " << mCurrentSMessageTemplate->mName << llendl; } callExceptionFunc(MX_RAN_OFF_END_OF_PACKET); } // decode a given message BOOL LLMessageSystem::decodeData(const U8* buffer, const LLHost& sender ) { llassert( mReceiveSize >= 0 ); llassert( mCurrentRMessageTemplate); llassert( !mCurrentRMessageData ); delete mCurrentRMessageData; // just to make sure S32 decode_pos = LL_PACKET_ID_SIZE + (S32)(mCurrentRMessageTemplate->mFrequency); // create base working data set mCurrentRMessageData = new LLMsgData(mCurrentRMessageTemplate->mName); // loop through the template building the data structure as we go for (LLMessageTemplate::message_block_map_t::iterator iter = mCurrentRMessageTemplate->mMemberBlocks.begin(); iter != mCurrentRMessageTemplate->mMemberBlocks.end(); iter++) { LLMessageBlock* mbci = iter->second; U8 repeat_number; S32 i; // how many of this block? if (mbci->mType == MBT_SINGLE) { // just one repeat_number = 1; } else if (mbci->mType == MBT_MULTIPLE) { // a known number repeat_number = mbci->mNumber; } else if (mbci->mType == MBT_VARIABLE) { // need to read the number from the message // repeat number is a single byte if (decode_pos >= mReceiveSize) { logRanOffEndOfPacket( sender ); return FALSE; } repeat_number = buffer[decode_pos]; decode_pos++; } else { llerrs << "Unknown block type" << llendl; return FALSE; } LLMsgBlkData* cur_data_block = NULL; // now loop through the block for (i = 0; i < repeat_number; i++) { if (i) { // build new name to prevent collisions // TODO: This should really change to a vector cur_data_block = new LLMsgBlkData(mbci->mName, repeat_number); cur_data_block->mName = mbci->mName + i; } else { cur_data_block = new LLMsgBlkData(mbci->mName, repeat_number); } // add the block to the message mCurrentRMessageData->addBlock(cur_data_block); // now read the variables for (LLMessageBlock::message_variable_map_t::iterator iter = mbci->mMemberVariables.begin(); iter != mbci->mMemberVariables.end(); iter++) { LLMessageVariable& mvci = *(iter->second); // ok, build out the variables // add variable block cur_data_block->addVariable(mvci.getName(), mvci.getType()); // what type of variable? if (mvci.getType() == MVT_VARIABLE) { // variable, get the number of bytes to read from the template S32 data_size = mvci.getSize(); U8 tsizeb = 0; U16 tsizeh = 0; U32 tsize = 0; if ((decode_pos + data_size) > mReceiveSize) { logRanOffEndOfPacket( sender ); return FALSE; } switch(data_size) { case 1: htonmemcpy(&tsizeb, &buffer[decode_pos], MVT_U8, 1); tsize = tsizeb; break; case 2: htonmemcpy(&tsizeh, &buffer[decode_pos], MVT_U16, 2); tsize = tsizeh; break; case 4: htonmemcpy(&tsizeb, &buffer[decode_pos], MVT_U32, 4); break; default: llerrs << "Attempting to read variable field with unknown size of " << data_size << llendl; break; } decode_pos += data_size; if ((decode_pos + (S32)tsize) > mReceiveSize) { logRanOffEndOfPacket( sender ); return FALSE; } cur_data_block->addData(mvci.getName(), &buffer[decode_pos], tsize, mvci.getType()); decode_pos += tsize; } else { // fixed! // so, copy data pointer and set data size to fixed size if ((decode_pos + mvci.getSize()) > mReceiveSize) { logRanOffEndOfPacket( sender ); return FALSE; } cur_data_block->addData(mvci.getName(), &buffer[decode_pos], mvci.getSize(), mvci.getType()); decode_pos += mvci.getSize(); } } } } if (mCurrentRMessageData->mMemberBlocks.empty() && !mCurrentRMessageTemplate->mMemberBlocks.empty()) { lldebugs << "Empty message '" << mCurrentRMessageTemplate->mName << "' (no blocks)" << llendl; return FALSE; } { static LLTimer decode_timer; if( mTimeDecodes ) { decode_timer.reset(); } // if( mCurrentRMessageTemplate->mName == _PREHASH_AgentToNewRegion ) // { // VTResume(); // VTune // } { LLFastTimer t(LLFastTimer::FTM_PROCESS_MESSAGES); if( !mCurrentRMessageTemplate->callHandlerFunc(this) ) { llwarns << "Message from " << sender << " with no handler function received: " << mCurrentRMessageTemplate->mName << llendl; } } // if( mCurrentRMessageTemplate->mName == _PREHASH_AgentToNewRegion ) // { // VTPause(); // VTune // } if( mTimeDecodes ) { F32 decode_time = decode_timer.getElapsedTimeF32(); mCurrentRMessageTemplate->mDecodeTimeThisFrame += decode_time; mCurrentRMessageTemplate->mTotalDecoded++; mCurrentRMessageTemplate->mTotalDecodeTime += decode_time; if( mCurrentRMessageTemplate->mMaxDecodeTimePerMsg < decode_time ) { mCurrentRMessageTemplate->mMaxDecodeTimePerMsg = decode_time; } if( decode_time > mTimeDecodesSpamThreshold ) { lldebugs << "--------- Message " << mCurrentRMessageTemplate->mName << " decode took " << decode_time << " seconds. (" << mCurrentRMessageTemplate->mMaxDecodeTimePerMsg << " max, " << (mCurrentRMessageTemplate->mTotalDecodeTime / mCurrentRMessageTemplate->mTotalDecoded) << " avg)" << llendl; } } } return TRUE; } void LLMessageSystem::getDataFast(const char *blockname, const char *varname, void *datap, S32 size, S32 blocknum, S32 max_size) { // is there a message ready to go? if (mReceiveSize == -1) { llerrs << "No message waiting for decode 2!" << llendl; return; } if (!mCurrentRMessageData) { llerrs << "Invalid mCurrentMessageData in getData!" << llendl; return; } char *bnamep = (char *)blockname + blocknum; // this works because it's just a hash. The bnamep is never derefference char *vnamep = (char *)varname; LLMsgData::msg_blk_data_map_t::iterator iter = mCurrentRMessageData->mMemberBlocks.find(bnamep); if (iter == mCurrentRMessageData->mMemberBlocks.end()) { llerrs << "Block " << blockname << " #" << blocknum << " not in message " << mCurrentRMessageData->mName << llendl; return; } LLMsgBlkData *msg_block_data = iter->second; LLMsgVarData& vardata = msg_block_data->mMemberVarData[vnamep]; if (!vardata.getName()) { llerrs << "Variable "<< vnamep << " not in message " << mCurrentRMessageData->mName<< " block " << bnamep << llendl; return; } if (size && size != vardata.getSize()) { llerrs << "Msg " << mCurrentRMessageData->mName << " variable " << vnamep << " is size " << vardata.getSize() << " but copying into buffer of size " << size << llendl; return; } const S32 vardata_size = vardata.getSize(); if( max_size >= vardata_size ) { switch( vardata_size ) { case 1: *((U8*)datap) = *((U8*)vardata.getData()); break; case 2: *((U16*)datap) = *((U16*)vardata.getData()); break; case 4: *((U32*)datap) = *((U32*)vardata.getData()); break; case 8: ((U32*)datap)[0] = ((U32*)vardata.getData())[0]; ((U32*)datap)[1] = ((U32*)vardata.getData())[1]; break; default: memcpy(datap, vardata.getData(), vardata_size); break; } } else { llwarns << "Msg " << mCurrentRMessageData->mName << " variable " << vnamep << " is size " << vardata.getSize() << " but truncated to max size of " << max_size << llendl; memcpy(datap, vardata.getData(), max_size); } } S32 LLMessageSystem::getNumberOfBlocksFast(const char *blockname) { // is there a message ready to go? if (mReceiveSize == -1) { llerrs << "No message waiting for decode 3!" << llendl; return -1; } if (!mCurrentRMessageData) { llerrs << "Invalid mCurrentRMessageData in getData!" << llendl; return -1; } char *bnamep = (char *)blockname; LLMsgData::msg_blk_data_map_t::iterator iter = mCurrentRMessageData->mMemberBlocks.find(bnamep); if (iter == mCurrentRMessageData->mMemberBlocks.end()) { // sprintf(errmsg, "Block %s not in message %s", bnamep, mCurrentRMessageData->mName); // llerrs << errmsg << llendl; // return -1; return 0; } return (iter->second)->mBlockNumber; } S32 LLMessageSystem::getSizeFast(const char *blockname, const char *varname) { // is there a message ready to go? if (mReceiveSize == -1) { llerrs << "No message waiting for decode 4!" << llendl; return -1; } if (!mCurrentRMessageData) { llerrs << "Invalid mCurrentRMessageData in getData!" << llendl; return -1; } char *bnamep = (char *)blockname; LLMsgData::msg_blk_data_map_t::iterator iter = mCurrentRMessageData->mMemberBlocks.find(bnamep); if (iter == mCurrentRMessageData->mMemberBlocks.end()) { llerrs << "Block " << bnamep << " not in message " << mCurrentRMessageData->mName << llendl; return -1; } char *vnamep = (char *)varname; LLMsgBlkData* msg_data = iter->second; LLMsgVarData& vardata = msg_data->mMemberVarData[vnamep]; if (!vardata.getName()) { llerrs << "Variable " << varname << " not in message " << mCurrentRMessageData->mName << " block " << bnamep << llendl; return -1; } if (mCurrentRMessageTemplate->mMemberBlocks[bnamep]->mType != MBT_SINGLE) { llerrs << "Block " << bnamep << " isn't type MBT_SINGLE," " use getSize with blocknum argument!" << llendl; return -1; } return vardata.getSize(); } S32 LLMessageSystem::getSizeFast(const char *blockname, S32 blocknum, const char *varname) { // is there a message ready to go? if (mReceiveSize == -1) { llerrs << "No message waiting for decode 5!" << llendl; return -1; } if (!mCurrentRMessageData) { llerrs << "Invalid mCurrentRMessageData in getData!" << llendl; return -1; } char *bnamep = (char *)blockname + blocknum; char *vnamep = (char *)varname; LLMsgData::msg_blk_data_map_t::iterator iter = mCurrentRMessageData->mMemberBlocks.find(bnamep); if (iter == mCurrentRMessageData->mMemberBlocks.end()) { llerrs << "Block " << bnamep << " not in message " << mCurrentRMessageData->mName << llendl; return -1; } LLMsgBlkData* msg_data = iter->second; LLMsgVarData& vardata = msg_data->mMemberVarData[vnamep]; if (!vardata.getName()) { llerrs << "Variable " << vnamep << " not in message " << mCurrentRMessageData->mName << " block " << bnamep << llendl; return -1; } return vardata.getSize(); } void LLMessageSystem::sanityCheck() { if (!mCurrentRMessageData) { llerrs << "mCurrentRMessageData is NULL" << llendl; } if (!mCurrentRMessageTemplate) { llerrs << "mCurrentRMessageTemplate is NULL" << llendl; } // if (!mCurrentRTemplateBlock) // { // llerrs << "mCurrentRTemplateBlock is NULL" << llendl; // } // if (!mCurrentRDataBlock) // { // llerrs << "mCurrentRDataBlock is NULL" << llendl; // } if (!mCurrentSMessageData) { llerrs << "mCurrentSMessageData is NULL" << llendl; } if (!mCurrentSMessageTemplate) { llerrs << "mCurrentSMessageTemplate is NULL" << llendl; } // if (!mCurrentSTemplateBlock) // { // llerrs << "mCurrentSTemplateBlock is NULL" << llendl; // } if (!mCurrentSDataBlock) { llerrs << "mCurrentSDataBlock is NULL" << llendl; } } void LLMessageSystem::showCircuitInfo() { llinfos << mCircuitInfo << llendl; } void LLMessageSystem::dumpCircuitInfo() { lldebugst(LLERR_CIRCUIT_INFO) << mCircuitInfo << llendl; } /* virtual */ U32 LLMessageSystem::getOurCircuitCode() { return mOurCircuitCode; } LLString LLMessageSystem::getCircuitInfoString() { LLString info_string; info_string += mCircuitInfo.getInfoString(); return info_string; } // returns whether the given host is on a trusted circuit BOOL LLMessageSystem::getCircuitTrust(const LLHost &host) { LLCircuitData *cdp = mCircuitInfo.findCircuit(host); if (cdp) { return cdp->getTrusted(); } return FALSE; } // Activate a circuit, and set its trust level (TRUE if trusted, // FALSE if not). void LLMessageSystem::enableCircuit(const LLHost &host, BOOL trusted) { LLCircuitData *cdp = mCircuitInfo.findCircuit(host); if (!cdp) { cdp = mCircuitInfo.addCircuitData(host, 0); } else { cdp->setAlive(TRUE); } cdp->setTrusted(trusted); } void LLMessageSystem::disableCircuit(const LLHost &host) { llinfos << "LLMessageSystem::disableCircuit for " << host << llendl; U32 code = gMessageSystem->findCircuitCode( host ); // Don't need to do this, as we're removing the circuit info anyway - djs 01/28/03 // don't clean up 0 circuit code entries // because many hosts (neighbor sims, etc) can have the 0 circuit if (code) { //if (mCircuitCodes.checkKey(code)) code_session_map_t::iterator it = mCircuitCodes.find(code); if(it != mCircuitCodes.end()) { llinfos << "Circuit " << code << " removed from list" << llendl; //mCircuitCodes.removeData(code); mCircuitCodes.erase(it); } U64 ip_port = 0; std::map::iterator iter = gMessageSystem->mCircuitCodeToIPPort.find(code); if (iter != gMessageSystem->mCircuitCodeToIPPort.end()) { ip_port = iter->second; gMessageSystem->mCircuitCodeToIPPort.erase(iter); U32 old_port = (U32)(ip_port & (U64)0xFFFFFFFF); U32 old_ip = (U32)(ip_port >> 32); llinfos << "Host " << LLHost(old_ip, old_port) << " circuit " << code << " removed from lookup table" << llendl; gMessageSystem->mIPPortToCircuitCode.erase(ip_port); } } else { // Sigh, since we can open circuits which don't have circuit // codes, it's possible for this to happen... //llwarns << "Couldn't find circuit code for " << host << llendl; } mCircuitInfo.removeCircuitData(host); } void LLMessageSystem::setCircuitAllowTimeout(const LLHost &host, BOOL allow) { LLCircuitData *cdp = mCircuitInfo.findCircuit(host); if (cdp) { cdp->setAllowTimeout(allow); } } void LLMessageSystem::setCircuitTimeoutCallback(const LLHost &host, void (*callback_func)(const LLHost & host, void *user_data), void *user_data) { LLCircuitData *cdp = mCircuitInfo.findCircuit(host); if (cdp) { cdp->setTimeoutCallback(callback_func, user_data); } } BOOL LLMessageSystem::checkCircuitBlocked(const U32 circuit) { LLHost host = findHost(circuit); if (!host.isOk()) { //llinfos << "checkCircuitBlocked: Unknown circuit " << circuit << llendl; return TRUE; } LLCircuitData *cdp = mCircuitInfo.findCircuit(host); if (cdp) { return cdp->isBlocked(); } else { llinfos << "checkCircuitBlocked(circuit): Unknown host - " << host << llendl; return FALSE; } } BOOL LLMessageSystem::checkCircuitAlive(const U32 circuit) { LLHost host = findHost(circuit); if (!host.isOk()) { //llinfos << "checkCircuitAlive: Unknown circuit " << circuit << llendl; return FALSE; } LLCircuitData *cdp = mCircuitInfo.findCircuit(host); if (cdp) { return cdp->isAlive(); } else { llinfos << "checkCircuitAlive(circuit): Unknown host - " << host << llendl; return FALSE; } } BOOL LLMessageSystem::checkCircuitAlive(const LLHost &host) { LLCircuitData *cdp = mCircuitInfo.findCircuit(host); if (cdp) { return cdp->isAlive(); } else { //llinfos << "checkCircuitAlive(host): Unknown host - " << host << llendl; return FALSE; } } void LLMessageSystem::setCircuitProtection(BOOL b_protect) { mbProtected = b_protect; } U32 LLMessageSystem::findCircuitCode(const LLHost &host) { U64 ip64 = (U64) host.getAddress(); U64 port64 = (U64) host.getPort(); U64 ip_port = (ip64 << 32) | port64; return get_if_there(mIPPortToCircuitCode, ip_port, U32(0)); } LLHost LLMessageSystem::findHost(const U32 circuit_code) { if (mCircuitCodeToIPPort.count(circuit_code) > 0) { return LLHost(mCircuitCodeToIPPort[circuit_code]); } else { return LLHost::invalid; } } void LLMessageSystem::setMaxMessageTime(const F32 seconds) { mMaxMessageTime = seconds; } void LLMessageSystem::setMaxMessageCounts(const S32 num) { mMaxMessageCounts = num; } std::ostream& operator<<(std::ostream& s, LLMessageSystem &msg) { U32 i; if (msg.mbError) { s << "Message system not correctly initialized"; } else { s << "Message system open on port " << msg.mPort << " and socket " << msg.mSocket << "\n"; // s << "Message template file " << msg.mName << " loaded\n"; s << "\nHigh frequency messages:\n"; for (i = 1; msg.mMessageNumbers[i] && (i < 255); i++) { s << *(msg.mMessageNumbers[i]); } s << "\nMedium frequency messages:\n"; for (i = (255 << 8) + 1; msg.mMessageNumbers[i] && (i < (255 << 8) + 255); i++) { s << *msg.mMessageNumbers[i]; } s << "\nLow frequency messages:\n"; for (i = (0xFFFF0000) + 1; msg.mMessageNumbers[i] && (i < 0xFFFFFFFF); i++) { s << *msg.mMessageNumbers[i]; } } return s; } LLMessageSystem *gMessageSystem = NULL; // update appropriate ping info void process_complete_ping_check(LLMessageSystem *msgsystem, void** /*user_data*/) { U8 ping_id; msgsystem->getU8Fast(_PREHASH_PingID, _PREHASH_PingID, ping_id); LLCircuitData *cdp; cdp = msgsystem->mCircuitInfo.findCircuit(msgsystem->getSender()); // stop the appropriate timer if (cdp) { cdp->pingTimerStop(ping_id); } } void process_start_ping_check(LLMessageSystem *msgsystem, void** /*user_data*/) { U8 ping_id; msgsystem->getU8Fast(_PREHASH_PingID, _PREHASH_PingID, ping_id); LLCircuitData *cdp; cdp = msgsystem->mCircuitInfo.findCircuit(msgsystem->getSender()); if (cdp) { // Grab the packet id of the oldest unacked packet U32 packet_id; msgsystem->getU32Fast(_PREHASH_PingID, _PREHASH_OldestUnacked, packet_id); cdp->clearDuplicateList(packet_id); } // Send off the response msgsystem->newMessageFast(_PREHASH_CompletePingCheck); msgsystem->nextBlockFast(_PREHASH_PingID); msgsystem->addU8(_PREHASH_PingID, ping_id); msgsystem->sendMessage(msgsystem->getSender()); } // Note: this is currently unused. --mark void open_circuit(LLMessageSystem *msgsystem, void** /*user_data*/) { U32 ip; U16 port; msgsystem->getIPAddrFast(_PREHASH_CircuitInfo, _PREHASH_IP, ip); msgsystem->getIPPortFast(_PREHASH_CircuitInfo, _PREHASH_Port, port); // By default, OpenCircuit's are untrusted msgsystem->enableCircuit(LLHost(ip, port), FALSE); } void close_circuit(LLMessageSystem *msgsystem, void** /*user_data*/) { msgsystem->disableCircuit(msgsystem->getSender()); } // static /* void LLMessageSystem::processAssignCircuitCode(LLMessageSystem* msg, void**) { // if we already have a circuit code, we can bail if(msg->mOurCircuitCode) return; LLUUID session_id; msg->getUUIDFast(_PREHASH_CircuitCode, _PREHASH_SessionID, session_id); if(session_id != msg->getMySessionID()) { llwarns << "AssignCircuitCode, bad session id. Expecting " << msg->getMySessionID() << " but got " << session_id << llendl; return; } U32 code; msg->getU32Fast(_PREHASH_CircuitCode, _PREHASH_Code, code); if (!code) { llerrs << "Assigning circuit code of zero!" << llendl; } msg->mOurCircuitCode = code; llinfos << "Circuit code " << code << " assigned." << llendl; } */ // static void LLMessageSystem::processAddCircuitCode(LLMessageSystem* msg, void**) { U32 code; msg->getU32Fast(_PREHASH_CircuitCode, _PREHASH_Code, code); LLUUID session_id; msg->getUUIDFast(_PREHASH_CircuitCode, _PREHASH_SessionID, session_id); (void)msg->addCircuitCode(code, session_id); // Send the ack back //msg->newMessageFast(_PREHASH_AckAddCircuitCode); //msg->nextBlockFast(_PREHASH_CircuitCode); //msg->addU32Fast(_PREHASH_Code, code); //msg->sendMessage(msg->getSender()); } bool LLMessageSystem::addCircuitCode(U32 code, const LLUUID& session_id) { if(!code) { llwarns << "addCircuitCode: zero circuit code" << llendl; return false; } code_session_map_t::iterator it = mCircuitCodes.find(code); if(it == mCircuitCodes.end()) { llinfos << "New circuit code " << code << " added" << llendl; //msg->mCircuitCodes[circuit_code] = circuit_code; mCircuitCodes.insert(code_session_map_t::value_type(code, session_id)); } else { llinfos << "Duplicate circuit code " << code << " added" << llendl; } return true; } //void ack_add_circuit_code(LLMessageSystem *msgsystem, void** /*user_data*/) //{ // By default, we do nothing. This particular message is only handled by the spaceserver //} // static void LLMessageSystem::processUseCircuitCode(LLMessageSystem* msg, void**) { U32 circuit_code_in; msg->getU32Fast(_PREHASH_CircuitCode, _PREHASH_Code, circuit_code_in); U32 ip = msg->getSenderIP(); U32 port = msg->getSenderPort(); U64 ip64 = ip; U64 port64 = port; U64 ip_port_in = (ip64 << 32) | port64; if (circuit_code_in) { //if (!msg->mCircuitCodes.checkKey(circuit_code_in)) code_session_map_t::iterator it; it = msg->mCircuitCodes.find(circuit_code_in); if(it == msg->mCircuitCodes.end()) { // Whoah, abort! We don't know anything about this circuit code. llwarns << "UseCircuitCode for " << circuit_code_in << " received without AddCircuitCode message - aborting" << llendl; return; } LLUUID id; msg->getUUIDFast(_PREHASH_CircuitCode, _PREHASH_ID, id); LLUUID session_id; msg->getUUIDFast(_PREHASH_CircuitCode, _PREHASH_SessionID, session_id); if(session_id != (*it).second) { llwarns << "UseCircuitCode unmatched session id. Got " << session_id << " but expected " << (*it).second << llendl; return; } // Clean up previous references to this ip/port or circuit U64 ip_port_old = get_if_there(msg->mCircuitCodeToIPPort, circuit_code_in, U64(0)); U32 circuit_code_old = get_if_there(msg->mIPPortToCircuitCode, ip_port_in, U32(0)); if (ip_port_old) { if ((ip_port_old == ip_port_in) && (circuit_code_old == circuit_code_in)) { // Current information is the same as incoming info, ignore llinfos << "Got duplicate UseCircuitCode for circuit " << circuit_code_in << " to " << msg->getSender() << llendl; return; } // Hmm, got a different IP and port for the same circuit code. U32 circut_code_old_ip_port = get_if_there(msg->mIPPortToCircuitCode, ip_port_old, U32(0)); msg->mCircuitCodeToIPPort.erase(circut_code_old_ip_port); msg->mIPPortToCircuitCode.erase(ip_port_old); U32 old_port = (U32)(ip_port_old & (U64)0xFFFFFFFF); U32 old_ip = (U32)(ip_port_old >> 32); llinfos << "Removing derelict lookup entry for circuit " << circuit_code_old << " to " << LLHost(old_ip, old_port) << llendl; } if (circuit_code_old) { LLHost cur_host(ip, port); llwarns << "Disabling existing circuit for " << cur_host << llendl; msg->disableCircuit(cur_host); if (circuit_code_old == circuit_code_in) { llwarns << "Asymmetrical circuit to ip/port lookup!" << llendl; llwarns << "Multiple circuit codes for " << cur_host << " probably!" << llendl; llwarns << "Permanently disabling circuit" << llendl; return; } else { llwarns << "Circuit code changed for " << msg->getSender() << " from " << circuit_code_old << " to " << circuit_code_in << llendl; } } // Since this comes from the viewer, it's untrusted, but it // passed the circuit code and session id check, so we will go // ahead and persist the ID associated. LLCircuitData *cdp = msg->mCircuitInfo.findCircuit(msg->getSender()); BOOL had_circuit_already = cdp ? TRUE : FALSE; msg->enableCircuit(msg->getSender(), FALSE); cdp = msg->mCircuitInfo.findCircuit(msg->getSender()); if(cdp) { cdp->setRemoteID(id); cdp->setRemoteSessionID(session_id); } if (!had_circuit_already) { // // HACK HACK HACK HACK HACK! // // This would NORMALLY happen inside logValidMsg, but at the point that this happens // inside logValidMsg, there's no circuit for this message yet. So the awful thing that // we do here is do it inside this message handler immediately AFTER the message is // handled. // // We COULD not do this, but then what happens is that some of the circuit bookkeeping // gets broken, especially the packets in count. That causes some later packets to flush // the RecentlyReceivedReliable list, resulting in an error in which UseCircuitCode // doesn't get properly duplicate suppressed. Not a BIG deal, but it's somewhat confusing // (and bad from a state point of view). DJS 9/23/04 // cdp->checkPacketInID(gMessageSystem->mCurrentRecvPacketID, FALSE ); // Since this is the first message on the circuit, by definition it's not resent. } msg->mIPPortToCircuitCode[ip_port_in] = circuit_code_in; msg->mCircuitCodeToIPPort[circuit_code_in] = ip_port_in; llinfos << "Circuit code " << circuit_code_in << " from " << msg->getSender() << " for agent " << id << " in session " << session_id << llendl; } else { llwarns << "Got zero circuit code in use_circuit_code" << llendl; } } static void check_for_unrecognized_messages( const char* type, const LLSD& map, LLMessageSystem::message_template_name_map_t& templates) { for (LLSD::map_const_iterator iter = map.beginMap(), end = map.endMap(); iter != end; ++iter) { const char* name = gMessageStringTable.getString(iter->first.c_str()); if (templates.find(name) == templates.end()) { llinfos << " " << type << " ban list contains unrecognized message " << name << llendl; } } } void LLMessageSystem::setMessageBans( const LLSD& trusted, const LLSD& untrusted) { llinfos << "LLMessageSystem::setMessageBans:" << llendl; bool any_set = false; for (message_template_name_map_t::iterator iter = mMessageTemplates.begin(), end = mMessageTemplates.end(); iter != end; ++iter) { LLMessageTemplate* mt = iter->second; std::string name(mt->mName); bool ban_from_trusted = trusted.has(name) && trusted.get(name).asBoolean(); bool ban_from_untrusted = untrusted.has(name) && untrusted.get(name).asBoolean(); mt->mBanFromTrusted = ban_from_trusted; mt->mBanFromUntrusted = ban_from_untrusted; if (ban_from_trusted || ban_from_untrusted) { llinfos << " " << name << " banned from " << (ban_from_trusted ? "TRUSTED " : " ") << (ban_from_untrusted ? "UNTRUSTED " : " ") << llendl; any_set = true; } } if (!any_set) { llinfos << " no messages banned" << llendl; } check_for_unrecognized_messages("trusted", trusted, mMessageTemplates); check_for_unrecognized_messages("untrusted", untrusted, mMessageTemplates); } void process_packet_ack(LLMessageSystem *msgsystem, void** /*user_data*/) { TPACKETID packet_id; LLHost host = msgsystem->getSender(); LLCircuitData *cdp = msgsystem->mCircuitInfo.findCircuit(host); if (cdp) { S32 ack_count = msgsystem->getNumberOfBlocksFast(_PREHASH_Packets); for (S32 i = 0; i < ack_count; i++) { msgsystem->getU32Fast(_PREHASH_Packets, _PREHASH_ID, packet_id, i); // llinfos << "ack recvd' from " << host << " for packet " << (TPACKETID)packet_id << llendl; cdp->ackReliablePacket(packet_id); } if (!cdp->getUnackedPacketCount()) { // Remove this circuit from the list of circuits with unacked packets gMessageSystem->mCircuitInfo.mUnackedCircuitMap.erase(host); } } } void send_template_reply(LLMessageSystem* msg, const LLUUID& token) { msg->newMessageFast(_PREHASH_TemplateChecksumReply); msg->nextBlockFast(_PREHASH_DataBlock); msg->addU32Fast(_PREHASH_Checksum, msg->mMessageFileChecksum); msg->addU8Fast(_PREHASH_MajorVersion, U8(msg->mSystemVersionMajor) ); msg->addU8Fast(_PREHASH_MinorVersion, U8(msg->mSystemVersionMinor) ); msg->addU8Fast(_PREHASH_PatchVersion, U8(msg->mSystemVersionPatch) ); msg->addU8Fast(_PREHASH_ServerVersion, U8(msg->mSystemVersionServer) ); msg->addU32Fast(_PREHASH_Flags, msg->mVersionFlags); msg->nextBlockFast(_PREHASH_TokenBlock); msg->addUUIDFast(_PREHASH_Token, token); msg->sendMessage(msg->getSender()); } void process_template_checksum_request(LLMessageSystem* msg, void**) { llinfos << "Message template checksum request received from " << msg->getSender() << llendl; send_template_reply(msg, LLUUID::null); } void process_secured_template_checksum_request(LLMessageSystem* msg, void**) { llinfos << "Secured message template checksum request received from " << msg->getSender() << llendl; LLUUID token; msg->getUUIDFast(_PREHASH_TokenBlock, _PREHASH_Token, token); send_template_reply(msg, token); } void process_log_control(LLMessageSystem* msg, void**) { U8 level; U32 mask; BOOL time; BOOL location; BOOL remote_infos; msg->getU8Fast(_PREHASH_Options, _PREHASH_Level, level); msg->getU32Fast(_PREHASH_Options, _PREHASH_Mask, mask); msg->getBOOLFast(_PREHASH_Options, _PREHASH_Time, time); msg->getBOOLFast(_PREHASH_Options, _PREHASH_Location, location); msg->getBOOLFast(_PREHASH_Options, _PREHASH_RemoteInfos, remote_infos); gErrorStream.setLevel(LLErrorStream::ELevel(level)); gErrorStream.setDebugMask(mask); gErrorStream.setTime(time); gErrorStream.setPrintLocation(location); gErrorStream.setElevatedRemote(remote_infos); llinfos << "Logging set to level " << gErrorStream.getLevel() << " mask " << std::hex << gErrorStream.getDebugMask() << std::dec << " time " << gErrorStream.getTime() << " loc " << gErrorStream.getPrintLocation() << llendl; } void process_log_messages(LLMessageSystem* msg, void**) { U8 log_message; msg->getU8Fast(_PREHASH_Options, _PREHASH_Enable, log_message); if (log_message) { llinfos << "Starting logging via message" << llendl; msg->startLogging(); } else { llinfos << "Stopping logging via message" << llendl; msg->stopLogging(); } } // Make circuit trusted if the MD5 Digest matches, otherwise // notify remote end that they are not trusted. void process_create_trusted_circuit(LLMessageSystem *msg, void **) { // don't try to create trust on machines with no shared secret std::string shared_secret = get_shared_secret(); if(shared_secret.empty()) return; LLUUID remote_id; msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_EndPointID, remote_id); LLCircuitData *cdp = msg->mCircuitInfo.findCircuit(msg->getSender()); if (!cdp) { llwarns << "Attempt to create trusted circuit without circuit data: " << msg->getSender() << llendl; return; } LLUUID local_id; local_id = cdp->getLocalEndPointID(); if (remote_id == local_id) { // Don't respond to requests that use the same end point ID return; } char their_digest[MD5HEX_STR_SIZE]; S32 size = msg->getSizeFast(_PREHASH_DataBlock, _PREHASH_Digest); if(size != MD5HEX_STR_BYTES) { // ignore requests which pack the wrong amount of data. return; } msg->getBinaryDataFast(_PREHASH_DataBlock, _PREHASH_Digest, their_digest, MD5HEX_STR_BYTES); their_digest[MD5HEX_STR_SIZE - 1] = '\0'; if(msg->isMatchingDigestForWindowAndUUIDs(their_digest, TRUST_TIME_WINDOW, local_id, remote_id)) { cdp->setTrusted(TRUE); llinfos << "Trusted digest from " << msg->getSender() << llendl; return; } else if (cdp->getTrusted()) { // The digest is bad, but this circuit is already trusted. // This means that this could just be the result of a stale deny sent from a while back, and // the message system is being slow. Don't bother sending the deny, as it may continually // ping-pong back and forth on a very hosed circuit. llwarns << "Ignoring bad digest from known trusted circuit: " << their_digest << " host: " << msg->getSender() << llendl; return; } else { llwarns << "Bad digest from known circuit: " << their_digest << " host: " << msg->getSender() << llendl; msg->sendDenyTrustedCircuit(msg->getSender()); return; } } void process_deny_trusted_circuit(LLMessageSystem *msg, void **) { // don't try to create trust on machines with no shared secret std::string shared_secret = get_shared_secret(); if(shared_secret.empty()) return; LLUUID remote_id; msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_EndPointID, remote_id); LLCircuitData *cdp = msg->mCircuitInfo.findCircuit(msg->getSender()); if (!cdp) { return; } LLUUID local_id; local_id = cdp->getLocalEndPointID(); if (remote_id == local_id) { // Don't respond to requests that use the same end point ID return; } // Assume that we require trust to proceed, so resend. // This catches the case where a circuit that was trusted // times out, and allows us to re-establish it, but does // mean that if our shared_secret or clock is wrong, we'll // spin. // *TODO: probably should keep a count of number of resends // per circuit, and stop resending after a while. llinfos << "Got DenyTrustedCircuit. Sending CreateTrustedCircuit to " << msg->getSender() << llendl; msg->sendCreateTrustedCircuit(msg->getSender(), local_id, remote_id); } #define LL_ENCRYPT_BUF_LENGTH 16384 void encrypt_template(const char *src_name, const char *dest_name) { // encrypt and decrypt are symmetric decrypt_template(src_name, dest_name); } BOOL decrypt_template(const char *src_name, const char *dest_name) { S32 buf_length = LL_ENCRYPT_BUF_LENGTH; char buf[LL_ENCRYPT_BUF_LENGTH]; FILE* infp = NULL; FILE* outfp = NULL; BOOL success = FALSE; char* bufp = NULL; U32 key = 0; S32 more_data = 0; if(src_name==NULL) { llwarns << "Input src_name is NULL!!" << llendl; goto exit; } infp = LLFile::fopen(src_name,"rb"); if (!infp) { llwarns << "could not open " << src_name << " for reading" << llendl; goto exit; } if(dest_name==NULL) { llwarns << "Output dest_name is NULL!!" << llendl; goto exit; } outfp = LLFile::fopen(dest_name,"w+b"); if (!outfp) { llwarns << "could not open " << src_name << " for writing" << llendl; goto exit; } while ((buf_length = (S32)fread(buf,1,LL_ENCRYPT_BUF_LENGTH,infp))) { // unscrozzle bits here bufp = buf; more_data = buf_length; while (more_data--) { *bufp = *bufp ^ ((key * 43) % 256); key++; bufp++; } if(buf_length != (S32)fwrite(buf,1,buf_length,outfp)) { goto exit; } } success = TRUE; exit: if(infp) fclose(infp); if(outfp) fclose(outfp); return success; } void dump_prehash_files() { U32 i; FILE *fp = LLFile::fopen("../../indra/llmessage/message_prehash.h", "w"); if (fp) { fprintf( fp, "/**\n" " * @file message_prehash.h\n" " * @brief header file of externs of prehashed variables plus defines.\n" " *\n" " * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.\n" " * $License$\n" " */\n\n" "#ifndef LL_MESSAGE_PREHASH_H\n#define LL_MESSAGE_PREHASH_H\n\n"); fprintf( fp, "/**\n" " * Generated from message template version number %.3f\n" " */\n", gMessageSystem->mMessageFileVersionNumber); fprintf(fp, "\n\nextern F32 gPrehashVersionNumber;\n\n"); for (i = 0; i < MESSAGE_NUMBER_OF_HASH_BUCKETS; i++) { if (!gMessageStringTable.mEmpty[i] && gMessageStringTable.mString[i][0] != '.') { fprintf(fp, "extern char * _PREHASH_%s;\n", gMessageStringTable.mString[i]); } } fprintf(fp, "\n\nvoid init_prehash_data();\n\n"); fprintf(fp, "\n\n"); fprintf(fp, "\n\n#endif\n"); fclose(fp); } fp = LLFile::fopen("../../indra/llmessage/message_prehash.cpp", "w"); if (fp) { fprintf( fp, "/**\n" " * @file message_prehash.cpp\n" " * @brief file of prehashed variables\n" " *\n" " * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.\n" " * $License$\n" " */\n\n" "/**\n" " * Generated from message template version number %.3f\n" " */\n", gMessageSystem->mMessageFileVersionNumber); fprintf(fp, "#include \"linden_common.h\"\n"); fprintf(fp, "#include \"message.h\"\n\n"); fprintf(fp, "\n\nF32 gPrehashVersionNumber = %.3ff;\n\n", gMessageSystem->mMessageFileVersionNumber); for (i = 0; i < MESSAGE_NUMBER_OF_HASH_BUCKETS; i++) { if (!gMessageStringTable.mEmpty[i] && gMessageStringTable.mString[i][0] != '.') { fprintf(fp, "char * _PREHASH_%s;\n", gMessageStringTable.mString[i]); } } fprintf(fp, "\nvoid init_prehash_data()\n"); fprintf(fp, "{\n"); for (i = 0; i < MESSAGE_NUMBER_OF_HASH_BUCKETS; i++) { if (!gMessageStringTable.mEmpty[i] && gMessageStringTable.mString[i][0] != '.') { fprintf(fp, "\t_PREHASH_%s = gMessageStringTable.getString(\"%s\");\n", gMessageStringTable.mString[i], gMessageStringTable.mString[i]); } } fprintf(fp, "}\n"); fclose(fp); } } BOOL start_messaging_system( const std::string& template_name, U32 port, S32 version_major, S32 version_minor, S32 version_patch, BOOL b_dump_prehash_file, const std::string& secret) { gMessageSystem = new LLMessageSystem( template_name.c_str(), port, version_major, version_minor, version_patch); g_shared_secret.assign(secret); if (!gMessageSystem) { llerrs << "Messaging system initialization failed." << llendl; return FALSE; } // bail if system encountered an error. if(!gMessageSystem->isOK()) { return FALSE; } if (b_dump_prehash_file) { dump_prehash_files(); exit(0); } else { init_prehash_data(); if (gMessageSystem->mMessageFileVersionNumber != gPrehashVersionNumber) { llinfos << "Message template version does not match prehash version number" << llendl; llinfos << "Run simulator with -prehash command line option to rebuild prehash data" << llendl; } else { llinfos << "Message template version matches prehash version number" << llendl; } } gMessageSystem->setHandlerFuncFast(_PREHASH_StartPingCheck, process_start_ping_check, NULL); gMessageSystem->setHandlerFuncFast(_PREHASH_CompletePingCheck, process_complete_ping_check, NULL); gMessageSystem->setHandlerFuncFast(_PREHASH_OpenCircuit, open_circuit, NULL); gMessageSystem->setHandlerFuncFast(_PREHASH_CloseCircuit, close_circuit, NULL); //gMessageSystem->setHandlerFuncFast(_PREHASH_AssignCircuitCode, LLMessageSystem::processAssignCircuitCode); gMessageSystem->setHandlerFuncFast(_PREHASH_AddCircuitCode, LLMessageSystem::processAddCircuitCode); //gMessageSystem->setHandlerFuncFast(_PREHASH_AckAddCircuitCode, ack_add_circuit_code, NULL); gMessageSystem->setHandlerFuncFast(_PREHASH_UseCircuitCode, LLMessageSystem::processUseCircuitCode); gMessageSystem->setHandlerFuncFast(_PREHASH_PacketAck, process_packet_ack, NULL); gMessageSystem->setHandlerFuncFast(_PREHASH_TemplateChecksumRequest, process_template_checksum_request, NULL); gMessageSystem->setHandlerFuncFast(_PREHASH_SecuredTemplateChecksumRequest, process_secured_template_checksum_request, NULL); gMessageSystem->setHandlerFuncFast(_PREHASH_LogControl, process_log_control, NULL); gMessageSystem->setHandlerFuncFast(_PREHASH_LogMessages, process_log_messages, NULL); gMessageSystem->setHandlerFuncFast(_PREHASH_CreateTrustedCircuit, process_create_trusted_circuit, NULL); gMessageSystem->setHandlerFuncFast(_PREHASH_DenyTrustedCircuit, process_deny_trusted_circuit, NULL); // We can hand this to the null_message_callback since it is a // trusted message, so it will automatically be denied if it isn't // trusted and ignored if it is -- exactly what we want. gMessageSystem->setHandlerFunc( "RequestTrustedCircuit", null_message_callback, NULL); // Initialize the transfer manager gTransferManager.init(); return TRUE; } void LLMessageSystem::startLogging() { mVerboseLog = TRUE; std::ostringstream str; str << "START MESSAGE LOG" << std::endl; str << "Legend:" << std::endl; str << "\t<-\tincoming message" <\toutgoing message" << std::endl; str << " <> host size zero id name"; llinfos << str.str() << llendl; } void LLMessageSystem::stopLogging() { if(mVerboseLog) { mVerboseLog = FALSE; llinfos << "END MESSAGE LOG" << llendl; } } void LLMessageSystem::summarizeLogs(std::ostream& str) { char buffer[MAX_STRING]; /* Flawfinder: ignore */ char tmp_str[MAX_STRING]; /* Flawfinder: ignore */ F32 run_time = mMessageSystemTimer.getElapsedTimeF32(); str << "START MESSAGE LOG SUMMARY" << std::endl; snprintf(buffer, MAX_STRING, "Run time: %12.3f seconds", run_time); /* Flawfinder: ignore */ // Incoming str << buffer << std::endl << "Incoming:" << std::endl; U64_to_str(mTotalBytesIn, tmp_str, sizeof(tmp_str)); snprintf(buffer, MAX_STRING, "Total bytes received: %20s (%5.2f kbits per second)", tmp_str, ((F32)mTotalBytesIn * 0.008f) / run_time); /* Flawfinder: ignore */ str << buffer << std::endl; U64_to_str(mPacketsIn, tmp_str, sizeof(tmp_str)); snprintf(buffer, MAX_STRING, "Total packets received: %20s (%5.2f packets per second)", tmp_str, ((F32) mPacketsIn / run_time)); /* Flawfinder: ignore */ str << buffer << std::endl; snprintf(buffer, MAX_STRING, "Average packet size: %20.0f bytes", (F32)mTotalBytesIn / (F32)mPacketsIn); /* Flawfinder: ignore */ str << buffer << std::endl; U64_to_str(mReliablePacketsIn, tmp_str, sizeof(tmp_str)); snprintf(buffer, MAX_STRING, "Total reliable packets: %20s (%5.2f%%)", tmp_str, 100.f * ((F32) mReliablePacketsIn)/((F32) mPacketsIn + 1)); /* Flawfinder: ignore */ str << buffer << std::endl; U64_to_str(mCompressedPacketsIn, tmp_str, sizeof(tmp_str)); snprintf(buffer, MAX_STRING, "Total compressed packets: %20s (%5.2f%%)", tmp_str, 100.f * ((F32) mCompressedPacketsIn)/((F32) mPacketsIn + 1)); /* Flawfinder: ignore */ str << buffer << std::endl; S64 savings = mUncompressedBytesIn - mCompressedBytesIn; U64_to_str(savings, tmp_str, sizeof(tmp_str)); snprintf(buffer, MAX_STRING, "Total compression savings: %20s bytes", tmp_str); /* Flawfinder: ignore */ str << buffer << std::endl; U64_to_str(savings/(mCompressedPacketsIn +1), tmp_str, sizeof(tmp_str)); snprintf(buffer, MAX_STRING, "Avg comp packet savings: %20s (%5.2f : 1)", tmp_str, ((F32) mUncompressedBytesIn)/((F32) mCompressedBytesIn+1)); /* Flawfinder: ignore */ str << buffer << std::endl; U64_to_str(savings/(mPacketsIn+1), tmp_str, sizeof(tmp_str)); snprintf(buffer, MAX_STRING, "Avg overall comp savings: %20s (%5.2f : 1)", tmp_str, ((F32) mTotalBytesIn + (F32) savings)/((F32) mTotalBytesIn + 1.f)); /* Flawfinder: ignore */ // Outgoing str << buffer << std::endl << std::endl << "Outgoing:" << std::endl; U64_to_str(mTotalBytesOut, tmp_str, sizeof(tmp_str)); snprintf(buffer, MAX_STRING, "Total bytes sent: %20s (%5.2f kbits per second)", tmp_str, ((F32)mTotalBytesOut * 0.008f) / run_time ); /* Flawfinder: ignore */ str << buffer << std::endl; U64_to_str(mPacketsOut, tmp_str, sizeof(tmp_str)); snprintf(buffer, MAX_STRING, "Total packets sent: %20s (%5.2f packets per second)", tmp_str, ((F32)mPacketsOut / run_time)); /* Flawfinder: ignore */ str << buffer << std::endl; snprintf(buffer, MAX_STRING, "Average packet size: %20.0f bytes", (F32)mTotalBytesOut / (F32)mPacketsOut); /* Flawfinder: ignore */ str << buffer << std::endl; U64_to_str(mReliablePacketsOut, tmp_str, sizeof(tmp_str)); snprintf(buffer, MAX_STRING, "Total reliable packets: %20s (%5.2f%%)", tmp_str, 100.f * ((F32) mReliablePacketsOut)/((F32) mPacketsOut + 1)); /* Flawfinder: ignore */ str << buffer << std::endl; U64_to_str(mCompressedPacketsOut, tmp_str, sizeof(tmp_str)); snprintf(buffer, MAX_STRING, "Total compressed packets: %20s (%5.2f%%)", tmp_str, 100.f * ((F32) mCompressedPacketsOut)/((F32) mPacketsOut + 1)); /* Flawfinder: ignore */ str << buffer << std::endl; savings = mUncompressedBytesOut - mCompressedBytesOut; U64_to_str(savings, tmp_str, sizeof(tmp_str)); snprintf(buffer, MAX_STRING, "Total compression savings: %20s bytes", tmp_str); /* Flawfinder: ignore */ str << buffer << std::endl; U64_to_str(savings/(mCompressedPacketsOut +1), tmp_str, sizeof(tmp_str)); snprintf(buffer, MAX_STRING, "Avg comp packet savings: %20s (%5.2f : 1)", tmp_str, ((F32) mUncompressedBytesOut)/((F32) mCompressedBytesOut+1)); /* Flawfinder: ignore */ str << buffer << std::endl; U64_to_str(savings/(mPacketsOut+1), tmp_str, sizeof(tmp_str)); snprintf(buffer, MAX_STRING, "Avg overall comp savings: %20s (%5.2f : 1)", tmp_str, ((F32) mTotalBytesOut + (F32) savings)/((F32) mTotalBytesOut + 1.f)); /* Flawfinder: ignore */ str << buffer << std::endl << std::endl; snprintf(buffer, MAX_STRING, "SendPacket failures: %20d", mSendPacketFailureCount); /* Flawfinder: ignore */ str << buffer << std::endl; snprintf(buffer, MAX_STRING, "Dropped packets: %20d", mDroppedPackets); /* Flawfinder: ignore */ str << buffer << std::endl; snprintf(buffer, MAX_STRING, "Resent packets: %20d", mResentPackets); /* Flawfinder: ignore */ str << buffer << std::endl; snprintf(buffer, MAX_STRING, "Failed reliable resends: %20d", mFailedResendPackets); /* Flawfinder: ignore */ str << buffer << std::endl; snprintf(buffer, MAX_STRING, "Off-circuit rejected packets: %17d", mOffCircuitPackets); /* Flawfinder: ignore */ str << buffer << std::endl; snprintf(buffer, MAX_STRING, "On-circuit invalid packets: %17d", mInvalidOnCircuitPackets); /* Flawfinder: ignore */ str << buffer << std::endl << std::endl; str << "Decoding: " << std::endl; snprintf(buffer, MAX_STRING, "%35s%10s%10s%10s%10s", "Message", "Count", "Time", "Max", "Avg"); /* Flawfinder: ignore */ str << buffer << std:: endl; F32 avg; for (message_template_name_map_t::iterator iter = mMessageTemplates.begin(), end = mMessageTemplates.end(); iter != end; iter++) { LLMessageTemplate* mt = iter->second; if(mt->mTotalDecoded > 0) { avg = mt->mTotalDecodeTime / (F32)mt->mTotalDecoded; snprintf(buffer, MAX_STRING, "%35s%10u%10f%10f%10f", mt->mName, mt->mTotalDecoded, mt->mTotalDecodeTime, mt->mMaxDecodeTimePerMsg, avg); /* Flawfinder: ignore */ str << buffer << std::endl; } } str << "END MESSAGE LOG SUMMARY" << std::endl; } void end_messaging_system() { gTransferManager.cleanup(); LLTransferTargetVFile::updateQueue(true); // shutdown LLTransferTargetVFile if (gMessageSystem) { gMessageSystem->stopLogging(); std::ostringstream str; gMessageSystem->summarizeLogs(str); llinfos << str.str().c_str() << llendl; delete gMessageSystem; gMessageSystem = NULL; } } void LLMessageSystem::resetReceiveCounts() { mNumMessageCounts = 0; for (message_template_name_map_t::iterator iter = mMessageTemplates.begin(), end = mMessageTemplates.end(); iter != end; iter++) { LLMessageTemplate* mt = iter->second; mt->mDecodeTimeThisFrame = 0.f; } } void LLMessageSystem::dumpReceiveCounts() { LLMessageTemplate *mt; for (message_template_name_map_t::iterator iter = mMessageTemplates.begin(), end = mMessageTemplates.end(); iter != end; iter++) { LLMessageTemplate* mt = iter->second; mt->mReceiveCount = 0; mt->mReceiveBytes = 0; mt->mReceiveInvalid = 0; } S32 i; for (i = 0; i < mNumMessageCounts; i++) { mt = get_ptr_in_map(mMessageNumbers,mMessageCountList[i].mMessageNum); if (mt) { mt->mReceiveCount++; mt->mReceiveBytes += mMessageCountList[i].mMessageBytes; if (mMessageCountList[i].mInvalid) { mt->mReceiveInvalid++; } } } if(mNumMessageCounts > 0) { llinfos << "Dump: " << mNumMessageCounts << " messages processed in " << mReceiveTime << " seconds" << llendl; for (message_template_name_map_t::iterator iter = mMessageTemplates.begin(), end = mMessageTemplates.end(); iter != end; iter++) { LLMessageTemplate* mt = iter->second; if (mt->mReceiveCount > 0) { llinfos << "Num: " << std::setw(3) << mt->mReceiveCount << " Bytes: " << std::setw(6) << mt->mReceiveBytes << " Invalid: " << std::setw(3) << mt->mReceiveInvalid << " " << mt->mName << " " << llround(100 * mt->mDecodeTimeThisFrame / mReceiveTime) << "%" << llendl; } } } } BOOL LLMessageSystem::isClear() const { return mbSClear; } S32 LLMessageSystem::flush(const LLHost &host) { if (mCurrentSendTotal) { S32 sentbytes = sendMessage(host); clearMessage(); return sentbytes; } else { return 0; } } U32 LLMessageSystem::getListenPort( void ) const { return mPort; } S32 LLMessageSystem::zeroCode(U8 **data, S32 *data_size) { S32 count = *data_size; S32 net_gain = 0; U8 num_zeroes = 0; U8 *inptr = (U8 *)*data; U8 *outptr = (U8 *)mEncodedSendBuffer; // skip the packet id field for (U32 i=0;i256 zeroes) while (count--) { if (!(*inptr)) // in a zero count { if (num_zeroes) { if (++num_zeroes > 254) { *outptr++ = num_zeroes; num_zeroes = 0; } net_gain--; // subseqent zeroes save one } else { *outptr++ = 0; net_gain++; // starting a zero count adds one num_zeroes = 1; } inptr++; } else { if (num_zeroes) { *outptr++ = num_zeroes; num_zeroes = 0; } *outptr++ = *inptr++; } } if (num_zeroes) { *outptr++ = num_zeroes; } if (net_gain < 0) { mCompressedPacketsOut++; mUncompressedBytesOut += *data_size; *data = mEncodedSendBuffer; *data_size += net_gain; mEncodedSendBuffer[0] |= LL_ZERO_CODE_FLAG; // set the head bit to indicate zero coding mCompressedBytesOut += *data_size; } mTotalBytesOut += *data_size; return(net_gain); } S32 LLMessageSystem::zeroCodeAdjustCurrentSendTotal() { if (!mbSBuilt) { buildMessage(); } mbSBuilt = FALSE; S32 count = mSendSize; S32 net_gain = 0; U8 num_zeroes = 0; U8 *inptr = (U8 *)mSendBuffer; // skip the packet id field for (U32 i=0;i256 zeroes) while (count--) { if (!(*inptr)) // in a zero count { if (num_zeroes) { if (++num_zeroes > 254) { num_zeroes = 0; } net_gain--; // subseqent zeroes save one } else { net_gain++; // starting a zero count adds one num_zeroes = 1; } inptr++; } else { if (num_zeroes) { num_zeroes = 0; } inptr++; } } if (net_gain < 0) { return net_gain; } else { return 0; } } S32 LLMessageSystem::zeroCodeExpand(U8 **data, S32 *data_size) { if ((*data_size ) < LL_PACKET_ID_SIZE) { llwarns << "zeroCodeExpand() called with data_size of " << *data_size << llendl; } mTotalBytesIn += *data_size; if (!(*data[0] & LL_ZERO_CODE_FLAG)) // if we're not zero-coded, just go 'way { return(0); } S32 in_size = *data_size; mCompressedPacketsIn++; mCompressedBytesIn += *data_size; *data[0] &= (~LL_ZERO_CODE_FLAG); S32 count = (*data_size); U8 *inptr = (U8 *)*data; U8 *outptr = (U8 *)mEncodedRecvBuffer; // skip the packet id field for (U32 i=0;i256 zeroes) while (count--) { if (outptr > (&mEncodedRecvBuffer[MAX_BUFFER_SIZE-1])) { llwarns << "attempt to write past reasonable encoded buffer size 1" << llendl; callExceptionFunc(MX_WROTE_PAST_BUFFER_SIZE); outptr = mEncodedRecvBuffer; break; } if (!((*outptr++ = *inptr++))) { while (((count--)) && (!(*inptr))) { *outptr++ = *inptr++; if (outptr > (&mEncodedRecvBuffer[MAX_BUFFER_SIZE-256])) { llwarns << "attempt to write past reasonable encoded buffer size 2" << llendl; callExceptionFunc(MX_WROTE_PAST_BUFFER_SIZE); outptr = mEncodedRecvBuffer; count = -1; break; } memset(outptr,0,255); outptr += 255; } if (count < 0) { break; } else { if (outptr > (&mEncodedRecvBuffer[MAX_BUFFER_SIZE-(*inptr)])) { llwarns << "attempt to write past reasonable encoded buffer size 3" << llendl; callExceptionFunc(MX_WROTE_PAST_BUFFER_SIZE); outptr = mEncodedRecvBuffer; } memset(outptr,0,(*inptr) - 1); outptr += ((*inptr) - 1); inptr++; } } } *data = mEncodedRecvBuffer; *data_size = (S32)(outptr - mEncodedRecvBuffer); mUncompressedBytesIn += *data_size; return(in_size); } void LLMessageSystem::addTemplate(LLMessageTemplate *templatep) { if (mMessageTemplates.count(templatep->mName) > 0) { llerrs << templatep->mName << " already used as a template name!" << llendl; } mMessageTemplates[templatep->mName] = templatep; mMessageNumbers[templatep->mMessageNumber] = templatep; } void LLMessageSystem::setHandlerFuncFast(const char *name, void (*handler_func)(LLMessageSystem *msgsystem, void **user_data), void **user_data) { LLMessageTemplate* msgtemplate = get_ptr_in_map(mMessageTemplates, name); if (msgtemplate) { msgtemplate->setHandlerFunc(handler_func, user_data); } else { llerrs << name << " is not a known message name!" << llendl; } } bool LLMessageSystem::callHandler(const char *name, bool trustedSource, LLMessageSystem* msg) { name = gMessageStringTable.getString(name); LLMessageTemplate* msg_template = mMessageTemplates[(char*)name]; if (!msg_template) { llwarns << "LLMessageSystem::callHandler: unknown message " << name << llendl; return false; } if (msg_template->isBanned(trustedSource)) { llwarns << "LLMessageSystem::callHandler: banned message " << name << " from " << (trustedSource ? "trusted " : "untrusted ") << "source" << llendl; return false; } return msg_template->callHandlerFunc(msg); } void LLMessageSystem::setExceptionFunc(EMessageException e, msg_exception_callback func, void* data) { callbacks_t::iterator it = mExceptionCallbacks.find(e); if(it != mExceptionCallbacks.end()) { mExceptionCallbacks.erase(it); } if(func) { mExceptionCallbacks.insert(callbacks_t::value_type(e, exception_t(func, data))); } } BOOL LLMessageSystem::callExceptionFunc(EMessageException exception) { callbacks_t::iterator it = mExceptionCallbacks.find(exception); if(it != mExceptionCallbacks.end()) { ((*it).second.first)(this, (*it).second.second,exception); return TRUE; } return FALSE; } BOOL LLMessageSystem::isCircuitCodeKnown(U32 code) const { if(mCircuitCodes.find(code) == mCircuitCodes.end()) return FALSE; return TRUE; } BOOL LLMessageSystem::isMessageFast(const char *msg) { if (mCurrentRMessageTemplate) { return(msg == mCurrentRMessageTemplate->mName); } else { return FALSE; } } char* LLMessageSystem::getMessageName() { if (mCurrentRMessageTemplate) { return mCurrentRMessageTemplate->mName; } else { return NULL; } } const LLUUID& LLMessageSystem::getSenderID() const { LLCircuitData *cdp = mCircuitInfo.findCircuit(mLastSender); if (cdp) { return (cdp->mRemoteID); } return LLUUID::null; } const LLUUID& LLMessageSystem::getSenderSessionID() const { LLCircuitData *cdp = mCircuitInfo.findCircuit(mLastSender); if (cdp) { return (cdp->mRemoteSessionID); } return LLUUID::null; } void LLMessageSystem::addVector3Fast(const char *varname, const LLVector3& vec) { addDataFast(varname, vec.mV, MVT_LLVector3, sizeof(vec.mV)); } void LLMessageSystem::addVector3(const char *varname, const LLVector3& vec) { addDataFast(gMessageStringTable.getString(varname), vec.mV, MVT_LLVector3, sizeof(vec.mV)); } void LLMessageSystem::addVector4Fast(const char *varname, const LLVector4& vec) { addDataFast(varname, vec.mV, MVT_LLVector4, sizeof(vec.mV)); } void LLMessageSystem::addVector4(const char *varname, const LLVector4& vec) { addDataFast(gMessageStringTable.getString(varname), vec.mV, MVT_LLVector4, sizeof(vec.mV)); } void LLMessageSystem::addVector3dFast(const char *varname, const LLVector3d& vec) { addDataFast(varname, vec.mdV, MVT_LLVector3d, sizeof(vec.mdV)); } void LLMessageSystem::addVector3d(const char *varname, const LLVector3d& vec) { addDataFast(gMessageStringTable.getString(varname), vec.mdV, MVT_LLVector3d, sizeof(vec.mdV)); } void LLMessageSystem::addQuatFast(const char *varname, const LLQuaternion& quat) { addDataFast(varname, quat.packToVector3().mV, MVT_LLQuaternion, sizeof(LLVector3)); } void LLMessageSystem::addQuat(const char *varname, const LLQuaternion& quat) { addDataFast(gMessageStringTable.getString(varname), quat.packToVector3().mV, MVT_LLQuaternion, sizeof(LLVector3)); } void LLMessageSystem::addUUIDFast(const char *varname, const LLUUID& uuid) { addDataFast(varname, uuid.mData, MVT_LLUUID, sizeof(uuid.mData)); } void LLMessageSystem::addUUID(const char *varname, const LLUUID& uuid) { addDataFast(gMessageStringTable.getString(varname), uuid.mData, MVT_LLUUID, sizeof(uuid.mData)); } void LLMessageSystem::getF32Fast(const char *block, const char *var, F32 &d, S32 blocknum) { getDataFast(block, var, &d, sizeof(F32), blocknum); if( !llfinite( d ) ) { llwarns << "non-finite in getF32Fast " << block << " " << var << llendl; d = 0; } } void LLMessageSystem::getF32(const char *block, const char *var, F32 &d, S32 blocknum) { getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), &d, sizeof(F32), blocknum); if( !llfinite( d ) ) { llwarns << "non-finite in getF32 " << block << " " << var << llendl; d = 0; } } void LLMessageSystem::getF64Fast(const char *block, const char *var, F64 &d, S32 blocknum) { getDataFast(block, var, &d, sizeof(F64), blocknum); if( !llfinite( d ) ) { llwarns << "non-finite in getF64Fast " << block << " " << var << llendl; d = 0; } } void LLMessageSystem::getF64(const char *block, const char *var, F64 &d, S32 blocknum) { getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), &d, sizeof(F64), blocknum); if( !llfinite( d ) ) { llwarns << "non-finite in getF64 " << block << " " << var << llendl; d = 0; } } void LLMessageSystem::getVector3Fast(const char *block, const char *var, LLVector3 &v, S32 blocknum ) { getDataFast(block, var, v.mV, sizeof(v.mV), blocknum); if( !v.isFinite() ) { llwarns << "non-finite in getVector3Fast " << block << " " << var << llendl; v.zeroVec(); } } void LLMessageSystem::getVector3(const char *block, const char *var, LLVector3 &v, S32 blocknum ) { getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), v.mV, sizeof(v.mV), blocknum); if( !v.isFinite() ) { llwarns << "non-finite in getVector4 " << block << " " << var << llendl; v.zeroVec(); } } void LLMessageSystem::getVector4Fast(const char *block, const char *var, LLVector4 &v, S32 blocknum ) { getDataFast(block, var, v.mV, sizeof(v.mV), blocknum); if( !v.isFinite() ) { llwarns << "non-finite in getVector4Fast " << block << " " << var << llendl; v.zeroVec(); } } void LLMessageSystem::getVector4(const char *block, const char *var, LLVector4 &v, S32 blocknum ) { getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), v.mV, sizeof(v.mV), blocknum); if( !v.isFinite() ) { llwarns << "non-finite in getVector3 " << block << " " << var << llendl; v.zeroVec(); } } void LLMessageSystem::getVector3dFast(const char *block, const char *var, LLVector3d &v, S32 blocknum ) { getDataFast(block, var, v.mdV, sizeof(v.mdV), blocknum); if( !v.isFinite() ) { llwarns << "non-finite in getVector3dFast " << block << " " << var << llendl; v.zeroVec(); } } void LLMessageSystem::getVector3d(const char *block, const char *var, LLVector3d &v, S32 blocknum ) { getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), v.mdV, sizeof(v.mdV), blocknum); if( !v.isFinite() ) { llwarns << "non-finite in getVector3d " << block << " " << var << llendl; v.zeroVec(); } } void LLMessageSystem::getQuatFast(const char *block, const char *var, LLQuaternion &q, S32 blocknum ) { LLVector3 vec; getDataFast(block, var, vec.mV, sizeof(vec.mV), blocknum); if( vec.isFinite() ) { q.unpackFromVector3( vec ); } else { llwarns << "non-finite in getQuatFast " << block << " " << var << llendl; q.loadIdentity(); } } void LLMessageSystem::getQuat(const char *block, const char *var, LLQuaternion &q, S32 blocknum ) { LLVector3 vec; getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), vec.mV, sizeof(vec.mV), blocknum); if( vec.isFinite() ) { q.unpackFromVector3( vec ); } else { llwarns << "non-finite in getQuat " << block << " " << var << llendl; q.loadIdentity(); } } void LLMessageSystem::getUUIDFast(const char *block, const char *var, LLUUID &u, S32 blocknum ) { getDataFast(block, var, u.mData, sizeof(u.mData), blocknum); } void LLMessageSystem::getUUID(const char *block, const char *var, LLUUID &u, S32 blocknum ) { getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), u.mData, sizeof(u.mData), blocknum); } bool LLMessageSystem::generateDigestForNumberAndUUIDs(char* digest, const U32 number, const LLUUID &id1, const LLUUID &id2) const { const char *colon = ":"; char tbuf[16]; /* Flawfinder: ignore */ LLMD5 d; LLString id1string = id1.getString(); LLString id2string = id2.getString(); std::string shared_secret = get_shared_secret(); unsigned char * secret = (unsigned char*)shared_secret.c_str(); unsigned char * id1str = (unsigned char*)id1string.c_str(); unsigned char * id2str = (unsigned char*)id2string.c_str(); memset(digest, 0, MD5HEX_STR_SIZE); if( secret != NULL) { d.update(secret, (U32)strlen((char *) secret)); } d.update((const unsigned char *) colon, (U32)strlen(colon)); /* Flawfinder: ignore */ snprintf(tbuf, sizeof(tbuf),"%i", number); /* Flawfinder: ignore */ d.update((unsigned char *) tbuf, (U32)strlen(tbuf)); /* Flawfinder: ignore */ d.update((const unsigned char *) colon, (U32)strlen(colon)); /* Flawfinder: ignore */ if( (char*) id1str != NULL) { d.update(id1str, (U32)strlen((char *) id1str)); } d.update((const unsigned char *) colon, (U32)strlen(colon)); /* Flawfinder: ignore */ if( (char*) id2str != NULL) { d.update(id2str, (U32)strlen((char *) id2str)); } d.finalize(); d.hex_digest(digest); digest[MD5HEX_STR_SIZE - 1] = '\0'; return true; } bool LLMessageSystem::generateDigestForWindowAndUUIDs(char* digest, const S32 window, const LLUUID &id1, const LLUUID &id2) const { if(0 == window) return false; std::string shared_secret = get_shared_secret(); if(shared_secret.empty()) { llerrs << "Trying to generate complex digest on a machine without a shared secret!" << llendl; } U32 now = time(NULL); now /= window; bool result = generateDigestForNumberAndUUIDs(digest, now, id1, id2); return result; } bool LLMessageSystem::isMatchingDigestForWindowAndUUIDs(const char* digest, const S32 window, const LLUUID &id1, const LLUUID &id2) const { if(0 == window) return false; std::string shared_secret = get_shared_secret(); if(shared_secret.empty()) { llerrs << "Trying to compare complex digests on a machine without a shared secret!" << llendl; } char our_digest[MD5HEX_STR_SIZE]; /* Flawfinder: ignore */ U32 now = time(NULL); now /= window; // Check 1 window ago, now, and one window from now to catch edge // conditions. Process them as current window, one window ago, and // one window in the future to catch the edges. const S32 WINDOW_BIN_COUNT = 3; U32 window_bin[WINDOW_BIN_COUNT]; window_bin[0] = now; window_bin[1] = now - 1; window_bin[2] = now + 1; for(S32 i = 0; i < WINDOW_BIN_COUNT; ++i) { generateDigestForNumberAndUUIDs(our_digest, window_bin[i], id2, id1); if(0 == strncmp(digest, our_digest, MD5HEX_STR_BYTES)) { return true; } } return false; } bool LLMessageSystem::generateDigestForNumber(char* digest, const U32 number) const { memset(digest, 0, MD5HEX_STR_SIZE); LLMD5 d; std::string shared_secret = get_shared_secret(); d = LLMD5((const unsigned char *)shared_secret.c_str(), number); d.hex_digest(digest); digest[MD5HEX_STR_SIZE - 1] = '\0'; return true; } bool LLMessageSystem::generateDigestForWindow(char* digest, const S32 window) const { if(0 == window) return false; std::string shared_secret = get_shared_secret(); if(shared_secret.empty()) { llerrs << "Trying to generate simple digest on a machine without a shared secret!" << llendl; } U32 now = time(NULL); now /= window; bool result = generateDigestForNumber(digest, now); return result; } bool LLMessageSystem::isMatchingDigestForWindow(const char* digest, S32 const window) const { if(0 == window) return false; std::string shared_secret = get_shared_secret(); if(shared_secret.empty()) { llerrs << "Trying to compare simple digests on a machine without a shared secret!" << llendl; } char our_digest[MD5HEX_STR_SIZE]; /* Flawfinder: ignore */ U32 now = (S32)time(NULL); now /= window; // Check 1 window ago, now, and one window from now to catch edge // conditions. Process them as current window, one window ago, and // one window in the future to catch the edges. const S32 WINDOW_BIN_COUNT = 3; U32 window_bin[WINDOW_BIN_COUNT]; window_bin[0] = now; window_bin[1] = now - 1; window_bin[2] = now + 1; for(S32 i = 0; i < WINDOW_BIN_COUNT; ++i) { generateDigestForNumber(our_digest, window_bin[i]); if(0 == strncmp(digest, our_digest, MD5HEX_STR_BYTES)) { return true; } } return false; } void LLMessageSystem::sendCreateTrustedCircuit(const LLHost &host, const LLUUID & id1, const LLUUID & id2) { std::string shared_secret = get_shared_secret(); if(shared_secret.empty()) return; char digest[MD5HEX_STR_SIZE]; /* Flawfinder: ignore */ if (id1.isNull()) { llwarns << "Can't send CreateTrustedCircuit to " << host << " because we don't have the local end point ID" << llendl; return; } if (id2.isNull()) { llwarns << "Can't send CreateTrustedCircuit to " << host << " because we don't have the remote end point ID" << llendl; return; } generateDigestForWindowAndUUIDs(digest, TRUST_TIME_WINDOW, id1, id2); newMessageFast(_PREHASH_CreateTrustedCircuit); nextBlockFast(_PREHASH_DataBlock); addUUIDFast(_PREHASH_EndPointID, id1); addBinaryDataFast(_PREHASH_Digest, digest, MD5HEX_STR_BYTES); llinfos << "xmitting digest: " << digest << " Host: " << host << llendl; sendMessage(host); } void LLMessageSystem::sendDenyTrustedCircuit(const LLHost &host) { mDenyTrustedCircuitSet.insert(host); } void LLMessageSystem::reallySendDenyTrustedCircuit(const LLHost &host) { LLCircuitData *cdp = mCircuitInfo.findCircuit(host); if (!cdp) { llwarns << "Not sending DenyTrustedCircuit to host without a circuit." << llendl; return; } llinfos << "Sending DenyTrustedCircuit to " << host << llendl; newMessageFast(_PREHASH_DenyTrustedCircuit); nextBlockFast(_PREHASH_DataBlock); addUUIDFast(_PREHASH_EndPointID, cdp->getLocalEndPointID()); sendMessage(host); } void null_message_callback(LLMessageSystem *msg, void **data) { // Nothing should ever go here, but we use this to register messages // that we are expecting to see (and spinning on) at startup. return; } // Try to establish a bidirectional trust metric by pinging a host until it's // up, and then sending auth messages. void LLMessageSystem::establishBidirectionalTrust(const LLHost &host, S64 frame_count ) { std::string shared_secret = get_shared_secret(); if(shared_secret.empty()) { llerrs << "Trying to establish bidirectional trust on a machine without a shared secret!" << llendl; } LLTimer timeout; timeout.setTimerExpirySec(20.0); setHandlerFuncFast(_PREHASH_StartPingCheck, null_message_callback, NULL); setHandlerFuncFast(_PREHASH_CompletePingCheck, null_message_callback, NULL); while (! timeout.hasExpired()) { newMessageFast(_PREHASH_StartPingCheck); nextBlockFast(_PREHASH_PingID); addU8Fast(_PREHASH_PingID, 0); addU32Fast(_PREHASH_OldestUnacked, 0); sendMessage(host); if (checkMessages( frame_count )) { if (isMessageFast(_PREHASH_CompletePingCheck) && (getSender() == host)) { break; } } processAcks(); ms_sleep(1); } // Send a request, a deny, and give the host 2 seconds to complete // the trust handshake. newMessage("RequestTrustedCircuit"); sendMessage(host); reallySendDenyTrustedCircuit(host); setHandlerFuncFast(_PREHASH_StartPingCheck, process_start_ping_check, NULL); setHandlerFuncFast(_PREHASH_CompletePingCheck, process_complete_ping_check, NULL); timeout.setTimerExpirySec(2.0); LLCircuitData* cdp = NULL; while(!timeout.hasExpired()) { cdp = mCircuitInfo.findCircuit(host); if(!cdp) break; // no circuit anymore, no point continuing. if(cdp->getTrusted()) break; // circuit is trusted. checkMessages(frame_count); processAcks(); ms_sleep(1); } } void LLMessageSystem::dumpPacketToLog() { llwarns << "Packet Dump from:" << mPacketRing.getLastSender() << llendl; llwarns << "Packet Size:" << mTrueReceiveSize << llendl; char line_buffer[256]; /* Flawfinder: ignore */ S32 i; S32 cur_line_pos = 0; S32 cur_line = 0; for (i = 0; i < mTrueReceiveSize; i++) { snprintf(line_buffer + cur_line_pos*3, sizeof(line_buffer),"%02x ", mTrueReceiveBuffer[i]); /* Flawfinder: ignore */ cur_line_pos++; if (cur_line_pos >= 16) { cur_line_pos = 0; llwarns << "PD:" << cur_line << "PD:" << line_buffer << llendl; cur_line++; } } if (cur_line_pos) { llwarns << "PD:" << cur_line << "PD:" << line_buffer << llendl; } } //static U64 LLMessageSystem::getMessageTimeUsecs(const BOOL update) { if (gMessageSystem) { if (update) { gMessageSystem->mCurrentMessageTimeSeconds = totalTime()*SEC_PER_USEC; } return (U64)(gMessageSystem->mCurrentMessageTimeSeconds * USEC_PER_SEC); } else { return totalTime(); } } //static F64 LLMessageSystem::getMessageTimeSeconds(const BOOL update) { if (gMessageSystem) { if (update) { gMessageSystem->mCurrentMessageTimeSeconds = totalTime()*SEC_PER_USEC; } return gMessageSystem->mCurrentMessageTimeSeconds; } else { return totalTime()*SEC_PER_USEC; } } std::string get_shared_secret() { static const std::string SHARED_SECRET_KEY("shared_secret"); if(g_shared_secret.empty()) { LLApp* app = LLApp::instance(); if(app) return app->getOption(SHARED_SECRET_KEY); } return g_shared_secret; }