diff options
| -rw-r--r-- | src/engine/client/client.cpp | 63 | ||||
| -rw-r--r-- | src/engine/client/client.h | 1 | ||||
| -rw-r--r-- | src/engine/server/server.cpp | 8 | ||||
| -rw-r--r-- | src/engine/server/server.h | 1 | ||||
| -rw-r--r-- | src/engine/shared/datafile.cpp | 26 | ||||
| -rw-r--r-- | src/engine/shared/datafile.h | 2 | ||||
| -rw-r--r-- | src/engine/shared/mapchecker.cpp | 106 | ||||
| -rw-r--r-- | src/engine/shared/mapchecker.h | 38 | ||||
| -rw-r--r-- | src/tools/map_version.cpp | 66 | ||||
| -rw-r--r-- | src/versionsrv/versionsrv.cpp | 71 | ||||
| -rw-r--r-- | src/versionsrv/versionsrv.h | 27 |
11 files changed, 389 insertions, 20 deletions
diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index 28c0fad4..8353bc0a 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -26,7 +26,7 @@ #include <engine/shared/compression.h> #include <engine/shared/datafile.h> #include <engine/shared/demo.h> -#include <engine/shared/memheap.h> +#include <engine/shared/mapchecker.h> #include <engine/shared/network.h> #include <engine/shared/packer.h> #include <engine/shared/protocol.h> @@ -1023,24 +1023,47 @@ int CClient::PlayerScoreComp(const void *a, const void *b) void CClient::ProcessConnlessPacket(CNetChunk *pPacket) { - // version info - if(pPacket->m_DataSize == (int)(sizeof(VERSIONSRV_VERSION) + sizeof(VERSION_DATA)) && - mem_comp(pPacket->m_pData, VERSIONSRV_VERSION, sizeof(VERSIONSRV_VERSION)) == 0 && - m_VersionInfo.m_State == CVersionInfo::STATE_READY && net_addr_comp(&pPacket->m_Address, &m_VersionInfo.m_VersionServeraddr.m_Addr) == 0) + // version server + if(m_VersionInfo.m_State == CVersionInfo::STATE_READY && net_addr_comp(&pPacket->m_Address, &m_VersionInfo.m_VersionServeraddr.m_Addr) == 0) { - unsigned char *pVersionData = (unsigned char*)pPacket->m_pData + sizeof(VERSIONSRV_VERSION); - int VersionMatch = !mem_comp(pVersionData, VERSION_DATA, sizeof(VERSION_DATA)); + // version info + if(pPacket->m_DataSize == (int)(sizeof(VERSIONSRV_VERSION) + sizeof(VERSION_DATA)) && + mem_comp(pPacket->m_pData, VERSIONSRV_VERSION, sizeof(VERSIONSRV_VERSION)) == 0) + + { + unsigned char *pVersionData = (unsigned char*)pPacket->m_pData + sizeof(VERSIONSRV_VERSION); + int VersionMatch = !mem_comp(pVersionData, VERSION_DATA, sizeof(VERSION_DATA)); + + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "version does %s (%d.%d.%d)", + VersionMatch ? "match" : "NOT match", + pVersionData[1], pVersionData[2], pVersionData[3]); + m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client/version", aBuf); - char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "version does %s (%d.%d.%d)", - VersionMatch ? "match" : "NOT match", - pVersionData[1], pVersionData[2], pVersionData[3]); - m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client/version", aBuf); + // assume version is out of date when version-data doesn't match + if (!VersionMatch) + { + str_format(m_aVersionStr, sizeof(m_aVersionStr), "%d.%d.%d", pVersionData[1], pVersionData[2], pVersionData[3]); + } + + // request the map version list now + CNetChunk Packet; + mem_zero(&Packet, sizeof(Packet)); + Packet.m_ClientID = -1; + Packet.m_Address = m_VersionInfo.m_VersionServeraddr.m_Addr; + Packet.m_pData = VERSIONSRV_GETMAPLIST; + Packet.m_DataSize = sizeof(VERSIONSRV_GETMAPLIST); + Packet.m_Flags = NETSENDFLAG_CONNLESS; + m_NetClient.Send(&Packet); + } - // assume version is out of date when version-data doesn't match - if (!VersionMatch) + // map version list + if(pPacket->m_DataSize >= (int)sizeof(VERSIONSRV_MAPLIST) && + mem_comp(pPacket->m_pData, VERSIONSRV_MAPLIST, sizeof(VERSIONSRV_MAPLIST)) == 0) { - str_format(m_aVersionStr, sizeof(m_aVersionStr), "%d.%d.%d", pVersionData[1], pVersionData[2], pVersionData[3]); + int Size = pPacket->m_DataSize-sizeof(VERSIONSRV_MAPLIST); + int Num = Size/sizeof(CMapVersion); + m_MapChecker.AddMaplist((CMapVersion *)((char*)pPacket->m_pData+sizeof(VERSIONSRV_MAPLIST)), Num); } } @@ -1073,9 +1096,9 @@ void CClient::ProcessConnlessPacket(CNetChunk *pPacket) NETADDR Addr; // copy address - Addr.type = (pAddrs->m_aType[0]<<24) | (pAddrs->m_aType[1]<<16) | (pAddrs->m_aType[2]<<8) | pAddrs->m_aType[3]; - mem_copy(Addr.ip, pAddrs->m_aIp, sizeof(Addr.ip)); - Addr.port = (pAddrs->m_aPort[0]<<8) | pAddrs->m_aPort[1]; + Addr.type = (pAddrs[i].m_aType[0]<<24) | (pAddrs[i].m_aType[1]<<16) | (pAddrs[i].m_aType[2]<<8) | pAddrs[i].m_aType[3]; + mem_copy(Addr.ip, pAddrs[i].m_aIp, sizeof(Addr.ip)); + Addr.port = (pAddrs[i].m_aPort[0]<<8) | pAddrs[i].m_aPort[1]; m_ServerBrowser.Set(Addr, IServerBrowser::SET_MASTER_ADD, -1, 0x0); } @@ -1159,6 +1182,10 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket) if(Unpacker.Error()) return; + // check for valid standard map + if(!m_MapChecker.IsMapValid(pMap, MapCrc, MapSize)) + pError = "invalid standard map"; + for(int i = 0; pMap[i]; i++) // protect the player from nasty map names { if(pMap[i] == '/' || pMap[i] == '\\') diff --git a/src/engine/client/client.h b/src/engine/client/client.h index b0696b96..86c56e0a 100644 --- a/src/engine/client/client.h +++ b/src/engine/client/client.h @@ -106,6 +106,7 @@ class CClient : public IClient, public CDemoPlayer::IListner class CDemoRecorder m_DemoRecorder; class CServerBrowser m_ServerBrowser; class CFriends m_Friends; + class CMapChecker m_MapChecker; char m_aServerAddressStr[256]; diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index f1d2e6e1..f170b1c8 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -15,6 +15,7 @@ #include <engine/shared/config.h> #include <engine/shared/datafile.h> #include <engine/shared/demo.h> +#include <engine/shared/mapchecker.h> #include <engine/shared/network.h> #include <engine/shared/packer.h> #include <engine/shared/protocol.h> @@ -1049,6 +1050,13 @@ int CServer::LoadMap(const char *pMapName) /*df = datafile_load(buf); if(!df) return 0;*/ + + // check for valid standard map + if(!m_MapChecker.ReadAndValidateMap(Storage(), aBuf, IStorage::TYPE_ALL)) + { + Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "mapchecker", "invalid standard map"); + return 0; + } if(!m_pMap->Load(aBuf)) return 0; diff --git a/src/engine/server/server.h b/src/engine/server/server.h index 00c1c648..abd86f92 100644 --- a/src/engine/server/server.h +++ b/src/engine/server/server.h @@ -121,6 +121,7 @@ public: CDemoRecorder m_DemoRecorder; CRegister m_Register; + CMapChecker m_MapChecker; CServer(); diff --git a/src/engine/shared/datafile.cpp b/src/engine/shared/datafile.cpp index 74583ab8..74936ea0 100644 --- a/src/engine/shared/datafile.cpp +++ b/src/engine/shared/datafile.cpp @@ -222,6 +222,32 @@ bool CDataFileReader::Open(class IStorage *pStorage, const char *pFilename, int return true; } +bool CDataFileReader::GetCrcSize(class IStorage *pStorage, const char *pFilename, int StorageType, unsigned *pCrc, unsigned *pSize) +{ + IOHANDLE File = pStorage->OpenFile(pFilename, IOFLAG_READ, StorageType); + if(!File) + return false; + + // get crc and size + unsigned Crc = 0; + unsigned Size = 0; + unsigned char aBuffer[64*1024]; + while(1) + { + unsigned Bytes = io_read(File, aBuffer, sizeof(aBuffer)); + if(Bytes <= 0) + break; + Crc = crc32(Crc, aBuffer, Bytes); // ignore_convention + Size += Bytes; + } + + io_close(File); + + *pCrc = Crc; + *pSize = Size; + return true; +} + int CDataFileReader::NumData() { if(!m_pDataFile) { return 0; } diff --git a/src/engine/shared/datafile.h b/src/engine/shared/datafile.h index ce7b11aa..15bed033 100644 --- a/src/engine/shared/datafile.h +++ b/src/engine/shared/datafile.h @@ -16,6 +16,8 @@ public: bool Open(class IStorage *pStorage, const char *pFilename, int StorageType); bool Close(); + + static bool GetCrcSize(class IStorage *pStorage, const char *pFilename, int StorageType, unsigned *pCrc, unsigned *pSize); void *GetData(int Index); void *GetDataSwapped(int Index); // makes sure that the data is 32bit LE ints when saved diff --git a/src/engine/shared/mapchecker.cpp b/src/engine/shared/mapchecker.cpp new file mode 100644 index 00000000..dec4271d --- /dev/null +++ b/src/engine/shared/mapchecker.cpp @@ -0,0 +1,106 @@ +/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ +/* If you are missing that file, acquire a complete release at teeworlds.com. */ +#include <base/math.h> +#include <base/system.h> + +#include <engine/storage.h> + +#include <versionsrv/versionsrv.h> + +#include "datafile.h" +#include "memheap.h" +#include "mapchecker.h" + +CMapChecker::CMapChecker() +{ + Init(); + SetDefaults(); +} + +void CMapChecker::Init() +{ + m_Whitelist.Reset(); + m_pFirst = 0; + m_RemoveDefaultList = false; +} + +void CMapChecker::SetDefaults() +{ + AddMaplist(s_aMapVersionList, s_NumMapVersionItems); + m_RemoveDefaultList = true; +} + +void CMapChecker::AddMaplist(CMapVersion *pMaplist, int Num) +{ + if(m_RemoveDefaultList) + Init(); + + for(int i = 0; i < Num; ++i) + { + CWhitelistEntry *pEntry = (CWhitelistEntry *)m_Whitelist.Allocate(sizeof(CWhitelistEntry)); + pEntry->m_pNext = m_pFirst; + m_pFirst = pEntry; + + str_copy(pEntry->m_aMapName, pMaplist[i].m_aName, sizeof(pEntry->m_aMapName)); + pEntry->m_MapCrc = (pMaplist[i].m_aCrc[0]<<24) | (pMaplist[i].m_aCrc[1]<<16) | (pMaplist[i].m_aCrc[2]<<8) | pMaplist[i].m_aCrc[3]; + pEntry->m_MapSize = (pMaplist[i].m_aSize[0]<<24) | (pMaplist[i].m_aSize[1]<<16) | (pMaplist[i].m_aSize[2]<<8) | pMaplist[i].m_aSize[3]; + } +} + +bool CMapChecker::IsMapValid(const char *pMapName, unsigned MapCrc, unsigned MapSize) +{ + bool StandardMap = false; + for(CWhitelistEntry *pCurrent = m_pFirst; pCurrent; pCurrent = pCurrent->m_pNext) + { + if(str_comp(pCurrent->m_aMapName, pMapName) == 0) + { + StandardMap = true; + if(pCurrent->m_MapCrc == MapCrc && pCurrent->m_MapSize == MapSize) + return true; + } + } + return StandardMap?false:true; +} + +bool CMapChecker::ReadAndValidateMap(IStorage *pStorage, const char *pFilename, int StorageType) +{ + bool LoadedMapInfo = false; + bool StandardMap = false; + unsigned MapCrc = 0; + unsigned MapSize = 0; + + // extract map name + char aMapName[MAX_MAP_LENGTH]; + const char *pExtractedName = pFilename; + const char *pEnd = 0; + for(const char *pSrc = pFilename; *pSrc; ++pSrc) + { + if(*pSrc == '/' || *pSrc == '\\') + pExtractedName = pSrc+1; + else if(*pSrc == '.') + pEnd = pSrc; + } + int Length = (int)(pEnd - pExtractedName); + if(Length <= 0 || Length >= MAX_MAP_LENGTH) + return true; + str_copy(aMapName, pExtractedName, min((int)MAX_MAP_LENGTH, (int)(pEnd-pExtractedName+1))); + + // check for valid map + for(CWhitelistEntry *pCurrent = m_pFirst; pCurrent; pCurrent = pCurrent->m_pNext) + { + if(str_comp(pCurrent->m_aMapName, aMapName) == 0) + { + StandardMap = true; + if(!LoadedMapInfo) + { + if(!CDataFileReader::GetCrcSize(pStorage, pFilename, StorageType, &MapCrc, &MapSize)) + return true; + LoadedMapInfo = true; + } + + if(pCurrent->m_MapCrc == MapCrc && pCurrent->m_MapSize == MapSize) + return true; + } + } + return StandardMap?false:true; +} diff --git a/src/engine/shared/mapchecker.h b/src/engine/shared/mapchecker.h new file mode 100644 index 00000000..0d4332d9 --- /dev/null +++ b/src/engine/shared/mapchecker.h @@ -0,0 +1,38 @@ +/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ +/* If you are missing that file, acquire a complete release at teeworlds.com. */ +#ifndef ENGINE_SHARED_MAPCHECKER_H +#define ENGINE_SHARED_MAPCHECKER_H + +#include "memheap.h" + +class CMapChecker +{ + enum + { + MAX_MAP_LENGTH=8, + }; + + struct CWhitelistEntry + { + char m_aMapName[MAX_MAP_LENGTH]; + unsigned m_MapCrc; + unsigned m_MapSize; + CWhitelistEntry *m_pNext; + }; + + class CHeap m_Whitelist; + CWhitelistEntry *m_pFirst; + + bool m_RemoveDefaultList; + + void Init(); + void SetDefaults(); + +public: + CMapChecker(); + void AddMaplist(class CMapVersion *pMaplist, int Num); + bool IsMapValid(const char *pMapName, unsigned MapCrc, unsigned MapSize); + bool ReadAndValidateMap(class IStorage *pStorage, const char *pFilename, int StorageType); +}; + +#endif diff --git a/src/tools/map_version.cpp b/src/tools/map_version.cpp new file mode 100644 index 00000000..d995f478 --- /dev/null +++ b/src/tools/map_version.cpp @@ -0,0 +1,66 @@ +/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ +/* If you are missing that file, acquire a complete release at teeworlds.com. */ +#include <base/math.h> +#include <base/system.h> + +#include <engine/kernel.h> +#include <engine/map.h> +#include <engine/storage.h> + + +static IOHANDLE s_File = 0; +static IStorage *s_pStorage = 0; +static IEngineMap *s_pEngineMap = 0; + +int MaplistCallback(const char *pName, int IsDir, int DirType, void *pUser) +{ + int l = str_length(pName); + if(l < 4 || IsDir || str_comp(pName+l-4, ".map") != 0) + return 0; + + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "maps/%s", pName); + if(!s_pEngineMap->Load(aBuf)) + return 0; + + unsigned MapCrc = s_pEngineMap->Crc(); + s_pEngineMap->Unload(); + + IOHANDLE MapFile = s_pStorage->OpenFile(aBuf, IOFLAG_READ, DirType); + unsigned MapSize = io_length(MapFile); + io_close(MapFile); + + char aMapName[8]; + str_copy(aMapName, pName, min((int)sizeof(aMapName),l-3)); + + str_format(aBuf, sizeof(aBuf), "\t{\"%s\", {0x%02x, 0x%02x, 0x%02x, 0x%02x}, {0x%02x, 0x%02x, 0x%02x, 0x%02x}},\n", aMapName, + (MapCrc>>24)&0xff, (MapCrc>>16)&0xff, (MapCrc>>8)&0xff, MapCrc&0xff, + (MapSize>>24)&0xff, (MapSize>>16)&0xff, (MapSize>>8)&0xff, MapSize&0xff); + io_write(s_File, aBuf, str_length(aBuf)); + + return 0; +} + +int main(int argc, const char **argv) // ignore_convention +{ + IKernel *pKernel = IKernel::Create(); + s_pStorage = CreateStorage("Teeworlds", argc, argv); + s_pEngineMap = CreateEngineMap(); + + bool RegisterFail = !pKernel->RegisterInterface(s_pStorage); + RegisterFail |= !pKernel->RegisterInterface(s_pEngineMap); + + if(RegisterFail) + return -1; + + s_File = s_pStorage->OpenFile("map_version.txt", IOFLAG_WRITE, 1); + if(s_File) + { + io_write(s_File, "static CMapVersion s_aMapVersionList[] = {\n", str_length("static CMapVersion s_aMapVersionList[] = {\n")); + s_pStorage->ListDirectory(1, "maps", MaplistCallback, 0); + io_write(s_File, "};\n", str_length("};\n")); + io_close(s_File); + } + + return 0; +} diff --git a/src/versionsrv/versionsrv.cpp b/src/versionsrv/versionsrv.cpp index 1ca3835f..d36fba3b 100644 --- a/src/versionsrv/versionsrv.cpp +++ b/src/versionsrv/versionsrv.cpp @@ -6,8 +6,54 @@ #include "versionsrv.h" +enum { + MAX_MAPS_PER_PACKET=48, + MAX_PACKETS=16, + MAX_MAPS=MAX_MAPS_PER_PACKET*MAX_PACKETS, +}; + +struct CPacketData +{ + int m_Size; + struct { + unsigned char m_aHeader[sizeof(VERSIONSRV_MAPLIST)]; + CMapVersion m_aMaplist[MAX_MAPS_PER_PACKET]; + } m_Data; +}; + +CPacketData m_aPackets[MAX_PACKETS]; +static int m_NumPackets = 0; + static CNetClient g_NetOp; // main +void BuildPackets() +{ + CMapVersion *pCurrent = &s_aMapVersionList[0]; + int ServersLeft = s_NumMapVersionItems; + m_NumPackets = 0; + while(ServersLeft && m_NumPackets < MAX_PACKETS) + { + int Chunk = ServersLeft; + if(Chunk > MAX_MAPS_PER_PACKET) + Chunk = MAX_MAPS_PER_PACKET; + ServersLeft -= Chunk; + + // copy header + mem_copy(m_aPackets[m_NumPackets].m_Data.m_aHeader, VERSIONSRV_MAPLIST, sizeof(VERSIONSRV_MAPLIST)); + + // copy map versions + for(int i = 0; i < Chunk; i++) + { + m_aPackets[m_NumPackets].m_Data.m_aMaplist[i] = *pCurrent; + pCurrent++; + } + + m_aPackets[m_NumPackets].m_Size = sizeof(VERSIONSRV_MAPLIST) + sizeof(CMapVersion)*Chunk; + + m_NumPackets++; + } +} + void SendVer(NETADDR *pAddr) { CNetChunk p; @@ -33,8 +79,15 @@ int main(int argc, char **argv) // ignore_convention net_init(); mem_zero(&BindAddr, sizeof(BindAddr)); + BindAddr.type = NETTYPE_ALL; BindAddr.port = VERSIONSRV_PORT; - g_NetOp.Open(BindAddr, 0); + if(!g_NetOp.Open(BindAddr, 0)) + { + dbg_msg("mastersrv", "couldn't start network"); + return -1; + } + + BuildPackets(); dbg_msg("versionsrv", "started"); @@ -51,6 +104,22 @@ int main(int argc, char **argv) // ignore_convention { SendVer(&Packet.m_Address); } + + if(Packet.m_DataSize == sizeof(VERSIONSRV_GETMAPLIST) && + mem_comp(Packet.m_pData, VERSIONSRV_GETMAPLIST, sizeof(VERSIONSRV_GETMAPLIST)) == 0) + { + CNetChunk p; + p.m_ClientID = -1; + p.m_Address = Packet.m_Address; + p.m_Flags = NETSENDFLAG_CONNLESS; + + for(int i = 0; i < m_NumPackets; i++) + { + p.m_DataSize = m_aPackets[i].m_Size; + p.m_pData = &m_aPackets[i].m_Data; + g_NetOp.Send(&p); + } + } } // be nice to the CPU diff --git a/src/versionsrv/versionsrv.h b/src/versionsrv/versionsrv.h index 68ba04ca..03ca2f21 100644 --- a/src/versionsrv/versionsrv.h +++ b/src/versionsrv/versionsrv.h @@ -4,8 +4,33 @@ #define VERSIONSRV_VERSIONSRV_H static const int VERSIONSRV_PORT = 8302; -static const unsigned char VERSION_DATA[] = {0x00, 0, 5, 1}; // TODO 0.6: fix me +struct CMapVersion +{ + char m_aName[8]; + unsigned char m_aCrc[4]; + unsigned char m_aSize[4]; +}; + +static CMapVersion s_aMapVersionList[] = { + {"ctf1", {0xd2, 0xa8, 0x20, 0xbd}, {0x00, 0x00, 0x11, 0x9b}}, + {"ctf2", {0xb2, 0x19, 0xe7, 0x13}, {0x00, 0x00, 0x63, 0xfc}}, + {"ctf3", {0xe8, 0xd3, 0x39, 0xd0}, {0x00, 0x00, 0x11, 0x0e}}, + {"ctf4", {0x09, 0xd1, 0x70, 0x1d}, {0x00, 0x00, 0x20, 0x3f}}, + {"ctf5", {0x60, 0x7e, 0x6d, 0x7e}, {0x00, 0x00, 0x2f, 0x51}}, + {"dm1", {0xf3, 0x5c, 0x93, 0x09}, {0x00, 0x00, 0x15, 0x75}}, + {"dm2", {0x81, 0xe0, 0xa8, 0x9e}, {0x00, 0x00, 0x20, 0x11}}, + {"dm6", {0xae, 0x83, 0x06, 0x9f}, {0x00, 0x00, 0x17, 0x1d}}, + {"dm7", {0x42, 0x6d, 0xa1, 0x67}, {0x00, 0x00, 0x27, 0x2a}}, + {"dm8", {0x92, 0x23, 0x91, 0x82}, {0x00, 0x00, 0x3a, 0x33}}, + {"dm9", {0x48, 0xca, 0xfd, 0x11}, {0x00, 0x00, 0x18, 0x94}}, +}; +static int s_NumMapVersionItems = sizeof(s_aMapVersionList)/sizeof(CMapVersion); + +static const unsigned char VERSION_DATA[] = {0x00, 0, 6, 0}; static const unsigned char VERSIONSRV_GETVERSION[] = {255, 255, 255, 255, 'v', 'e', 'r', 'g'}; static const unsigned char VERSIONSRV_VERSION[] = {255, 255, 255, 255, 'v', 'e', 'r', 's'}; + +static const unsigned char VERSIONSRV_GETMAPLIST[] = {255, 255, 255, 255, 'v', 'm', 'l', 'g'}; +static const unsigned char VERSIONSRV_MAPLIST[] = {255, 255, 255, 255, 'v', 'm', 'l', 's'}; #endif |