diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt
index 41dae01a7..b4dc33db0 100644
--- a/indra/llmessage/CMakeLists.txt
+++ b/indra/llmessage/CMakeLists.txt
@@ -55,6 +55,7 @@ set(llmessage_SOURCE_FILES
llmessagethrottle.cpp
llmime.cpp
llnamevalue.cpp
+ llnetcanary.cpp
llnullcipher.cpp
llpacketack.cpp
llpacketbuffer.cpp
@@ -147,6 +148,7 @@ set(llmessage_HEADER_FILES
llmime.h
llmsgvariabletype.h
llnamevalue.h
+ llnetcanary.h
llnullcipher.h
llpacketack.h
llpacketbuffer.h
diff --git a/indra/llmessage/llnetcanary.cpp b/indra/llmessage/llnetcanary.cpp
new file mode 100644
index 000000000..59ede8029
--- /dev/null
+++ b/indra/llmessage/llnetcanary.cpp
@@ -0,0 +1,140 @@
+//
+#include "llnetcanary.h"
+#include "llerror.h"
+#ifdef _MSC_VER
+#include
+static WSADATA trapWSAData;
+#else
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+extern int errno; //error number
+#define SOCKADDR_IN struct sockaddr_in
+#define SOCKADDR struct sockaddr
+#define SOCKET int
+#define INVALID_SOCKET -1
+#define SOCKET_ERROR -1
+#define SD_BOTH 2
+#define closesocket close
+#endif
+SOCKADDR_IN trapLclAddr;
+LLNetCanary::LLNetCanary(int requested_port)
+{
+ mGood = TRUE;
+ int nRet;
+ int hSocket;
+ int snd_size = 400000;
+ int rec_size = 400000;
+ int buff_size = 4;
+#ifdef _MSC_VER
+ if(WSAStartup(0x0202, &trapWSAData))
+ {
+ S32 err = WSAGetLastError();
+ WSACleanup();
+ llwarns << "WSAStartup() failure: " << err << llendl;
+ mGood = FALSE;
+ return;
+ }
+#endif
+ // Get a datagram socket
+ hSocket = (int)socket(AF_INET, SOCK_DGRAM, 0);
+ if (hSocket == INVALID_SOCKET)
+ {
+#ifdef _MSC_VER
+ S32 err = WSAGetLastError();
+ WSACleanup();
+#else
+ S32 err = errno;
+#endif
+ mGood = FALSE;
+ llwarns << "socket() failure: " << err << llendl;
+ return;
+ }
+
+ // Name the socket (assign the local port number to receive on)
+ trapLclAddr.sin_family = AF_INET;
+ trapLclAddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ trapLclAddr.sin_port = htons(requested_port);
+ //llinfos << "bind() port: " << requested_port << llendl;
+ nRet = bind(hSocket, (struct sockaddr*) &trapLclAddr, sizeof(trapLclAddr));
+ if (nRet == SOCKET_ERROR)
+ {
+#ifdef _MSC_VER
+ S32 err = WSAGetLastError();
+ WSACleanup();
+#else
+ S32 err = errno;
+#endif
+ mGood = FALSE;
+ llwarns << "bind() failed: " << err << llendl;
+ return;
+ }
+
+ // Find out what address we got
+ SOCKADDR_IN socket_address;
+ S32 socket_address_size = sizeof(socket_address);
+ getsockname(hSocket, (SOCKADDR*) &socket_address, (socklen_t *)&socket_address_size);
+ mPort = ntohs(socket_address.sin_port);
+ //llinfos << "got port " << mPort << llendl;
+
+ // Set socket to be non-blocking
+#ifdef _MSC_VER
+ unsigned long argp = 1;
+ nRet = ioctlsocket (hSocket, FIONBIO, &argp);
+#else
+ nRet = fcntl(hSocket, F_SETFL, O_NONBLOCK);
+#endif
+ if (nRet == SOCKET_ERROR)
+ {
+#ifdef _MSC_VER
+ S32 err = WSAGetLastError();
+ WSACleanup();
+#else
+ S32 err = errno;
+#endif
+ mGood = FALSE;
+ llwarns << "Failed to set socket non-blocking, Err: " << err << llendl;
+ return;
+ }
+
+ // set a large receive buffer
+ nRet = setsockopt(hSocket, SOL_SOCKET, SO_RCVBUF, (char *)&rec_size, buff_size);
+ if (nRet)
+ {
+ llinfos << "Can't set receive buffer size!" << llendl;
+ }
+ // set a large send buffer
+ nRet = setsockopt(hSocket, SOL_SOCKET, SO_SNDBUF, (char *)&snd_size, buff_size);
+ if (nRet)
+ {
+ llinfos << "Can't set send buffer size!" << llendl;
+ }
+ //getsockopt(hSocket, SOL_SOCKET, SO_RCVBUF, (char *)&rec_size, &buff_size);
+ //getsockopt(hSocket, SOL_SOCKET, SO_SNDBUF, (char *)&snd_size, &buff_size);
+ //LL_DEBUGS("AppInit") << "startNet - receive buffer size : " << rec_size << LL_ENDL;
+ //LL_DEBUGS("AppInit") << "startNet - send buffer size : " << snd_size << LL_ENDL;
+
+ mSocket = hSocket;
+}
+LLNetCanary::~LLNetCanary()
+{
+ if(mGood)
+ {
+ if(mSocket)
+ {
+ shutdown(mSocket, SD_BOTH);
+ closesocket(mSocket);
+ }
+#ifdef _MSC_VER
+ WSACleanup();
+#endif
+ }
+}
+//
diff --git a/indra/llmessage/llnetcanary.h b/indra/llmessage/llnetcanary.h
new file mode 100644
index 000000000..a5a5d6b4e
--- /dev/null
+++ b/indra/llmessage/llnetcanary.h
@@ -0,0 +1,25 @@
+//
+#ifndef LL_LLNETCANARY_H
+#define LL_LLNETCANARY_H
+#include "stdtypes.h"
+#include
+class LLNetCanary
+{
+public:
+ LLNetCanary(int requested_port);
+ ~LLNetCanary();
+ BOOL mGood;
+ S32 mSocket;
+ U16 mPort;
+ U8 mBuffer[8192];
+ typedef struct
+ {
+ F64 time; // LLTimer::getElapsedSeconds();
+ //U8 message[4];
+ U32 message;
+ U32 points;
+ std::string name;
+ } entry;
+};
+#endif
+//
diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp
index a66a1013f..ed70e5408 100644
--- a/indra/llmessage/message.cpp
+++ b/indra/llmessage/message.cpp
@@ -550,6 +550,76 @@ BOOL LLMessageSystem::checkMessages( S64 frame_count, bool faked_message, U8 fak
S32 receive_size = 0;
do
{
+ //
+ // Expire old canary entries
+ F64 now = LLTimer::getElapsedSeconds();
+ std::map::iterator canary_entry_iter;
+ std::map::iterator canary_entry_end = mCanaryEntries.end();
+ for( canary_entry_iter = mCanaryEntries.begin();
+ canary_entry_iter != mCanaryEntries.end(); )
+ {
+ if( ((*canary_entry_iter).second.time + 30.0f) < now)
+ {
+ llinfos << "Expiring ban on " << (*canary_entry_iter).second.name << " message, " << (*canary_entry_iter).second.points << " points" << llendl;
+ mCanaryEntries.erase(canary_entry_iter++);
+ }
+ else
+ {
+ canary_entry_iter++;
+ }
+ }
+ if(!faked_message && mSpoofProtectionLevel > 1)
+ {
+ // Canaries receive
+ std::vector::iterator canary_iter = mCanaries.begin();
+ std::vector::iterator canary_end = mCanaries.end();
+ for( ; canary_iter != canary_end; ++canary_iter)
+ {
+ U8* canary_buffer = (*canary_iter)->mBuffer;
+ S32 len = receive_packet((*canary_iter)->mSocket, (char*)canary_buffer);
+ if(len)
+ {
+ //llinfos << "canary received " << len << " bytes on port " << (*canary_iter)->mPort << llendl;
+ zeroCodeExpand(&canary_buffer, &len);
+ if(len < 7) continue; // too short to be an slmsg
+ LLNetCanary::entry entry;
+ entry.message = 0;
+ if(canary_buffer[6] != 0xFF) // High XX
+ entry.message = canary_buffer[6];
+ else if((len >= 8) && (canary_buffer[7] != 0xFF)) // Medium FFXX
+ entry.message = (255 << 8) | canary_buffer[7];
+ else if((len >= 10) && (canary_buffer[7] == 0xFF)) // Low FFFFXXXX
+ {
+ U16 message_id_U16 = 0;
+ memcpy(&message_id_U16, &canary_buffer[8], 2);
+ message_id_U16 = ntohs(message_id_U16);
+ entry.message = 0xFFFF0000 | message_id_U16;
+ }
+ else continue; // not an slmsg
+
+ if(mCanaryEntries.find(entry.message) == mCanaryEntries.end())
+ {
+ // brand new entry
+ LLMessageTemplate* temp = get_ptr_in_map(mMessageNumbers, entry.message);
+ entry.name = temp ? temp->mName : "Invalid";
+ entry.points = 1;
+ entry.time = now;
+ mCanaryEntries[entry.message] = entry;
+ if(mSpoofProtectionLevel == 2)
+ llinfos << "Temporarily banning a " << entry.name << " message" << llendl;
+ }
+ else
+ {
+ // strike two, three...
+ mCanaryEntries[entry.message].points++;
+ mCanaryEntries[entry.message].time = now;
+ if((mSpoofProtectionLevel > 2) && (mCanaryEntries[entry.message].points == 2))
+ llinfos << "Temporarily banning a " << mCanaryEntries[entry.message].name << " message" << llendl;
+ }
+ }
+ }
+ }
+ //
clearReceiveState();
BOOL recv_reliable = FALSE;
@@ -625,6 +695,61 @@ BOOL LLMessageSystem::checkMessages( S64 frame_count, bool faked_message, U8 fak
// process the message as normal
mIncomingCompressedSize = zeroCodeExpand(&buffer, &receive_size);
mCurrentRecvPacketID = ntohl(*((U32*)(&buffer[1])));
+ //
+ BOOL spoofed_packet = FALSE;
+ if(mSpoofProtectionLevel > 0)
+ {
+ S32 len = receive_size;
+ U32 message = 0;
+ if(buffer[6] != 0xFF) // High XX
+ message = buffer[6];
+ else if((len >= 8) && (buffer[7] != 0xFF)) // Medium FFXX
+ message = (255 << 8) | buffer[7];
+ else if((len >= 10) && (buffer[7] == 0xFF)) // Low FFFFXXXX
+ {
+ U16 message_id_U16 = 0;
+ memcpy(&message_id_U16, &buffer[8], 2);
+ message_id_U16 = ntohs(message_id_U16);
+ message = 0xFFFF0000 | message_id_U16;
+ }
+ if(!mCurrentRecvPacketID)
+ {
+ LL_WARNS("Messaging") << "CODE DONKEY_A" << llendl;
+ if(mSpoofDroppedCallback)
+ {
+ LLNetCanary::entry entry;
+ LLMessageTemplate* temp = get_ptr_in_map(mMessageNumbers, message);
+ entry.name = temp ? temp->mName : "Invalid";
+ entry.message = message;
+ entry.time = now;
+ entry.points = 1;
+ mSpoofDroppedCallback(entry);
+ }
+ spoofed_packet = TRUE;
+ valid_packet = FALSE;
+ }
+ else if((mSpoofProtectionLevel > 1) && (receive_size >= 7))
+ {
+ if(mCanaryEntries.find(message) != mCanaryEntries.end())
+ {
+ if(
+ (mSpoofProtectionLevel == 2) ||
+ (mCanaryEntries[message].points > 1)
+ )
+ {
+ LL_WARNS("Messaging") << "Dropped probably spoofed " << mCanaryEntries[message].name << " packet, " << mCanaryEntries[message].points << " points" << llendl;
+ if(mSpoofDroppedCallback)
+ {
+ mSpoofDroppedCallback(mCanaryEntries[message]);
+ }
+ spoofed_packet = TRUE;
+ valid_packet = FALSE;
+ break;
+ }
+ }
+ }
+ } // mSpoofProtectionLevel
+ //
host = getSender();
const bool resetPacketId = true;
@@ -707,6 +832,9 @@ BOOL LLMessageSystem::checkMessages( S64 frame_count, bool faked_message, U8 fak
// But we don't want to acknowledge UseCircuitCode until the circuit is
// available, which is why the acknowledgement test is done above. JC
bool trusted = cdp && cdp->getTrusted();
+ //
+ if(!spoofed_packet)
+ //
valid_packet = mTemplateMessageReader->validateMessage(
buffer,
receive_size,
@@ -1862,7 +1990,11 @@ void open_circuit(LLMessageSystem *msgsystem, void** /*user_data*/)
msgsystem->getIPPortFast(_PREHASH_CircuitInfo, _PREHASH_Port, port);
// By default, OpenCircuit's are untrusted
- msgsystem->enableCircuit(LLHost(ip, port), FALSE);
+ //
+//#ifndef LL_RELEASE_FOR_DOWNLOAD
+ llwarns << "OpenCircuit " << LLHost(ip, port) << llendl;
+//#endif
+ // msgsystem->enableCircuit(LLHost(ip, port), FALSE);
}
void close_circuit(LLMessageSystem *msgsystem, void** /*user_data*/)
@@ -2718,6 +2850,9 @@ void end_messaging_system(bool print_summary)
LLTransferTargetVFile::updateQueue(true); // shutdown LLTransferTargetVFile
if (gMessageSystem)
{
+ //
+ gMessageSystem->stopSpoofProtection();
+ //
gMessageSystem->stopLogging();
if (print_summary)
@@ -4080,4 +4215,155 @@ const LLHost& LLMessageSystem::getSender() const
LLHTTPRegistration >
gHTTPRegistrationTrustedMessageWildcard("/trusted-message/");
+//
+void LLMessageSystem::startSpoofProtection(U32 level)
+{
+ if(!mPort)
+ {
+ llwarns << "listen port is 0!!!" << llendl;
+ }
+ mSpoofProtectionLevel = level;
+ mCanaries.clear();
+ mCanaryEntries.clear();
+ // Make canaries
+ std::string canary_info("");
+
+ if(mSpoofProtectionLevel > 2) // 3 or greater
+ {
+ // FULL CANARY POWER
+ int rPort = 768 + ll_rand(32);
+ if(mPort < rPort) rPort = mPort - 16;
+ if(rPort < 1) rPort = 1;
+ while(rPort < 65536)
+ {
+ if(rPort != mPort)
+ {
+ LLNetCanary* canary = new LLNetCanary(rPort);
+ if(canary->mGood)
+ {
+ mCanaries.push_back(canary);
+ canary_info.append(llformat(" %d", canary->mPort));
+ }
+ else
+ delete canary;
+ }
+ int dist = llabs(mPort - rPort);
+ if(dist > 4096)
+ rPort += ll_rand(2048) + 2048;
+ else if(dist > 2048)
+ rPort += ll_rand(1024) + 1024;
+ else if(dist > 1024)
+ rPort += ll_rand(512) + 512;
+ else if(dist > 512)
+ rPort += ll_rand(256) + 256;
+ else if(dist > 128)
+ rPort += ll_rand(64) + 64;
+ else if(dist > 16)
+ rPort += ll_rand(8) + 8;
+ else
+ rPort += 4;
+ }
+ }
+ else if(mSpoofProtectionLevel == 2)
+ {
+ // Minimal canaries
+ for(int o = -32; o <= 32; o += 8)
+ {
+ int rPort = mPort + o;
+ if(rPort != mPort)
+ {
+ LLNetCanary* canary = new LLNetCanary(rPort);
+ if(canary->mGood)
+ {
+ mCanaries.push_back(canary);
+ canary_info.append(llformat(" %d", canary->mPort));
+ }
+ else
+ delete canary;
+ }
+ }
+ }
+
+ if(mCanaries.size())
+ {
+ llinfos << "level " << mSpoofProtectionLevel << ", " << mCanaries.size() << " canaries: " << canary_info << llendl;
+ }
+ else
+ {
+ llinfos << "level " << mSpoofProtectionLevel << ", no canaries" << llendl;
+ }
+}
+
+void LLMessageSystem::stopSpoofProtection()
+{
+ llinfos << "cleaning up" << llendl;
+ // Shut down canaries
+ std::vector::iterator canary_iter = mCanaries.begin();
+ std::vector::iterator canary_end = mCanaries.end();
+ for( ; canary_iter != canary_end; ++canary_iter)
+ {
+ LLNetCanary* canary = (*canary_iter);
+ delete canary;
+ }
+ mCanaries.clear();
+ // Empty canary entries
+ mCanaryEntries.clear();
+}
+
+void LLMessageSystem::setSpoofDroppedCallback(void (*callback)(LLNetCanary::entry))
+{
+ mSpoofDroppedCallback = callback;
+}
+
+// Copypasta from LLTemplateMessageReader
+BOOL LLMessageSystem::decodeTemplate( const U8* buffer, S32 buffer_size, LLMessageTemplate** msg_template )
+{
+ const U8* header = buffer + LL_PACKET_ID_SIZE;
+ if (buffer_size <= 0) 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)
+ {
+ return(FALSE);
+ }
+
+ LLMessageTemplate* temp = get_ptr_in_map(mMessageNumbers,num);
+ if (temp)
+ {
+ *msg_template = temp;
+ }
+ else
+ {
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+//
+#include "llnetcanary.h"
+//
const U32 MESSAGE_MAX_STRINGS_LENGTH = 64;
const U32 MESSAGE_NUMBER_OF_HASH_BUCKETS = 8192;
@@ -215,6 +218,12 @@ class LLMessageSystem : public LLMessageSenderInterface
private:
U8 mSendBuffer[MAX_BUFFER_SIZE];
S32 mSendSize;
+ //
+ U32 mSpoofProtectionLevel;
+ std::vector mCanaries;
+ std::map mCanaryEntries;
+ void (*mSpoofDroppedCallback)(LLNetCanary::entry);
+ //
bool mBlockUntrustedInterface;
LLHost mUntrustedInterface;
@@ -236,7 +245,6 @@ class LLMessageSystem : public LLMessageSenderInterface
//
message_template_name_map_t mMessageTemplates;
message_template_number_map_t mMessageNumbers;
-
//
//public:
//
@@ -614,6 +622,11 @@ public:
// Change this message to be UDP black listed.
void banUdpMessage(const std::string& name);
+ //
+ void startSpoofProtection(U32 level);
+ void stopSpoofProtection();
+ void setSpoofDroppedCallback(void (*callback)(LLNetCanary::entry));
+ //
private:
// A list of the circuits that need to be sent DenyTrustedCircuit messages.
@@ -756,7 +769,13 @@ private:
LLUUID mSessionID;
void addTemplate(LLMessageTemplate *templatep);
+//
+public:
+//
BOOL decodeTemplate( const U8* buffer, S32 buffer_size, LLMessageTemplate** msg_template );
+//
+private:
+//
void logMsgFromInvalidCircuit( const LLHost& sender, BOOL recv_reliable );
void logTrustedMsgFromUntrustedCircuit( const LLHost& sender );
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 1baa295d3..e2dcd6ad4 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -784,6 +784,28 @@
S32
Value
5
+
+ SpoofProtectionAlerts
+
+ SpoofProtectionLevel
+
Socks5ProxyEnabled