/** * @file message.h * @brief LLMessageSystem class header file * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #ifndef LL_MESSAGE_H #define LL_MESSAGE_H #include <cstring> #include <set> #if LL_LINUX #include <endian.h> #include <netinet/in.h> #endif #if LL_WINDOWS #include "winsock2.h" // htons etc. #endif #include "llerror.h" #include "net.h" #include "llstringtable.h" #include "llcircuit.h" #include "lltimer.h" #include "llpacketring.h" #include "llhost.h" #include "llhttpnode.h" //#include "llpacketack.h" #include "llsingleton.h" #include "message_prehash.h" #include "llstl.h" #include "llmsgvariabletype.h" #include "llmessagesenderinterface.h" #include "llstoredmessage.h" #include "boost/function.hpp" #include "llpounceable.h" #include "llcoros.h" #include LLCOROS_MUTEX_HEADER const U32 MESSAGE_MAX_STRINGS_LENGTH = 64; const U32 MESSAGE_NUMBER_OF_HASH_BUCKETS = 8192; const S32 MESSAGE_MAX_PER_FRAME = 400; class LLMessageStringTable : public LLSingleton<LLMessageStringTable> { LLSINGLETON(LLMessageStringTable); ~LLMessageStringTable(); public: char *getString(const char *str); U32 mUsed; bool mEmpty[MESSAGE_NUMBER_OF_HASH_BUCKETS]; char mString[MESSAGE_NUMBER_OF_HASH_BUCKETS][MESSAGE_MAX_STRINGS_LENGTH]; /* Flawfinder: ignore */ }; // Individual Messages are described with the following format // Note that to ease parsing, keywords are used // // // Comment (Comment like a C++ single line comment) // Comments can only be placed between Messages // { // MessageName (same naming restrictions as C variable) // Frequency ("High", "Medium", or "Low" - determines whether message ID is 8, 16, or 32-bits -- // there can 254 messages in the first 2 groups, 32K in the last group) // (A message can be made up only of the Name if it is only a signal) // Trust ("Trusted", "NotTrusted" - determines if a message will be accepted // on a circuit. "Trusted" messages are not accepted from NotTrusted circuits // while NotTrusted messages are accepted on any circuit. An example of a // NotTrusted circuit is any circuit from the viewer.) // Encoding ("Zerocoded", "Unencoded" - zerocoded messages attempt to compress sequences of // zeros, but if there is no space win, it discards the compression and goes unencoded) // { // Block Name (same naming restrictions as C variable) // Block Type ("Single", "Multiple", or "Variable" - determines if the block is coded once, // a known number of times, or has a 8 bit argument encoded to tell the decoder // how many times the group is repeated) // Block Repeat Number (Optional - used only with the "Multiple" type - tells how many times the field is repeated // { // Variable 1 Name (same naming restrictions as C variable) // Variable Type ("Fixed" or "Variable" - determines if the variable is of fixed size or needs to // encode an argument describing the size in bytes) // Variable Size (In bytes, either of the "Fixed" variable itself or of the size argument) // // repeat variables // // } // // Repeat for number of variables in block // } // // Repeat for number of blocks in message // } // Repeat for number of messages in file // // Constants const S32 MAX_MESSAGE_INTERNAL_NAME_SIZE = 255; const S32 MAX_BUFFER_SIZE = NET_BUFFER_SIZE; const S32 MAX_BLOCKS = 255; const U8 LL_ZERO_CODE_FLAG = 0x80; const U8 LL_RELIABLE_FLAG = 0x40; const U8 LL_RESENT_FLAG = 0x20; const U8 LL_ACK_FLAG = 0x10; // 1 byte flags, 4 bytes sequence, 1 byte offset + 1 byte message name (high) const S32 LL_MINIMUM_VALID_PACKET_SIZE = LL_PACKET_ID_SIZE + 1; enum EPacketHeaderLayout { PHL_FLAGS = 0, PHL_PACKET_ID = 1, PHL_OFFSET = 5, PHL_NAME = 6 }; const S32 LL_DEFAULT_RELIABLE_RETRIES = 3; const F32Seconds LL_MINIMUM_RELIABLE_TIMEOUT_SECONDS(1.f); const F32Seconds LL_MINIMUM_SEMIRELIABLE_TIMEOUT_SECONDS(1.f); const F32Seconds LL_PING_BASED_TIMEOUT_DUMMY(0.0f); const F32 LL_SEMIRELIABLE_TIMEOUT_FACTOR = 5.f; // averaged ping const F32 LL_RELIABLE_TIMEOUT_FACTOR = 5.f; // averaged ping const F32 LL_LOST_TIMEOUT_FACTOR = 16.f; // averaged ping for marking packets "Lost" const F32Seconds LL_MAX_LOST_TIMEOUT(5.f); // Maximum amount of time before considering something "lost" const S32 MAX_MESSAGE_COUNT_NUM = 1024; // Forward declarations class LLVector3; class LLVector4; class LLVector3d; class LLQuaternion; class LLSD; class LLUUID; class LLMessageSystem; class LLPumpIO; // message system exceptional condition handlers. enum EMessageException { MX_UNREGISTERED_MESSAGE, // message number not part of template MX_PACKET_TOO_SHORT, // invalid packet, shorter than minimum packet size MX_RAN_OFF_END_OF_PACKET, // ran off the end of the packet during decode MX_WROTE_PAST_BUFFER_SIZE // wrote past buffer size in zero code expand }; typedef void (*msg_exception_callback)(LLMessageSystem*,void*,EMessageException); // message data pieces are used to collect the data called for by the message template class LLMsgData; class LLMsgBlkData; class LLMessageTemplate; class LLMessagePollInfo; class LLMessageBuilder; class LLTemplateMessageBuilder; class LLSDMessageBuilder; class LLMessageReader; class LLTemplateMessageReader; class LLSDMessageReader; class LLUseCircuitCodeResponder { LOG_CLASS(LLMessageSystem); public: virtual ~LLUseCircuitCodeResponder(); virtual void complete(const LLHost& host, const LLUUID& agent) const = 0; }; /** * SL-12204: We've observed crashes when consumer code sets * LLMessageSystem::mMessageReader, assuming that all subsequent processing of * the current message will use the same mMessageReader value -- only to have * a different coroutine sneak in and replace mMessageReader before * completion. This is a limitation of sharing a stateful global resource for * message parsing; instead code receiving a new message should instantiate a * (trivially constructed) local message parser and use that. * * Until then, when one coroutine sets a particular LLMessageReader subclass * as the current message reader, ensure that no other coroutine can replace * it until the first coroutine has finished with its message. * * This is achieved with two helper classes. LLMessageSystem::mMessageReader * is now an LLMessageReaderPointer instance, which can efficiently compare or * dereference its contained LLMessageReader* but which cannot be directly * assigned. To change the value of LLMessageReaderPointer, you must * instantiate LockMessageReader with the LLMessageReader* you wish to make * current. mMessageReader will have that value for the lifetime of the * LockMessageReader instance, then revert to nullptr. Moreover, as its name * implies, LockMessageReader locks the mutex in LLMessageReaderPointer so * that any other coroutine instantiating LockMessageReader will block until * the first coroutine has destroyed its instance. */ class LLMessageReaderPointer { public: LLMessageReaderPointer(): mPtr(nullptr) {} // It is essential that comparison and dereferencing must be fast, which // is why we don't check for nullptr when dereferencing. LLMessageReader* operator->() const { return mPtr; } bool operator==(const LLMessageReader* other) const { return mPtr == other; } bool operator!=(const LLMessageReader* other) const { return ! (*this == other); } private: // Only LockMessageReader can set mPtr. friend class LockMessageReader; LLMessageReader* mPtr; LLCoros::Mutex mMutex; }; /** * To set mMessageReader to nullptr: * * @code * // use an anonymous instance that is destroyed immediately * LockMessageReader(gMessageSystem->mMessageReader, nullptr); * @endcode * * Why do we still require going through LockMessageReader at all? Because it * would be Bad if any coroutine set mMessageReader to nullptr while another * coroutine was still parsing a message. */ class LockMessageReader { public: LockMessageReader(LLMessageReaderPointer& var, LLMessageReader* instance): mVar(var.mPtr), mLock(var.mMutex) { mVar = instance; } // Some compilers reportedly fail to suppress generating implicit copy // operations even though we have a move-only LockType data member. LockMessageReader(const LockMessageReader&) = delete; LockMessageReader& operator=(const LockMessageReader&) = delete; ~LockMessageReader() { mVar = nullptr; } private: // capture a reference to LLMessageReaderPointer::mPtr decltype(LLMessageReaderPointer::mPtr)& mVar; // while holding a lock on LLMessageReaderPointer::mMutex LLCoros::LockType mLock; }; /** * LockMessageReader is great as long as you only need mMessageReader locked * during a single LLMessageSystem function call. However, empirically the * sequence from checkAllMessages() through processAcks() need mMessageReader * locked to LLTemplateMessageReader. Enforce that by making them require an * instance of LockMessageChecker. */ class LockMessageChecker; class LLMessageSystem : public LLMessageSenderInterface { private: U8 mSendBuffer[MAX_BUFFER_SIZE]; S32 mSendSize; bool mBlockUntrustedInterface; LLHost mUntrustedInterface; public: LLPacketRing mPacketRing; LLReliablePacketParams mReliablePacketParams; // Set this flag to true when you want *very* verbose logs. bool mVerboseLog; F32 mMessageFileVersionNumber; typedef std::map<const char *, LLMessageTemplate*> message_template_name_map_t; typedef std::map<U32, LLMessageTemplate*> message_template_number_map_t; private: message_template_name_map_t mMessageTemplates; message_template_number_map_t mMessageNumbers; public: S32 mSystemVersionMajor; S32 mSystemVersionMinor; S32 mSystemVersionPatch; S32 mSystemVersionServer; U32 mVersionFlags; bool mbProtected; U32 mNumberHighFreqMessages; U32 mNumberMediumFreqMessages; U32 mNumberLowFreqMessages; S32 mPort; S32 mSocket; U32 mPacketsIn; // total packets in, including compressed and uncompressed U32 mPacketsOut; // total packets out, including compressed and uncompressed U64 mBytesIn; // total bytes in, including compressed and uncompressed U64 mBytesOut; // total bytes out, including compressed and uncompressed U32 mCompressedPacketsIn; // total compressed packets in U32 mCompressedPacketsOut; // total compressed packets out U32 mReliablePacketsIn; // total reliable packets in U32 mReliablePacketsOut; // total reliable packets out U32 mDroppedPackets; // total dropped packets in U32 mResentPackets; // total resent packets out U32 mFailedResendPackets; // total resend failure packets out U32 mOffCircuitPackets; // total # of off-circuit packets rejected U32 mInvalidOnCircuitPackets; // total # of on-circuit but invalid packets rejected S64 mUncompressedBytesIn; // total uncompressed size of compressed packets in S64 mUncompressedBytesOut; // total uncompressed size of compressed packets out S64 mCompressedBytesIn; // total compressed size of compressed packets in S64 mCompressedBytesOut; // total compressed size of compressed packets out S64 mTotalBytesIn; // total size of all uncompressed packets in S64 mTotalBytesOut; // total size of all uncompressed packets out bool mSendReliable; // does the outgoing message require a pos ack? LLCircuit mCircuitInfo; F64Seconds mCircuitPrintTime; // used to print circuit debug info every couple minutes F32Seconds mCircuitPrintFreq; std::map<U64, U32> mIPPortToCircuitCode; std::map<U32, U64> mCircuitCodeToIPPort; U32 mOurCircuitCode; S32 mSendPacketFailureCount; S32 mUnackedListDepth; S32 mUnackedListSize; S32 mDSMaxListDepth; public: // Read file and build message templates LLMessageSystem(const std::string& filename, U32 port, S32 version_major, S32 version_minor, S32 version_patch, bool failure_is_fatal, const F32 circuit_heartbeat_interval, const F32 circuit_timeout); ~LLMessageSystem(); bool isOK() const { return !mbError; } S32 getErrorCode() const { return mErrorCode; } // Read file and build message templates filename must point to a // valid string which specifies the path of a valid linden // template. void loadTemplateFile(const std::string& filename, bool failure_is_fatal); // methods for building, sending, receiving, and handling messages void setHandlerFuncFast(const char *name, void (*handler_func)(LLMessageSystem *msgsystem, void **user_data), void **user_data = NULL); void setHandlerFunc(const char *name, void (*handler_func)(LLMessageSystem *msgsystem, void **user_data), void **user_data = NULL) { setHandlerFuncFast(LLMessageStringTable::getInstance()->getString(name), handler_func, user_data); } // Set a callback function for a message system exception. void setExceptionFunc(EMessageException exception, msg_exception_callback func, void* data = NULL); // Call the specified exception func, and return true if a // function was found and called. Otherwise return false. bool callExceptionFunc(EMessageException exception); // Set a function that will be called once per packet processed with the // hashed message name and the time spent in the processing handler function // measured in seconds. JC typedef void (*msg_timing_callback)(const char* hashed_name, F32 time, void* data); void setTimingFunc(msg_timing_callback func, void* data = NULL); msg_timing_callback getTimingCallback() { return mTimingCallback; } void* getTimingCallbackData() { return mTimingCallbackData; } // This method returns true if the code is in the circuit codes map. bool isCircuitCodeKnown(U32 code) const; // usually called in response to an AddCircuitCode message, but // may also be called by the login process. bool addCircuitCode(U32 code, const LLUUID& session_id); bool poll(F32 seconds); // Number of seconds that we want to block waiting for data, returns if data was received bool checkMessages(LockMessageChecker&, S64 frame_count = 0 ); void processAcks(LockMessageChecker&, F32 collect_time = 0.f); bool isMessageFast(const char *msg); bool isMessage(const char *msg) { return isMessageFast(LLMessageStringTable::getInstance()->getString(msg)); } void dumpPacketToLog(); char *getMessageName(); const LLHost& getSender() const; U32 getSenderIP() const; // getSender() is preferred U32 getSenderPort() const; // getSender() is preferred const LLHost& getReceivingInterface() const; // This method returns the uuid associated with the sender. The // UUID will be null if it is not yet known or is a server // circuit. const LLUUID& getSenderID() const; // This method returns the session id associated with the last // sender. const LLUUID& getSenderSessionID() const; // set & get the session id (useful for viewers for now.) void setMySessionID(const LLUUID& session_id) { mSessionID = session_id; } const LLUUID& getMySessionID() { return mSessionID; } void newMessageFast(const char *name); void newMessage(const char *name); public: LLStoredMessagePtr getReceivedMessage() const; LLStoredMessagePtr getBuiltMessage() const; S32 sendMessage(const LLHost &host, LLStoredMessagePtr message); private: LLSD getReceivedMessageLLSD() const; LLSD getBuiltMessageLLSD() const; // NOTE: babbage: Only use to support legacy misuse of the // LLMessageSystem API where values are dangerously written // as one type and read as another. LLSD does not support // dangerous conversions and so converting the message to an // LLSD would result in the reads failing. All code which // misuses the message system in this way should be made safe // but while the unsafe code is run in old processes, this // method should be used to forward unsafe messages. LLSD wrapReceivedTemplateData() const; LLSD wrapBuiltTemplateData() const; public: void copyMessageReceivedToSend(); void clearMessage(); void nextBlockFast(const char *blockname); void nextBlock(const char *blockname); public: void addBinaryDataFast(const char *varname, const void *data, S32 size); void addBinaryData(const char *varname, const void *data, S32 size); void addBOOLFast( const char* varname, bool b); // typed, checks storage space void addBOOL( const char* varname, bool b); // typed, checks storage space void addS8Fast( const char *varname, S8 s); // typed, checks storage space void addS8( const char *varname, S8 s); // typed, checks storage space void addU8Fast( const char *varname, U8 u); // typed, checks storage space void addU8( const char *varname, U8 u); // typed, checks storage space void addS16Fast( const char *varname, S16 i); // typed, checks storage space void addS16( const char *varname, S16 i); // typed, checks storage space void addU16Fast( const char *varname, U16 i); // typed, checks storage space void addU16( const char *varname, U16 i); // typed, checks storage space void addF32Fast( const char *varname, F32 f); // typed, checks storage space void addF32( const char *varname, F32 f); // typed, checks storage space void addS32Fast( const char *varname, S32 s); // typed, checks storage space void addS32( const char *varname, S32 s); // typed, checks storage space void addU32Fast( const char *varname, U32 u); // typed, checks storage space void addU32( const char *varname, U32 u); // typed, checks storage space void addU64Fast( const char *varname, U64 lu); // typed, checks storage space void addU64( const char *varname, U64 lu); // typed, checks storage space void addF64Fast( const char *varname, F64 d); // typed, checks storage space void addF64( const char *varname, F64 d); // typed, checks storage space void addVector3Fast( const char *varname, const LLVector3& vec); // typed, checks storage space void addVector3( const char *varname, const LLVector3& vec); // typed, checks storage space void addVector4Fast( const char *varname, const LLVector4& vec); // typed, checks storage space void addVector4( const char *varname, const LLVector4& vec); // typed, checks storage space void addVector3dFast( const char *varname, const LLVector3d& vec); // typed, checks storage space void addVector3d( const char *varname, const LLVector3d& vec); // typed, checks storage space void addQuatFast( const char *varname, const LLQuaternion& quat); // typed, checks storage space void addQuat( const char *varname, const LLQuaternion& quat); // typed, checks storage space void addUUIDFast( const char *varname, const LLUUID& uuid); // typed, checks storage space void addUUID( const char *varname, const LLUUID& uuid); // typed, checks storage space void addIPAddrFast( const char *varname, const U32 ip); // typed, checks storage space void addIPAddr( const char *varname, const U32 ip); // typed, checks storage space void addIPPortFast( const char *varname, const U16 port); // typed, checks storage space void addIPPort( const char *varname, const U16 port); // typed, checks storage space void addStringFast( const char* varname, const char* s); // typed, checks storage space void addString( const char* varname, const char* s); // typed, checks storage space void addStringFast( const char* varname, const std::string& s); // typed, checks storage space void addString( const char* varname, const std::string& s); // typed, checks storage space S32 getCurrentSendTotal() const; TPACKETID getCurrentRecvPacketID() { return mCurrentRecvPacketID; } // This method checks for current send total and returns true if // you need to go to the next block type or need to start a new // message. Specify the current blockname to check block counts, // otherwise the method only checks against MTU. bool isSendFull(const char* blockname = NULL); bool isSendFullFast(const char* blockname = NULL); bool removeLastBlock(); //void buildMessage(); S32 zeroCode(U8 **data, S32 *data_size); S32 zeroCodeExpand(U8 **data, S32 *data_size); S32 zeroCodeAdjustCurrentSendTotal(); // Uses ping-based retry S32 sendReliable(const LLHost &host); // Uses ping-based retry S32 sendReliable(const U32 circuit) { return sendReliable(findHost(circuit)); } // Use this one if you DON'T want automatic ping-based retry. S32 sendReliable( const LLHost &host, S32 retries, bool ping_based_retries, F32Seconds timeout, void (*callback)(void **,S32), void ** callback_data); S32 sendSemiReliable( const LLHost &host, void (*callback)(void **,S32), void ** callback_data); // flush sends a message only if data's been pushed on it. S32 flushSemiReliable( const LLHost &host, void (*callback)(void **,S32), void ** callback_data); S32 flushReliable( const LLHost &host ); void forwardMessage(const LLHost &host); void forwardReliable(const LLHost &host); void forwardReliable(const U32 circuit_code); S32 forwardReliable( const LLHost &host, S32 retries, bool ping_based_timeout, F32Seconds timeout, void (*callback)(void **,S32), void ** callback_data); S32 sendMessage(const LLHost &host); S32 sendMessage(const U32 circuit); private: S32 sendMessage(const LLHost &host, const char* name, const LLSD& message); public: // bool decodeData(const U8 *buffer, const LLHost &host); /** gets binary data from the current message. @param blockname the name of the block in the message (from the message template) @param varname @param datap @param size expected size - set to zero to get any amount of data up to max_size. Make sure max_size is set in that case! @param blocknum @param max_size the max number of bytes to read */ void getBinaryDataFast(const char *blockname, const char *varname, void *datap, S32 size, S32 blocknum = 0, S32 max_size = S32_MAX); void getBinaryData(const char *blockname, const char *varname, void *datap, S32 size, S32 blocknum = 0, S32 max_size = S32_MAX); void getBOOLFast( const char *block, const char *var, bool &data, S32 blocknum = 0); void getBOOL( const char *block, const char *var, bool &data, S32 blocknum = 0); void getS8Fast( const char *block, const char *var, S8 &data, S32 blocknum = 0); void getS8( const char *block, const char *var, S8 &data, S32 blocknum = 0); void getU8Fast( const char *block, const char *var, U8 &data, S32 blocknum = 0); void getU8( const char *block, const char *var, U8 &data, S32 blocknum = 0); void getS16Fast( const char *block, const char *var, S16 &data, S32 blocknum = 0); void getS16( const char *block, const char *var, S16 &data, S32 blocknum = 0); void getU16Fast( const char *block, const char *var, U16 &data, S32 blocknum = 0); void getU16( const char *block, const char *var, U16 &data, S32 blocknum = 0); void getS32Fast( const char *block, const char *var, S32 &data, S32 blocknum = 0); void getS32( const char *block, const char *var, S32 &data, S32 blocknum = 0); void getF32Fast( const char *block, const char *var, F32 &data, S32 blocknum = 0); void getF32( const char *block, const char *var, F32 &data, S32 blocknum = 0); void getU32Fast( const char *block, const char *var, U32 &data, S32 blocknum = 0); void getU32( const char *block, const char *var, U32 &data, S32 blocknum = 0); void getU64Fast( const char *block, const char *var, U64 &data, S32 blocknum = 0); void getU64( const char *block, const char *var, U64 &data, S32 blocknum = 0); void getF64Fast( const char *block, const char *var, F64 &data, S32 blocknum = 0); void getF64( const char *block, const char *var, F64 &data, S32 blocknum = 0); void getVector3Fast( const char *block, const char *var, LLVector3 &vec, S32 blocknum = 0); void getVector3( const char *block, const char *var, LLVector3 &vec, S32 blocknum = 0); void getVector4Fast( const char *block, const char *var, LLVector4 &vec, S32 blocknum = 0); void getVector4( const char *block, const char *var, LLVector4 &vec, S32 blocknum = 0); void getVector3dFast(const char *block, const char *var, LLVector3d &vec, S32 blocknum = 0); void getVector3d(const char *block, const char *var, LLVector3d &vec, S32 blocknum = 0); void getQuatFast( const char *block, const char *var, LLQuaternion &q, S32 blocknum = 0); void getQuat( const char *block, const char *var, LLQuaternion &q, S32 blocknum = 0); void getUUIDFast( const char *block, const char *var, LLUUID &uuid, S32 blocknum = 0); void getUUID( const char *block, const char *var, LLUUID &uuid, S32 blocknum = 0); void getIPAddrFast( const char *block, const char *var, U32 &ip, S32 blocknum = 0); void getIPAddr( const char *block, const char *var, U32 &ip, S32 blocknum = 0); void getIPPortFast( const char *block, const char *var, U16 &port, S32 blocknum = 0); void getIPPort( const char *block, const char *var, U16 &port, S32 blocknum = 0); void getStringFast( const char *block, const char *var, S32 buffer_size, char *buffer, S32 blocknum = 0); void getString( const char *block, const char *var, S32 buffer_size, char *buffer, S32 blocknum = 0); void getStringFast( const char *block, const char *var, std::string& outstr, S32 blocknum = 0); void getString( const char *block, const char *var, std::string& outstr, S32 blocknum = 0); // Utility functions to generate a replay-resistant digest check // against the shared secret. The window specifies how much of a // time window is allowed - 1 second is good for tight // connections, but multi-process windows might want to be upwards // of 5 seconds. For generateDigest, you want to pass in a // character array of at least MD5HEX_STR_SIZE so that the hex // digest and null termination will fit. bool generateDigestForNumberAndUUIDs(char* digest, const U32 number, const LLUUID &id1, const LLUUID &id2) const; bool generateDigestForWindowAndUUIDs(char* digest, const S32 window, const LLUUID &id1, const LLUUID &id2) const; bool isMatchingDigestForWindowAndUUIDs(const char* digest, const S32 window, const LLUUID &id1, const LLUUID &id2) const; bool generateDigestForNumber(char* digest, const U32 number) const; bool generateDigestForWindow(char* digest, const S32 window) const; bool isMatchingDigestForWindow(const char* digest, const S32 window) const; void showCircuitInfo(); void getCircuitInfo(LLSD& info) const; U32 getOurCircuitCode(); void enableCircuit(const LLHost &host, bool trusted); void disableCircuit(const LLHost &host); // Use this to establish trust on startup and in response to // DenyTrustedCircuit. void sendCreateTrustedCircuit(const LLHost& host, const LLUUID & id1, const LLUUID & id2); // Use this to inform a peer that they aren't currently trusted... // This now enqueues the request so that we can ensure that we only send // one deny per circuit per message loop so that this doesn't become a DoS. // The actual sending is done by reallySendDenyTrustedCircuit() void sendDenyTrustedCircuit(const LLHost &host); /** Return false if host is unknown or untrusted */ // Note:DaveH/Babbage some trusted messages can be received without a circuit bool isTrustedSender(const LLHost& host) const; /** Return true if current message is from trusted source */ bool isTrustedSender() const; /** Return false true if name is unknown or untrusted */ bool isTrustedMessage(const std::string& name) const; /** Return false true if name is unknown or trusted */ bool isUntrustedMessage(const std::string& name) const; // Mark an interface ineligible for trust void setUntrustedInterface( const LLHost host ) { mUntrustedInterface = host; } LLHost getUntrustedInterface() const { return mUntrustedInterface; } void setBlockUntrustedInterface( bool block ) { mBlockUntrustedInterface = block; } // Throw a switch to allow, sending warnings only bool getBlockUntrustedInterface() const { return mBlockUntrustedInterface; } // Change this message to be UDP black listed. void banUdpMessage(const std::string& name); private: // A list of the circuits that need to be sent DenyTrustedCircuit messages. typedef std::set<LLHost> host_set_t; host_set_t mDenyTrustedCircuitSet; // Really sends the DenyTrustedCircuit message to a given host // related to sendDenyTrustedCircuit() void reallySendDenyTrustedCircuit(const LLHost &host); public: // Use this to establish trust to and from a host. This blocks // until trust has been established, and probably should only be // used on startup. void establishBidirectionalTrust(const LLHost &host, S64 frame_count = 0); // returns whether the given host is on a trusted circuit // Note:DaveH/Babbage some trusted messages can be received without a circuit bool getCircuitTrust(const LLHost &host); void setCircuitAllowTimeout(const LLHost &host, bool allow); void setCircuitTimeoutCallback(const LLHost &host, void (*callback_func)(const LLHost &host, void *user_data), void *user_data); bool checkCircuitBlocked(const U32 circuit); bool checkCircuitAlive(const U32 circuit); bool checkCircuitAlive(const LLHost &host); void setCircuitProtection(bool b_protect); U32 findCircuitCode(const LLHost &host); LLHost findHost(const U32 circuit_code); void sanityCheck(); bool has(const char *blockname) const; S32 getNumberOfBlocksFast(const char *blockname) const; S32 getNumberOfBlocks(const char *blockname) const; S32 getSizeFast(const char *blockname, const char *varname) const; S32 getSize(const char *blockname, const char *varname) const; S32 getSizeFast(const char *blockname, S32 blocknum, const char *varname) const; // size in bytes of data S32 getSize(const char *blockname, S32 blocknum, const char *varname) const; void resetReceiveCounts(); // resets receive counts for all message types to 0 void dumpReceiveCounts(); // dumps receive count for each message type to LL_INFOS() void dumpCircuitInfo(); // Circuit information to LL_INFOS() bool isClear() const; // returns mbSClear; S32 flush(const LLHost &host); U32 getListenPort( void ) const; void startLogging(); // start verbose logging void stopLogging(); // flush and close file void summarizeLogs(std::ostream& str); // log statistics S32 getReceiveSize() const; S32 getReceiveCompressedSize() const { return mIncomingCompressedSize; } S32 getReceiveBytes() const; S32 getUnackedListSize() const { return mUnackedListSize; } //const char* getCurrentSMessageName() const { return mCurrentSMessageName; } //const char* getCurrentSBlockName() const { return mCurrentSBlockName; } // friends friend std::ostream& operator<<(std::ostream& s, LLMessageSystem &msg); void setMaxMessageTime(const F32 seconds); // Max time to process messages before warning and dumping (neg to disable) void setMaxMessageCounts(const S32 num); // Max number of messages before dumping (neg to disable) static U64Microseconds getMessageTimeUsecs(const bool update = false); // Get the current message system time in microseconds static F64Seconds getMessageTimeSeconds(const bool update = false); // Get the current message system time in seconds static void setTimeDecodes(bool b); static void setTimeDecodesSpamThreshold(F32 seconds); // message handlers internal to the message systesm //static void processAssignCircuitCode(LLMessageSystem* msg, void**); static void processAddCircuitCode(LLMessageSystem* msg, void**); static void processUseCircuitCode(LLMessageSystem* msg, void**); static void processError(LLMessageSystem* msg, void**); // dispatch llsd message to http node tree static void dispatch(const std::string& msg_name, const LLSD& message); static void dispatch(const std::string& msg_name, const LLSD& message, LLHTTPNode::ResponsePtr responsep); // this is added to support specific legacy messages and is // ***not intended for general use*** Si, Gabriel, 2009 static void dispatchTemplate(const std::string& msg_name, const LLSD& message, LLHTTPNode::ResponsePtr responsep); void setMessageBans(const LLSD& trusted, const LLSD& untrusted); /** * @brief send an error message to the host. This is a helper method. * * @param host Destination host. * @param agent_id Destination agent id (may be null) * @param code An HTTP status compatible error code. * @param token A specific short string based message * @param id The transactionid/uniqueid/sessionid whatever. * @param system The hierarchical path to the system (255 bytes) * @param message Human readable message (1200 bytes) * @param data Extra info. * @return Returns value returned from sendReliable(). */ S32 sendError( const LLHost& host, const LLUUID& agent_id, S32 code, const std::string& token, const LLUUID& id, const std::string& system, const std::string& message, const LLSD& data); // Check UDP messages and pump http_pump to receive HTTP messages. bool checkAllMessages(LockMessageChecker&, S64 frame_count, LLPumpIO* http_pump); // Moved to allow access from LLTemplateMessageDispatcher void clearReceiveState(); // This will cause all trust queries to return true until the next message // is read: use with caution! void receivedMessageFromTrustedSender(); private: typedef boost::function<void(S32)> UntrustedCallback_t; void sendUntrustedSimulatorMessageCoro(std::string url, std::string message, LLSD body, UntrustedCallback_t callback); bool mLastMessageFromTrustedMessageService; // The mCircuitCodes is a map from circuit codes to session // ids. This allows us to verify sessions on connect. typedef std::map<U32, LLUUID> code_session_map_t; code_session_map_t mCircuitCodes; // Viewers need to track a process session in order to make sure // that no one gives them a bad circuit code. LLUUID mSessionID; void addTemplate(LLMessageTemplate *templatep); bool decodeTemplate( const U8* buffer, S32 buffer_size, LLMessageTemplate** msg_template ); void logMsgFromInvalidCircuit( const LLHost& sender, bool recv_reliable ); void logTrustedMsgFromUntrustedCircuit( const LLHost& sender ); void logValidMsg(LLCircuitData *cdp, const LLHost& sender, bool recv_reliable, bool recv_resent, bool recv_acks ); void logRanOffEndOfPacket( const LLHost& sender ); class LLMessageCountInfo { public: U32 mMessageNum; U32 mMessageBytes; bool mInvalid; }; LLMessagePollInfo *mPollInfop; U8 mEncodedRecvBuffer[MAX_BUFFER_SIZE]; U8 mTrueReceiveBuffer[MAX_BUFFER_SIZE]; S32 mTrueReceiveSize; // Must be valid during decode bool mbError; S32 mErrorCode; F64Seconds mResendDumpTime; // The last time we dumped resends LLMessageCountInfo mMessageCountList[MAX_MESSAGE_COUNT_NUM]; S32 mNumMessageCounts; F32Seconds mReceiveTime; F32Seconds mMaxMessageTime; // Max number of seconds for processing messages S32 mMaxMessageCounts; // Max number of messages to process before dumping. F64Seconds mMessageCountTime; F64Seconds mCurrentMessageTime; // The current "message system time" (updated the first call to checkMessages after a resetReceiveCount // message system exceptions typedef std::pair<msg_exception_callback, void*> exception_t; typedef std::map<EMessageException, exception_t> callbacks_t; callbacks_t mExceptionCallbacks; // stuff for logging LLTimer mMessageSystemTimer; static F32 mTimeDecodesSpamThreshold; // If mTimeDecodes is on, all this many seconds for each msg decode before spamming static bool mTimeDecodes; // Measure time for all message decodes if true; msg_timing_callback mTimingCallback; void* mTimingCallbackData; void init(); // ctor shared initialisation. LLHost mLastSender; LLHost mLastReceivingIF; S32 mIncomingCompressedSize; // original size of compressed msg (0 if uncomp.) TPACKETID mCurrentRecvPacketID; // packet ID of current receive packet (for reporting) LLMessageBuilder* mMessageBuilder; LLTemplateMessageBuilder* mTemplateMessageBuilder; LLSDMessageBuilder* mLLSDMessageBuilder; LLMessageReaderPointer mMessageReader; LLTemplateMessageReader* mTemplateMessageReader; LLSDMessageReader* mLLSDMessageReader; friend class LLMessageHandlerBridge; friend class LockMessageChecker; bool callHandler(const char *name, bool trustedSource, LLMessageSystem* msg); /** Find, create or revive circuit for host as needed */ LLCircuitData* findCircuit(const LLHost& host, bool resetPacketId); }; // external hook into messaging system extern LLPounceable<LLMessageSystem*, LLPounceableStatic> gMessageSystem; // Implementation of LockMessageChecker depends on definition of // LLMessageSystem, hence must follow it. class LockMessageChecker: public LockMessageReader { public: LockMessageChecker(LLMessageSystem* msgsystem); // For convenience, provide forwarding wrappers so you can call (e.g.) // checkAllMessages() on your LockMessageChecker instance instead of // passing the instance to LLMessageSystem::checkAllMessages(). Use // perfect forwarding to avoid having to maintain these wrappers in sync // with the target methods. template <typename... ARGS> bool checkAllMessages(ARGS&&... args) { return mMessageSystem->checkAllMessages(*this, std::forward<ARGS>(args)...); } template <typename... ARGS> bool checkMessages(ARGS&&... args) { return mMessageSystem->checkMessages(*this, std::forward<ARGS>(args)...); } template <typename... ARGS> void processAcks(ARGS&&... args) { return mMessageSystem->processAcks(*this, std::forward<ARGS>(args)...); } private: LLMessageSystem* mMessageSystem; }; // Must specific overall system version, which is used to determine // if a patch is available in the message template checksum verification. // Return true if able to initialize system. 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, const LLUseCircuitCodeResponder* responder, bool failure_is_fatal, const F32 circuit_heartbeat_interval, const F32 circuit_timeout); void end_messaging_system(bool print_summary = true); void null_message_callback(LLMessageSystem *msg, void **data); // // Inlines // #if !defined( LL_BIG_ENDIAN ) && !defined( LL_LITTLE_ENDIAN ) #error Unknown endianness for htolememcpy. Did you miss a common include? #endif static inline void *htolememcpy(void *vs, const void *vct, EMsgVariableType type, size_t n) { char *s = (char *)vs; const char *ct = (const char *)vct; #ifdef LL_BIG_ENDIAN S32 i, length; #endif switch(type) { case MVT_FIXED: case MVT_VARIABLE: case MVT_U8: case MVT_S8: case MVT_BOOL: case MVT_LLUUID: case MVT_IP_ADDR: // these two are swizzled in the getters and setters case MVT_IP_PORT: // these two are swizzled in the getters and setters return(memcpy(s,ct,n)); /* Flawfinder: ignore */ case MVT_U16: case MVT_S16: if (n != 2) { LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL; } #ifdef LL_BIG_ENDIAN *(s + 1) = *(ct); *(s) = *(ct + 1); return(vs); #else return(memcpy(s,ct,n)); /* Flawfinder: ignore */ #endif case MVT_U32: case MVT_S32: case MVT_F32: if (n != 4) { LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL; } #ifdef LL_BIG_ENDIAN *(s + 3) = *(ct); *(s + 2) = *(ct + 1); *(s + 1) = *(ct + 2); *(s) = *(ct + 3); return(vs); #else return(memcpy(s,ct,n)); /* Flawfinder: ignore */ #endif case MVT_U64: case MVT_S64: case MVT_F64: if (n != 8) { LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL; } #ifdef LL_BIG_ENDIAN *(s + 7) = *(ct); *(s + 6) = *(ct + 1); *(s + 5) = *(ct + 2); *(s + 4) = *(ct + 3); *(s + 3) = *(ct + 4); *(s + 2) = *(ct + 5); *(s + 1) = *(ct + 6); *(s) = *(ct + 7); return(vs); #else return(memcpy(s,ct,n)); /* Flawfinder: ignore */ #endif case MVT_LLVector3: case MVT_LLQuaternion: // We only send x, y, z and infer w (we set x, y, z to ensure that w >= 0) if (n != 12) { LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL; } #ifdef LL_BIG_ENDIAN htolememcpy(s + 8, ct + 8, MVT_F32, 4); htolememcpy(s + 4, ct + 4, MVT_F32, 4); return(htolememcpy(s, ct, MVT_F32, 4)); #else return(memcpy(s,ct,n)); /* Flawfinder: ignore */ #endif case MVT_LLVector3d: if (n != 24) { LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL; } #ifdef LL_BIG_ENDIAN htolememcpy(s + 16, ct + 16, MVT_F64, 8); htolememcpy(s + 8, ct + 8, MVT_F64, 8); return(htolememcpy(s, ct, MVT_F64, 8)); #else return(memcpy(s,ct,n)); /* Flawfinder: ignore */ #endif case MVT_LLVector4: if (n != 16) { LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL; } #ifdef LL_BIG_ENDIAN htolememcpy(s + 12, ct + 12, MVT_F32, 4); htolememcpy(s + 8, ct + 8, MVT_F32, 4); htolememcpy(s + 4, ct + 4, MVT_F32, 4); return(htolememcpy(s, ct, MVT_F32, 4)); #else return(memcpy(s,ct,n)); /* Flawfinder: ignore */ #endif case MVT_U16Vec3: if (n != 6) { LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL; } #ifdef LL_BIG_ENDIAN htolememcpy(s + 4, ct + 4, MVT_U16, 2); htolememcpy(s + 2, ct + 2, MVT_U16, 2); return(htolememcpy(s, ct, MVT_U16, 2)); #else return(memcpy(s,ct,n)); /* Flawfinder: ignore */ #endif case MVT_U16Quat: if (n != 8) { LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL; } #ifdef LL_BIG_ENDIAN htolememcpy(s + 6, ct + 6, MVT_U16, 2); htolememcpy(s + 4, ct + 4, MVT_U16, 2); htolememcpy(s + 2, ct + 2, MVT_U16, 2); return(htolememcpy(s, ct, MVT_U16, 2)); #else return(memcpy(s,ct,n)); /* Flawfinder: ignore */ #endif case MVT_S16Array: if (n % 2) { LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL; } #ifdef LL_BIG_ENDIAN length = n % 2; for (i = 1; i < length; i++) { htolememcpy(s + i*2, ct + i*2, MVT_S16, 2); } return(htolememcpy(s, ct, MVT_S16, 2)); #else return(memcpy(s,ct,n)); #endif default: return(memcpy(s,ct,n)); /* Flawfinder: ignore */ } } inline void *ntohmemcpy(void *s, const void *ct, EMsgVariableType type, size_t n) { return(htolememcpy(s,ct,type, n)); } inline const LLHost& LLMessageSystem::getReceivingInterface() const {return mLastReceivingIF;} inline U32 LLMessageSystem::getSenderIP() const { return mLastSender.getAddress(); } inline U32 LLMessageSystem::getSenderPort() const { return mLastSender.getPort(); } //----------------------------------------------------------------------------- // Transmission aliases //----------------------------------------------------------------------------- inline S32 LLMessageSystem::sendMessage(const U32 circuit) { return sendMessage(findHost(circuit)); } #endif