From e3e4bcc015fd601d506ae7dd6735e2e618077fff Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 19 Oct 2024 14:05:00 +0300 Subject: [PATCH] engine: global refactoring of connectionless packets, now they can be customized from single protocol.h header --- engine/client/cl_game.c | 2 +- engine/client/cl_main.c | 589 +++++++++++++++++++------------------- engine/common/protocol.h | 57 +++- engine/server/server.h | 2 +- engine/server/sv_client.c | 147 ++++++---- engine/server/sv_init.c | 2 +- engine/server/sv_main.c | 20 +- engine/server/sv_query.c | 44 ++- 8 files changed, 469 insertions(+), 394 deletions(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index e80b075d..7b540f84 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -3485,7 +3485,7 @@ static void GAME_EXPORT NetAPI_SendRequest( int context, int request, int flags, nr->flags = flags; // local servers request - Netchan_OutOfBandPrint( NS_CLIENT, nr->resp.remote_address, "netinfo %i %i %i", FBitSet( flags, FNETAPI_LEGACY_PROTOCOL ) ? PROTOCOL_LEGACY_VERSION : PROTOCOL_VERSION, context, request ); + Netchan_OutOfBandPrint( NS_CLIENT, nr->resp.remote_address, A2A_NETINFO" %i %i %i", FBitSet( flags, FNETAPI_LEGACY_PROTOCOL ) ? PROTOCOL_LEGACY_VERSION : PROTOCOL_VERSION, context, request ); } /* diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 2537185f..a0491787 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1144,7 +1144,7 @@ static void CL_SendConnectPacket( connprotocol_t proto, int challenge ) MSG_Init( &send, "GoldSrcConnect", send_buf, sizeof( send_buf )); MSG_WriteLong( &send, NET_HEADER_OUTOFBANDPACKET ); - MSG_WriteStringf( &send, "connect %i %i \"%s\" \"%s\"\n", + MSG_WriteStringf( &send, C2S_CONNECT" %i %i \"%s\" \"%s\"\n", PROTOCOL_GOLDSRC_VERSION, challenge, protinfo, cls.userinfo ); MSG_SeekToBit( &send, -8, SEEK_CUR ); // rewrite null terminator CL_WriteSteamTicket( &send ); @@ -1172,7 +1172,7 @@ static void CL_SendConnectPacket( connprotocol_t proto, int challenge ) Info_SetValueForKey( protinfo, "i", key, sizeof( protinfo )); - Netchan_OutOfBandPrint( NS_CLIENT, adr, "connect %i %i %i \"%s\" %d \"%s\"\n", + Netchan_OutOfBandPrint( NS_CLIENT, adr, C2S_CONNECT" %i %i %i \"%s\" %d \"%s\"\n", PROTOCOL_LEGACY_VERSION, qport, challenge, cls.userinfo, NET_LEGACY_EXT_SPLIT, protinfo ); Con_Printf( "Trying to connect with legacy protocol\n" ); } @@ -1195,7 +1195,7 @@ static void CL_SendConnectPacket( connprotocol_t proto, int challenge ) Info_SetValueForKey( protinfo, "qport", qport, sizeof( protinfo )); Info_SetValueForKeyf( protinfo, "ext", sizeof( protinfo ), "%d", extensions); - Netchan_OutOfBandPrint( NS_CLIENT, adr, "connect %i %i \"%s\" \"%s\"\n", PROTOCOL_VERSION, challenge, protinfo, cls.userinfo ); + Netchan_OutOfBandPrint( NS_CLIENT, adr, C2S_CONNECT" %i %i \"%s\" \"%s\"\n", PROTOCOL_VERSION, challenge, protinfo, cls.userinfo ); Con_Printf( "Trying to connect with modern protocol\n" ); } @@ -1223,7 +1223,7 @@ static void CL_SendGetChallenge( netadr_t to ) // always send GoldSrc-styled getchallenge message // Xash servers will ignore it but for GoldSrc it will help // in auto-detection - Netchan_OutOfBandPrint( NS_CLIENT, to, "getchallenge steam\n" ); + Netchan_OutOfBandPrint( NS_CLIENT, to, C2S_GETCHALLENGE" steam\n" ); } /* @@ -1314,14 +1314,15 @@ static void CL_CheckForResend( void ) cls.connect_retry++; if( bandwidthTest ) + { Con_Printf( "Connecting to %s... (retry #%i, fragment size %i)\n", cls.servername, cls.connect_retry, cls.max_fragment_size ); + Netchan_OutOfBandPrint( NS_CLIENT, adr, C2S_BANDWIDTHTEST" %i %i\n", PROTOCOL_VERSION, cls.max_fragment_size ); + } else + { Con_Printf( "Connecting to %s... (retry #%i)\n", cls.servername, cls.connect_retry ); - - if( bandwidthTest ) - Netchan_OutOfBandPrint( NS_CLIENT, adr, "bandwidth %i %i\n", PROTOCOL_VERSION, cls.max_fragment_size ); - else CL_SendGetChallenge( adr ); + } } static resource_t *CL_AddResource( resourcetype_t type, const char *name, int size, qboolean bFatalIfMissing, int index ) @@ -1470,7 +1471,7 @@ static void CL_Rcon_f( void ) NET_Config( true, false ); // allow remote - Q_strncat( message, "rcon ", sizeof( message )); + Q_strncat( message, C2S_RCON" ", sizeof( message )); Q_strncat( message, rcon_password.string, sizeof( message )); Q_strncat( message, " ", sizeof( message ) ); @@ -1746,10 +1747,10 @@ static void CL_LocalServers_f( void ) // send a broadcast packet adr.type = NA_BROADCAST; adr.port = MSG_BigShort( PORT_SERVER ); - Netchan_OutOfBandPrint( NS_CLIENT, adr, "info %i", PROTOCOL_VERSION ); + Netchan_OutOfBandPrint( NS_CLIENT, adr, A2A_INFO" %i", PROTOCOL_VERSION ); adr.type = NA_MULTICAST_IP6; - Netchan_OutOfBandPrint( NS_CLIENT, adr, "info %i", PROTOCOL_VERSION ); + Netchan_OutOfBandPrint( NS_CLIENT, adr, A2A_INFO" %i", PROTOCOL_VERSION ); } /* @@ -1951,7 +1952,7 @@ static void CL_ParseStatusMessage( netadr_t from, sizebuf_t *msg ) if( len >= magiclen && !Q_strcmp( s + len - magiclen, magic )) { - Netchan_OutOfBandPrint( NS_CLIENT, from, "info %i", PROTOCOL_LEGACY_VERSION ); + Netchan_OutOfBandPrint( NS_CLIENT, from, A2A_INFO" %i", PROTOCOL_LEGACY_VERSION ); return; } @@ -2155,6 +2156,235 @@ static qboolean CL_IsFromConnectingServer( netadr_t from ) NET_CompareAdr( cls.serveradr, from ); } +static void CL_HandleTestPacket( netadr_t from, sizebuf_t *msg ) +{ + byte recv_buf[NET_MAX_FRAGMENT]; + dword crcValue; + int realsize; + dword crcValue2 = 0; + + // this message only used during connection + // it doesn't make sense after client_connect + if( cls.state != ca_connecting ) + return; + + if( !CL_IsFromConnectingServer( from )) + return; + + crcValue = MSG_ReadLong( msg ); + realsize = MSG_GetMaxBytes( msg ) - MSG_GetNumBytesRead( msg ); + + if( cls.max_fragment_size != MSG_GetMaxBytes( msg )) + { + if( cls.connect_retry >= CL_TEST_RETRIES ) + { + // too many fails use default connection method + Con_Printf( "hi-speed connection is failed, use default method\n" ); + CL_SendGetChallenge( from ); + Cvar_SetValue( "cl_dlmax", FRAGMENT_DEFAULT_SIZE ); + cls.connect_time = host.realtime; + return; + } + + // if we waiting more than cl_timeout or packet was trashed + cls.connect_time = MAX_HEARTBEAT; + return; // just wait for a next responce + } + + // reading test buffer + MSG_ReadBytes( msg, recv_buf, realsize ); + + // procssing the CRC + CRC32_ProcessBuffer( &crcValue2, recv_buf, realsize ); + + if( crcValue == crcValue2 ) + { + // packet was sucessfully delivered, adjust the fragment size and get challenge + + Con_DPrintf( "CRC %x is matched, get challenge, fragment size %d\n", crcValue, cls.max_fragment_size ); + CL_SendGetChallenge( from ); + Cvar_SetValue( "cl_dlmax", cls.max_fragment_size ); + cls.connect_time = host.realtime; + } + else + { + if( cls.connect_retry >= CL_TEST_RETRIES ) + { + // too many fails use default connection method + Con_Printf( "hi-speed connection is failed, use default method\n" ); + CL_SendGetChallenge( from ); + Cvar_SetValue( "cl_dlmax", FRAGMENT_MIN_SIZE ); + cls.connect_time = host.realtime; + return; + } + + Msg( "got testpacket, CRC mismatched 0x%08x should be 0x%08x, trying next fragment size %d\n", crcValue2, crcValue, cls.max_fragment_size >> 1 ); + + // trying the next size of packet + cls.connect_time = MAX_HEARTBEAT; + } +} + +static void CL_ClientConnect( const char *c, netadr_t from, sizebuf_t *msg ) +{ + if( !CL_IsFromConnectingServer( from )) + return; + + if( cls.state == ca_connected ) + { + Con_DPrintf( S_ERROR "dup connect received. ignored\n"); + return; + } + + if( cls.legacymode != PROTO_GOLDSRC && !Q_strcmp( c, S2C_GOLDSRC_CONNECTION )) + { + Con_DPrintf( S_ERROR "GoldSrc client connect received but wasn't expected, ignored\n"); + return; + } + + CL_Reconnect( true ); + UI_SetActiveMenu( cl.background ); +} + +static void CL_Print( const char *c, const char *args, netadr_t from, sizebuf_t *msg ) +{ + const char *s; + + s = c[0] == A2C_GOLDSRC_PRINT ? args + 1 : MSG_ReadString( msg ); + + if( !COM_CheckStringEmpty( s )) + return; + + Con_Printf( "Remote message from %s:\n", NET_AdrToString( from )); + Con_Printf( "%s%c", s, s[Q_strlen( s ) - 1] != '\n' ? '\n' : '\0' ); +} + +static void CL_Challenge( const char *c, netadr_t from ) +{ + if( cls.state != ca_connecting ) + return; + + if( !CL_IsFromConnectingServer( from )) + return; + + // try to autodetect protocol by challenge response + if( !Q_strcmp( c, S2C_GOLDSRC_CHALLENGE )) + cls.legacymode = PROTO_GOLDSRC; + + // challenge from the server we are connecting to + CL_SendConnectPacket( cls.legacymode, Q_atoi( Cmd_Argv( 1 ))); +} + +static void CL_ErrorMsg( const char *c, const char *args, netadr_t from, sizebuf_t *msg ) +{ + char formatted_msg[MAX_VA_STRING]; + + if( !CL_IsFromConnectingServer( from )) + return; + + if( msg != NULL && !Q_strcmp( c, S2C_ERRORMSG )) + { + const char *s = MSG_ReadString( msg ); + Q_snprintf( formatted_msg, sizeof( formatted_msg ), "^3Server message^7\n%s", s ); + } + else if( c[0] == S2C_GOLDSRC_REJECT ) + { + Q_snprintf( formatted_msg, sizeof( formatted_msg ), "^3Server message^7\n%s", args + 1 ); + } + else if( c[0] == S2C_GOLDSRC_REJECT_BADPASSWORD ) + { + if( !Q_strnicmp( &c[1], "BADPASSWORD", 11 )) + Q_snprintf( formatted_msg, sizeof( formatted_msg ), "^3Server message^7\n%s", args + 12 ); + else + Q_snprintf( formatted_msg, sizeof( formatted_msg ), "^3Server message^7\n%s", args + 1 ); + } + + // in case we're in console or it's classic mainui which doesn't support messageboxes + if( !UI_IsVisible() || !UI_ShowMessageBox( formatted_msg )) + Msg( "%s\n", formatted_msg ); + + // don't disconnect, errormsg is a FWGS extension and + // always followed by disconnect message +} + +static void CL_Reject( const char *c, const char *args, netadr_t from ) +{ + // this message only used during connection + // it doesn't make sense after client_connect + if( cls.state != ca_connecting ) + return; + + if( !CL_IsFromConnectingServer( from )) + return; + + CL_ErrorMsg( c, args, from, NULL ); + + // a disconnect message from the server, which will happen if the server + // dropped the connection but it is still getting packets from us + CL_Disconnect_f(); +} + +static void CL_ServerList( netadr_t from, sizebuf_t *msg ) +{ + if( !NET_IsMasterAdr( from )) + { + Con_Printf( S_WARN "unexpected server list packet from %s\n", NET_AdrToString( from )); + return; + } + + // check the extra header + if( MSG_ReadByte( msg ) == 0x7f ) + { + uint32_t key = MSG_ReadDword( msg ); + + if( cls.internetservers_key != key ) + { + Con_Printf( S_WARN "unexpected server list packet from %s (invalid key)\n", NET_AdrToString( from )); + return; + } + + MSG_ReadByte( msg ); // reserved byte + } + else + { + Con_Printf( S_WARN "invalid server list packet from %s (missing extra header)\n", NET_AdrToString( from )); + return; + } + + // serverlist got from masterserver + while( MSG_GetNumBitsLeft( msg ) > 8 ) + { + uint8_t addr[16]; + netadr_t servadr; + + if( from.type6 == NA_IP6 ) // IPv6 master server only sends IPv6 addresses + { + MSG_ReadBytes( msg, addr, sizeof( addr )); + NET_IP6BytesToNetadr( &servadr, addr ); + servadr.type6 = NA_IP6; + } + else + { + MSG_ReadBytes( msg, servadr.ip, sizeof( servadr.ip )); // 4 bytes for IP + servadr.type = NA_IP; + } + servadr.port = MSG_ReadShort( msg ); // 2 bytes for Port + + // list is ends here + if( !servadr.port ) + break; + + NET_Config( true, false ); // allow remote + Netchan_OutOfBandPrint( NS_CLIENT, servadr, A2A_INFO" %i", PROTOCOL_VERSION ); + } + + if( cls.internetservers_pending ) + { + UI_ResetPing(); + cls.internetservers_pending = false; + } +} + /* ================= CL_ConnectionlessPacket @@ -2166,9 +2396,6 @@ static void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) { char *args; const char *c; - char buf[MAX_SYSPATH]; - int len = sizeof( buf ); - netadr_t servadr; MSG_Clear( msg ); MSG_ReadLong( msg ); // skip the -1 @@ -2181,290 +2408,70 @@ static void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) Con_Reportf( "%s: %s : %s\n", __func__, NET_AdrToString( from ), c ); // server connection - if( !Q_strcmp( c, "client_connect" ) || !Q_strcmp( c, S2C_CONNECTION )) + if( !Q_strcmp( c, S2C_GOLDSRC_CONNECTION ) || !Q_strcmp( c, S2C_CONNECTION )) { - if( !CL_IsFromConnectingServer( from )) - return; + CL_ClientConnect( c, from, msg ); + } + else if( !Q_strcmp( c, A2A_INFO )) + { + CL_ParseStatusMessage( from, msg ); // server responding to a status broadcast + } + else if( !Q_strcmp( c, A2A_NETINFO )) + { + CL_ParseNETInfoMessage( from, args ); // server responding to a status broadcast + } + else if( c[0] == A2C_GOLDSRC_PRINT || !Q_strcmp( c, A2C_PRINT )) + { + CL_Print( c, args, from, msg ); + } + else if( !Q_strcmp( c, S2C_BANDWIDTHTEST )) + { + CL_HandleTestPacket( from, msg ); + } + else if( !Q_strcmp( c, A2A_PING )) + { + Netchan_OutOfBandPrint( NS_CLIENT, from, A2A_ACK ); + } + else if( !Q_strcmp( c, A2A_GOLDSRC_PING )) + { + Netchan_OutOfBandPrint( NS_CLIENT, from, A2A_GOLDSRC_ACK ); + } + else if( !Q_strcmp( c, A2A_ACK ) || !Q_strcmp( c, A2A_GOLDSRC_ACK )) + { + // no-op + } + else if( !Q_strcmp( c, S2C_CHALLENGE ) || !Q_strcmp( c, S2C_GOLDSRC_CHALLENGE )) + { + CL_Challenge( c, from ); + } + else if( !Q_strcmp( c, S2C_REJECT ) || c[0] == S2C_GOLDSRC_REJECT || c[0] == S2C_GOLDSRC_REJECT_BADPASSWORD ) + { + CL_Reject( c, args, from ); + } + else if( !Q_strcmp( c, S2C_ERRORMSG )) + { + CL_ErrorMsg( c, args, from, msg ); + } + else if( !Q_strcmp( c, M2A_SERVERSLIST )) + { + CL_ServerList( from, msg ); + } + else + { + char buf[MAX_SYSPATH]; + int len = sizeof( buf ); - if( cls.state == ca_connected ) + if( clgame.dllFuncs.pfnConnectionlessPacket( &from, args, buf, &len )) { - Con_DPrintf( S_ERROR "dup connect received. ignored\n"); - return; - } - - if( cls.legacymode != PROTO_GOLDSRC && !Q_strcmp( c, S2C_CONNECTION )) - { - Con_DPrintf( S_ERROR "GoldSrc client connect received but wasn't expected, ignored\n"); - return; - } - - CL_Reconnect( true ); - UI_SetActiveMenu( cl.background ); - } - else if( !Q_strcmp( c, "info" )) - { - // server responding to a status broadcast - CL_ParseStatusMessage( from, msg ); - } - else if( !Q_strcmp( c, "netinfo" )) - { - // server responding to a status broadcast - CL_ParseNETInfoMessage( from, args ); - } - else if( !Q_strcmp( c, "cmd" )) - { - // remote command from gui front end - if( !NET_IsLocalAddress( from )) - { - Con_Printf( "Command packet from remote host. Ignored.\n" ); - return; - } - -#if XASH_SDL == 2 - SDL_ShowWindow( host.hWnd ); -#endif - args = MSG_ReadString( msg ); - Cbuf_AddText( args ); - Cbuf_AddText( "\n" ); - } - else if( c[0] == 'l' ) - { - char *s = args + 1; - - Con_Printf( S_CYAN "r:" S_DEFAULT " %s", s ); - if( !COM_CheckStringEmpty( s ) || s[Q_strlen( s ) - 1] != '\n' ) - Con_Printf( "\n" ); - } - else if( !Q_strcmp( c, "print" )) - { - // print command from somewhere - char *s = MSG_ReadString( msg ); - - Con_Printf( S_CYAN "r:" S_DEFAULT " %s", s ); - if( !COM_CheckStringEmpty( s ) || s[Q_strlen( s ) - 1] != '\n' ) - Con_Printf( "\n" ); - } - else if( !Q_strcmp( c, "testpacket" )) - { - byte recv_buf[NET_MAX_FRAGMENT]; - dword crcValue; - int realsize; - dword crcValue2 = 0; - - // this message only used during connection - // it doesn't make sense after client_connect - if( cls.state != ca_connecting ) - return; - - if( !CL_IsFromConnectingServer( from )) - return; - - crcValue = MSG_ReadLong( msg ); - realsize = MSG_GetMaxBytes( msg ) - MSG_GetNumBytesRead( msg ); - - if( cls.max_fragment_size != MSG_GetMaxBytes( msg )) - { - if( cls.connect_retry >= CL_TEST_RETRIES ) - { - // too many fails use default connection method - Con_Printf( "hi-speed connection is failed, use default method\n" ); - CL_SendGetChallenge( from ); - Cvar_SetValue( "cl_dlmax", FRAGMENT_DEFAULT_SIZE ); - cls.connect_time = host.realtime; - return; - } - - // if we waiting more than cl_timeout or packet was trashed - cls.connect_time = MAX_HEARTBEAT; - return; // just wait for a next responce - } - - // reading test buffer - MSG_ReadBytes( msg, recv_buf, realsize ); - - // procssing the CRC - CRC32_ProcessBuffer( &crcValue2, recv_buf, realsize ); - - if( crcValue == crcValue2 ) - { - // packet was sucessfully delivered, adjust the fragment size and get challenge - - Con_DPrintf( "CRC %x is matched, get challenge, fragment size %d\n", crcValue, cls.max_fragment_size ); - CL_SendGetChallenge( from ); - Cvar_SetValue( "cl_dlmax", cls.max_fragment_size ); - cls.connect_time = host.realtime; + // user out of band message (must be handled in SV_ConnectionlessPacket) + if( len > 0 ) + Netchan_OutOfBand( NS_SERVER, from, len, (byte *)buf ); } else { - if( cls.connect_retry >= CL_TEST_RETRIES ) - { - // too many fails use default connection method - Con_Printf( "hi-speed connection is failed, use default method\n" ); - CL_SendGetChallenge( from ); - Cvar_SetValue( "cl_dlmax", FRAGMENT_MIN_SIZE ); - cls.connect_time = host.realtime; - return; - } - - Msg( "got testpacket, CRC mismatched 0x%08x should be 0x%08x, trying next fragment size %d\n", crcValue2, crcValue, cls.max_fragment_size >> 1 ); - - // trying the next size of packet - cls.connect_time = MAX_HEARTBEAT; + Con_DPrintf( S_ERROR "bad connectionless packet from %s:\n%s\n", NET_AdrToString( from ), args ); } } - else if( !Q_strcmp( c, "ping" )) - { - // ping from somewhere - Netchan_OutOfBandPrint( NS_CLIENT, from, "ack" ); - } - else if( !Q_strcmp( c, "challenge" ) || !Q_strcmp( c, S2C_CHALLENGE )) - { - // this message only used during connection - // it doesn't make sense after client_connect - if( cls.state != ca_connecting ) - return; - - if( !CL_IsFromConnectingServer( from )) - return; - - // try to autodetect protocol by challenge response - if( !Q_strcmp( c, S2C_CHALLENGE )) - cls.legacymode = PROTO_GOLDSRC; - - // challenge from the server we are connecting to - CL_SendConnectPacket( cls.legacymode, Q_atoi( Cmd_Argv( 1 ))); - return; - } - else if( !Q_strcmp( c, "echo" )) - { - if( !CL_IsFromConnectingServer( from )) - return; - - // echo request from server - Netchan_OutOfBandPrint( NS_CLIENT, from, "%s", Cmd_Argv( 1 )); - } - else if( !Q_strcmp( c, "disconnect" )) - { - // this message only used during connection - // it doesn't make sense after client_connect - if( cls.state != ca_connecting ) - return; - - if( !CL_IsFromConnectingServer( from )) - return; - - // a disconnect message from the server, which will happen if the server - // dropped the connection but it is still getting packets from us - CL_Disconnect_f(); - } - else if( !Q_strcmp( c, "errormsg" ) || c[0] == S2C_REJECT || c[0] == S2C_REJECT_BADPASSWORD ) - { - char formatted_msg[MAX_VA_STRING]; - - if( !CL_IsFromConnectingServer( from )) - return; - - args = MSG_ReadString( msg ); - if( c[0] == S2C_REJECT || c[0] == S2C_REJECT_BADPASSWORD ) - args++; // skip one byte - - Q_snprintf( formatted_msg, sizeof( formatted_msg ), "^3Server message^7\n%s", args ); - - // in case we're in console or it's classic mainui which doesn't support messageboxes - if( !UI_IsVisible() || !UI_ShowMessageBox( formatted_msg )) - Msg( "%s\n", formatted_msg ); - - CL_Disconnect_f(); - } - else if( !Q_strcmp( c, "updatemsg" )) - { - // got an update message from master server - // show update dialog from menu - netadr_t adr; - qboolean preferStore = true; - - if( !Q_strcmp( Cmd_Argv( 1 ), "nostore" ) ) - preferStore = false; - - // trust only hardcoded master server - if( NET_StringToAdr( MASTERSERVER_ADR, &adr ) ) - { - if( NET_CompareAdr( from, adr )) - { - UI_ShowUpdateDialog( preferStore ); - } - } - else - { - // in case we don't have master anymore - UI_ShowUpdateDialog( preferStore ); - } - } - else if( !Q_strcmp( c, "f" )) - { - if( !NET_IsMasterAdr( from )) - { - Con_Printf( S_WARN "unexpected server list packet from %s\n", NET_AdrToString( from )); - return; - } - - // check the extra header - if( MSG_ReadByte( msg ) == 0x7f ) - { - uint32_t key = MSG_ReadDword( msg ); - - if( cls.internetservers_key != key ) - { - Con_Printf( S_WARN "unexpected server list packet from %s (invalid key)\n", NET_AdrToString( from )); - return; - } - - MSG_ReadByte( msg ); // reserved byte - } - else - { - Con_Printf( S_WARN "invalid server list packet from %s (missing extra header)\n", NET_AdrToString( from )); - return; - } - - // serverlist got from masterserver - while( MSG_GetNumBitsLeft( msg ) > 8 ) - { - uint8_t addr[16]; - - if( from.type6 == NA_IP6 ) // IPv6 master server only sends IPv6 addresses - { - MSG_ReadBytes( msg, addr, sizeof( addr )); - NET_IP6BytesToNetadr( &servadr, addr ); - servadr.type6 = NA_IP6; - } - else - { - MSG_ReadBytes( msg, servadr.ip, sizeof( servadr.ip )); // 4 bytes for IP - servadr.type = NA_IP; - } - servadr.port = MSG_ReadShort( msg ); // 2 bytes for Port - - // list is ends here - if( !servadr.port ) - break; - - NET_Config( true, false ); // allow remote - Netchan_OutOfBandPrint( NS_CLIENT, servadr, "info %i", PROTOCOL_VERSION ); - } - - if( cls.internetservers_pending ) - { - UI_ResetPing(); - cls.internetservers_pending = false; - } - } - else if( clgame.dllFuncs.pfnConnectionlessPacket( &from, args, buf, &len )) - { - // user out of band message (must be handled in CL_ConnectionlessPacket) - if( len > 0 ) Netchan_OutOfBand( NS_SERVER, from, len, (byte *)buf ); - } - else Con_DPrintf( S_ERROR "bad connectionless packet from %s:\n%s\n", NET_AdrToString( from ), args ); } /* diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 98814f99..c525d659 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -343,12 +343,6 @@ extern const char *const clc_strings[clc_lastmsg+1]; #define clc_goldsrc_requestcvarvalue2 11 #define clc_goldsrc_lastmsg 11 -#define S2C_REJECT_BADPASSWORD '8' -#define S2C_REJECT '9' -#define S2C_CHALLENGE "A00000000" -#define S2C_CONNECTION "B" -#define A2C_PRINT 'l' - #define MAX_GOLDSRC_BACKUP_CMDS 8 #define MAX_GOLDSRC_TOTAL_CMDS 16 #define MAX_GOLDSRC_MODEL_BITS 10 @@ -358,4 +352,55 @@ extern const char *const clc_strings[clc_lastmsg+1]; #define MAX_GOLDSRC_EDICTS ( BIT( MAX_ENTITY_BITS ) + ( MAX_CLIENTS * 15 )) #define LAST_GOLDSRC_EDICT ( BIT( MAX_ENTITY_BITS ) - 1 ) + +// from any to any (must be handled on both server and client) + +#define A2A_PING "ping" // reply with A2A_ACK +#define A2A_ACK "ack" // no-op +#define A2A_INFO "info" // different format for client and server, see code +#define A2A_NETINFO "netinfo" // different format for client and server, see code +#define A2A_GOLDSRC_PING "i" // reply with A2A_GOLDSRC_ACK +#define A2A_GOLDSRC_ACK "j" // no-op + +// from any to server +#define A2S_GOLDSRC_INFO 'T' +#define A2S_GOLDSRC_RULES 'V' +#define A2S_GOLDSRC_PLAYERS 'U' + +// from server to any +#define S2A_GOLDSRC_INFO 'I' +#define S2A_GOLDSRC_RULES 'E' +#define S2A_GOLDSRC_PLAYERS 'D' + +// from master to server +#define M2S_CHALLENGE "s" +#define M2S_NAT_CONNECT "c" + +// from server to master +#define S2M_INFO "0\n" + +// from client to server +#define C2S_BANDWIDTHTEST "bandwidth" +#define C2S_GETCHALLENGE "getchallenge" +#define C2S_CONNECT "connect" +#define C2S_RCON "rcon" + +// from server to client +#define S2C_BANDWIDTHTEST "testpacket" +#define S2C_CHALLENGE "challenge" +#define S2C_CONNECTION "client_connect" +#define S2C_ERRORMSG "errormsg" +#define S2C_REJECT "disconnect" +#define S2C_GOLDSRC_REJECT_BADPASSWORD '8' +#define S2C_GOLDSRC_REJECT '9' +#define S2C_GOLDSRC_CHALLENGE "A00000000" +#define S2C_GOLDSRC_CONNECTION "B" + +// from any to client +#define A2C_PRINT "print" +#define A2C_GOLDSRC_PRINT 'l' + +// from master to client +#define M2A_SERVERSLIST "f" + #endif//NET_PROTOCOL_H diff --git a/engine/server/server.h b/engine/server/server.h index 4d7fa41e..f5d68943 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -683,6 +683,6 @@ int SV_LightForEntity( edict_t *pEdict ); // // sv_query.c // -qboolean SV_SourceQuery_HandleConnnectionlessPacket( const char *c, netadr_t from ); +void SV_SourceQuery_HandleConnnectionlessPacket( const char *c, netadr_t from ); #endif//SERVER_H diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index edaff653..ffe4d777 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -131,7 +131,7 @@ static void SV_GetChallenge( netadr_t from ) } // send it back - Netchan_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, "challenge %i", svs.challenges[i].challenge ); + Netchan_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, S2C_CHALLENGE" %i", svs.challenges[i].challenge ); } static int SV_GetFragmentSize( void *pcl, fragsize_t mode ) @@ -194,9 +194,9 @@ void SV_RejectConnection( netadr_t from, const char *fmt, ... ) va_end( argptr ); Con_Reportf( "%s connection refused. Reason: %s\n", NET_AdrToString( from ), text ); - Netchan_OutOfBandPrint( NS_SERVER, from, "errormsg\n^1Server was reject the connection:^7 %s", text ); - Netchan_OutOfBandPrint( NS_SERVER, from, "print\n^1Server was reject the connection:^7 %s", text ); - Netchan_OutOfBandPrint( NS_SERVER, from, "disconnect\n" ); + Netchan_OutOfBandPrint( NS_SERVER, from, S2C_ERRORMSG"\n^1Server was reject the connection:^7 %s", text ); + Netchan_OutOfBandPrint( NS_SERVER, from, A2C_PRINT"\n^1Server was reject the connection:^7 %s", text ); + Netchan_OutOfBandPrint( NS_SERVER, from, S2C_REJECT"\n" ); } /* @@ -456,7 +456,7 @@ static void SV_ConnectClient( netadr_t from ) Info_SetValueForKeyf( protinfo, "ext", sizeof( protinfo ), "%d", newcl->extensions ); // send the connect packet to the client - Netchan_OutOfBandPrint( NS_SERVER, from, "client_connect %s", protinfo ); + Netchan_OutOfBandPrint( NS_SERVER, from, S2C_CONNECTION" %s", protinfo ); newcl->upstate = us_inactive; newcl->connection_started = host.realtime; @@ -604,7 +604,7 @@ void SV_KickPlayer( sv_client_t *cl, const char *fmt, ... ) SV_BroadcastPrintf( cl, "%s was kicked with message: \"%s\"\n", cl->name, buf ); SV_ClientPrintf( cl, "You were kicked from the game with message: \"%s\"\n", buf ); if( cl->useragent[0] ) - Netchan_OutOfBandPrint( NS_SERVER, cl->netchan.remote_address, "errormsg\nKicked with message:\n%s\n", buf ); + Netchan_OutOfBandPrint( NS_SERVER, cl->netchan.remote_address, S2C_ERRORMSG"\nKicked with message:\n%s\n", buf ); } else { @@ -612,7 +612,7 @@ void SV_KickPlayer( sv_client_t *cl, const char *fmt, ... ) SV_BroadcastPrintf( cl, "%s was kicked\n", cl->name ); SV_ClientPrintf( cl, "You were kicked from the game\n" ); if( cl->useragent[0] ) - Netchan_OutOfBandPrint( NS_SERVER, cl->netchan.remote_address, "errormsg\nYou were kicked from the game\n" ); + Netchan_OutOfBandPrint( NS_SERVER, cl->netchan.remote_address, S2C_ERRORMSG"\nYou were kicked from the game\n" ); } SV_DropClient( cl, false ); @@ -720,7 +720,7 @@ static void SV_FlushRedirect( netadr_t adr, int dest, char *buf ) switch( dest ) { case RD_PACKET: - Netchan_OutOfBandPrint( NS_SERVER, adr, "print\n%s", buf ); + Netchan_OutOfBandPrint( NS_SERVER, adr, A2C_PRINT"\n%s", buf ); break; case RD_CLIENT: if( !sv.current_client ) return; // client not set @@ -960,7 +960,23 @@ static void SV_Info( netadr_t from, int protocolVersion ) Info_SetValueForKey( s, "host", temp, sizeof( s )); } - Netchan_OutOfBandPrint( NS_SERVER, from, "info\n%s", s ); + Netchan_OutOfBandPrint( NS_SERVER, from, A2A_INFO"\n%s", s ); +} + +static void SV_ConnectNatClient( netadr_t from ) +{ + netadr_t to; + + if( !sv_nat.value || !NET_IsMasterAdr( from )) + return; + + if( !NET_StringToAdr( Cmd_Argv( 1 ), &to )) + return; + + if( NET_IsReservedAdr( to )) + return; + + SV_Info( to, PROTOCOL_VERSION ); } /* @@ -994,7 +1010,7 @@ static void SV_BuildNetAnswer( netadr_t from ) { // send error unsupported protocol Info_SetValueForKey( string, "neterror", "protocol", sizeof( string )); - Netchan_OutOfBandPrint( NS_SERVER, from, "netinfo %i %i %s\n", context, type, string ); + Netchan_OutOfBandPrint( NS_SERVER, from, A2A_NETINFO" %i %i %s\n", context, type, string ); return; } @@ -1065,19 +1081,7 @@ static void SV_BuildNetAnswer( netadr_t from ) break; } - Netchan_OutOfBandPrint( NS_SERVER, from, "netinfo %i %i %s\n", context, type, string ); -} - -/* -================ -SV_Ping - -Just responds with an acknowledgement -================ -*/ -static void SV_Ping( netadr_t from ) -{ - Netchan_OutOfBandPrint( NS_SERVER, from, "ack" ); + Netchan_OutOfBandPrint( NS_SERVER, from, A2A_NETINFO" %i %i %s\n", context, type, string ); } /* @@ -3148,17 +3152,14 @@ connectionless packets. */ void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) { - char *args; - const char *pcmd; - char buf[MAX_SYSPATH]; - int len = sizeof( buf ); + const char *pcmd, *args; // prevent flooding from banned address - if( SV_CheckIP( &from ) ) + if( SV_CheckIP( &from )) return; MSG_Clear( msg ); - MSG_ReadLong( msg );// skip the -1 marker + MSG_SeekToBit( msg, sizeof( uint32_t ) << 3, SEEK_CUR ); // skip the -1 marker args = MSG_ReadStringLine( msg ); Cmd_TokenizeString( args ); @@ -3168,29 +3169,79 @@ void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) if( sv_log_outofband.value ) Con_Reportf( "%s: %s : %s\n", __func__, NET_AdrToString( from ), pcmd ); - if( !Q_strcmp( pcmd, "ping" )) SV_Ping( from ); - else if( !Q_strcmp( pcmd, "ack" )) SV_Ack( from ); - else if( !Q_strcmp( pcmd, "info" )) SV_Info( from, Q_atoi( Cmd_Argv( 1 ))); - else if( !Q_strcmp( pcmd, "bandwidth" )) SV_TestBandWidth( from ); - else if( !Q_strcmp( pcmd, "getchallenge" )) SV_GetChallenge( from ); - else if( !Q_strcmp( pcmd, "connect" )) SV_ConnectClient( from ); - else if( !Q_strcmp( pcmd, "rcon" )) SV_RemoteCommand( from, msg ); - else if( !Q_strcmp( pcmd, "netinfo" )) SV_BuildNetAnswer( from ); - else if( !Q_strcmp( pcmd, "s" )) SV_AddToMaster( from, msg ); - else if( !Q_strcmp( pcmd, "i" )) NET_SendPacket( NS_SERVER, 5, "\xFF\xFF\xFF\xFFj", from ); // A2A_PING - else if( SV_SourceQuery_HandleConnnectionlessPacket( pcmd, from )) { } // function handles replies - else if( !Q_strcmp( pcmd, "c" ) && sv_nat.value && NET_IsMasterAdr( from )) + if( !svs.initialized ) { - netadr_t to; - if( NET_StringToAdr( Cmd_Argv( 1 ), &to ) && !NET_IsReservedAdr( to )) - SV_Info( to, PROTOCOL_VERSION ); + // only process rcon if server not initialized + if( !Q_strcmp( pcmd, C2S_RCON )) + SV_RemoteCommand( net_from, &net_message ); + + return; } - else if( svgame.dllFuncs.pfnConnectionlessPacket( &from, args, buf, &len )) + + if( pcmd[0] == A2S_GOLDSRC_INFO || pcmd[0] == A2S_GOLDSRC_PLAYERS || pcmd[0] == A2S_GOLDSRC_RULES ) { - // user out of band message (must be handled in CL_ConnectionlessPacket) - if( len > 0 ) Netchan_OutOfBand( NS_SERVER, from, len, (byte*)buf ); + SV_SourceQuery_HandleConnnectionlessPacket( pcmd, from ); + } + else if( !Q_strcmp( pcmd, A2A_NETINFO )) + { + SV_BuildNetAnswer( from ); + } + else if( !Q_strcmp( pcmd, A2A_INFO )) + { + SV_Info( from, Q_atoi( Cmd_Argv( 1 ))); + } + else if( !Q_strcmp( pcmd, M2S_CHALLENGE )) + { + SV_AddToMaster( from, msg ); + } + else if( !Q_strcmp( pcmd, M2S_NAT_CONNECT )) + { + SV_ConnectNatClient( from ); + } + else if( !Q_strcmp( pcmd, C2S_BANDWIDTHTEST )) + { + SV_TestBandWidth( from ); + } + else if( !Q_strcmp( pcmd, C2S_GETCHALLENGE )) + { + SV_GetChallenge( from ); + } + else if( !Q_strcmp( pcmd, C2S_CONNECT )) + { + SV_ConnectClient( from ); + } + else if( !Q_strcmp( pcmd, A2A_PING )) + { + Netchan_OutOfBandPrint( NS_SERVER, from, A2A_ACK ); + } + else if( !Q_strcmp( pcmd, A2A_GOLDSRC_PING )) + { + Netchan_OutOfBandPrint( NS_SERVER, from, A2A_GOLDSRC_ACK ); + } + else if( !Q_strcmp( pcmd, C2S_RCON )) + { + SV_RemoteCommand( from, msg ); + } + else if( !Q_strcmp( pcmd, A2A_ACK ) || !Q_strcmp( pcmd, A2A_GOLDSRC_ACK )) + { + SV_Ack( from ); + } + else + { + char buf[MAX_SYSPATH]; + int len = sizeof( buf ); + + if( svgame.dllFuncs.pfnConnectionlessPacket( &from, args, buf, &len )) + { + // user out of band message (must be handled in CL_ConnectionlessPacket) + if( len > 0 ) + Netchan_OutOfBand( NS_SERVER, from, len, (byte*)buf ); + } + else + { + Con_DPrintf( S_ERROR "bad connectionless packet from %s:\n%s\n", NET_AdrToString( from ), args ); + } } - else Con_DPrintf( S_ERROR "bad connectionless packet from %s:\n%s\n", NET_AdrToString( from ), args ); } /* diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index adf18309..edd62259 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -973,7 +973,7 @@ static void SV_GenerateTestPacket( void ) // write packet base data MSG_Init( &svs.testpacket, "BandWidthTest", svs.testpacket_buf, maxsize ); MSG_WriteLong( &svs.testpacket, -1 ); - MSG_WriteString( &svs.testpacket, "testpacket" ); + MSG_WriteString( &svs.testpacket, S2C_BANDWIDTHTEST ); svs.testpacket_crcpos = svs.testpacket.pData + MSG_GetNumBytesWritten( &svs.testpacket ); MSG_WriteDword( &svs.testpacket, 0 ); // to be changed by crc diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index f8e68f06..2701f972 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -389,23 +389,7 @@ static void SV_ReadPackets( void ) // check for connectionless packet (0xffffffff) first if( MSG_GetMaxBytes( &net_message ) >= 4 && *(int *)net_message.pData == -1 ) { - if( !svs.initialized ) - { - char *args; - const char *c; - - MSG_Clear( &net_message ); - MSG_ReadLong( &net_message );// skip the -1 marker - - args = MSG_ReadStringLine( &net_message ); - Cmd_TokenizeString( args ); - c = Cmd_Argv( 0 ); - - if( !Q_strcmp( c, "rcon" )) - SV_RemoteCommand( net_from, &net_message ); - } - else SV_ConnectionlessPacket( net_from, &net_message ); - + SV_ConnectionlessPacket( net_from, &net_message ); continue; } @@ -730,7 +714,7 @@ Master will validate challenge and this server to public list void SV_AddToMaster( netadr_t from, sizebuf_t *msg ) { uint challenge, challenge2, heartbeat_challenge; - char s[MAX_INFO_STRING] = "0\n"; // skip 2 bytes of header + char s[MAX_INFO_STRING] = S2M_INFO; // skip 2 bytes of header int clients, bots; double last_heartbeat; const int len = sizeof( s ); diff --git a/engine/server/sv_query.c b/engine/server/sv_query.c index f7f15ca4..636309d8 100644 --- a/engine/server/sv_query.c +++ b/engine/server/sv_query.c @@ -16,15 +16,6 @@ GNU General Public License for more details. #include "common.h" #include "server.h" -#define SOURCE_QUERY_DETAILS 'T' -#define SOURCE_QUERY_DETAILS_RESPONSE 'I' - -#define SOURCE_QUERY_RULES 'V' -#define SOURCE_QUERY_RULES_RESPONSE 'E' - -#define SOURCE_QUERY_PLAYERS 'U' -#define SOURCE_QUERY_PLAYERS_RESPONSE 'D' - /* ================== SV_SourceQuery_Details @@ -41,7 +32,8 @@ static void SV_SourceQuery_Details( netadr_t from ) MSG_Init( &buf, "TSourceEngineQuery", answer, sizeof( answer )); - MSG_WriteByte( &buf, SOURCE_QUERY_DETAILS_RESPONSE ); + MSG_WriteDword( &buf, 0xFFFFFFFFU ); + MSG_WriteByte( &buf, S2A_GOLDSRC_INFO ); MSG_WriteByte( &buf, PROTOCOL_VERSION ); MSG_WriteString( &buf, hostname.string ); @@ -69,7 +61,7 @@ static void SV_SourceQuery_Details( netadr_t from ) MSG_WriteByte( &buf, GI->secure ); MSG_WriteString( &buf, XASH_VERSION ); - Netchan_OutOfBand( NS_SERVER, from, MSG_GetNumBytesWritten( &buf ), MSG_GetData( &buf )); + NET_SendPacket( NS_SERVER, MSG_GetNumBytesWritten( &buf ), MSG_GetData( &buf ), from ); } /* @@ -87,7 +79,8 @@ static void SV_SourceQuery_Rules( netadr_t from ) MSG_Init( &buf, "TSourceEngineQueryRules", answer, sizeof( answer )); - MSG_WriteByte( &buf, SOURCE_QUERY_RULES_RESPONSE ); + MSG_WriteDword( &buf, 0xFFFFFFFFU ); + MSG_WriteByte( &buf, S2A_GOLDSRC_RULES ); pos = MSG_GetNumBitsWritten( &buf ); MSG_WriteShort( &buf, 0 ); @@ -117,7 +110,7 @@ static void SV_SourceQuery_Rules( netadr_t from ) MSG_SeekToBit( &buf, pos, SEEK_SET ); MSG_WriteShort( &buf, cvar_count ); - Netchan_OutOfBand( NS_SERVER, from, total, MSG_GetData( &buf )); + NET_SendPacket( NS_SERVER, total, MSG_GetData( &buf ), from ); } } @@ -139,7 +132,8 @@ static void SV_SourceQuery_Players( netadr_t from ) MSG_Init( &buf, "TSourceEngineQueryPlayers", answer, sizeof( answer )); - MSG_WriteByte( &buf, SOURCE_QUERY_PLAYERS_RESPONSE ); + MSG_WriteDword( &buf, 0xFFFFFFFFU ); + MSG_WriteByte( &buf, S2A_GOLDSRC_PLAYERS ); pos = MSG_GetNumBitsWritten( &buf ); MSG_WriteByte( &buf, 0 ); @@ -168,7 +162,7 @@ static void SV_SourceQuery_Players( netadr_t from ) MSG_SeekToBit( &buf, pos, SEEK_SET ); MSG_WriteByte( &buf, count ); - Netchan_OutOfBand( NS_SERVER, from, total, MSG_GetData( &buf )); + NET_SendPacket( NS_SERVER, total, MSG_GetData( &buf ), from ); } } @@ -177,24 +171,18 @@ static void SV_SourceQuery_Players( netadr_t from ) SV_SourceQuery_HandleConnnectionlessPacket ================== */ -qboolean SV_SourceQuery_HandleConnnectionlessPacket( const char *c, netadr_t from ) +void SV_SourceQuery_HandleConnnectionlessPacket( const char *c, netadr_t from ) { switch( c[0] ) { - case SOURCE_QUERY_DETAILS: + case A2S_GOLDSRC_INFO: SV_SourceQuery_Details( from ); - return true; - - case SOURCE_QUERY_RULES: + break; + case A2S_GOLDSRC_RULES: SV_SourceQuery_Rules( from ); - return true; - - case SOURCE_QUERY_PLAYERS: + break; + case A2S_GOLDSRC_PLAYERS: SV_SourceQuery_Players( from ); - return true; - - default: - return false; + break; } - return false; }