diff --git a/engine/server/server.h b/engine/server/server.h index fba9d9c3..26bb775f 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -378,6 +378,13 @@ typedef struct entity_state_t *static_entities; // [MAX_STATIC_ENTITIES]; challenge_t challenges[MAX_CHALLENGES]; // to prevent invalid IPs from connecting + + sizebuf_t testpacket; // pregenerataed testpacket, only needs CRC32 patching + byte *testpacket_buf; // check for NULL if testpacket is available + byte *testpacket_crcpos; // pointer to write pregenerated crc (unaligned!!!) + uint32_t *testpacket_crcs; // checksums lookup table + int testpacket_filepos; // file position (need to calculate lookup table pos) + int testpacket_filelen; // file and lookup table length } server_static_t; //============================================================================= diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index e3ad0795..8a9c2f92 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -832,14 +832,10 @@ SV_TestBandWidth */ void SV_TestBandWidth( netadr_t from ) { - int version = Q_atoi( Cmd_Argv( 1 )); - int packetsize = Q_atoi( Cmd_Argv( 2 )); - byte send_buf[FRAGMENT_MAX_SIZE]; - dword crcValue = 0; - byte *filepos; - int crcpos; - file_t *test; - sizebuf_t send; + const int version = Q_atoi( Cmd_Argv( 1 )); + const int packetsize = Q_atoi( Cmd_Argv( 2 )); + uint32_t crc; + int ofs; // don't waste time of protocol mismatched if( version != PROTOCOL_VERSION ) @@ -848,32 +844,29 @@ void SV_TestBandWidth( netadr_t from ) return; } - test = FS_Open( "gfx.wad", "rb", false ); - - if( FS_FileLength( test ) < sizeof( send_buf )) + // quickly reject invalid packets + if( !svs.testpacket_buf || + ( packetsize <= FRAGMENT_MIN_SIZE ) || + ( packetsize > FRAGMENT_MAX_SIZE )) { // skip the test and just get challenge SV_GetChallenge( from ); return; } - // write the packet header - MSG_Init( &send, "BandWidthPacket", send_buf, sizeof( send_buf )); - MSG_WriteLong( &send, -1 ); // -1 sequence means out of band - MSG_WriteString( &send, "testpacket" ); - crcpos = MSG_GetNumBytesWritten( &send ); - MSG_WriteLong( &send, 0 ); // reserve space for crc - filepos = send.pData + MSG_GetNumBytesWritten( &send ); - packetsize = packetsize - MSG_GetNumBytesWritten( &send ); // adjust the packet size - FS_Read( test, filepos, packetsize ); - FS_Close( test ); + // don't go out of bounds + ofs = packetsize - svs.testpacket_filepos - 1; + if(( ofs < 0 ) || ( ofs > svs.testpacket_filelen )) + { + SV_GetChallenge( from ); + return; + } - CRC32_ProcessBuffer( &crcValue, filepos, packetsize ); // calc CRC - MSG_SeekToBit( &send, packetsize << 3, SEEK_CUR ); - *(uint *)&send.pData[crcpos] = crcValue; + crc = svs.testpacket_crcs[ofs]; + memcpy( svs.testpacket_crcpos, &crc, sizeof( crc )); // send the datagram - NET_SendPacket( NS_SERVER, MSG_GetNumBytesWritten( &send ), MSG_GetData( &send ), from ); + NET_SendPacket( NS_SERVER, packetsize, MSG_GetData( &svs.testpacket ), from ); } /* diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index 7f43d8c3..21b6dee8 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -882,6 +882,77 @@ static qboolean CRC32_MapFile( dword *crcvalue, const char *filename, qboolean m return 1; } +/* +================ +SV_GenerateTestPacket +================ +*/ +static void SV_GenerateTestPacket( void ) +{ + const int maxsize = FRAGMENT_MAX_SIZE; + uint32_t crc; + file_t *file; + byte *filepos; + int i, filesize; + + // testpacket already generated once, exit + // testpacket and lookup table takes ~300k of memory + // disable for low memory mode + if( svs.testpacket_buf || XASH_LOW_MEMORY >= 0 ) + return; + + // don't need in singleplayer with full client + if( svs.maxclients <= 1 && !Host_IsDedicated( )) + return; + + file = FS_Open( "gfx.wad", "rb", false ); + if( FS_FileLength( file ) < maxsize ) + { + FS_Close( file ); + return; + } + + svs.testpacket_buf = Mem_Malloc( host.mempool, sizeof( *svs.testpacket_buf ) * maxsize ); + + // write packet base data + MSG_Init( &svs.testpacket, "BandWidthTest", svs.testpacket_buf, maxsize ); + MSG_WriteLong( &svs.testpacket, -1 ); + MSG_WriteString( &svs.testpacket, "testpacket" ); + svs.testpacket_crcpos = svs.testpacket.pData + MSG_GetNumBytesWritten( &svs.testpacket ); + MSG_WriteDword( &svs.testpacket, 0 ); // to be changed by crc + + // time to read our file + svs.testpacket_filepos = MSG_GetNumBytesWritten( &svs.testpacket ); + svs.testpacket_filelen = maxsize - svs.testpacket_filepos; + + filepos = svs.testpacket.pData + svs.testpacket_filepos; + FS_Read( file, filepos, svs.testpacket_filelen ); + FS_Close( file ); + + // now generate checksums lookup table + svs.testpacket_crcs = Mem_Malloc( host.mempool, sizeof( *svs.testpacket_crcs ) * svs.testpacket_filelen ); + crc = 0; // intentional omit of CRC32_Init because of the client + + // TODO: shrink to minimum! + for( i = 0; i < svs.testpacket_filelen; i++ ) + { + uint32_t crc2; + + CRC32_ProcessByte( &crc, filepos[i] ); + svs.testpacket_crcs[i] = crc; +#if 0 + // test + crc2 = 0; + CRC32_ProcessBuffer( &crc2, filepos, i + 1 ); + if( svs.testpacket_crcs[i] != crc2 ) + { + Con_Printf( "%d: 0x%x != 0x%x\n", i, svs.testpacket_crcs[i], crc2 ); + svs.testpacket_crcs[i] = crc = crc2; + } +#endif + } +} + /* ================ SV_SpawnServer @@ -1026,6 +1097,9 @@ qboolean SV_SpawnServer( const char *mapname, const char *startspot, qboolean ba // clear physics interaction links SV_ClearWorld(); + // pregenerate test packet + SV_GenerateTestPacket(); + return true; }